Play sound in Godot when a kinematicbody2D collides with a tile map? - godot

The player in my game is a kinematicbody2D, and I want a sound effect to play whenever it collides with the tile map. My player uses move_and_slide() to collide with the tile map.
How can I trigger a sound to play when the player hits the tilemap? I only want the sound to play after the player initially collides with the tile map; it shouldn’t play over and over if the player is sitting on the floor.
I know how to use sounds with the StreamAudioPlayer, I just need help triggering the sound to play when I want it to play.

The simpler way to keep track of is_on_floor before and after move_and_slide.
For example:
var was_on_floor = is_on_floor()
move_and_slide(velocity, Vector2.UP)
if is_on_floor() and not was_on_floor:
$sound.play()
Whenever you call move_and_slide Godot updates the values you get from is_on_floor, is_on_wall and is_on_ceiling. To know what is wall, floor or ceiling, Godot uses the vector you pass as second parameter (if you pass Vector2.ZERO then everything is wall).
If you only care to check if it collided with something at all, you may use get_slide_count after move_and_slide. The function get_slide_count should give you 0 if there were no collisions:
if get_slide_count() > 0:
$sound.play()
Or you could check if the velocity that move_and_slide returns a different velocity that the one you passed. Meaning it did collide (or slide).
var old_velocity = velocity
velocity = move_and_slide(velocity, Vector2.UP)
if velocity != old_velocity:
$sound.play()
Alternatively you may use get_slide_collision, which gives you a KinematicCollision2D object:
for i in get_slide_count():
var collision = get_slide_collision(i)
print("Collided with: ", collision.collider.name)
Which will let you identify with what did the KinematicBody2D collide with, and then you could pick a sound accordingly.

Related

How do I check if two KinematicBody2D's are overlapping in Godot?

I've just gotten into coding, and I'm trying to make a simple duck shooter game in Godot 3.0. I have a crosshair(Kinematicbody2D) and ducks flying across the screen(also KinematicBody2D). Is there a way that I can detect if the duck and crosshair are overlapping? Like an if statement?
In case anyone's curious, this is the code I've got so far (This is the duck script and the comments are what I need to add in on that line).
func _physics_process(delta: float) -> void:
position.x += 4
if (position.x > 1600):
Score -= 1
position.x = rand_range(-250, -1000)
if Input.is_action_just_pressed("shoot"):
#check if overlapping with crosshair
Score += 1
#bird explodes in feathers
position.x = rand_range(-250, -1000)
else:
Score -= 0.25
I don't think it makes sense to make the crosshair into a physics body. Does it collide/push/bounce off obstacles? Much less a KinematicBody2D. Do you need move_and_slide or can you get away with writing the position? So here are some alternatives to go about it, before I answer the question as posted.
Input Pickable
If you want to find out if the pointer interacts with a physics body, you can enable input_pickable, and you will get an "input_event" signal whenever it happens.
Mimic input Pickable
Otherwise you can get the position of the pointing device in an _input event and query what physics objects are there. Something like this:
if (
event is InputEventScreenDrag
or event is InputEventScreenTouch
or event is InputEventMouse
):
var viewport := get_viewport()
var position:Vector2 = viewport.get_canvas_transform().affine_inverse().xform(event.position)
var objects := get_world_2d().direct_space_state.intersect_point(position)
for object in objects:
print(object.collider)
And see if the one you want is there.
Area2D
Another option is to make the crosshair into an Area2D, then you can ask the Area2D for its overlapping_bodies, and see if the one you want is there. Or call overlaps_body passing your physics body as parameter and it returns true or false if it is overlapping the Area2D or not, respectively.
KinematicBody2D collisions
And to answer the question on the title, if you have two kinematic bodies, if you moved them with move_and_* methods (which is how it is intended). You can a lists of what they collided with like this:
for i in get_slide_count():
var collision = get_slide_collision(i)
print("Collided with: ", collision.collider.name)
Otherwise, you can do this:
var parameters := Physics2DShapeQueryParameters.new()
parameters.transform = global_transform
parameters.set_shape($CollisionShape2D.shape)
var results = get_world_2d().direct_space_state.intersect_shape(parameters)
for result in results:
print(result.collider)

Godot - Enemy does not move to player when player is on the left

The enemy in my game will not move towards the left when player is on the left but will move to the right. Then when the player is on the right the enemy will move to the player.
code for enemy:
extends KinematicBody2D
var run_speed = 100
var velocity = Vector2.ZERO
var collider = null
func _physics_process(delta):
velocity = Vector2.ZERO
if collider:
velocity = position.direction_to(collider.position) * run_speed
velocity = move_and_slide(velocity)
func _on_DetectRadius_body_entered(body):
collider = body
func _on_DetectRadius_body_exited(body):
collider = null
I suspect you are using an Area2D and it is detecting something other than the player character. I remind you can use collision_mask and collision_layer to narrow what objects are detected. For example, are you sure the Area2D is not detecting the enemy itself? For a quick check you can have Godot print the collider to double check it is what you expect.
Furthermore, notice that wen an object leaves the Area2D it will set collider to null regardless if there is still some object inside the Area2D or not. I remind you that an alternative approach is using get_overlapping_bodies.
And, of course, you can query each body you get to see if it is the player character (for example by checking its node group, name, class, etc...). I go over filtering in more detail in another answer.
If you are getting the player character, there is another possible source of problems: position vs global_position. The code you have like this:
position.direction_to(collider.position)
Is correct if both the enemy and the collider it got have the same parent. In that case their positions are in the same space. Otherwise, you may want to either work on global coordinates:
global_position.direction_to(collider.global_position)
Or you can bring the collider position to local coordinates:
position.direction_to(to_local(collider.global_position))
And of course, double check that the Area2D is positioned correctly. You can enable "Visible Collision Shapes" on the "Debug" menu, which will show it when running the game from the editor.

Moving my 3D Kinematic Body horizontally by setting the velocity

I want to move my kinematic body horizontally by just setting the velocity but I'm keep on getting some error on this program. I'm using godot software to do this. Need a hand to help me. Below I have attached my code:
extends KinematicBody
var KinematicBodyWidth: int
var KinematicBodyHeight: int
var velocity: float
func _enter_tree():
setupVelocity()
setupKinematicBody()
positionLeftCenter()
func _physics_process(_delta):
move_and_slide(delta)
func move_and_slide(delta: float)-> void:
position.x += delta * velocity
func setupVelocity():
velocity = 100.0
Thanks and regards,
Abhi
If you put that code on a KinematicBody it will give you the following error:
Parser Error: The function signature doesn't match the parent. Parent signature is: "Vector3 move_and_slide(Vector3, Vector3=default, bool, int, float, bool)".
That is because KinematicBody defines a move_and_slide. And no, you can't have an overload. GDScript does not support that. Instead creating a method with the same name is taken as an attempt to override it, which is failing when it checks if the parameters match. The method move_and_slide is not virtual anyway.
I also want to mention that the slide part of the name of move_and_slide refers to how it interacts with ramps. That is, if the KinematicBody collides with a surface that it considers a ramp, it will slide on it.
Now, you could put the code directly on _physics_process, like this:
func _physics_process(delta):
position.x += delta * velocity
Notice I renamed the parameter from _delta to delta. You were passing delta into your move_and_slide method, but the parameter name was _delta.
Except - wait - there is no position in KinematicBody (there is in KinematicBody2D), if you want to change the position you can write the origin of the transform:
func _physics_process(delta):
transform.origin.x += delta * velocity
And you need to be aware that that is a teleport. It would not be checking for collisions. Use move_and_slide (or if you don't want the slide part, use move_and_collide) that comes defined in the KinematicBody class. That way, it does not only moves the KinematicBody but also takes into account collisions.
By the way, I would argue the variable is a speed (scalar) not a velocity (vector). Yet, I'm keeping the way you named it.
Given that you have unused KinematicBodyWidth and KinematicBodyHeight I suspect you don't want to use Godot physic objects. If that is the case, you can use a regular Spatial node, and use physics queries to detect collisions.
I have an explanation from the basics of setting up Godot physics to how to use physic queries elsewhere. Either if you want to use Godot physic objects and don't know how, or if you don't want to use them and use physics queries instead, I think that would help you.
The next errors are that setupKinematicBody and positionLeftCenter are not defined. Either define the methods, or remove the calls. I can't help further with those.
By the way, are you sure you want to use _enter_tree instead of _ready? And are you sure you don't want to initialize velocity with the value you want (var velocity: float = 100.0), or you can export it so it is easy to set from the inspector panel (export var velocity: float = 100.0).
Your code could be just this:
extends KinematicBody
export var speed := 100.0
func _physics_process(delta):
transform.origin.x += delta * speed
Or this:
extends KinematicBody
export var velocity := Vector3(100.0, 0.0, 0.0)
func _physics_process(_delta):
move_and_slide(velocity)

Godot 3.3.2 - Changing child variables from the parent without them being reset

I'm new to godot and I was trying to make a asteriods type game. But i'm having trouble getting my space ship to shoot.
The issue is that I can't get the bullet to fire in direction of the ship. I set an 'angle' variable in the ship and assign it to the bullet once it has been instanced, but i'm not sure how to use it in the bullet. The bullet will just move right no matter the angle of the ship.
Ship Firing Code:
func _process(_delta):
if Input.is_action_just_pressed("action_fire"):
var Bullet_Instance = Bullet.instance()
Bullet_Instance.angle = angle
owner.add_child(Bullet_Instance)
Bullet Code:
extends KinematicBody2D
var angle = 0
var direction = Vector2(cos(angle), sin(angle))
func _physics_process(_delta):
move_and_slide(direction*500)
I'm pretty sure that the angle variable is being reset to 0 in the bullet code after it has been set in the ship code, but i'm not sure how to fix this. Thanks.
The angle variable is not being reset. The direction variable is not being updated when you set angle.
When the scene is instanced, here:
var Bullet_Instance = Bullet.instance()
These variables get their value:
var angle = 0
var direction = Vector2(cos(angle), sin(angle))
Then you set angle, here:
Bullet_Instance.angle = angle
But you don't use angle anymore, instead, you use direction:
func _physics_process(_delta):
move_and_slide(direction*500)
I'll give a few ways to solve this:
If you want direction to update every time you set angle, you could make a setter with setget. Like this:
var angle = 0 setget set_angle
func set_angle(new_value) -> void:
angle = new_value
direction = Vector2(cos(angle), sin(angle))
You could write direction directly from your other script, and not have the angle variable at all. In fact, you could set a velocity, and save tha multiplication by 500 every time.
You could compute the vector on _physics_process (you are already doing a vector scaling operation anyway):
func _physics_process(_delta):
move_and_slide(Vector2(cos(angle)*500, sin(angle)*500))
There likely are more ways to go about it. It is up to you.

Detect impact force in Phaser with P2JS

When running a Phaser with the P2JS Physics engine, I know that it's possible to detect collisions with Phaser.Physics.P2.Body#onBeginContact. But is there a way to test the impact force of the collision, so that I can apply a realistic level of damage to my spaceship when it collides with an asteroid?
You can determine the momentum of the impact using Phaser.Physics.P2.Body#onBeginContact like so:
Create your ship and add the onBeginContact signal handler to it's P2 body
ship.body.onBeginContact.add(contactHandler);
The contactHandler event will be sent 4 parameters: The body it is in contact with, the shape from this body that caused the contact, the shape from the contact body and the contact equation data array.
function contactHandler(body, shape1, shape2, equation) {
//get the velocity of asteroid at point of contact
var v = body.velocity.y;
//and the asteroid's mass
var m = body.mass;
//calculate momentum
var momentum = m*v;
//momentum affects ship damage
ship.damage(momentum);
}
Note in this example that I'm just using the vertical velocity of the asteroid to calculate the momentum. For a more accurate calculation use both the ship's and asteroid's velocities... something like this:
var v1 = new Phaser.Point(ship.velocity.x, ship.velocity.y);
var v2 = new Phaser.Point(body.velocity.x, body.velocity.y);
// calculate difference
var v = Math.abs( v1 - v2 );
Here's the code in action:
http://jsfiddle.net/bstevens/a6paycw6/
Well the easiest way I could think of off the top of my head would be to take the asteroid objects velocity into account. So if the asteroid is coming down on the ship from the top of the screen and the asteroids y-velocity is say greater than 200 add more damage then you would add for the y-velocity being say 100. I'm not sure what the actual numbers in your game are but I think the concept of using the velocity of the asteroid still stands.
--Edit--
Say you have a spaceship and an asteroid and they have collided (vertically with spaceship towards bottom of screen) and now you are in a method that will reduce the health of the spaceship based on how hard the collision was the if statement would look something like this:
if(asteroid.body.velocity.y<100)
spaceship.health-=10;
else if(asteroid.body.velocity.y<200)
spaceship.health-=20;
else
spaceship.health-=30;
(Also to make the numbers a bit more realistic I changed the y velocity values. I realize now that a y-velocity of 20 is actually quite slow and 100-300 makes a bit more sense)

Resources