Creating a proc. animated goop creature
Hi! This was my semester long project for the advanced programming class. Below I will explain how i got to where this thing is now, what went wrong along the way, and how you can make something similar in your engine of choice!
How I got here
First I tried to make a system for IK legs that were intended to be a drop-in addon. After assuming that legs are just a series of knees protruding from the central axis I created an atrocious system that could only handle legs inside a very specific range of values. Tweaking it a lot didn't help, turned out that this approach is deeply flawed as it assumes that any position can be reached by a constrained leg, which is not true, especially with the algorithm used. Feet would often sink into the ground despite my best efforts (as seen in gif above) as well as be placed to the side of where they were supposed to be.
Physically accurate amoeba
Next came a physically simulated blob. It could sense ground all around it and its legs would realistically push and pull on it (depicted as simple lines to avoid previous complications). In this system the dependence chain looked like this:
In addition to that, the system for placing legs was not based on intent and just placed them at the point closest to the ground. Any attempts to stabilize system failed. It was physically accurate but looked ridiculous and unintelligent.
Final version: Gunkspider
(Scroll to the top of the page to play with it!)
After making stupid things and failing I returned to one of my sources - the Rain World GDC talk about making daddy long legs creature in their game. The problem of the system entering a weird feedback loop was avoided by only having systems feed into each other in one direction:
The elements in this chain seem kind of abstract at the moment so I'll try to break them down one by one so you can see how this works under the hood.
Movement & Raw Data
Click, tap, or use direction inputs to move the target and see how the spider reacts.
The body moves toward the target ( User Input ) . While doing so it is affected by gravity. The acceleration and damping of the body are based on % of legs securely attached to a surface, making the critter more responsive the more legs are touching a wall.
If you hold spacebar while playing with the spider you will see different icons pop up around the legs. They represent different positions important to how they function:
- Red squares - these are the spots where the legs would end up in an ideal world. The way they are facing changes based on the current movement direction and gravity like so
- Purple diamonds - these are the closest viable positions a leg can actually occupy. Calculated by finding the closest point to the ideal position on the navmesh. To avoid the leg changing its mind where it should be all of a sudden - the realistic position is only allowed to move when the leg is lifted OR if the old position is too far away to be reachable
- Green triangles - tips of the legs. Lifting leg is visualized by them getting bigger. When they are determined to reach a position but are unable to do so they vibrate in despair. Legs have a kind of a state machine for being lifted and placed:
- i wanna move and i can - there are enough legs securely attached for another one to be lifted, and that leg is out of the ideal range
- i have to move - the leg is stretched further than should be possible given its length. This disregards the number of legs currently planted and forcefully lifts the leg
- i'm lifted and on my way - leg is not attached, moves towards its realistic position, realistic position is allowed to change while this is happening
- i have arrived - when a lifted leg is close enough to its target it will secure itself on the spot. Because the legs move fast and there are only so many physics frames in a second it pulls a trick: if it would move further than is the distance to target - it just places itself where it should go instead of overshooting and correcting afterwards
Now that we know where the leg starts (body) and ends (its tip), we can draw everything in between. Instead of doing anything complex and sophisticated i just use a rope simulation.
The simulation assumes the leg is a chain made of elastic links that push and pull on each other in an attempt to reach their desired length.
While doing this - the positions, speeds and forces affecting each link in the chain are stored in an array and do not touch the objects that actually display the legs, allowing the simulation to run faster.
What it does is:
- fetch link positions, and speeds
- calculate distances between them
- if they re closer than half their desired length - push them apart, if further - pull them together (calculated using the elastic deformation formula)
- apply gravity & acceleration resulting from moving to all links
- make sure the first and last have no forces applied to them and remain where they need to be
- change speeds based on forces
- change positions based on speeds
Because this simulation is relatively cheap to run and because it does not like high delta time values - each physics time step is divided into 5 smaller ones. After all the calculations are complete - the positions are fed into the line renderer that displays the legs.
This one s gotta be its own big category because there's a lot to talk about.
To get the bubbling mass to look how it does i just used a particle emitter that spawns black round particles in a circle and pushes them away before attracting them back to centre.
- line renderer creates a limb of non uniform size with a texture on it.
- that texture has a scrolling perlin noise added to it to make it non uniform
- all values below threshold are made transparent, all above - white
- self modulate to be same color as body
What happens to each eye during a physics step:
- experience slight pull towards body centre
- if outside the large dotted line - experience strong pull towards body centre
- check distance to other eyes
- if personal spaces intersect - repel each other proportional to how much they overlap (also larger eyes push with more force)
- get pulled in a random direction each frame to keep the system from settling into a static equilibrium
- change speed based on forces
- dampen speed to avoid perpetual motion in same direction
- change position based on speed
- roll dice to see if you want to blink
- if yes - scale the eye vertically and change random offset from target
- slowly stretch eye back to its open position
- move iris to look at target (+ offset that changes when you blink to keep things interesting)
Fun fact! To make sure the eyes do not visually go out of the body, each eye has a black outline to make sure that even if its sticking out a bit it still looks fine.
from right to left:
- color correction
- blur and offset image, incorporate into main using multiplication
- blur image, make everything darker than threshold into pure black, keep rest intact. This results in smoothed edges and more organic shapes
Eyes and parts of cursor are not affected by the last 2 as they are rendered after them.
That's all folks!
|Tags||2D, Exploration, Horror, Procedural Generation, Short, spider, toy|