In the previous post of this game tutorial series, we added the enemy script and it moves according to the player speed variable in sync with the bg. In this part we will do the collision response for the enemy. We will also setup the roadblocks and the barrels.
We set the position of the enemy car using set_pos method in Godot. This is because our enemy car is in Kinematic mode. We could use the Rigid mode but this forces us to set the velocity of the car instead of the position. As you can see our bg scroll speed is not a constant one. So setting the velocity of a RigidBody will not make it sync with the bg scrolling speed.
By setting the mode to Kinematic, Godot releases some of the physics constraints from the object and expects us to update the position. Now take a look at this table.
Add a signal callback for “body_enter” on the player script. The complete player code will now look like this.In Godot, not every bodies will report collisions to our script. So we need to check where we want to write our callbacks and plan those scripts so. Our enemy and player are both Kinematic and if we use two Kinematic bodies, what? No callbacks on anyone? Ok, don’t go and change your nodes but remember this, we used RigidBody node instead of KinematicBody for both player and the enemy. We only changed the behaviour mode to Kinematic. Now check the table again. A collision with two rigid bodies and the callback will be reported to… yeah, BOTH!
extends RigidBody2D var speed = 0 var _max_speed = 500 var _acceleration = 0 func _ready(): connect("body_enter",self,"_on_body_enter") set_process(true) func _process(delta): speed += _acceleration speed = min(speed,_max_speed) if(speed < _max_speed): _acceleration += delta func _on_body_enter(other): print(other.get_name())
We added a signal callback in the _ready method. Let us take a look at the connect method. It is inherited from the Object class so almost all the classes in Godot has it. This is a huge plus point of Godot Engine as we don’t need to create any custom notification systems. Godot supports custom signals with the same api. We will look into it later.
The first argument of this connect method is none other than the signal name. We are telling the Player node to listen to its own “body_enter” signal. When a RigidBody collides with another, it emits a “body_enter” signal. You can refer the Godot class reference to know more about different signals for different node types. The third argument is the function callback to run when this signal emits. The second one is where the function callback is written. The whole thing will be like the script telling its own class to listen for the signal “body_enter” and when it happens, run “_on_body_enter” callback written on this node script(self).
We can connect to signals in the editor itself by selecting the Player node and going to the ‘Node’ tab. But I don’t recommend this because it is very difficult to see what is happening if things are not visible in plain sight. If we open this project after two months and we don’t know which nodes are connected to signals. I recommend to keep things simple and contained. So I put the signal connection in the script itself so that I can look at the script and know things right away by just looking at it. You may follow either way as you prefer.
Run the game and check if there is something coming on the Output tab when the enemy hits the player. You may want to move the position of the enemy so that it will collide with the player while playing.
Before running your game, you need to set the ‘Contacts Reported’ property of the Player node to 1 and enable the ‘Contact Monitoring’. Usually we setup all things right and add scripts but the collision reporting won’t work because we didn’t enable reporting. So please remember this, even I forget this sometimes.
Make sure you disable the gravity scale parameter to 0 inside the Enemy Scene. I’ll tell you later why this is set to 0.
I just duplicated the Enemy and moved to the side so you can see it passing by. Don’t mind it. We will remove it anyway.
Great! The output shows that the enemy callback is working. Now change the Player script to the one like below.
extends RigidBody2D var speed = 0 var _max_speed = 500 var _acceleration = 0 func _ready(): connect("body_enter",self,"_on_body_enter") set_process(true) func _process(delta): speed += _acceleration speed = min(speed,_max_speed) if(speed < _max_speed): _acceleration += delta func _on_body_enter(other): if(other.is_in_group("enemy")): other.hit_by_player() speed = 0 set_process(false)
We just check if the enemy is in “enemy” group and if yes, then call its method ‘hit_by_player’. Yes, we need to add the enemy to the ‘enemy’ group and create a ‘hit_by_player’ method inside the enemy script to make it work.
So change the enemy script.
extends RigidBody2D var _player = null var _speed_factor = 0.5 func _ready(): _player = get_node("../Player") add_to_group("enemy") set_process(true) func _process(delta): set_pos(get_pos() + Vector2(0, _player.speed * _speed_factor * delta)) func hit_by_player(): set_process(false) set_mode(MODE_RIGID) set_linear_velocity(Vector2(0,-_player.speed * _speed_factor))
We added the enemy to the group on the _ready callback. As I said earlier for the case of signals, this group setting can also be done inside the editor. But now, there is no option in editor to show all used groups. So we need to manually check all nodes to know to which group it has added. This is too difficult and one spelling mistake can cause the logic to break. So we do this on the script itself.
Inside the ‘hit_by_player’ method, we change the mode to Rigid and apply a linear velocity. Because we change the linear velocity, we don’t need to update the position so we disable the _process callback. When we switch it to rigid mode, the body drops down because of gravity. But we already set the gravity scale to 0 and the enemy will stay there.Godot Engine game tutorial for beginners – Create a 2D Racing Game 5 Click To Tweet
RoadBlock and Barrels
Open both scenes and set their mode to Kinematic and set their gravity scale to 0. Now create the scripts, the code is same for both, but we create two separate scripts for each of them – res://packed/barrel/Barrel.gd, and res://packed/roadblock/RoadBlock.gd
extends RigidBody2D var _player = null var _speed_factor = 1 func _ready(): _player = get_node("../Player") add_to_group("block") set_process(true) func _process(delta): set_pos(get_pos() + Vector2(0, _player.speed * _speed_factor * delta)) func hit_by_player(): set_process(false)
I thought we could add the spawner in this part, but this one became a long one and we will do that in the next part.
Thanks for checking out.
The source can be downloaded from here.