—    —  Home

   jamesprimate on August 08, 2014, 04:28:15 AM:

An example would be that a dumb green lizard probably shouldn't be able to be too clever about how you're able to climb and it's not. Instead it should assume that you have the same limitations it has, and emulate your ghosts with its own terrain preferences. That way when you climb up and away from the area it's searching, the proper reaction should be confusion.

classic green lizards!

very cool visualization. how do you suppose that system scales? it is something light-weight enough to run, say, 15-20 instances each with 15 ghosts?





   JLJac on August 08, 2014, 08:05:43 AM (Last Edit: August 08, 2014, 08:18:35 AM):

I think so! Also I've noticed that the search behavior doesn't change much from 2 ghosts and up, so I think 3 or 4 ghosts would be just fine. Also, for creatures that the tracking creature doesn't have much interest in (neither prey nor threat) tracking just 1 ghost or none at all will save some space, if that becomes an issue.

Update 285
Made the ghosts work between different rooms.

Created a system which gives ghosts a "second chance" before dismissing them on visual contact - this second chance consists of scurrying away very quickly hoping to find an obscured tile. This comes in handy when a ghost is sitting for example next to a short cut entrance, in that case it makes more sense to just move the ghost in through the shortcut when it's seen, rather than remove it. Its score is however downgraded, so the hypothesis of that particular ghost is considered less likely.

When I had this "second chance" system in place, I decided to have that apply to the last existing ghost always, and never delete it. This means that the tracking creature will always have some theory about where the tracked creature might be, though the hypothesis is considered more and more unlikely.

By having the lizard follow the "best guess" ghost of the player, I managed to have it display some pretty cool search behavior.



The little pink number is the estimated chance of relocating the player - this will play a part in actual AI decisions later, such as when it's time to abandon a hunt. The chance is calculated using an average number between the amount of ticks since the player was last spotted and the lowest generation among the ghosts (times ten). This means it takes both pure time and how much effort it has actually put into looking for the player into account.

Inspired by the Utility Based AI stuff, I made this curve for evaluating that probability:



Up until the intersection (45 in the image) it follows the red curve, after that the blue one. This means that during the very first seconds it will estimate the chance of re-locating the creature as pretty high, then it will start to drop quickly. Then the blue curve kicks in, which never actually reaches 0, just gets closer and closer to it.

The idea is that the Tracker will always be able to suggest some small chance of finding any given creature and a coordinate to start looking, even though the search is more or less futile. This way I can make sure that all the decisions are actually made in the decision making AI. If the Tracker could "give up" that would essentially mean that the Tracker made the decision to stop looking for that creature, which isn't its job.

Here's a gif of the lizard prototype trying to search for a player that has been removed from the map. The estimated probability goes down, but it could theoretically continue this search for ever and ever unless told otherwise. Note how the search is first focused to the nearby area to where the creature was last spotted, but later moves on to further away areas as all of those options are exhausted.






   jamesprimate on August 09, 2014, 09:08:26 AM:

I love it when devlogs get real like this, with people breaking out the the graphs and the log and the curve functions





   JLJac on August 11, 2014, 08:02:09 AM:

Oh, but the point is that after the plateaux it should become a 1/x type of curve, that approaches 0 but never gets to it ... I really love your graph tool! Which one is it?

Update 286
Today wasn't the best day, as I spent quite a lot of it messing with flight tickets and online payment and stuff. Still got somewhere though! Tied together some lose ends in the Tracker module, and fixed a few errors where it would throw null reference exceptions. Also made it able to call out to its parent AI when a creature is spotted, with a distinction between first time spottings and spotting a creature that it's already aware of.

With this a pretty cool mile stone is reached - the shared AI assets are done! That would be the Path Finder and the Tracker, which are AI modules that are going to be used by pretty much every AI creature. There's an asterisk after done, as they will be revisited to accommodate for the needs of the individual creatures, but they are done as far as they can be before starting on making the creatures. This means that the next step is a step away from these boring generic functions - now I can actually start creating some content! The first creature up is the lizard, I imagine. Stay tuned!





   JLJac on August 12, 2014, 08:08:43 AM:

Update 287
Lizard animations are slowly coming together. But also I spent 40 minutes chasing my own tail (WHHHHHYYYYYYY DOEEEEEEESSSSS THISSSSSS VARRRRRIIIIABLLLEEEE CHAAAAAAAAAAAANNNNGEEEE!!!?!?!?!?!?)before finding out that I had put a 1 instead of a 2 in a specific spot - the line was copied from another place and I hadn't gotten around to change that specific digit. By the time I found out I had commented out pretty much every line in the entire game, disabling physics, gravity, and the very force that holds creatures together. The game was reduced to some floating body parts drifting in and out through the walls. So then I had to repair everything after my furious bug hunt.

It's crazy how focused I get when something like that happens... Nothing matters except squashing that one bug.

Speaking of bugs, another old friend has returned: when enabling the lizard flip (when they turn 180 degrees on a flat floor) the good old behavior where they just LOVE to do that flip over and over has resurfaced. They'll see me, do a flip for fun, another flip in the other direction, then one more just for the hell of it, and then they need to do a last one so they'll actually face the correct direction before they start moving. It looks ridiculous. Giving the flip animation a pathing weight corresponding to walking seven times around the world doesn't seem to work, they just love the flipping all that much. Last time I came up with some weird solution where I wouldn't allow it to flip close to where it flipped the last time, but that was a hack, this time I'll try to find out what's actually going on...





   jamesprimate on August 12, 2014, 07:55:59 PM:

id say thats a fair assessment





   JLJac on August 13, 2014, 11:34:46 AM:

98% autobiographic.

Update 288
Ok, got some good progress today, even though it isn't much to look at. Quite a lot of the lizard animations are in now - it's able to walk, climb, crawl, turn on flat surfaces, reach across gaps horizontally and vertically, and drop down to floors below it (though not very gracefully). Coming up is the ability to drop to pipes and grab them mid air, this always fails miserably as it is. I might have to nail the upper body to the grab spot for a few frames so the lower body can do a cool swinging motion before the lizard gains control and gets up on the pipe.

What's cool here is that the creature is made into a physical object with forces pulling it along, rather than just a ghost like sprite floating along. It's really fun already to throw spears at it and see how it tries to re-adjust and find new footing to continue moving.

The body physics are going to be cooler than in the old game. Whenever no body segment is inside a tile that's considered legal to that lizard, a "framesInLegalTerrain" counter is set to 0. When any segment is legal, it starts ticking.

If this "framesInLegalTerrain" is above 10 (numbers are subject to change/diversity between lizard breeds) the physics behave differently - it is assumed that the lizard is in total control of its situation, and the velocities of the body segments are slowed down each frame, and they aren't affected by gravity. The body basically "floats" around. If the value is less than 10 though, the body behaves as any physical body in the game, and can be tossed around the level. Basically I'm trying to get a good balance between the lizard being an agent and a physical body, where it's supposed to behave like a little bit of both.

In this gif I'm tossing a lizard around as it tries to traverse this courtyard - whenever I press a button it's pulled towards the mouse pointer. The idea is that it should be able to continue moving towards its goal despite being antagonized like this - it shouldn't get stuck or be unable to adapt to a new situation.






   JLJac on August 14, 2014, 11:10:39 AM:

Update 289
Lizard movement is now basically working as I intended it to. Dropping from ledges, grabbing, and all that stuff is in and reasonably cool looking for pink boxes.

Other than that efforts have been somewhat unfocused today - I've started many things and finished few. Flipping back and forth has been sorted by having the lizard have a counter set to something like 2 seconds when flipping, and during that time the path finder will assign the flip move a last resort status.

The embryo of an engine that will understand when the creature has gotten stuck is started on but not more. The idea here is that when a creature gets stuck it will be able to understand where and possibly even why it got stuck, and then be able to actually do something else rather than just trying the same thing over and over. It's actually a little bit of learning, but it will basically boil down to "not getting stuck as much" rather than anything mind blowing. More on that when I actually get to it.

The last unfinished effort of the day was starting on the system for dividing the lizard into separate breeds. This took some thinking; where to put the division in the code? The current solution is that each lizard breed has its own Creature Template, all of which inherit from a parent Creature Template, and that they also own a Type Object containing some breed-specific parameters. We'll see how that works out!





   JLJac on August 15, 2014, 11:01:21 AM:

Update 290
Got some lizard breeds in! It's funny how just some small differences in speed, size etc drastically change the personalities you project on these boxes. They still suffer from quite a lot of getting stuck everywhere, but it's getting there!



Also, these inheritance based generalized systems... wow. Having laid out the general frame work, it really is super fast to define stuff! If I'm going to want another lizard breed down the line, it'll be like a day to add it!





   jamesprimate on August 15, 2014, 07:49:36 PM:

Update 290
Got some lizard breeds in! It's funny how just some small differences in speed, size etc drastically change the personalities you project on these boxes. They still suffer from quite a lot of getting stuck everywhere, but it's getting there!



Also, these inheritance based generalized systems... wow. Having laid out the general frame work, it really is super fast to define stuff! If I'm going to want another lizard breed down the line, it'll be like a day to add it!

Joar, you don't even know how happy this gif makes me  Tears of Joy





   JLJac on August 16, 2014, 01:17:37 AM (Last Edit: August 16, 2014, 01:25:51 AM):

Joar, you don't even know how happy this gif makes me  Tears of Joy
Hand Thumbs Up Left Cool

It's looking more and more like Rain World with every update!

Quick question though, it seems like for the A.I./Slugcat/Pathfinding/etc you are creating new solutions, completely different from the Lingo build. My question is, why? Is it because you're using Unity now? Or maybe that you realized that the Lingo version was inefficient? (I've been wondering this for a while now, so I hope it's not confusing Tongue)
The reason why I'm re-working a lot of the solutions is that in the old build, I never intended to have more content. Everything I added was "the last thing before I'm gonna ship this", meaning that I never did generally applicable solutions, only patch work and quick fixes for whatever I had in front of me at the time. In the end the bulk of the game ended up being band-aids on band-aids, meaning that the machinery worked alright, but whenever something was to be changed or added, it was a mess.

This time around, I'm making generalized systems for everything, such as path finding. This means that down the line, if I want to for example add a creature, I can just plug in the path finder, and bam, it's up and running (after three hours of tweaking, so maybe not BAM, but way faster than building it from the ground up). If there is a bug in said path finder, it will have to be fixed in just one place, rather than one for each creature.

And when I'm at it, of course I'm taking the re-write as an opportunity to do some optimizations. I'm not really "using Unity" from how I understand it, I'm using it for rendering sprites and I use some of its C# library, but I'm not doing anything in the Unity editor. However I am converting the game, from Lingo to C#, so while I can glance at the old code I in fact have to re-write everything. For the slugcat I did a straight port of the old movement behavior, but with the lizard I'm not quite as thrilled about the old physics, so I'm taking this opportunity to do some changes.

Edit: Oh, and a question for you seasoned programmers out there - I think I have an infinite loop somewhere in my code. Unity just freezes, and it's really hard to find what's causing it because each time I have to force exit with the task manager and restart everything. It's somewhere in the lizard code, because it only happens when there are lizards... Are there any ways to find infinite loops? Can you for example tell the monodevelop debugger to throw an exception if any loop has run more than 1000 times, so I can find where this thing is hiding?





   jamesprimate on August 18, 2014, 01:56:36 AM:

the thing that was posted was WAAAAY old (like, years) and wouldnt be any sort of representation. even the builds we're showing at PAX and such are 6-7 months old at this point and in a completely other programming language! a lot has changed, so let us get rain world to a point where we are proud to show it!





   JLJac on August 18, 2014, 06:45:32 AM:

Absolutely  Grin

Update 291
Found that darn bug! Thanks to Gimym JIMBERT, as always! In one place I made a division with a counter that could, at times, be 0. Contrary to throwing an error, as I'm used to from director, it set the value to NaN. In this case that value was the vertical velocity, which was then applied to the vertical position, and then at the end of the frame when it wanted to check terrain collision it had to loop through all the tiles from negative infinity to where the object was at, and that's the infinite loop. It wasn't really infinite though, given a few years it might have completed  Cheesy

Other than that, further work on assorted anti-getting-stuck measures. The current task - if having moved into a dead end, the lizard should start moving backwards, and continue moving backwards through bends and corners until it reaches an intersection and can pick another direction. Proved to be somewhat tricky, but I have a few ideas!





   JLJac on August 19, 2014, 11:32:54 AM:

Update 292
Finally I've gotten to work on the lizard cosmetics!



There is some kind of tail now!

James and I have also been having a few talks today. One of them was about was about individual variation on creatures. We both like the idea that each creature should have slight individual deviances, probably from a random seed, to make them look less like clones.

The problem as I see it is this - we have the breeds of lizards. The idea is that the player should be able to learn the behavior of each breed. "Greens are slow and dumb" for example, or "Blues can't take much abuse, but can climb walls". What I worry about is two scenarios. One is that individual differences drowns the breed traits, so that everything becomes a random sludge. The other is that in order to not end up in the random sludge, we will need to make the individual traits so very small that they are actually not significant in any way, and then the effort was in vain.

One solution is to have the differences be purely cosmetic. Short tail, long tail, that sort of stuff. This is safe, so I think we might do this kind of stuff regardless.

Another is to have somewhat rare, but very clear unique individuals that appear late in the game when you've already come to know the categories. That would be stuff like "It is very clearly a blue lizard in every part of its behavior, but it's large as a green lizard". The kind of stuff that can be easily understood by an experienced player. This solution would probably take on a bit of a flavor of "bosses", as these deviant individuals are likely going to be mostly harder than regulars, and the element of unfamiliarity and surprise will play in their favor either way.

The jury is still out on this one, and luckily the jury probably have quite a lot of time to think about it before it's time for an implementation. But it's never a bad thing to plan ahead!

What do you guys think?





   JLJac on August 20, 2014, 10:27:29 AM:

Stop me before we get there!

Update 293
Feet are in! And some more sophisticated tail animation.

This time around I decided I wanted to give the lizard breeds more characteristic walks. They are all a tad slow at this point I think, that is a gameplay balance question that will be tweaked a lot later. Quite a lot of the stuff that's supposed to be in isn't yet - for example they are supposed to move with different efficiency when occupying different terrain (pink can climb on poles, but it's somewhat slow and clumsy) and when moving in different directions (green is considerably slower in any vertical movement that requires it to lift its heavy body).

Here's the pink lizard with legs. It's a bit boring and anonymous in my opinion - which it's partly supposed to be as pink is the default, but it could have something more going on, definitely. Remember that this is just movement through the terrain, so they still don't have much to work with to convey personality.




Here's the green one. It's heavier, slower, and moving more unevenly. It also lifts its feet with each step, sometimes resting on its belly for a moment between steps. Green is also supposed to have a stiffer tail than the others, to give it a bit of an alligator feel contrary to the soft gecko bodies of most of them. Can you tell?




Blue has a rapid, even, smooth movement where all four limbs are constantly working. You might also notice how it's able to grab on to walls, and how it is more agile and more inclined to climb.




So much fun! I was dying to get back to this kind of stuff :D The feet still have some weird flickering at times, and I'm not sure how happy I am about when the legs overlap each other perfectly, so there is still tweaking to do. For fun, here's one of them moving together:



You might notice that the interactions still feel a little blocky and uneven. There are some getting stuck scenarios left, especially as the lizards are not aware of each other at this point. When two of them get in a deadlock pushing against each other they have no protocol for what to do in that situation. But you can see some interesting stuff too, such as how the green one is way heavier than the blue one and pushes it out of the way without any impact on its own movement. In the future I hope to create a really interesting dynamic between lizards, having them getting annoyed with each other and whatnot.





   jamesprimate on August 20, 2014, 10:42:36 AM:

this is so massively exciting. i seriously cant believe this is ONE DAY after that last post.





   JLJac on August 20, 2014, 11:18:36 AM:

Yeah, this is not behavior, just movement. They have no AI at this point, so your observation is correct  Hand Thumbs Up Right





   jamesprimate on August 20, 2014, 03:02:24 PM:

one thing joar and i were talking about earlier is the difference in stride between the lingo lizards and these.

previously, locomotion was done via a sort of faked "ghost pull" situation, meaning that the lizard walk was basically the body being dragged along the ground. now in the new version there are nice actual legs to walk on, and so the stance and stride of the lizard has changed a little. HAS SCIENCE GONE TOO FAR??

this is all adjustable though, so your guys opinion on the "feel" of new lizards stride vs the old could be really helpful!

to me the new lizards look much more agile, which could make them appear even more threatening (and thats a good thing of course!) maybe a mix of some low and some high? keep the slower lizards close to the ground and the faster ones with the more agile stance? wot u think





   JLJac on August 21, 2014, 06:17:16 AM:

@Loren Schmidt, yeah, that sounds a lot like what I've been trying to do, but I might have to throw a few more counters in there to get a good rhythm to it o.0 Are you doing procedural animation too?

@Kytin, interesting idea! Maybe we could keep physical traits and "personality" separate, and only have the latter be individual...

Update 294

I made a couple of pretty big infra structure changes, such as moving all of the lizard breed definition stuff to the same place. Now there's a static class which is fed a lizard color and returns the complete template, all of it based on one huge enum switch. Maybe not the most elegant solution, but it works flawlessly and is only supposed to run once when the game is started up.

Also got the thing where lizards have different speeds in different terrain and when moving in different directions down. At this point it averages between the terrain type that each of the three body chunks occupy.

Oh, and sometimes you just have to do something that doesn't make any sense to the schedule, but you want to do just for fun! So I made the lizards' limbs have health, and go limp and useless if they're too low.



This is going to be really cool later, when spears and stuff is in... Note how one of the front legs is sometimes working, sometimes not - when they have low health, but not 0, they're switched on and off a bit like that.

The other thing I got started on today was an obstacle tracker. Because of the physics based nature of RW, not everything is very predictable, and even after me trying to eliminate all the getting stuck scenarios, there will be some obscure getting stuck scenarios left. The obstacle tracker isn't only there to patch over problems I'm too lazy to fix, though Wink It plays a part in making the NPC's seem more intelligent over all, and makes them able to account for some weird unpredictable scenarios that might occur in any game session. What it basically boils down to is being able to identify that a certain move is not possible after a number of failed attempts, and trying something else.

This (rather tragic) gif shows a lizard that tries to climb up to the player, but is rather unsuccessful due to its limbs being disabled. The gif is sped up, because without functioning limbs the lizard is really slow.



Whenever a move fails, the destination tile is reported (red in the gif). After being reported a certain amount of times, the tile is marked as an obstacle (yellow) and the path finder is told to restart. This time around the yellow tiles will have a higher cost assigned to them.

This means that after trying to climb a couple of times, the lizard concludes that "OK, this isn't working" and decides to take another way up. This system works no matter what the obstacle is - if it's lame limbs, a stream of water, some heavy creature grabbing on to the lizards tail, anything.

This multiusability is both its strength and its problem - the lizard still isn't very smart about cause and effect. A smart lizard would deduce that climbing was the problem, not these specific tiles. With this system it might very well go to another climbing pole and try that before realizing that it has to get up some other way.

The point is, however, that this is a fallback. If I want to make the lizard able to understand that it can't climb without functioning limbs, that's something I'd write specifically in the AI ( if (functioningLimbs < 4) noClimb = true; )  but for all the scenarios I won't be able to predict, this fallback solution is a lot better than infinitely trying to get past some impossible hurdle.

Before it's done I want the obstacle tracker to have one more function - it should be able to identify when the obstacle is in fact a creature blocking its way. When a huge green lizard is sitting in an opening and a tiny blue one is pushing and pushing in vain to get through, it seems kind of unreasonable for it to assume that "it must be something wrong with this specific tile!" Also, once the green lizard is gone, the blue one should really stop avoiding that tile. So I think a specific framework for being blocked by colliding in other objects might be in place.





   JLJac on August 22, 2014, 11:03:03 AM:

Thanks! Yeah, there was a little bit of that in the old version. I don't want it to be too agonizing to watch them when injured, after all it's sort of unclear whether they are actually creatures like us capable of pain, but they should certainly react to injury in their behavior  Hand Thumbs Up Right

Update 295

Oh wow. Today was spent in vain. But it was awesome! But it was in vain. Basically I did what I do best, spent way too much time on monstrously complex AI machinery that no-one will ever notice.

Remember yesterday, when I talked about an obstacle tracker for other game objects? That's what I did, basically. The idea is pretty simple: On a "movement failed" call, first check if you have collided with any other creatures these last couple of frames. If so, don't write to the obstacle map (the one showed in the last post, where the lizard was able to figure out that climbing wasn't going to work after a couple of tries), but instead call the Obstacle Object Tracker, telling it which object you were supposedly blocked by.

The Obstacle Object Tracker has a list of suspected obstacle objects. It checks against that list, and if the object is already in there, it adds to a Reports integer. After a certain number of reports, the object is moved to the Confirmed Obstacles list. If it's not on the suspects list, it's added to it. All the suspects on the list that were not the right one this time around get a Lifetime counter decreased, and when it hits zero they are forgotten.

Whenever the path finder is asking a tile for its pathing cost, the Confirmed Obstacles list is checked, and if any of them are hanging out in that particular area, the cost is increased.

Simple enough!

Then it comes to my no omniscience purism. Say I'm a blue lizard. On my Confirmed Obstacles list is a green lizard, I've bumped into him 5 times and am frankly a bit tired of him, so whenever I'm pathing around the level I'd prefer not to run in to him again.

But... It was 240 frames ago since I saw him. And the creator of this little universe doesn't like omniscient AI, so I can't just ask the game engine for the position of the green lizard. Contrary, I have an AI component used for just that, the Tracker from a few pages back. I can ask the tracker where I think another creature is, but never ask for its position straight up.

So the Obstacle Object Tracker needs to be hooked up to the Tracker, with each object on the Confirmed Obstacles list having a reference to a Tracker ghost, so it can use that creature's supposed position when asking what tiles to avoid.

Problem is, that I don't want to have every creature track every creature - some creatures are not interested in each other as neither eat the other, and it would be total overkill for all of them to run 12 ghosts for every other for no reason. So I needed to make it so that when an object is moved from the Suspected Obstacles list to the Confirmed Obstacles list, the Tracker gets a call. The Tracker looks through its ghosts, and if it finds one for the obstacle creature, it returns it to the Obstacle Object Tracker to be stored. If it doesn't, the "A creature has been spotted" method is called, and a ghost is created and returned. In short, the tracker will start tracking that creature despite it being disinteresting because it has become interesting in the role of a physical obstacle to avoid.

[/insanity]

This means, if we return to the blue and the green lizard, that at this point a blue lizard can chose another path to its target because it's dodging a misconception of where it expects the green lizard to be. o_0

Do you think this comes across to the player? Hahahahahaha nooooooo it doesn't. But it was fun, and now it's in there, useful or not. At least it has helped dissolve huge knots of lizards being stuck in each other.

It does create some cool behavior though. When a big bunch of lizards are moving towards you, some of them will strategically take alternate routes as to not get stuck behind the crowd. This indirectly leads to some surrounding behavior that can be pretty nice. But boy, could it be done with simpler methods  Cheesy