# Blog

## Isometric 3D in 2D Environment #4 Fall and jump

Apr 21, 2021 | 4 minutes read

Series: 3D_iso_in_2D

This is part 4 of the 2d/3d(game/engine) conversion used in my puzzle game: Ladder Box, which is available on Steam: You can find all the textures, scripts in this GitHub repo: https://github.com/fengjiongmax/3D_iso_in_2D

Godot 3.2.x is used in this project, you can check the commits for what’s new in each part of this series.

# Jump state

When a movable has a block at its same height in its moving direction, that movable will switch to the jump state.
Of course, before actually make the jump, the block needs to know if the way of jumping is clear or not, we write such function in movable.gd:

``````func is_direction_jumpable(direction:Vector3) -> bool:
if direction in [Vector3.UP,Vector3.DOWN]:
return false

var _self_up_coordinate = game_pos + Vector3.UP
if !Grid.coordinate_within_rangev(_self_up_coordinate):
return false
var _self_up_item = Grid.get_game_axisv(_self_up_coordinate)
if _self_up_item != null:
return false

var _direction_up_axie = game_pos + direction + Vector3.UP
if !Grid.coordinate_within_rangev(_direction_up_axie):
return false

var _direction_up_item = Grid.get_game_axisv(_direction_up_axie)
if _direction_up_item != null:
return false

return true
``````

What’s in this function:

• check if `movable.game_pos.y +1` is empty or not
• check if the game position after jump and move is clear or not.

Now we can check if the block can jump at the end of the `set_next_target` function in the move.gd:

``````func set_next_target():
......
else:
if movable.is_direction_jumpable(direction):
_state_machine.switch_state("jump",{"direction":direction})
else:
_state_machine.switch_state("idle")
pass
``````

There are also scenarios when an unmovable is right next to a movable, so from idle to jump is necessary, so in `idle.gd`, modify the `_command` function:

``````func _command(_msg:Dictionary={}) -> void:
if _msg.keys().has("direction"):
var command = _msg["direction"]
match command:
Vector3.FORWARD,Vector3.BACK,Vector3.LEFT,Vector3.RIGHT:
pass
_:
return
var _move_target_coordinate = movable.game_pos + command

if Grid.coordinate_within_rangev(_move_target_coordinate) &&\
Grid.get_game_axisv(_move_target_coordinate) == null:
_state_machine.switch_state("move",{"direction":command})
elif movable.is_direction_jumpable(command):
_state_machine.switch_state("jump",{"direction":command})
else:
print("%s not able to move" % _move_target_coordinate)
``````

Then in our `jump.gd`, let’s write something that stores the move direction, and print something when movable enters jump state:

``````extends StateBase

var move_direction:Vector3

func _enter(_msg:={}) -> void:
if !_msg.keys().has("direction"):
return

move_direction = _msg["direction"]
print("enter jump")
``````

And in our `main.gd`, to test the code we’ve written:

``````new_movable(0,0,0)
new_unmovable(0,0,2)
new_unmovable(1,0,0)
``````

Then run the project, you can see the output by hitting ‘Z or ‘X’.

Like in the move state, we will have a `set_next_target` function and some variables.

``````var engine_direction:Vector2 = GridUtils.game_direction_to_engine(Vector3.UP)

var target_game_pos:Vector3
var target_z:int
var target_engine_pos:Vector2

func set_next_target():
target_game_pos = movable.game_pos + Vector3.UP
var _target_game_pos_obj = Grid.get_game_axisv(target_game_pos)
if Grid.coordinate_within_rangev(target_game_pos) && _target_game_pos_obj == null:
var _target_v3 = GridUtils.game_to_enginev(target_game_pos)
target_engine_pos = Vector2(_target_v3.x,_target_v3.y)
target_z = _target_v3.z-1
movable.set_game_posv(target_game_pos)
else:
_state_machine.switch_state("idle")
``````
• and call the set_next_target function at the end of the _enter function

note we did not use the direction passed from the last state to calculate `engine_direction`, but we need to store this because the jump state always goes up, and when movable exits jump state, it can go back to move state, then we will need `move_direction`.

Then in the `_update` function, we will move the movable, bit by bit like in the move state:

``````func _update(_delta:float) -> void:
var _after_move = movable.position + engine_direction * _delta * movable.MOVESPEED
var _reach_target = Math.is_betweenv(movable.position,_after_move,target_engine_pos)

if !_reach_target:
movable.position = _after_move
else:
movable.position = target_engine_pos
_state_machine.switch_state("move",{"direction":move_direction})
``````

And run the scene and hit ‘Z’, then you can see this: the movable floats after it moves past the unmovable.

# Fall state

Just like move and jump state, there will be a `set_next_target` and all those variables:

``````func set_next_target():
target_game_pos = movable.game_pos + Vector3.DOWN
var _target_game_pos_obj = Grid.get_game_axisv(target_game_pos)

if Grid.coordinate_within_rangev(target_game_pos) && _target_game_pos_obj == null:
var _target_v3 = GridUtils.game_to_enginev(target_game_pos)
target_engine_pos = Vector2(_target_v3.x,_target_v3.y)
target_z = _target_v3.z
movable.set_game_posv(target_game_pos)
else:
if movable.is_direction_movable(move_direction):
_state_machine.switch_state("move",{"direction":move_direction})
elif movable.is_direction_jumpable(move_direction):
_state_machine.switch_state("jump",{"direction":move_direction})
else:
_state_machine.switch_state("idle")
``````

And we can copy the `_enter` function from the jump state and paste it in fall.gd, and modify a little bit in the `_update` function:

``````func _update(_delta:float) -> void:
var _after_move = movable.position + engine_direction * _delta * movable.MOVESPEED
var _reach_target = Math.is_betweenv(movable.position,_after_move,target_engine_pos)

if !_reach_target:
movable.position = _after_move
else:
movable.position = target_engine_pos
set_next_target()
``````

Then run the scene and hit ‘Z’: run the scene hit ‘X’: That’s it, we have the basics for the jump and fall.

Till now, we don’t have many movables or complicated unmovable structures on our board like in Ladder Box, you can try to add more movables and see what happens. In the next part, we will test the code with different board designs and improve our codes, stay tuned! You can check out the Github repo for the code committed.