Posts Tagged Game Development
I’ve been working on applying torques to the creatures in a meaningful and well thought out manner (read: banging my head against the keyboard whilst trying to fix all the horrible signage errors). The original intention was to simply pass the torques on to the energy system and let natural selection weed out the unbalanced and physically impossible specimens. But I realised I have the perfect opportunity to overhaul a system that has bothered me since its inception: the quadruped system.
Logically, if a creature has slightly smaller front limbs than back limbs, it should lean forward in order to walk on them (unless it’s a pangolin, but they’re too awesome to be swayed by our petty logic).
Without something to enforce this, every creature in Species ALRE would be a biped, because every creature has at least a *slightly* different leg size.
The quadruped system governs how this is implemented. A hideously simplified description would be “if the torso rotation is less than 45 degrees, and it has fore or back legs, the creature will fall forward onto its front legs or chest. If it’s higher than 45 degrees, or it has mid-legs, it will remain bipedal.”
That “45 degrees” is completely arbitrary. It’s just a value I picked.
This system is why ‘head-down butt-up’ creatures, whose bodies hang forward from their back legs are so common: Any creature with large back legs and a small torso rotation will end up with this body plan.
That’s the old system. The new one is much more sophisticated.
It starts by completely eliminating the quadruped system: creatures will initially be created *exactly* as their genes would have them (ie. with only one pair of their legs reaching the ground). It then calculates the skeleton and torques as normal.
Once that’s done, it passes control to the droop system. This applies “droops” to every body part attached to the torso: limbs, neck and tail. Legs are bent upwards by the reaction force on their legs, neck and tail are bent downwards, and the torso droops based on the creature’s balance, all in direct proportion to the torque applied on the joints.
If a body part droops into the ground, it stops drooping and adds a ground contact point, which will take some of the creatures weight in the next step.
Then pass 2 starts, the body-plan is completely reinitialised: bones are regenerated, torques and forces are recalculated. The results of pass 2 are what the creature will ultimately use for most of it’s energy calculations, so as to take advantage of the creature new quadrupedal body plan. (physical droops will affect energy, but proportionally to how much they rotate rather than as a function of torque. Small drops onto legs will be a negligible cost)
This also has a noticeable effect on creature appearances. I haven’t adjusted the rotation genes at all in the following screenshot: these variations are entirely the result of torso, neck and tail thickness changes causing different torques in the creature’s body plan.
One particularly noticeable change is that the creatures tail now has an effect: increasing its mass is the only way to counter the torque produced by the head and neck in the second body plan. For bipeds with centrally situated legs (raptor-like creatures), a sizable tail will be the only way to maintain balance (other than having an upright torso rotation gene and incurring a large energy cost in droop).
I’m hoping this system will be realistic enough to promote life-like body plans in place of a real-time physics system, which would be far too CPU intensive to implement across thousands of creatures. More likely, it will be exploited ruthlessly in ways I can’t yet imagine. But hey, that’s what makes it fun!
“Anime references. There goes the neigbourhood”
Hello my totally-not-expedible minions!
Finally feel like I’m getting somewhere with these torques, so I thought I’d share a bit of the
pain and frustration fun times!
I gave in and repurposed the debug text routine to draw the actual values of torques to the screen so I could debug them. I wasn’t sure whether the actual calculated values were wrong, or just the display arrows.
Turns out, both were: the calculated values were wrong, and the display arrows were not even reflecting them accurately. So I’ve stopped looking for a quick fix and am stepping through the code as best I can, fixing things as I go.
I think I’ve found the first major problem: it’s a bit hard to explain, so I’ll diagram it:
The short version is that in order to calculate torque I need to calculate both distance and magnitude perpendicular to the bone. If they were parallel, then there would be no torque and the bone would face compressive forces instead.
The problem is that by crossing the Force’s Normalised Magnitude Vector with the Bone Rotation, we introduce it’s *direction* into the equation. Then later on we calculate the Dot Product of that and the Forces full magnitude vector, multiplying it’s direction a second time and resulting in a torque going the wrong way.
If you’re by some miracle still following, then a) you have a better attention span than me and b) I’ll probably fix this by removing the final Dot product: just multiply cross2 by the force length and call it a day.
And then I can finally get to work on why the display arrows are crap.
* * * * *
PS: The Orbital EMP Cannon is down again. We sent an investigation party, and they reported that the living quarters are for some reason highly radioactive and not fit for human habitation. They also said something about millions of spiders before descending into incoherent and extremely uninformative screaming.
I’m sure they’re fine.
We’re now taking volunteers for the cleanup party. Biohazard suits and flamethrowers will be provided.
I forgot to charge my laptop yesterday so used the forced break to practice my sketching, but I’m back in it today and development continues.
The Mutation Map tool now has the ability to view and edit both the overall map and the individual stats of each head. It can also do the same for Feet, and I’m working on getting the remaining mutation maps (Limbs, Coverings, Colour Patterns, Feature Models and Feature Textures) into the tool. Hope to be done by the end of the week and moving on to other things, though that may be a bit optimistic.
Also took some time to begin taking my Stat encapsulation to the next level. Currently we have 6 types of Stat:
- GeneticStats encapsulate a mutation rate, plus mutate, randomise and blank routines, for every gene.
- FloatingGeneticStat: A hereditable decimal number, like “torsoWidth” or “headSize”. Encapsulates an upper limit and lower limit in addition to the mutation rate.
- DiscreetGeneticStat: A hereditable integer number, like “numberOfFeatures”. Just encapsulates the mutation rate.
- BooleanGeneticStat: A hereditable yes/no value, like “hasTail” or “hasNeck”. Just the mutation rate again.
- MappedGeneticStat: A stat with a mutation map, like “headType” or “bodyCovering”. Encapsulates the entire mutation map structure.
- BasalStat: a non-hereditable stat which is imported from an external file, linked to a MappedGeneticStat. Like how HeadType determines Diet, or how LimbTipType determines BaseLimbDamage. These stats are rarely used directly: usually, they feed into formulas to determine other stats. (diet is an exception)
- UpdatableAttribute: a type of Stat I have encapsulated but haven’t derived from IStat yet: these are things like Health, Energy and Biomass, which change over the course of a creature’s life. Encapsulates a list of “Deltas”, making it possible to track the source of changes to these stats (you can see these deltas listed out on the energy page in creature details).
There’s only one major type of stat still missing, at least on a per-creature level:
- DerivedStat: Any stat which calculates itself from other stats. For instance, Speed takes into account LimbSize, LimbBaseSpeed, TorsoWidth, TorsoRotation, LimbTipBaseSpeed, and so on. DerivedStat will encapsulate all these stats, plus the formula used to combine them.
The method I’m using to encapsulate the formula uses something I haven’t spent much time with in the past: delegates. I’m storing it the entire formula as a delegate inside the DerivedStat instance. This means I get to define the formula in StatData, but still use the built-in accessors of the objects, so I get the same intellisense benefits and high performance as if I was defining the calculation over in Phenotype.
The result looks like this:
"Mass", //Printable Name
"Determines how much stuff there is in the thing", //Printable Description
delegate(Genome genome, BodyPlan bodyPlan, Phenotype phenotype) //boring code stuff
return bodyplan.Volume * genome.Skin.Density; //Formula
The relevant bit is the second-last line: that’s the only place that formula needs to be defined. All I have to do later is call Initialise, and the DerivedStat takes care of calculating itself on behalf of it’s parent creature, pulling other stats in and combining them, as well as throwing errors if stats haven’t been correctly initialised.
Yes yes, making with the boring and the code and the words and the stuff, so what? Why do we care?
Here’s why we care: every calculation, limit and mutation rate from the raw genes to attributes like speed and stamina is now stored in StatData. This one file is becoming the spinal column of a lot of the simulation’s behaviour, so tweaking mutation rates, adding limits, changing names and descriptions, and now adjusting stat formula will all be possible simply by changing the input data here.
And I intend to expose that input data to modding.
Cue the maniacal laughter,
I really should have done this sooner.
It’s a graphical representation of the head type mutation map. Took me half a week to make, and it’s already showing me a number of interesting defects in the map. There’s one in the screenshot above: see if you can spot it.
If you’re wondering where that particular head should be, you can see it’s originally intended location here:
The reason for these defects is simple: so far I’ve had to build this entire map textually, with nothing to go on but the names. That’s the “Gen3C”, “Gen4CaA” and “Gen5CcAAA”. The abstract naming scheme was designed to make it easier to connect them up without being able to see them, and without making mistakes. Guess it didn’t work as well as I’d hoped.
This tool should fix that little problem by allowing me to see what I’m doing. In fact, I’m considering taking it a step further and allowing me to edit the map directly from this window.
Also, I know 0.6.1 had a smaller Content to Time-Spent ratio than previous updates. This was because I spent half my time on cleanup, rather than new features. I justified this by telling myself it would make development faster in the long run. Turns out I was right! This simple tool would have taken at least a few weeks without being able to reuse the DrawableHead class, and the fancy MutationMap classes I made for the Mod Maker all the way back in 0.4.1.
The 0.6.1 release went reasonably well: we managed to track down and fix most reported bugs, and the game now appears to be reasonably stable. There is, however, one remaining bug, and it’s rather major: on a number of AMD graphics cards, the shaders responsible for drawing the creatures and terrain are not working.
Other programmers get bugs that result in exceptions. I get bugs that result in unseen creatures drifting silently through a world of infinite darkness. I love my job.
Nonetheless, this is a frustrating bug. Since Google is turning up nothing, I have no elegant fix for it (a small number of people reporting similar problems with AMD cards on other games, and that’s about it). Since I don’t have a computer with an AMD Card in it, I can’t even go for the inelegant fix for it (namely, continually keep tweaking random stuff until the damn thing works).
My current plan is to create a suite of non-functional shaders, each commenting out or otherwise reducing a different section of the original shader, then distributing these to people with the bug to try out. If we can narrow down what it is specifically that the AMD cards have trouble with, we might be able to work around this issue.
While trying to find that solution, I’ve been further developing my new Stat Control System. It’s actually the first time I’ve been genuinely proud not just of what the game does, but of how it does it. The code behind this new system is a pleasure to work with, and it realises a dream I’ve had for for a while: a centralised, decoupled stat library.
The ultimate goal of this particular piece of work is to extract every creature stat in the game into a central StatData class, which defines all their common characteristics. This includes things like a Printable Name and Description for the UI’s benefit, as well as mutation rate (for genetic stats), upper and lower limits, where to find them in external files (for base stats), and so on.
"Limb Type", //Printable Name
"The shape of the limb.", //Description
1f / 40f); //Mutation Rate
"Limb Tricep Width", //Printable Name
"The cross sectional radius of the limb's upper bone.", //Description
0.6f, //Mutation Rate
0.01f, //Lower Limit
float.MaxValue); //Upper Limit
The way it’s implemented imposes certain restrictions: for instance, I am no longer able to bias the mutation direction for any stat. I wasn’t doing this anyway (well, not deliberately. People who were with us back in 0.4.1 will remember the fat-bug which caused creatures to swell progressively larger until they became crippled by their own weight and died out), but now the compiler will make it physically impossible. Mutations must be random.
A huge bonus of the way it’s implemented is that it’s no longer necessary to hard code stat properties. I still have to hard code their usage and implementation (softening those is stage 2), but all of the properties I mentioned above could easily be extracted to an editable file. Want to triple the mutation rate for leg size? Edit the file! Re-enable negative tail lengths? Edit the file! Translate the UI into ancient Sumerian? File!
As always with cleanup tasks, though, the real advantage will come in making it an order of magnitude faster to add, remove and tweak things in the future.
I’ve also been doing some design work. I’ve decided, rather than just progressively improving the stat calculations for 0.7.0, I’m going to rewrite them as a low level physics engine. The result will work like this:
Each body part will have a given mass and a pivot. A few simple engineering calculations later, we have a linear force and a torque for that pivot. (okay, technically we’re treating the creature as a static mechanism so the proper term is “moment”, but torque’s easier). The linear force will propagate from leaf bones like the head towards the ground contacts, so torque on the neck pivot will come from both head mass and neck mass.
The Passive Strength of the pivot will come from cross-sectional area, and will determine how much torque it can handle. If torque exceeds Passive Strength, the joint will “droop” and the creature will need to continually expend energy on it to compensate. Creatures will need to find their own balance between Passive Strength and Growth Cost for each joint.
My hope is that this bottom-up approach will pay off as creature’s grow more complex. It would be awesome to see stuff from biophysics appearing as emergent properties of the simuation.
Finally, my real life has recently been a little more interesting than usual, and my free time has dropped significantly. I haven’t even had time to feed my swarm of carnivorous nano-squid, let alone reconfigure the Orbital EMP Cannon to deploy them upon the hapless citizens of earth.
It hasn’t had any effect on actual development since I have dedicated time for that, but it’s made it impossible to pay as much attention to spreading the word about the 0.6.1 release as I would have liked. Yeesh, the front page of the website still has 0.6.0 screenshots: that’s how busy it’s been.
Hopefully the event responsible for this will come to a close in the next month or so, and I can get back to the important things in life. Like swarms of carnivorous nano-squid.
First, fixed an introduced bug that was giving canivores the ability to eat vegetation and removing herbivores ability to eat anything. A single missing “-” and herbivory stops being viable. Let’s hope that’s the last major error and the release is stable! (“Haaaaaahahahahahah!”)
Second, since I haven’t spotted any other bugs recently, I followed through on my
threat suggestion to include an Indirect Rover Control Mode, where the rover uses it’s AI to attempt to drive in the direction you point the camera. The result is every bit as broken and insane as you’d expect, and it. is. GLORIOUS.
Finally, Species has adopted a new, less minimalistic logo.
The clade diagram was planned from the start: the symbolism there is an obvious match for Species. I find it amusing how the game’s name correlates with a mass extinction, but then again I’m easily amused.
The cycad leaf was more a case of Throw It In: it was originally going to just be the middle arc, but that looked too much like a planet. While that wouldn’t have been a bad representation of the games’ scope, it’s too reminiscent of space and science fiction in general.
I was about to delete it when I threw some fronds on it on a whim, and liked the result. And the additional symbolism is nice: cycads are an ancient clade*, evocative of prehistory, nature and dinosaurs. All good evolutionary stuff.
We’re still on track to release 0.6.1 on February 1. 🙂
*footnote: if anyone uses the term “living fossil” I will personally inject live cockatoo’s into their bloodstream.** I generally don’t do this because it’s a bugger to find a syringe big enough, but I’ll make an exception.
**footnote: don’t worry, it doesn’t hurt the birds. It’s actually fairly close to how they reproduce in nature (they’re Australian).