Good morning everyone. It's been about a month, I've been quiet on the blog. I got myself a new laptop to work on the game sy the times I'd usually write these posts in the bus. As I'll be going to a concert with my wife after work, I left the laptop at home and here I am!
What have I been up to for the last couple of weeks? It's all mainly been about goal oriented action planning (GOAP) AI for the characters in Holocene. This means that the world is a world of states and tasks, but not really in a state machine way. A character has a simple state machine attached, but all it can do is be idle, go somewhere, and perform an action.
Where to go and what to do is dependent on the goals weer formulate. For instance, the character is hungry. This gives him a goal: eat. Quite simple. So he eats and he's happy. But what happens when the guy does not have food? Well, he needs to eat, so how do we make that possible?
Any job a character can have consists of the action (removing the food from the inventory and unticking him being hungry) and an effect (he's not hungry), but also some preconditions (he had food). The action will only run if the character actually had food.
The thing with GOAP is that once the goal of eating its picked, the character will try to find a sequence of actions leading to having eaten. Via some A* pathfinding algorithm from a tutorial, which I do not yet fully understand, the character will find a road from the current situation to one where the preconditions for eating are met. We're picking up the apple over there, and we're not attacking the enemy or making fire or building river dykes. Because those won't lead to eating.
The GOAP system makes for flexibility because all you do it formulate small building blocks for behaviour and a system to build sequences of tasks based on very simple nodes. The thing is always that stuff like this isn't as easy as it seems. For instance, right now I'm having trouble making the character understand when a building he's building is finished. I find it hard to debug this system and the fact I don't really understand the underlying pathfinding does not help, but I know what to study and I'll be ok in the end, because I expect it'll all be so much easier once I get it.
Showing posts with label AI. Show all posts
Showing posts with label AI. Show all posts
Wednesday, 5 February 2020
Thursday, 9 January 2020
Sequencing the states
Good afternoon. Yes you heard that right. It's even almost evening and I'm in the bus back home from work. I was just thinking about the state machine that I'm running in Holocene and there's a problem.
The problem is not unfixable by any means, but it'll make my job a lot more difficult because I am bound to create a gigantic AI class if I continue what I'm doing now. Shop what am I doing really? Well, I'm using a state class that has actions defined for entering and exiting the state and for executing whatever the state needs for every frame. All these methods are written in the CharacterAI class.
The idea is that once something is happening, like the character finds out he's hungry, the decision class will look if there's apples to eat and if there are, trigger Moving state with an apple as target. That's all fine and that works, but...
Walking to an apple is not really the same as eating it and moving to any target does not satisfy hunger feelings, so I have to make it more complex. Once the character arrives at the apple, he'll have to interact with it. Pick it up, maybe prepare it and consume it. That means we need more information on what to do next. That is going to clog the system quickly.
Now here's my idea: I'm going to use the Unity animator controller and add behaviour scripts to any of the states, keeping them very modular. Moving does just the walking until it's at the destination, picking up just picks things up, eating only destroys the object and sees if that does any good to the character. All these states can me called by a simple trigger that takes a string or an int.
Back in the AI script, all I have to keep track of its a stack of triggers. The states will let the AI know they're finished and the AI will know what to do next. In the eating example, the character will run a "hunger" method and that'll populate a stack with the state for finding it there it's food nearby, plotting the road, going there, picking the food up, seeing if it needs preparing, doing that, eating the food and going back to seeing what to do next.
I'm sure this is easier said than done, but I think this is what I'm going to do. This way I keep the code separate and unrelated and that often means less bugs and easier to manage.
The problem is not unfixable by any means, but it'll make my job a lot more difficult because I am bound to create a gigantic AI class if I continue what I'm doing now. Shop what am I doing really? Well, I'm using a state class that has actions defined for entering and exiting the state and for executing whatever the state needs for every frame. All these methods are written in the CharacterAI class.
The idea is that once something is happening, like the character finds out he's hungry, the decision class will look if there's apples to eat and if there are, trigger Moving state with an apple as target. That's all fine and that works, but...
Walking to an apple is not really the same as eating it and moving to any target does not satisfy hunger feelings, so I have to make it more complex. Once the character arrives at the apple, he'll have to interact with it. Pick it up, maybe prepare it and consume it. That means we need more information on what to do next. That is going to clog the system quickly.
Now here's my idea: I'm going to use the Unity animator controller and add behaviour scripts to any of the states, keeping them very modular. Moving does just the walking until it's at the destination, picking up just picks things up, eating only destroys the object and sees if that does any good to the character. All these states can me called by a simple trigger that takes a string or an int.
Back in the AI script, all I have to keep track of its a stack of triggers. The states will let the AI know they're finished and the AI will know what to do next. In the eating example, the character will run a "hunger" method and that'll populate a stack with the state for finding it there it's food nearby, plotting the road, going there, picking the food up, seeing if it needs preparing, doing that, eating the food and going back to seeing what to do next.
I'm sure this is easier said than done, but I think this is what I'm going to do. This way I keep the code separate and unrelated and that often means less bugs and easier to manage.
Wednesday, 8 January 2020
Agabouga likes apples
Good morning again. I have a little update on the development of what I started to call Holocene again. I'm focusing on the game mechanics and specifically the satisfying of needs by characters. As I might have said before, characters need food, water, sleep, warmth and probably something social.
These five are instances of a Need class. Needs have a current value, a maximum value and some priority stuff. A character will be able to find out which need needs to be fulfilled and find targets for that accordingly.
I have this system working now, but I'm going to refactor that, because I'm not very happy with it and I think we're making too many calculations which will be problematic when we're having more than a couple of characters.
Anyway, afters whatever happens, the character has a state machine component that gets fired to a new state when that gets triggered. The idea is that once the guy is hungry, he'll find a food source and sets that as target to go to. Then the state machine will trigger a state to pick up the source and then eat it. Or bring it somewhere.
What I have now is a system that presets the target for Agabouga to the apple and tells him to go there. The triggering of the move state is not implemented fully, but that will be. By the end we'll have characters being able to find out what they need and if that's available. The state machine will then get a sequence of states going. Move to the apple, pick it up, eat it and find out if we need anything else.
A lot of work left to do, but it's actual mechanics so I like it.
These five are instances of a Need class. Needs have a current value, a maximum value and some priority stuff. A character will be able to find out which need needs to be fulfilled and find targets for that accordingly.
I have this system working now, but I'm going to refactor that, because I'm not very happy with it and I think we're making too many calculations which will be problematic when we're having more than a couple of characters.
Anyway, afters whatever happens, the character has a state machine component that gets fired to a new state when that gets triggered. The idea is that once the guy is hungry, he'll find a food source and sets that as target to go to. Then the state machine will trigger a state to pick up the source and then eat it. Or bring it somewhere.
What I have now is a system that presets the target for Agabouga to the apple and tells him to go there. The triggering of the move state is not implemented fully, but that will be. By the end we'll have characters being able to find out what they need and if that's available. The state machine will then get a sequence of states going. Move to the apple, pick it up, eat it and find out if we need anything else.
A lot of work left to do, but it's actual mechanics so I like it.
Wednesday, 23 October 2019
What we also really need
Yesterday I wrote about my new system to find out what need is most prevalent for a character and that worked by deciding which needs passed a threshold and then picking one based on a level and a priority. Well, I found out that I forgot something.
Imagine walking around in the desert and being both very hungry and very thirsty. Luckily there's some food lying around right next to you. You're going to pick that up and eat it, right? Well, that's not how our worked in Holocene. You see, you have 9.1 out of 10 hunger and a staggering 9.2 out of 10 thirst. This means that thirst is more and hunger and thirst have the same priority in deciding what to do, so you'll try to drink. There's no water, but your mind is set. So you die.
The problem of course is that the decision what need to satisfy does not entirely depend on which need is biggest, but also which needs are most easily satisfied. Like we programmers often say (and rarely do) we should mind the low hanging fruit too. In this case even quite literally. Hunger is prevalent too and easily fixed, so we should focus in that too.
Now how does this work? Well when the character triggers picking the need to satisfy, they make a list of needs that hit the threshold to be prevalent. Then the list will be cleansed of needs that cannot be satisfied right now. From what is left, the most prevalent need is picked and action is taken. Unsatisfiable needs will trigger some warning in three UI when implemented, so that the player can check them.
In our example, the needs for food and drink are both added to the list. Then drink its removed because there's no water. Food is there, so the eating routine starts, which involves moving to the food, picking it up and eating it.
Imagine walking around in the desert and being both very hungry and very thirsty. Luckily there's some food lying around right next to you. You're going to pick that up and eat it, right? Well, that's not how our worked in Holocene. You see, you have 9.1 out of 10 hunger and a staggering 9.2 out of 10 thirst. This means that thirst is more and hunger and thirst have the same priority in deciding what to do, so you'll try to drink. There's no water, but your mind is set. So you die.
The problem of course is that the decision what need to satisfy does not entirely depend on which need is biggest, but also which needs are most easily satisfied. Like we programmers often say (and rarely do) we should mind the low hanging fruit too. In this case even quite literally. Hunger is prevalent too and easily fixed, so we should focus in that too.
Now how does this work? Well when the character triggers picking the need to satisfy, they make a list of needs that hit the threshold to be prevalent. Then the list will be cleansed of needs that cannot be satisfied right now. From what is left, the most prevalent need is picked and action is taken. Unsatisfiable needs will trigger some warning in three UI when implemented, so that the player can check them.
In our example, the needs for food and drink are both added to the list. Then drink its removed because there's no water. Food is there, so the eating routine starts, which involves moving to the food, picking it up and eating it.
Tuesday, 22 October 2019
What we really, really need
So. Back in the bus again. It's dark outside the windows, so why look there? I have a short update again, because yesterday I started the AI basics for Holocene. This is mostly about fleshing out the structure of needs for our characters.
There's five needs defined right now: food, water, sleep, warmth and an ill-defined social one that I did not put much thought in yet. Any character had these needs in some measure and they're what drive the behaviour of characters if you don't ask them to do anything.
The aim is that characters will try to satisfy their most important need by doing basic stuff. For instance, if the guy is hungry, he'll look for apples and eat them. Don't expect him to go on mammoth hunt because he's hungry. There'll probably be some message system to notify if the character can't immediately satisfy his needs, because you'll have a job then.
Right now, we only have a system that decides what need to focus on. A need has a priority value which it's highest for food and drink and a bit lower for warmth and sleep. Sleep had another perk, because once you get over a certain threshold, the character will go to sleep, no matter what.
For the other needs, there's a threshold once one or more of the needs ate over it, the priority matters. The highest level need with the highest priority will get focus. This means that with a threshold at level 8 out of 10, 8.5 food need will beat 9 warmth, but will be trumped by a 8.6 water need. Like that.
The AI will then have the specific need to focus on. Depending on the distance to the sole and stuff like that, the character will make his move.
The idea behind this stuff is that the need checking system should be able to be bypassed by player actions. So the guy needs social interactions? I don't care, there's flint rock to carve!
I'm not sure about how the autonomy of a character might sometimes override your decisions. Do we want characters to die of thirst while fighting a mammoth? Probably there's going to be some special thing in the Need class that'll make sure you get notified at least if that's about to happen. Need to think of that some more.
There's five needs defined right now: food, water, sleep, warmth and an ill-defined social one that I did not put much thought in yet. Any character had these needs in some measure and they're what drive the behaviour of characters if you don't ask them to do anything.
The aim is that characters will try to satisfy their most important need by doing basic stuff. For instance, if the guy is hungry, he'll look for apples and eat them. Don't expect him to go on mammoth hunt because he's hungry. There'll probably be some message system to notify if the character can't immediately satisfy his needs, because you'll have a job then.
Right now, we only have a system that decides what need to focus on. A need has a priority value which it's highest for food and drink and a bit lower for warmth and sleep. Sleep had another perk, because once you get over a certain threshold, the character will go to sleep, no matter what.
For the other needs, there's a threshold once one or more of the needs ate over it, the priority matters. The highest level need with the highest priority will get focus. This means that with a threshold at level 8 out of 10, 8.5 food need will beat 9 warmth, but will be trumped by a 8.6 water need. Like that.
The AI will then have the specific need to focus on. Depending on the distance to the sole and stuff like that, the character will make his move.
The idea behind this stuff is that the need checking system should be able to be bypassed by player actions. So the guy needs social interactions? I don't care, there's flint rock to carve!
I'm not sure about how the autonomy of a character might sometimes override your decisions. Do we want characters to die of thirst while fighting a mammoth? Probably there's going to be some special thing in the Need class that'll make sure you get notified at least if that's about to happen. Need to think of that some more.
Monday, 2 September 2019
Chasing goats
The gathering mechanics are nearly finished. I have a system to send a worker to a resource. Once it arrives there, he'll start carrying resources from the thing until it is depleted or the worker reaches his carrying capacity. He returns the stuff to a building and goes back to the resource to continue gathering or find another resource of the same kind. If there are no resources available within a certain range, the worker will look for other objects that become resources when they die and will attack that.
Every worldobject has a slot for a prefab to instantiate when its health goes to zero. For instance, our inevitable goat is a unit, much like a worker, but when it dies, it turns into a source for food. While that could be done in many different ways, in Holocene, it's done by destroying the goat object and replacing it by a dead goat one.
Anyway, when the worker has been gathering food and the source is depleted, he may start chasing goats to kill them and get access to the food. For that, I made the chase state in my state machine. Chasing is actually mostly the same as walking, but with two main differences.
Firstly, chasing updates the destination all the time. If you walk to an object and the object moves away, you still walk to the spot where the object used to be. Chasing will update the position of the target and change the direction accordingly.
Secondly, chasing might result in fighting. In fact, that is the aim of it all. When the target gets inside a certain range, the unit might start a range attack. If it gets closer there'll be melee fighting. Of course this needs fine tuning, but this is what it will probably be like.
Every worldobject has a slot for a prefab to instantiate when its health goes to zero. For instance, our inevitable goat is a unit, much like a worker, but when it dies, it turns into a source for food. While that could be done in many different ways, in Holocene, it's done by destroying the goat object and replacing it by a dead goat one.
Anyway, when the worker has been gathering food and the source is depleted, he may start chasing goats to kill them and get access to the food. For that, I made the chase state in my state machine. Chasing is actually mostly the same as walking, but with two main differences.
Firstly, chasing updates the destination all the time. If you walk to an object and the object moves away, you still walk to the spot where the object used to be. Chasing will update the position of the target and change the direction accordingly.
Secondly, chasing might result in fighting. In fact, that is the aim of it all. When the target gets inside a certain range, the unit might start a range attack. If it gets closer there'll be melee fighting. Of course this needs fine tuning, but this is what it will probably be like.
Wednesday, 28 August 2019
Worker is too lazy
Good morning CET. An update on what I've been doing yesterday. I told you there was going to be 3D modelling for the gathering mechanics. We have the rock and the worker that is supposed to mine the rock. We also have the mining mechanics, but once the worker has enough stuff, there's nowhere to go to to deliver it. So insert s simple 3D hut that I made in Blender.
Well, no. That was because there was something wrong with the rock. Somehow I had that specific one yield gold instead of stone. And the hut only accepted stone, so the worker's not going anywhere. Well, fixed that. Still not going. This means there must be something wrong with the AI state picking thing and it is. The worker finishes mining, switches to walking as it's supposed to. Then it goes idle.
Being lazy myself, I get that. Sometimes you just go idle, but not this time. We're talking algorithms and they are supposed to do what I tell them to do. It hurts to say, but I guess I could have made a bug then. Let's fix that. Next!
Monday, 26 August 2019
Transitions are important
Working with and getting used to Unitys Animator, I find that the beef of these state machines is in the transitions between the states. Different states have the agent do different things, but the main thing is to get the agent to go to a specific state when you want it to.
I'm working on the gather mechanics for workers. I put a little rock from Blender in my scene, added a resource script and focussed on the interaction between worker and rock (and explaining to a friend that not all interaction is sexual).
In short, you right click on the rock and the worker will move towards it. When it's there, it'll make a difference if the agent really is a worker and not, say, a catapult. Catapults can't gather resources in Holocene. Workers can. I managed that by adding a task enum to the unit script. That enum contains certain things that only certain units can do, like gather. The worker script will override the unit script for moving to a resource, adding the gather task. Now that is used to trigger gathering. Guys without the task will go idle on arrival. Others will go to the gather state.
Then gathering happens. The worker will get resources over time and the rock will loose some. Then we need to decide what to do next. When the rock is 'empty' or the worker can't hold any more resources, we need to drop it to a building.
I've not finished this yet, but I will save the resource object and location in a separate variable and find a suitable drop off point and object to go there, setting the task to return. The walking behaviour takes the worker to the drop off point and triggers the dropping. Next the resource will be the target again and the worker returns to gathering. When the wotker does not have any resources and the rock is empty too, he's finished and will look for another rock to carve.
This should be the gathering AI. It took my some time to think up, especially since I need to get used to the triggers in animators. I am at a point though where I think I know what to do next.
This is one of those things I love about making games. There's little victories around every corner, much more than in my day job. That's very motivating.
I'm working on the gather mechanics for workers. I put a little rock from Blender in my scene, added a resource script and focussed on the interaction between worker and rock (and explaining to a friend that not all interaction is sexual).
In short, you right click on the rock and the worker will move towards it. When it's there, it'll make a difference if the agent really is a worker and not, say, a catapult. Catapults can't gather resources in Holocene. Workers can. I managed that by adding a task enum to the unit script. That enum contains certain things that only certain units can do, like gather. The worker script will override the unit script for moving to a resource, adding the gather task. Now that is used to trigger gathering. Guys without the task will go idle on arrival. Others will go to the gather state.
Then gathering happens. The worker will get resources over time and the rock will loose some. Then we need to decide what to do next. When the rock is 'empty' or the worker can't hold any more resources, we need to drop it to a building.
I've not finished this yet, but I will save the resource object and location in a separate variable and find a suitable drop off point and object to go there, setting the task to return. The walking behaviour takes the worker to the drop off point and triggers the dropping. Next the resource will be the target again and the worker returns to gathering. When the wotker does not have any resources and the rock is empty too, he's finished and will look for another rock to carve.
This should be the gathering AI. It took my some time to think up, especially since I need to get used to the triggers in animators. I am at a point though where I think I know what to do next.
This is one of those things I love about making games. There's little victories around every corner, much more than in my day job. That's very motivating.
Sunday, 25 August 2019
Fighting for position
Ok. Weekend is over again. I have not been able to get too much work done in Holocene this weekend as I had other things to do (and yesterday night I wasted my time watching The Marathon Man).
What I did do is making a start with moving units. For that, I made another of these primitive shapes based Workers. A capsule for the body, spheres for head and red nose to know which way he's facing and an elongated cube for arms. And a worker script and some needed Unity components.
First thing I set out to do is to implement the walking behaviour that I made for goats. When the unit is selected and the player rightclicks on the ground somewhere, the unit is going there. Quite easy.
When clicking on a friendly unit, the unit should go there and stop right next to it. Well, that gave us this little gif that I shared on Twitter:
What I did do is making a start with moving units. For that, I made another of these primitive shapes based Workers. A capsule for the body, spheres for head and red nose to know which way he's facing and an elongated cube for arms. And a worker script and some needed Unity components.
First thing I set out to do is to implement the walking behaviour that I made for goats. When the unit is selected and the player rightclicks on the ground somewhere, the unit is going there. Quite easy.
When clicking on a friendly unit, the unit should go there and stop right next to it. Well, that gave us this little gif that I shared on Twitter:
Now what happens there? Note that I have not yet implemented any fighting mechanics. These workers are not fighting per se, but yet they actually are. The thing is, the moving one is trying to stand on the spot where the stationary one is already standing. Because two entities can't occupy the same position, there's squeezing going on.
This was actually quite easy to fix. The worker script knows what object it's going to and that object knows its size. All we need to do is tell the walking behaviour script what the size of both objects is. Add them up and we have the stopping distance.
This is actually really all I pulled off this weekend. Hopefully I have more time for my game this week.
Thursday, 22 August 2019
Setting up the interaction
Ok, another update. Yesterday, I wrote about a little issue in the WalkAround state of goats. Well, it somehow took the best part of the evening to fix that. Somehow the trigger for starting to move kept getting hit before the position was picked, having to goat literally go nowhere. Well, done now.
The more interesting thing to talk about is a little groundwork for giving orders. I already had a system to find objects when given a location. I upgraded that with a class of references to everything you need to interact with an object. In the end, orders will all be "interact with this object over there" and the kind of object is central to know what "interact" really means.
When I right click on an object, selected objects will run a method on a base Worldobject class to decide which virtual void to run, with some basic general stuff and possibly overriding methods in a derived class.
Sorry if this is technical. It means that I define basic behaviours for interacting with objects of a certain kind that should apply to anyone. When a certain agent has its own behaviour for some interaction, I'll program that and run that instead. This is how I understand object inheritance in C#.
Well, all I ended up with visually is that Holocene looks exactly like it did the day before yesterday, so I won't bother you with screenshots or the like, but now it should be set up to do things I did not program yet. I like to have it flexible, because I know it is getting more complex quickly now.
The more interesting thing to talk about is a little groundwork for giving orders. I already had a system to find objects when given a location. I upgraded that with a class of references to everything you need to interact with an object. In the end, orders will all be "interact with this object over there" and the kind of object is central to know what "interact" really means.
When I right click on an object, selected objects will run a method on a base Worldobject class to decide which virtual void to run, with some basic general stuff and possibly overriding methods in a derived class.
Sorry if this is technical. It means that I define basic behaviours for interacting with objects of a certain kind that should apply to anyone. When a certain agent has its own behaviour for some interaction, I'll program that and run that instead. This is how I understand object inheritance in C#.
Well, all I ended up with visually is that Holocene looks exactly like it did the day before yesterday, so I won't bother you with screenshots or the like, but now it should be set up to do things I did not program yet. I like to have it flexible, because I know it is getting more complex quickly now.
Wednesday, 21 August 2019
To our goat overlords
So I opened Pandoras jar. Yesterday was quite productive and I am pretty pleased with it. I started working on a new AI system, based on Unitys animator component and the theory I found at the likes of Tommy Thompson who has a compulsory YouTube channel on AI in games. It took a little rewriting, but it looks really workable. Someone on Twitter already greeted our new AI goat overlords when I shared this one.
This one is as complex as it looks really. A goat enters the game in a WalkAround state where after a time a trigger "arrived" is hit. When that happens, the goat will go to the NextTarget state. It then immediately returns to WalkAround. If, after some time, a trigger "died" is hit, the state will transition to Dead.
Now that I'm typing this, I see a flaw. It works, but only for randomly walking around. That is because I use the behaviour scripts for this state machine. NextTarget does not actually anything but having the WalkAround state restart. Picking the destination for walking is done in WalkAround, making it useless for any agent that does not walk around randomly.
What I'm going to do tonight is move that destination deciding thing to NextTarget and rename both states to be used for other agents. The Walk state will deal with a destination in the Unit object and walk there. When it arrives, the next state will decide where to go next or what to do next. A worker might want to collect food after moving to the bushes, a goat will go somewhere else. Both could use the Walk state for walking. The difference is in the transition after that.
Anyway... I also did a little thing to trigger the "died" thing. For that, I added health to objects. When health gets to zero or below, it hits the "died" trigger. Objects then change to other objects. For instance, when a tree dies of cutting, it turns into a horizontal thing that gives wood supplies. It gets different attributes by dying. Technically it's become a different object, but ssssht, don't tell anyone.
Next steps will be to first fix the issue with the states, to add a healthbar and damaging mechanism to test the death thing and then to start Worker AI. With AI here I mean the intelligence to perform orders like "go there" and "harvest that". Giving the orders by enemy players is for much later. I call that Player AI and I did not give that much thought yet. Did I forget to mention anything? Probably, but the bus hits the "arrived" trigger, so I sign out for now.
Thursday, 15 August 2019
The ground is not flat
Just a small update, because time is an illusion. What have I been up to yesterday? Well, bits and pieces. First of all, I have been working on the camera system. Next, I'm reimplementing the features I already had.
You might have noticed I am working on a different Unity project since starting the procedural level generation. This is because I found out that much of what I made was dependent on the ground being flat. That, and when you're learning, sometimes you want major refactoring and it's often better to do that early.
The camera then. Because the ground is not flat, I cannot just clamp its position to a certain height on the y-axis. I need to find out how high the ground below is and then clamp in to a certain height above the ground.
Typing this, I now know what to do tonight. I need to send out a raycast and see how far away it collides. Then I have this point. Add some minimum value to the y-value of the camera position and we are done. I just need to make sure the camera doesn't go beyond the level, looking to the void that we all fear.
Next, this guy.
You know the goat. It's our first agent walki g around on the navmesh. It works, although I need to tweak some parameters. That's nice, because I am implementing a new AI state machine. With the floor not being flat, rotation will be changed, but I didn't have time for that yet.
Now, the bus is almost at the station, so you know what that means...
You might have noticed I am working on a different Unity project since starting the procedural level generation. This is because I found out that much of what I made was dependent on the ground being flat. That, and when you're learning, sometimes you want major refactoring and it's often better to do that early.
The camera then. Because the ground is not flat, I cannot just clamp its position to a certain height on the y-axis. I need to find out how high the ground below is and then clamp in to a certain height above the ground.
Typing this, I now know what to do tonight. I need to send out a raycast and see how far away it collides. Then I have this point. Add some minimum value to the y-value of the camera position and we are done. I just need to make sure the camera doesn't go beyond the level, looking to the void that we all fear.
Next, this guy.
You know the goat. It's our first agent walki g around on the navmesh. It works, although I need to tweak some parameters. That's nice, because I am implementing a new AI state machine. With the floor not being flat, rotation will be changed, but I didn't have time for that yet.
Now, the bus is almost at the station, so you know what that means...
Wednesday, 31 July 2019
Between dinner and Casa de Papel
So holiday has ended a couple of days ago and I'm writing this blog from the bus again. On my way to that thing we call the office. As I probably said before, I have a full time job as programmer making office applications for customers. And by the fact I write my corpus of blogposts in the bus for a part, I need to travel every day.
This means that I am home quite late and that doesn't afford me much time to work on my game. Holiday is over indeed.
Having said that, I'm glad I was able to lay some groundwork for things I could implement in the hours and half hours of the evening, between dinner and Casa de Papel that me and my wife just started watching.
With the placeholder GUI in placeholder place, the coming time will mostly be about object interaction, about unit AI and the like, but only in following orders. Letting the enemies control their units is for later. For now, I'll be focussing on making sure that when a decision is made, the objects know what to do.
Things I am planning to do soon include:
This means that I am home quite late and that doesn't afford me much time to work on my game. Holiday is over indeed.
Having said that, I'm glad I was able to lay some groundwork for things I could implement in the hours and half hours of the evening, between dinner and Casa de Papel that me and my wife just started watching.
With the placeholder GUI in placeholder place, the coming time will mostly be about object interaction, about unit AI and the like, but only in following orders. Letting the enemies control their units is for later. For now, I'll be focussing on making sure that when a decision is made, the objects know what to do.
Things I am planning to do soon include:
- Changing a dead object or a dead and resourceless object to look and behave differently. For instance, when you kill a goat, it'll fall over and you can harvest the food. When the food is gone, it looks the same, but you can't interact with it anymore. I want this to be clearer. Maybe turn the goat into a skeleton and have it vanish over time.
- Right now, resources can be dropped at any building. I'd like to change that. You shouldn't be able to drop wood at the barracks. I'll probably build granaries, storage buildings and the like for that.
- When you attack a unit and it runs, following it is very simple. Have the unit close in and if it's close enough, continue attacking. I'd like the attacker to have to face the defender before fighting. Besides, I don't want the following to go infinitely. That could be a tricky thing later when players discover luring enemy units.
- If you gather resources, you return to a building. That works. But if gathering or returning is interrupted, there's no way to just drop the stuff at the building and be done with it. To drop something now, you need to first send the unit to the resource to have it return. I'd like to trigger returning directly.
I don't think that these points are very big, so these should be fine for the evenings. Besides, I really like the small successes that such things make. They are really motivating.
Tuesday, 30 July 2019
Just GoThere(place)
Yesterday evening I found and fixed the bug that was haunting me for a couple of days and it was one of those bugs I'll probably see return every now and then, because that's the kind of programmer I am. I overused a method that did more than I intended.
You see, units are supposed to be able to go places and for that, they have a navmesh agent that moves once a destination is set and the destination is not their current position. So far so good. Can't have that fail (well, we can, but I won't bother you with that right now).
The other main part of the unit script is a finite state machine AI system. In short, units have a state and a task. The state says what the unit is doing and the task what its objective is. So when we want to cut a tree over there, clicking the tree will cause the unit to have Cut as task, the tree as target and Moving as state. The moving state will find out if the unit is near enough to the target to change the state to Fighting. Etc.
Now the problem with the bug was that the task was no longer to Cut. The unit went to the tree and that was it. It turned to the Idle state with Nothing on its mind. Actually, that was easy, because I made the mistake of messing with the Moving state. It had a simple statement to set the destination and I, thinking that it would be smart, replaced that by a GoThere method.
And that method set the destination, so the guy did go to the tree. That was good. It also set the state to Moving, which is fine. But is ALSO set the task to Move. That was the problem.
The unit made its way to the tree and stopped near it. That was good. Then the Moving state wondered what to do next, so it asked the script. The script said. If you're moving and the task is to move, then once you reach your destination, you're done and you can run SetIdle(), whatever that may mean. No cutting the tree.
Now, dear rubber duck, this is a clash of principles. We have the DRY-principle (Don't Repeat Yourself) that will stop you from writing the same code multiple times. I try to live by this principle as much as possible.
Then there is this programming thing to keep things small. I wrongly assumed that the GoThere method would just set the destination even though I just added the messing up code for another aim. I wanted to make things consistent, but did not think about it enough. Because some things should be different from other things.
Lesson learned, but by no means expecting not to make the mistake again. I do think it should now be easier to detect.
You see, units are supposed to be able to go places and for that, they have a navmesh agent that moves once a destination is set and the destination is not their current position. So far so good. Can't have that fail (well, we can, but I won't bother you with that right now).
The other main part of the unit script is a finite state machine AI system. In short, units have a state and a task. The state says what the unit is doing and the task what its objective is. So when we want to cut a tree over there, clicking the tree will cause the unit to have Cut as task, the tree as target and Moving as state. The moving state will find out if the unit is near enough to the target to change the state to Fighting. Etc.
Now the problem with the bug was that the task was no longer to Cut. The unit went to the tree and that was it. It turned to the Idle state with Nothing on its mind. Actually, that was easy, because I made the mistake of messing with the Moving state. It had a simple statement to set the destination and I, thinking that it would be smart, replaced that by a GoThere method.
And that method set the destination, so the guy did go to the tree. That was good. It also set the state to Moving, which is fine. But is ALSO set the task to Move. That was the problem.
The unit made its way to the tree and stopped near it. That was good. Then the Moving state wondered what to do next, so it asked the script. The script said. If you're moving and the task is to move, then once you reach your destination, you're done and you can run SetIdle(), whatever that may mean. No cutting the tree.
Now, dear rubber duck, this is a clash of principles. We have the DRY-principle (Don't Repeat Yourself) that will stop you from writing the same code multiple times. I try to live by this principle as much as possible.
Then there is this programming thing to keep things small. I wrongly assumed that the GoThere method would just set the destination even though I just added the messing up code for another aim. I wanted to make things consistent, but did not think about it enough. Because some things should be different from other things.
Lesson learned, but by no means expecting not to make the mistake again. I do think it should now be easier to detect.
Wednesday, 24 July 2019
Cutting the trees, blacksmithies and trebuchets
Right, today was the hottest day ever in the Netherlands and tomorrow, they expect another record, so what did I do? I only worked on Holocene in the morning and now, in the middle of the night, right before going to bed, here's what I made.
Most of the stuff I've been working on is work in progress, except for turning trees into animals, like I announced yesterday. Turning trees into animals allows easier implementation of changing states. Trees will yield wood as resources, but only after cutting them. So first we're looking at a stationary unit and when it dies, it'll change into a resource object that has an animation and all the stuff we expect from resources. This is how it now looks.
The other thing I've been working on is trying to have the guy to the left have any say in the fall of the tree. The aim is to allow the unit to interact with the tree and then cut it. There are some different kinds of objects to interact with which will all need slightly different ways to do so. for instance, there's the ground and if you right click there with a unit selected, you order the unit to go there. If you right click on a building you own, you move the guy to the edge of the building and see if the building has any interaction of its own, like healing the unit or fortifying the building. If you right click on an enemy unit, you run to it and attack it. And if you right click an tree, you go towards it and start dealing damage.
In theory, this all works fine, but somehow I experience problems having the BigBookBasic script find out what kind of object it is. Apart from the ground, all clickable objects derive from a WorldObject class, for now in this way:
- Building
- IncompleteBuildings
- Units
- Resources
All of these could have different members. A Barracks and a Wall are both buildings, A Worker and a Warrior are units and Rocks and Berry Bushes are resources. Further, Units could be animals too. BigBookBasic will need to find out what kind of object we're looking at a a certain moment. Well, that does not yet work. I'm sure I'll figure it our, but not today and I'm also sure that I need to figure it out soon, because it's central to give units the information they need to decide what to do with an order. In other words, I think this part of the program is what will turn it into a game.
When I get this to work, there'll be some refactoring needed, because I have some scripts, like the gather script, that do work, but it a roundabout way and I have different variables to track target unit, building, resource and "object" for units. I want them in one variable. For that I made a small class to store both the object itself and some information on it. This class will be the main focus in interactions between objects.
In short, the trees do fall and turn into resources. I cheated, because you see the health bar decrease, but that's just because I added some testing code. Sneak preview perhaps, or will I delete it later?
protected override void Update()
{
if (Input.GetKeyDown(KeyCode.T))
{
DamageHealth(10);
}
base.Update();
}
Labels:
AI,
design,
game mechanics,
Holocene,
refactoring,
RTS
Monday, 10 June 2019
Can't do, sir!
It's funny, learning a language or a framework while working on an all-in application of the thing. Today was one of those days with a big job and a good end, because I fixed it in a way that I understand myself. Luckily Unity has a very good documentation to work with if you understand what questions to ask. I think I might be getting better in that and in the end, that's a very powerful skill if you ask me (or is that a wrong question to ask?)
I already had the mechanism of moving to a certain place via Unitys in-built NavMeshAgent. I also have had a system to make resource numbers cross over from a resource object to a guy, so the guy is harvesting some resource. Next thing which took me the better part of a weekend (bless christian holidays for that and bless my wife for not forcing me to go furniture shopping like the rest of the dutch) was hooking these two behaviours in a meaningful way.
To be specific, when the player selects a worker and right-clicks a tree, the worker should go there, gather resources, find the nearest drop-off-location, go there, drop the stuff there in the stockpile and return to gathering. Sounds easy, but I found it is not. At least, it took time to find out what was wrong.
In the beginning, the worker looked like his arms were stuck in the tree after gathering. So I made sure that didn't happen. The guy kept his distance by making the NavMesh agent wider. That did not solve anything, because the guy did not move anyway, just sloooooooowwwwwwllllllllllyyyyy turn in the direction of the cube building to the left. After a lot of time, I found out what seemed to be wrong. I point the guy to move to a place that is an obstacle. Normally, units move around the building and that's how it should be. I was asking the guy to do something he couldn't. when I turn of Navigation static on the building, my guy started running, but that would mean anyone would be able to do that.
I had a couple of options. First, I could try and make the building accessible to just this guy. I was trying stuff with extra objects with area masks and certain stepping heights, but that did not only not work, it felt and still feels like a wood-rope solution (like we dutch say). It was too much a move to something strange to avoid the problem, not a solution.
The other thing I tried was find a spot just outside the building and move there. Unity seems to have a lot of ways to do that and I only got one to work. A building object always consists of at least two child objects. There's the healthbar on top and the graphic/collider thing below. I had a method to find the nearest building object with certain attributes and that object was input in a new method to find the nearest place just outside it. I found out which of the child objects was lowest and asked the Physics engine to find the nearest spot outside.
To that spot, I sent the guy, let him do his dropping magic, update the UI and return gathering. That works and I'm happy with that.
Typing and rubber ducking this, I found out that I could better tag an entrance child for the delivery object and take that one instead of finding out which is lowest. I'll do that tomorrow. For now I'm happy with the solution and going to sleep. Tomorrow, the office is there again.
Saturday, 8 June 2019
Unnecessary complexity
Well, today there's just some small stuff I worked on. First think was that I needed healthbars for all the units and buildings and some of the resources. For that, I took my HealthSystem script from my RPG that I got from a Code Monkey tutorial. No bad words about this tutorial and I would point anyone to that one for making a healthbar, but I think I'd better not use it this time. The reason is the complexity and the extra level of code I need to have it work.
You see, my other game is about one player fighting one enemy at the time (if at all). Besides, the guys involved have health, fatigue and oxigen to work with. Now, in Holocene, Every unit, every building and quite some resources have health. No fatigue, no oxigen. Just health. And health is two numbers: a current health and a maximum health. All I have to do now is work with these in the unit itself. It has a current health that should not go below zero (because then the guy is dead) and not above the maximum health (or why else should we call it "maximum"?). That's all.
It's funny to see that in my drive to set things up in modules, I end up with complexity in layer upon layer, while that's often totally unnecessary. I don't need a complete class of HealthSystem objects. All I need is a couple of floats to track if the guy is dead yet, a method for dealing damage and healing health and a reference to a sprite that gets bigger and smaller if something happens. Nothing more complex than that.
Now that the guys can die and be destroyed, I can go on with the AI that I started with the other day. First job is to find out what right-clicking means when a unit is selected. First, what are we clicking? Are we clicking the ground? Go there and go idle. Are we clicking a building? Is it ours? Go there and go idle. Is it a unit? Whoose is it? Is it a resource? Is it minable? Can this unit even gather resources? Is there any resource left there? That kind of questions are what are driving the game in the week to come, probable.
Wednesday, 5 June 2019
Giving tasks
So yesterday, I did some groundwork for the behaviour of units in Holocene. I decided to go with a Finite State Machine that keeps track of two things the unit is doing. There is a state and there is a task.
The state keeps track of what the unit is doing, like moving, fighting and gathering resources. The task is about what the unit is trying to achieve. For instance we have this amazingcylinder tree to cut for wood.
The state keeps track of what the unit is doing, like moving, fighting and gathering resources. The task is about what the unit is trying to achieve. For instance we have this amazing
By sending the unit to the object, the system understands it will be a gathering operation, because the object has a Resource component. The player will then get the task of gathering, related to this specific object.
Gathering will then mean the unit has to first go there, so the state will be set to moving. Then, when the unit has reached its destination (which will have different meanings depending on the type of movement) the state will go to gathering untill some threshold is reached. Next, the state will be moving, because the unit will need to drop off the resources at a certain place and then the circle starts over again.
I'm sure there'll be some complications, but I think that this FSM should be the basics for the units and their behaviours. I'll first focus on the player's units. If I do this well, getting the enemy to work should be about getting the states and tasks to change, which will be complex in its own right.
I do however intend to keep these AI parts separate. There's the way a unit deals with a specific task. That's up to the unit. Giving tasks is the player's job. That's for later.
Monday, 6 May 2019
Video: So Far So Good
I did not have too much time to work on my game, so I thought It'd be nice to show some game footage of me playing whatever it is that I do have. I know it's not perfect and you'll see a couple of bugs, but it's starting to get interesting, if I can say so myself.
`
Looking at letters user the same mechanics as picking up things, opening doors and talking to NPC's. There's an interact script to do things with objects, depending on what kind of object it is. Is it an interactable, like a letter, examine it, is it an NPC, talk to it, etcetera.
Well, if it's a door, open or close it. Behind the door, there's the blue Friendly Guy that wants a red ball from me, and a red Unfriendly Guy that wants to walk towards me in a buggy fashion. Talking to the Friendly Guy reveals a quest that he has. This one is easy to complete, so I do that and return the ball. Turns out out friend does not have enough stuff on him to reward me. Usually, I'd be angry, but he, this is a public video, so I'll let him go.
I might have more time tomorrow and Wednesday. Hopefully I'll be able to complete the quest for movement quests too. There are - for now - three kinds of quests (or rather objective types; a quest can have a number of different objectives to complete): Gathering quests, movement quests, and defeat quests.
The defeat quest will have to wait before I have combat mechanics, which is not my first priority right now. That'll be movement objectives. Movement objectives are mainly meant as helper objectives to get the player to a place to have him receive further instructions. it should be quite easy to implement. That's what I'll do next.
Furthermore, I plan to make GUI for inventory, journal and quests. Later there'll be trading, equipment and player stats screens. There will probably not be based around the dialog window that I use for examining and dialoging.
Friday, 29 March 2019
Back to where I were
Alright, good stuff. I'm back to where I were when I started with the NavMesh, but now with NavMesh agents implemented for both the Maze Guy and the Forest Guy. They can both walk their patrol routes without dropping half way into the ground and getting stuck. Ánd, they can navigate the terrain when I put obstacles in their way. A copuple of days ago, they could too, but then they just pushed the walls over or kept humping into them with a small angle until they slide past. This looks better.
I needed to rebuild the stack of shapes (two capsules, a sphere and an enlongated cube) and tinker a bit with the height of the NavMesh Agent. This, and I had the Parent gameobject a physical one. While MazeGuy and ForestGuy were empty game objects with a bunch of children, the new ones (with a space in their name) are actually capsules that aren't rendered.
I might tinker with the walking stuff again, especially the sideways rotation. I'm not yet sure how this simple object rotation works with movement by the NavMesh. It looks like to goes great, but maybe that might not work later, so I'll keep a careful watch over it.
Tomorrow I'll not be working. Maybe a little bit in the morning, but we're going to a wedding and we'll be back late at night. Sunday'll be my birthday, so we'll have visitors. After that, I'll see if I could make a paralel AI system with an Animator with a FSM (I think of it as the Flying Spaghetti Monster, but it's Finite State Machine here). If that works alright and is really better, I'll replace what I have now.
Next I'll be building on it. What will the guy do when he sees you, does it make sense to freeze all activity when the guy is miles away? Do I find more interesting stuff in my book? Or do I have new toys to play with for my birthday? See you in a couple of days!
Subscribe to:
Posts (Atom)










