Well now, a lot has gone on since my last update. A lot of it has been behind-the-scenes stuff like refactoring and optimizations and more refactoring (which I’ll talk about a bit further below), but I have introduced something new to the artificial ecosystem. For the time being they’re called “herbivores”, and they eat grass!
You might also notice the grass looking a bit different; I’ve been doing a bunch of tweaks to existing behaviour as I go, though nothing huge. I’ve mostly just been trying to make something that looks aesthetically pleasing at this point. The biggest change is letting the grass populate up to about 10 squares away; this makes it more possible for grass to return to areas rendered barren by voracious herbivores, as well as makes the overall shapes less homogenous.
Now, a significant issue with the herbivores at the moment – at least in terms of establishing some sort of equilibrium in the ecosystem – is that they are far too efficient. I essentially built them on top of grass with modified parameters – plus the ability to move and consume grass – so they are essentially always trying to either eat and reproduce if possible.
Reproduction currently is only limited by a percentage chance, which means that if a single herbivore is likely to reproduce at least once in its lifetime, then it’s at least possible that it could reproduce twice, and each additional herbivore increases the likelihood of more being born, leading to overpopulation inevitably consuming all available grass.
You can see near the end a small pocket of grass was able to re-establish due to the newly increased propagation range, however, it ends up being moot. I have some plans for how to improve this behaviour that mostly revolve around making the herbivores less efficient; such as making them get hungry less often, give them a diminishing likelihood to reproduce multiple times, etc. I would like the reproductive cycle to be more “generational” than “relentless swarm.”
You may also notice in the 3rd image that it’s starting to encounter some slowdown; these herbivores have been a really excellent exercise in pointing out poorly optimized portions of my code. This is also another big issue with how efficient the herbivores are; it was fine for grass to be very efficient because their operations are relatively low-cost. Herbivores, however, need to regularly search for meals as well as pathfind to reach those meals, which can become very expensive to do constantly – particularly when they start getting further away from meals and/or can’t find a valid path. The measures I talked about above should help with this as well, plus I also want to give them the ability to remember their paths for a period of time; currently each herbivore re-checks that the entire path to its next target is valid every time it wants to take a step. That’s good for accuracy, but very bad for performance.
My next main goals are, in order:
- Add behaviours and tweak the herbivores until the ecosystem can find some sense of equilibrium without grinding the FPS to a halt. I want to be able to let this run for some time.
- Allow the critters’ parameters to drift over time. When a creature reproduces, I want its descendant to have slightly different inclinations.
- Build some sort of UI that lets me inspect the individual entities and see what sort of parameters they optimize towards as the “fitter” settings should produce more offspring.
And now I’m going to delve a bit into the more technical stuff that I’ve been up to. I’m putting this at the end in case up until this point has somehow been within your threshold for nerd shit, but no further.
Aside from lots and lots of incremental changes in order to get specific things to work, I’ve made two major changes in how the code is structured and operates.
Firstly, I consolidated all of my non-water entities into their own singular class. This was due to the fact that in Unity if I want a script attached to one game object to interact with the script of a different game object, I need to write the name of Script B into Script A. This isn’t an issue if I just want to allow the Herbivore object to trigger some interaction in the Grass script, but my end goal isn’t to hard-code specific interactions – rather to build the rules behind the interactions and let those operate in a more arbitrary way. So, in the interest of not coding myself into a corner, I moved all entity behaviour into a single script and allowed those to be controlled through parameters. E.g. a creature with 0 “speed” isn’t ambulatory, even though its script has the code required to move in it.
I had actually started on an early version of the Herbivore and sorted out a pathfinding script before deciding to make this change, so it required a lot of backtracking. But I’m feeling much better about my ability to scale this to additional entities; any work I put into building any given entity is also extended to any future or existing entities, which is the sort of efficiency I can get behind.
The other significant change was getting the controlling “brains” to split off when they reached an entity limit. This was something that I hit a roadblock on before condensing all entities into a single class. Each brain needs to know what kind of entities it controls, and the master list of brains also needs to know what kind of entities the brains it lists control, and the thought of creating a new list of brains for each entity was unacceptable. I made a few attempts to make this dynamic and work with multiple entity types, but it just ended up getting tied in knots that I didn’t know how to untangle, and I shelved it for a while. Moving everything into a single entity cleared that up, and let me finish the implementation. This was also necessary, because the behaviour of multiple herbivores under a single brain was quite poor (each herbivore under a brain would have a random chance to move towards its next meal each cycle), so this let me keep using the brain approach, but only allow each brain to control a single herbivore before spawning a new one.
Some technical goals that I have coming up at some point aside from the more functional ones above:
- Allow for entity configuration to be stored to and read from physical files.
- Figure out a good way to store entity parameters in a sort of balance; e.g. as one raises, one or more could lower. I’ll delve more into the functional reasons behind this when I get closer to doing it.
Hopefully it won’t be a full damn year before my next update, but, it might!