A.I, Data and Software Engineering

Smooth nature snake – game re-dev with Kotlin (part 2)

S

This is the second part of creating a smooth nature snake game. You can read the first part here. In this part, we will cover the challenges in simulating the snake’s movement pattern.

How snakes move

Snakes move in zigzag patterns as demonstrated in the following figure.

Smooth nature snake movement patterns
Smooth nature snake movement patterns

We can see that the whole body of the snake forms a shape that is similar to the sine graph.

Sine function: amplitude 1
Sine function: amplitude 1

The sine function is more suitable for serpentine movement but not quite for side-winding. If you do care about the latter, I have tried a couple of equations to get it done and will cover that in another article.

Serpentine movement
Serpentine movement (extract)

Head-first approach

Now we go for the serpentine movement pattern. “How to simulate such movement in code?”

From the animation, it is not hard to see that the body follows its head. When the snake moves, the head moves forward and followed by other body parts. Similarly, we can first calculate the head’s next position and then the other body path can follow the previous location of the head.

Here is the pseudo-code:

snake_move {
     for i from tail to head
          location of body_segment(i) =  location of body_segment(i -1)
     head move to new position
}

OK then, to calculate the position of the head in the real game, we will need more than just a sine function. But to make it simple, let ‘s go for the easiest case first: calculate position supposed that the snake is moving horizontally.

The following equation is to draw the sinusoidal graph based on the phase.

y = A sin(p_t)

Where A\) is the amplitude,latexp_t\) is the phase at time t\), andlatexy\) is the amplitude at the time t\) <!-- /wp:paragraph --> <!-- wp:image --> <figure class="wp-block-image"><img src="https://upload.wikimedia.org/wikipedia/commons/3/3b/Circle_cos_sin.gif" alt="Draw sine and cosine graph based on the phase (angle)"/><figcaption>Draw sine and cosine graph based on the phase (angle)</figcaption></figure> <!-- /wp:image --> <!-- wp:paragraph --> To implement this, we will define two variables, i.e. amplitude and phase. To update the phase, we increase  "mHeadRadial" by a fixed radial value every step. The current amplitude (snake 's y coordinate) is dyOrigin. <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code>... private var mHeadRadial = 0.0 //Moving up and down of sine phase private var mMoveAmplitude = 0f //head moving amplitude ... private fun move() {         ...         //updating phase         mHeadRadial += 2 * Math.PI / mAnimationFR         val dxOrigin = 0f         val dyOrigin = mMoveAmplitude * Math.sin(mHeadRadial)        ... } </code></pre> <!-- /wp:code --> <!-- wp:heading {"level":3} --> <h3>Calculate the actual position of the head</h3> <!-- /wp:heading --> <!-- wp:paragraph --> Above we described how to calculate the current amplitude of the head in case of the horizontal movement. In the game, the snake can go to any direction while keeping the movement pattern. <!-- /wp:paragraph --> <!-- wp:paragraph --> Therefore, we need to calculate the actual point of the head based on the movement direction. Namely, how can we translate the current (dxOrigin, dyOrigin) to the new coordinates? The angle between the movement vector and thelatexx\) axis is \theta\). <!-- /wp:paragraph --> <!-- wp:image {"linkDestination":"custom"} --> <figure class="wp-block-image"><a href="https://en.wikipedia.org/wiki/Rotation_of_axes" target="_blank" rel="noreferrer noopener"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/9f/Rotation_of_coordinates.svg/480px-Rotation_of_coordinates.svg.png" alt="Rotation of axes"/></a><figcaption>Rotation of axes</figcaption></figure> <!-- /wp:image --> <!-- wp:paragraph --> Thank mathematics, we can achieve that by using the axis rotation equations.  <!-- /wp:paragraph --> <!-- wp:paragraph {"align":"center"} -->latex x’ = x \cos \theta + y \sin \theta<!-- /wp:paragraph --> <!-- wp:paragraph {"align":"center"} -->latex y’ = – x \sin \theta + y \cos \theta$

The code for rotating axes is as follows:

/**
         * @param rotateVector vector to rotate from original cartesian coordinate
         * @param x: original x cooordinate
         * @param y original y coordinate
         * @return the transposed PointF containing new coordinates x, y
         */
        fun RotatedCoordinate(x: Float, y: Float, rotateVector: PointF): PointF {
            val diag = VectorNorm(rotateVector)
            val sinOfCurrentMoveVec = rotateVector.y / diag
            val cosOfCurrentMoveVec = rotateVector.x / diag
            val dxTransposed = x * cosOfCurrentMoveVec - y * sinOfCurrentMoveVec
            val dyTransposed = x * sinOfCurrentMoveVec + y * cosOfCurrentMoveVec
            return PointF(dxTransposed.toFloat(), dyTransposed.toFloat())
        }

And the code to update the head position based on movement vector.

move(){
   ...
   val dTranspose = Maths.RotatedCoordinate(dxOrigin, dyOrigin.toFloat(), mCurrentMoveVector)
   //Location of head
   bodyPoints[0].set(
     mHeadTracker.x + dTranspose.x,
     mHeadTracker.y + dTranspose.y
  )
  ...
}

And the result:

Snake movement result
Snake movement result

To be continued…

So in this article, we have covered the smooth nature snake’s movement by using the head-first approach. Another tricky part is to draw the snake’s head that is turning to the movement direction. We will cover this in part 3.

Add comment

💬

A.I, Data and Software Engineering

PetaMinds focuses on developing the coolest topics in data science, A.I, and programming, and make them so digestible for everyone to learn and create amazing applications in a short time.

Categories