Be Koi

Hey Guys,

I was at the Cape Town meetup on the 27th, met the community for the first time and you're all awesome people. I'd like to thank those that were there for their amazing feedback on my game. Your advice was definitely from experience not only in the coding but the entire process of community exposure and pivoting successfully.

With this, I would like to make a first installment to Be Koi - a fishy trip. I think I will split it up as follows:

chapter 0 - Intro
chapter 1 - Fish Mechanics and the zen of the swish.
chapter 2 - Making your toaster do FEM
the rest N - Hopeful heading for future updates

INTRO:

I like coding natural phenomenon stuff. My games tend to grow in scope on technical details and have a severe lack of game play value. I know this, but since I do this for fun, and I have plenty of other things to do for a healthy dose of not fun, these games are just a way of scratching itches my head develops.

During this scratching, flakes of redundant time wasted mostly falls off, but now and then something neat falls out and I'll present the neat things about Be Koi here. Feel free to do something more useful with them.

image

So I have to do some non fun stuff for a bit. But the first installment - zen of the swish - will come soon.

Comments

  • edited
    PART 1 - The Zen of the Swish

    When you push a thing away from you, you and the thing move away from each other the same distance, but by how much each of you moves is divided between the ratio of your masses. Push a light thing, it will move a lot, you move a little, push a heavy thing, you move a lot, it moves a little.

    If a fish wants to move forward, it needs to push water back. If you just swish the tail in random patterns you'll move the water around in random directions, most of those just canceling each other out and your fish don't go nowhere.
    You ideally want to push all the water you touch in the opposite direction you want to move (mainly backwards, sometimes not).

    A simple low poly fish animation meant to just pepper some fishy atmosphere into a scene (Bomb site B in Inferno for example) probably doesn't warrant anything other than the door hinge animation. But if a fish or some other self propelling animal will get a big slice of user attention, then even someone with no knowledge of physics would still "feel" like something is off with a door hinge animation.

    I made a small gif showing a door hinge fin and a few particles, and how none of them are really pushed backwards.

    image

    The next gif illustrates a better swish. More zen, more satisfying to the imagination. This one is called the slider fin. It always angles the fin so that the particles are pushed backwards. It illustrates the path of the zen swish, but not mechanically possible by a fish.

    image

    Okay, so what's the minimum mechanical structure that would permit a zen swish? Turns out we just need 3 bars. One for a fin, one for the tail, and one for the rump.

    The rump serves as the heavy anchoring point, something that won't move too much and ruin our angling of the fin. The tail does the simple door hinge movements, but this is fine since all we want is a point that translates left and right for us to attach the fin to. I apologise for the awkward animations, once I understand Synfig better I'll upload some better ones, but I think they convey the message.

    The 3 bone swish, the minimum amount of rigid bodies that can perform a zen swish.
    image

    Okay cool, this has all been nice toy drawings but how do you actually make a fish DO the swish. How do you implement a zen swish?
    Well here we need some math. Simple math though, and I'll give the algorithm straight up and then explain how it works:

    First, if we are moving a plate or a fin through a fluid on its edge, there will be very little resistance as we slice through the fluid. If we move it against the fluid on its flat side, we'll smack a lot of particles who will in turn smack us in the opposite direction. So somehow we need an equation to tell us how much we have angled our plate into the flow. Is it smacking or is it slicing, or is it some kinky superposition of both?

    I present to you, the DOT PRODUCT. I'm not gonna go into detail about how it works here (click here if you want to know: https://en.wikipedia.org/wiki/Dot_product).

    What the dot product gives us, is how much one vector is like another vector. If they are both unit vectors this won't cause any weird scaling business since they are of equal length, and the dot product will simply tell us how much they are facing in the same direction.
    COOL! since if we said the vector pointing perpendicularly out of the surface is one vector and the plate's velocity is the other vector, we can see how much smack we're getting!

    Now we just need to add some simple lift equations and we got ourselves a thrusting swisher!
    Again, you can just google this but the lift equation just says the resultant thrust you get is some constants times the velocity SQUARED. I sum all the fluid property components up (just experiment, 0.001 worked for me here), and combine the coefficient of lift and surface area into one variable I can assign to each fin.

    This image just shows the vectors involved
    image

    Here is my plate thrust calculation function. If anyone knows how I could syntax highlight this here, please help?
    I am using LibGDX's Vector2 library here, any vector library or half decent game engine will have similar functions.

    private Vector2 CalculatePlateThrust(Vector2 Vel, Vector2 Norm, float coef)
        {
    
            //Calculate the magnitude of our THRUST.
            //Note the MINUS since thrust occurs OPPOSITE TO MOVEMENT OF PLATE
            //Note vel.len2 gives the squared lenth of the vector (from our lift equation V^2)
            //Note coef is a mixture of values denoting how good this plate is at THRUSTING
            //Also note bodge scaling factor of 0.001 since our thrust is too amazing
            float ThrustMag = -Vel.len2()*Vel.dot(Norm)*0.001f*coef;
            //Limit our thrust if it is too magnificent
            if(ThrustMag > 0.2) ThrustMag = 0.2f;
            if(ThrustMag < -0.2) ThrustMag = -0.2f;
            //Copy the normalised surface normal and scale it with our magnitude
            Vector2 Thrust = Norm.cpy().scl(ThrustMag);
    
            return Thrust;
        }



    Don't forget about the rump and the tail! This algorithm should be applied to the tail as well as the rump, scaling the generated force's magnitude based on the surface area smacking into the water. As a tip, make the tail's surface area small, and the rump's surface area large. We don't want the tail to really generate a force since it will just cause the fish to rotate, canceling the much needed translation of the fin that should slice through at an angle. You want to give the rump a large surface area because then it will be anchored by the force of the water, rotate less, meaning the tail will rotate more, meaning you can get much larger translation of the tail while being able to make the rump lighter, resulting in a more efficient fish.

    I believe simple observation of how these tasty meat swords have evolved shows this:

    image

    If we looked at some vectors that will form when we give the FIN and RUMP high coefficient values and the TAIL a low coefficient, the universe will reward us with a swift swisher.

    image


    RIGHT. That's not chapter one done yet but I need to go for now. I will make another entry soon.
    Thanked by 2mattbenic vince
  • edited
    Okay, I'm back.

    I opted for faster posts, and maybe less detail between steps, but if you guys want to ask anything about it, I will answer.
    I made a WEBM clip of the 3-bone fish swish in action but can't seem to make it display on the forums here:

    https://my.mixtape.moe/jdkwst.webm

    I implemented the bones as physboxes in Box2D and hinged them together at the appropriate places.
    One thing to do is to make the fin hinge springy but damped since a loose hinge will not angle itself only slightly to the flow, but completely flip 90 deg and only slice through the water like a piece of toilet paper waved by an eccentric small man.
    For even better swishes, you can apply a small torque to the fin hinge to add a bit of a flick to the end of your swish stroke. This will also have it rotate faster to the new zen angle for the back stroke.

    This just shows with some dumb swim code applied to the bone frame the principles described makes some good looking fish. I will present some simple swim code to you next.

    image

    Some dumb swim code:

    So your fish needs to swish his fins left and right, there exists an optimal point in the stroke where the force vector generated by the swish points partly in the direction you want to go, and partly in a direction that will cause the fish to rotate. If you just want to swim straight, then your left and right stroke should be equal, if you want to turn, they need to be unequal.

    image

    A fish may apply forces to the tail using muscles on its sides that contract or relax, but it's analogous to applying a torque at each bone joint. Luckily Box2D or any other physics engine will provide you with easy functions to apply torques to hinges of bodies.

    We can use a sinusoidal function to generate values from -1 to 1 that could be used to apply a clockwise torque, and anti-clockwise torque to our 3 bones. We can adjust the stroke length by the frequency and the stroke magnitude by the amplitude.

    image


    What if we want to turn? We can simply superimpose a value to the sinusoid making the fin spend more time swishing off-center and causing a moment that will rotate the fish:

    image


    I will finish the zen of the swish chapter here soon. But I need to go cut my hair now.
  • edited
    Hey guys, sorry for the delay the barber took a while.

    So to finish the Zen of the Swish there's not much left to discuss. The magic sits in modeling the underlying principles that cause the behavior we want. If you wanted a bouncing ball you could have animated it, giving you a once off limited use case ball, or you could model the underlying behavior of acceleration and momentum and watch the complex behavior emerge from the dynamic nature of repeatedly applied simple principles.

    I could give some suggestions on AI for where to go but that becomes very application specific. For the game I'm currently making the fish have certain points they want to go to, and certain points they want to avoid.

    Say you have a point you want the fish to go to, you can take a vector pointing in the direction the fish is going as well as a vector pointing from the fish to the point it needs to go to. Getting the angle between the two you and assuming you are working in degrees, you will get an answer from -180 to +180 where 0 means the fish is pointing directly at the goal. Scaling this to the required range you can directly add this as your turning offset and you should see the fish swimming towards the point.

    Some fine tuning to the swimming can be done by deciding between some behaviors the fish may have. I personally used two values, Urgency and Exploration not very complex but ey, it's a fish, not a dolphin.
    Exploration would see the fish randomly changing the stroke frequency and magnitude as well as the offset so the fish would seem like it's just exploring its environment. When an event happened that the fish would respond to (pellet of food dropped, or a finger in the water) it would rate the urgency of the event and then set its stroke magnitude and frequency to match. This combined with the heading difference offset to the sine wave for turning gives quite a nice little scaly swisher.

    Below is a video of my fishing doing their thing (added some of the graphical components I will talk about in the next chapter):

    (THIS IS NOT A MIXTAPE [you can find my youtube music list if you want] THIS IS A FISH VIDEO)
    https://my.mixtape.moe/xbzxri.mp4


Sign In or Register to comment.