Teki – Jump, crawl and gravity

Reading Time: 7 minutes

In the very last chapter we have implemented the touch of the up and down arrows. We gonna use these arrows to the jump and crawl control our heroine, Teki. In our game the controls means, that she will be able to jump and to crawl. She needs this moves, because she has to avoid the barriers front of her.

So our task in this chapter gonna be to implement this moves. For this we will define the gravity system as well.

Step 1 – Explanation of the movements

Before we start, we have to understand how these two movements are working. The system will be the same for the jump and for the crawl.

When we touch the up arrow, then Teki’s velocity in the horizontal direction will be even slower. When she has reached the maximum height of the jump, then the velocity will be arround 0, then it will be increased again, but now backwards.

The velovity will be modified using the gravity variable. It’s going to be a constan during the game.

So the question is where do we know if Teki reached the max height of the jump? For this we will have a variable, which will contain the horizontally distance of the jump (or of the crawl) and will be increased the velocitiy of the jump. If the distance reached a specific value, then the velocity of the jump will be increased by the gravity until Teki reaches the ground.

Let’s start coding. 🙂

Step 2 – Declare variables

Basically we will have 4 new variables, to handle the movements. They gonna be declared at the beginning the TekiTutorial::class.

var jumpDistance = 0f
var jumpPushed = false
var crawlPushed = false

var jumpVelocity = 0f

Step 3 – Touching of the arrows

When we have touched the up or the down arrow Teki don’t want to jump or crawl again during the movement, otherwise she will travel to the Space. 🙂 We have to handle this.

Until the boolean variables are true, the jump and crawl algorithms should’t run. So we will have 2 if-statements inside of the touch of the arrows.

``````fun touchUpAndDownArrow()
{
val upArrowCircle = Circle(
size*2,
screenHeight - (size*4 + size*0.5f),
size)
val downArrowCircle = Circle(
size*2,
screenHeight - (size*2),
size)
if(Gdx.input.justTouched())
{
//Touch of the upArrow
if(upArrowCircle.contains(Gdx.input.getX().toFloat(), Gdx.input.getY().toFloat()))
{
Gdx.app.log("justTouched", "upArrow")
if(!crawlPushed && !jumpPushed)
{
jumpPushed = true
}
}
//Touch of the downArrow
if(downArrowCircle.contains(Gdx.input.getX().toFloat(), Gdx.input.getY().toFloat()))
{
Gdx.app.log("justTouched", "downArrow")
if(!crawlPushed && !jumpPushed)
{
crawlPushed = true
}
}
}
}

touchUpAndDownArrow()``````

Step 4 – Add the gravity

Next step is to add to the game the gravity variable. This goes to to the TekiTutorial::class.

var gravity = 0.0f

The value of it could be set only, when the game is created, because we will use here again the size variable to have the same game on every device.

The value of the gravity gonna be 1% of the size. This line of code goes after the set of the size variable.

gravity = (size*0.01).toFloat()

Step 5 – The algorithm

First we will have an if-statement when the game is running. It will detect which button was touched.

if (jumpPushed)
{

}
else if(crawlPushed)
{

}

handleJump()

When the jumpPushed is true, then this method will run.

Our Teki, when she is in the air, won’t tread with her legs, she will pull her legs up. So first we will set the tekisStates to 3.

tekiState: This variable is the responsible which picture will be shown on the screen.

Then we will start counting the distance of the jump using the jumpVelocity, what we have to initialize in the create() method.

jumpVelocity = (size*0.29).toFloat()

Next we are going to define an if-else-if-statement. This will check where is our Teki in her jump, so the distance of the jump.

if (jumpDistance in 0f..(size*4))
{

}
else if (jumpDistance in (size*4 + 1)..(size*8))
{

}
else
{

}

The first condition checks the upwards part. Here we are going to use again the size variable. Otherwise Teki will jump faster or slower on the different devices. The second condition is already the downwards part. The third condition is when Teki is outside of this ranges. Again because of the different resolutions of the phones, it can happen thet our heroin won’t land where the ground is, so we have to take her back there.

First condition

jumpVelocity -= gravity
tekiY += jumpVelocity

Second condition

In the second condition we have to check again where was the last position, when the jump has been finished. If it is finished, but Teki is still not at the initial position, then we have to take her there back again.

jumpVelocity += gravity
tekiY += -jumpVelocity
if (jumpDistance >= (size*8).toInt())
{
jumpPushed = false
jumpDistance = 0f
tekiY = screenHeight*0.22f
}

Third condition

if (jumpDistance >= (size*8).toInt())
{
jumpPushed = false
jumpDistance = 0f
tekiY = screenHeight*0.22f
}

Finally the handleJump() method.

``````fun handleJump()
{
tekisStates = 3
jumpDistance += jumpVelocity
if(jumpDistance in 0f..(size*4))
{
jumpVelocity -= gravity
tekiY += jumpVelocity
}
else if(jumpDistance in (size*4 + 1)..(size*8))
{
jumpVelocity += gravity
tekiY += -jumpVelocity
if(jumpDistance >= (size*8).toInt())
{
jumpPushed = false
jumpDistance = 0f
tekiY = screenHeight*0.22f
}
}
else
{
if(jumpDistance >= (size*8))
{
jumpPushed = false
jumpDistance = 0f
tekiY = screenHeight*0.22f
}
}
}

handleJump()``````
handleCrawl()

The handleCrawl() method will be much sorter, because Teki just go a little bit lower, thenafter up. No gravity and velocity needed.

The second difference is, that Teki won’t make such a distance down what she does during the jump. It means, she will reach the original position faster, so she has to be down a little bit longer.

This is the body of the handleCrawl() method.

``````fun handleCrawl()
{
tekisStates = 3
jumpDistance += jumpVelocity
if(jumpDistance >= size*9)
{
crawlPushed = false
jumpDistance = 0f
tekiY = screenHeight*0.22f
}
}

handleJump()``````

One more thing, what we have to add in case of the crawl is that our Teki has to be down, when we push the down arrow. It has to be done directly in the touch method of the down arrow, otherwise she never comes back to the ground. It means, we gonna set here the tekiY variable.

``````if(downArrowCircle.contains(Gdx.input.getX().toFloat(), Gdx.input.getY().toFloat()))
{
Gdx.app.log("justTouched", "downArrow")
if(!crawlPushed && !jumpPushed)
{
crawlPushed = true
tekiY -= (size*0.55).toFloat()
}
}

touchUpAndDownArrow()``````
Call the methods

What is still missing, is the call of these methods. They have to be called inside the if-statement, what we have already in the render() method, when the game is running.

if (jumpPushed)
{
handleJump()
}
else if (crawlPushed)
{
handleCrawl()
}

Run the app. If you have done everything right, then your Teki will jump and get down, when you press the buttons.

TekiTutorial::class

Finally, as always the source code….

``````import com.badlogic.gdx.ApplicationAdapter
import java.util.*

class TekiTutorial : ApplicationAdapter()
{
var batch: SpriteBatch? = null

var screenWidth = 0
var screenHeight = 0

var gameState = 0

// Background
var backgrounds = mutableListOf()
val numberOfBackgrounds = 3
var backgroundX = FloatArray(numberOfBackgrounds)

// Teki
var tekisLegStates = mutableListOf()
var tekiX = 0f
var tekiY = 0f
var tekisStates = 0

//Timer
var timer: Timer = Timer()

var numbersTextures = mutableListOf()

//Score
var score = 0
var nextLevel = 20
var scoreTextTexture: Texture? = null

//Level
var level = 0
var levelTextTexture: Texture? = null

//Up and Down Arrow
var upArrowTexture: Texture? = null
var downArrowTexture: Texture? = null

//Jump and crawl
var jumpDistance = 0f
var jumpPushed = false
var jumpVelocity = 0f
var crawlPushed = false

var gravity = 0.0f

var velocity = 12
var distance = 0

var size: Float = 0f

override fun create()
{
batch = SpriteBatch()

screenWidth = Gdx.graphics.width
screenHeight = Gdx.graphics.height

size = (screenWidth*0.06).toFloat()
gravity = (size*0.01).toFloat()

//Background
for(i in 1..numberOfBackgrounds)
{
}

//Teki
tekiX = screenWidth*0.35f
tekiY = screenHeight*0.22f

for (i in 0 until numberOfBackgrounds)
{
backgroundX[i] = screenWidth.toFloat() * i
}

// Numbers
for(i in 0..9)
{
}

//Scoring
scoreTextTexture = Texture("score.png")

//Level
levelTextTexture = Texture("level.png")

//Up and Down Arrow
upArrowTexture = Texture("upArrow.png")
downArrowTexture = Texture("downArrow.png")

jumpVelocity = (size*0.29).toFloat()
}

override fun render()
{
batch!!.begin()

for(i in 0 until numberOfBackgrounds)
{
drawBackground(i)
}

distance += velocity

if (gameState == 0) // Main Menu screen
{
drawTeki(0)

if(Gdx.input.justTouched())
{
gameState = 1
startTimer()
}
}
else if (gameState == 1) //Running game screen
{
for(i in 0 until numberOfBackgrounds)
{
if (backgroundX[i] <= -screenWidth)
{
if (i == 0)
{
backgroundX[i] = backgroundX.max()!!.plus(screenWidth) - velocity
}
else
{
backgroundX[i] = backgroundX.max()!!.plus(screenWidth)
}
}
else
{
backgroundX[i] = backgroundX[i] - velocity
}
}

//Teki
if(distance % 20 == 0)
{
when(tekisStates)
{
0 -> tekisStates = 1
1 -> tekisStates = 2
2 -> tekisStates = 1
3 -> tekisStates = 0
}
}

if(jumpPushed)
{
handleJump()
}
else if(crawlPushed)
{
handleCrawl()
}

drawTeki(tekisStates)

touchUpAndDownArrow()

drawUpArrow()
drawDownArrow()

scoring()
leveling()
}
else if (gameState == 2) //Game Over screen
{
stopTime()

scoring()
leveling()
}
else if (gameState == 3) //High Score screen
{

}

batch!!.end()
}

fun startTimer()
{
timer = Timer()

{
override fun run()
{
if(score == nextLevel)
{
level++
nextLevel += 20
velocity += 2
}

score++
Gdx.app.log("score", "\$score")
Gdx.app.log("level", "\$level")
}
}, 0, 1000)
}
fun stopTime()
{
timer.cancel()
}

fun handleJump()
{
tekisStates = 3
jumpDistance += jumpVelocity
if(jumpDistance in 0f..(size*4))
{
jumpVelocity -= gravity
tekiY += jumpVelocity
}
else if(jumpDistance in (size*4 + 1)..(size*8))
{
jumpVelocity += gravity
tekiY += -jumpVelocity
if(jumpDistance >= (size*8).toInt())
{
jumpPushed = false
jumpDistance = 0f
tekiY = screenHeight*0.22f
}
}
else
{
if(jumpDistance >= (size*8))
{
jumpPushed = false
jumpDistance = 0f
tekiY = screenHeight*0.22f
}
}
}
fun handleCrawl()
{
tekisStates = 3
jumpDistance += jumpVelocity
if(jumpDistance >= size*9)
{
crawlPushed = false
jumpDistance = 0f
tekiY = screenHeight*0.22f
}
}

fun drawBackground(number: Int)
{
// Background
batch!!.draw(
backgrounds[number],
backgroundX[number],
0f,
screenWidth.toFloat(),
screenHeight.toFloat())
}
fun drawTeki(number: Int)
{
//Teki
batch!!.draw(
tekisLegStates[number],
tekiX,
tekiY,
size*1.2f,
size)
}
fun scoring()
{
batch!!.draw(
scoreTextTexture,
size,
screenHeight - size*1.2f,
size*2,
size*0.8f)

val scoreDigits = mutableListOf()
var _score = score
if (_score == 0)
{
batch!!.draw(
numbersTextures[0],
size*3 + size*0.5f,
screenHeight - size*1.2f,
size*0.5f,
size*0.8f)
}
else
{
while (_score > 0)
{
_score /= 10
}
scoreDigits.reverse()

for (i in 0 until scoreDigits.count())
{
batch!!.draw(
numbersTextures[scoreDigits[i]],
size*3 + size*0.5f + (size*0.5f)*i,
screenHeight - size*1.2f,
size*0.5f,
size*0.8f)
}
}
}
fun leveling()
{
batch!!.draw(
levelTextTexture,
size,
screenHeight - size*1.2f - size,
size*2*0.93f, //*0.93 - to don't be stretched
size*0.8f)

val scoreDigits = mutableListOf()
var _level = level
if (_level == 0)
{
batch!!.draw(
numbersTextures[0],
size + size*2*0.93f + size*0.5f,
screenHeight - size*1.2f - size,
size*0.5f,
size*0.8f)
}
else
{
while (_level > 0)
{
_level /= 10
}
scoreDigits.reverse()

for (i in 0 until scoreDigits.count())
{
batch!!.draw(
numbersTextures[scoreDigits[i]],
size + size*2*0.93f + size*0.5f + (size*0.5f)*i,
screenHeight - size*1.2f - size,
size*0.5f,
size*0.8f)
}
}
}
fun drawUpArrow()
{
batch!!.draw(
upArrowTexture,
size,
size*3 + size*0.5f,
size*2,
size*2)
}
fun drawDownArrow()
{
batch!!.draw(
downArrowTexture,
size,
size,
size*2,
size*2)
}

fun touchUpAndDownArrow()
{
val upArrowCircle = Circle(
size*2,
screenHeight - (size*4 + size*0.5f),
size)
val downArrowCircle = Circle(
size*2,
screenHeight - (size*2),
size)
if(Gdx.input.justTouched())
{
//Touch of the upArrow
if(upArrowCircle.contains(Gdx.input.getX().toFloat(), Gdx.input.getY().toFloat()))
{
Gdx.app.log("justTouched", "upArrow")
if(!crawlPushed && !jumpPushed)
{
jumpPushed = true
}
}
//Touch of the downArrow
if(downArrowCircle.contains(Gdx.input.getX().toFloat(), Gdx.input.getY().toFloat()))
{
Gdx.app.log("justTouched", "downArrow")
if(!crawlPushed && !jumpPushed)
{
crawlPushed = true
tekiY -= (size*0.55).toFloat()
}
}
}
}
}

TekiTutorial::class``````

Questions

I hope the description was understandable and clear. But if you have still questions, then leave me comments below! 😉

Have a nice a day! 🙂

Click to rate this post!
[Total: 0 Average: 0]
Follow and like us:

stay informed!

Subscribe to receive exclusive content and notifications