Detect collision between two 2D kinematics bodies godot - game-development

I'm doing a very simple 2D platform game project, you can see here how it is so far: https://master.d3kjckivyd1c76.amplifyapp.com/src/Games/PlatformGame2D101/index.html
But I can't detect the collision between the enemy (the chainsaw) and the player.
I have this code to detect if the two bodies are colliding, but it only prints the Enemy when the player is moving:
func _physics_process(delta: float) -> void:
velocity = move_and_slide(velocity, Vector2.UP)
for i in get_slide_count():
var collision = get_slide_collision(i)
if collision.collider.is_in_group("Enemy"): print("Enemy")
original file
I uploaded the project to the Bitbucket
Thanks any help : )

I did plenty of 2d games using pygame and i used this two.
You can use sprite.spritecollideany() which takes 2 arguments. The sprite u want to collide, in your case the chainsaw with another group of sprites. this requires a loop, which will look like this:
for swordman in swordsmen1_army:
# Swordsman1 attackin Swordsman2
colliding_sword2 = pygame.sprite.spritecollideany(swordman, swordsmen2_army)
if colliding_sword2:
swordman.attacking = True
swordman.unleash = False
if swordman.index == 3:
colliding_sword2.health -= SWORD_HIT
If you want to collide just two sprites you can use pygame.Rect.colliderect() which checks for detection of 2 sprites with each other. This one requires no loop and looks like this:
if pygame.Rect.colliderect(ball.rect, border1.rect):
ball.down_collision = True
ball_y_speed *= -1
Both of them will return a boolean value and from there, you can use an if statement and continue with the code.

The answer: https://www.reddit.com/r/godot/comments/yojmz8/detect_collision_between_two_2d_kinematics_bodies/
But basically the player, it’s not moving, so it's not colliding 🤦
Here it's the changes that I made to fix: https://bitbucket.org/201flaviosilva-labs/platform-game-2d-101-godot/commits/61e37823eb9758fdafd222f2cdecf3e0668a3808

Related

I have a question about a YT tutorial because I wanna customize it a little

in this video, https://youtu.be/klBvssJE5Qg I shows you how to spawn enemies outside of a fixed camera. (this is in GDscript by the way) How could I make this work with a moving camera? I wanna make a zombie fighting game with a moving camera and zombies spawning outside that.
I would really appreciate help with this.
I've tried researching on the internet about how to do it, but I just didn't find it.
N/A..................................
After looking at the video, I see they are using this line to spawn:
Global.instance_node(enemy_1, enemy_position, self)
This suggest to me a couple thing:
The position is probably either relative to the self passed as argument or global.
There must be an Autoload called Global that I need to check to make sure.
And the answer is in another castle video.
In the video Godot Wave Shooter Tutorial #2 - Player Shooting we find this code:
extends Node
func instance_node(node, location, parent):
var node_isntance = node.instance()
parent.add_child(node_instance)
node_instance.global_position = location
return node_instance
And thus, we are working with global coordinates global_position. Thus enemy_position is used as global coordinates.
Ok, instead of using enemy_position as global coordinates we are going to use it as local coordinates of the Camera2D (or a child of it). Which means you need a reference to the Camera2D (which I don't know where do you have it).
You could make your code in a child of the Camera2D, or take the transform of the Camera2D using a RemoteTransform2D. Either way, you could then work in its local coordinates. Thus you would do this:
Global.instance_node(enemy_1, to_global(enemy_position), self)
Or you could have a reference by exporting a NodePath (or in the newest Godot you can export a Camera2D) from your script and set it via the inspector. So you can do this:
Global.instance_node(enemy_1, camera.to_global(enemy_position), self)
Where camera is your reference to the Camera2D.
In the following section of Arena.gd:
func _on_Enemy_spawn_timer_timeout():
var enemy_position = Vector2(rand_range(-160, 670), rand_range(-90, 390))
I believe you can add the X and Y coordinates of the camera to their corresponding random ranges in the enemy position Vector2. This will displace the enemy depending on where the camera is currently located.
You can get the position of the camera with this:
get_parent().get_node("Name of your camera").position
When this is all put together:
func _on_Enemy_spawn_timer_timeout():
var enemy_position = Vector2(rand_range(-160, 670) + get_parent().get_node("Name of your camera").position.x, rand_range(-90, 390) + get_parent().get_node("Name of your camera").position.y)
Keep in mind that you might need to displace the values in the following while loop as well. I hope this helps.

Player doesn't spawn correctly in procedural generated map

I've followed "Procedural Generation in Godot: Dungeon Generation" by KidsCanCode #https://www.youtube.com/watch?v=o3fwlk1NI-w and find myself unable to debug the current problem.
This specific commit has the code, but I'll try to explain in more detail bellow.
My main scene has a Camera2D node, a generic Node2D calles Rooms and a TileMap, everything is empty.
When the script starts, it runs a
func make_room(_pos, _size):
position = _pos
size = _size
var s = RectangleShape2D.new()
s.custom_solver_bias = 0.5
s.extents = size
$CollisionShape2D.shape = s
A few times and it fills $Rooms using .add_child(r) where r is a instance of the node that has the make_room() function. It will then iterate over $Rooms.get_children() a few times to create a AStar node to link all the rooms:
The magic comes when make_map() is called after afterwards, it fills the map with non-walkable blocks and then it carves the empty spaces, which works fine too:
There is a find_start_room() that is called to find the initial room, it also sets a global variable to the Main script start_room, which is used to write 'Start' on the map using draw_string(font, start_room.position - Vector2(125,0),"start",Color(3,4,8))
When I hit 'esc' it runs this simple code to instance the player:
player = Player.instance()
add_child(player)
player.position = start_room.position + Vector2(start_room.size.x/2, start_room.size.y/2)
play_mode = true
The problem comes when spawning the player. I tried doing some 'blind' fixing, such as adding or subtracting a Vector2(start_room.size.x/2, start_room.size.y/2) to player.position to see if I could make it fall within the room, to no avail.
Turning to the debugger didn't help, as the positions expressed by the variable inspectors don't seem to mean anything.
I tried implementing a simple 'mouse click print location':
print("Mouse Click/Unclick at: ", event.position)
print("Node thing",get_node("/root/Main/TileMap").world_to_map(event.position))
And also a 'start_room' print location:
print(get_node("/root/Main/TileMap").world_to_map(start_room.position))
And a when player moves print location, written directly into the Character script:
print(get_node("/root/Main/TileMap").world_to_map(self.position))
Getting results like the ones bellow:
Mouse Click/Unclick at: (518, 293)
Node thing(16, 9)
(-142, 0)
(-147, -3)
So, the player doesn't spawn on the same position as the start_room and the mouse position information is not the same as anything else.
Why is the player now spawning correctly? How can I debug this situation?
EDIT1: User Theraot mentioned about how the RigidBody2D is doing some weird collisions, and from what I understood, changing their collision behavior should fix the whole thing.
There's a section on the code that -after generating the random rooms- it removes some of the rooms like this:
for room in $Rooms.get_children():
if randf() < cull:
room.queue_free()
else:
room.mode = RigidBody2D.MODE_STATIC
room_positions.append(Vector3(room.position.x, room.position.y, 0))
From what I understand, if the room is randomly selected it will be deleted using queue_free() OR it will be appended to a room_positions for further processing. This means if I shift all the rooms to a different collision layer, the player/character instance would be alone with the TileMap on the same collision layer.
So I just added a simple room.collision_layer = 3 changing this section of the code to
for room in $Rooms.get_children():
if randf() < cull:
room.queue_free()
else:
room.mode = RigidBody2D.MODE_STATIC
room.collision_layer = 3
room_positions.append(Vector3(room.position.x, room.position.y, 0))
It doesn't seem to have changed anything, the player still spawns outside the room.
Do you see the rooms spread outwards?
You didn't write code to move the rooms. Sure, the code gives them a random position. But even if you set their position to Vector2.ZERO they move outwards, avoiding overlaps.
Why? Because these rooms are RigidBody2D, and they do push other physics objects. Such as other rooms or the player character.
That's the problem: These rooms are RigidBody2D, and you put your KinematicBody2D player character on top of one of them. The RigidBody2D pushes it out.
The tutorial you followed is exploiting this behavior of RigidBody2Ds to spread the rooms. However you don't need these RigidBody2D after you are done populating your TileMap.
Instead, you can store the start position in a variable for later placing the player character (you don't need offsets - by the way - the position of the room is the center of the room), and then remove the RigidBody2Ds. If you want to keep the code that writes the text, you would also have to modify it, so it does not fail when the room no longer exists.
Alternatively, you can edit their collision layer and mask so they don't collide with the player character (or anything for that matter, but why would you want these RigidBody2Ds that collide with nothing?).
Addendum post edit: Collision layers and mask don't work as you expect.
First of all, the collision layer and mask are flags. The values of the layers are powers of two (1, 2, 4, 8...). So, when you set it to 3, it is the layer 1 plus the layer 2. So it still collides with a collision mask of 1.
And second, even if you changed the collision layer of the rooms to 2 (so it does not match the collision mask of 1 that the player character has). The player character still has a layer 1 which match the collision mask of the rooms.
See also the proposal Make physics layers and masks logic simple and consistent.
Thus, you would need to change the layer and mask. Both. in such way that they don't collide. For example, you can set layer and mask to 0 (which disable all collisions). The algorithm that populates the TileMap does not use the layer and mask.

Box2D collision detection fails after firing a lot of bullets

I am working on a physics game and encountered a strange bug: sometimes, after firing a lot of bullets, collision detection starts to fail.
As can be seen in the following GIF, the collision works only on half the platform, which is very strange. Also, the Box2D debug renderer is enabled and it can also be seen that the platform is a single body.
Here is how I get this bug to happen, as it only happens only after firing lots of bullets (in the beginning everything works fine):
Notes:
- the bullet has the bullet field set to true
- I set the player's bullet field to true, did not make a difference
- the player is 1 meter by 1 meter
- the player and the bullets are DynamicBodies and the platforms are StaticBodies
- the map is near to (0, 0), though it goes a bit in the negatives (-1.5), I doubt it matters
- the categoryBits and maskBits are correct (the collision should happen, and it does happen, but glitches)
- after the bullets disappear, the number of bodies is the same as when the game starts (so they are actually destroyed)
- the World's gravity is (0, -25f)
- the game runs at 60fps
Here is the Box2D timestep code, similar to the stepping code from the libGDX wiki:
companion object {
private const val TIME_STEP = 1f / 300f
private const val VELOCITY_ITERATIONS = 6
private const val POSITION_ITERATIONS = 2
}
private var accumulator = 0f
override fun update(deltaTime: Float) {
accumulator += Math.min(deltaTime, 0.25f)
while (accumulator >= TIME_STEP) {
world.step(TIME_STEP, VELOCITY_ITERATIONS, POSITION_ITERATIONS)
accumulator -= TIME_STEP
}
}
I tried changing:
- TIME_STEP to a lower value, like 1/60f
- VELOCITY_ITERATIONS a bit higher, to 8
- POSITION_ITERATIONS a bit higher, to 6
- VELOCITY_ITERATIONS and POSITION_ITERATIONS to 100
and there was no (obvious) difference.
Concern:
The bugged platform seems to start behaving like a bullet (it doesn't collide with other bullets or with the player), at least halfway. So could its categoryBits and maskBits be changed on-the-fly, after a lot of world.createBody() and world.destroyBody(), maybe due to pooling?
So what should I try for the collision not to fail in this case?
I finally managed to fix it.
The solution I found was to loop through every entity that has a body and call refilter(), which seems to fix it:
engine.getEntitiesFor(Family.one(BodyComponent::class.java).get()).forEach {
it.body.body.fixtureList.first().refilter()
}
In the future, I could call refilter() only when needed (if I can determine when I have to call it) instead of calling it every frame, but it works for now.
It looks like you should call refilter() after you change the filterData of a body (changing its categoryBits or maskBits) outside the FixtureDef, but I don't seem to be doing that (or maybe I am missing something), so it is a bit weird.

Unity Change Ball Direction after hitting paddle in a Pong game (like in dx-ball)

I'm struggling with how to get the ball to change the bounce based on where it hits on the paddle. Normally in a pong game, the angle changes, depending on how far from center the ball bounces, and which direction of the center it bounces.
I managed to do something like that:
//rb = rigidbody, velOnPaddleHit = predefined float
float dist = transform.position.x - paddle.position.x;
dist = transform.position.x > paddle.position.x ? dist : -dist;
dist /= paddle.localScale.x/2;
dist *= velOnPaddleHit;
rb.addForce(dist, 0,0);
But it's just not working / it's weird.
Can anyone help me?
Edit: Here's the video showing this kind of behaviour. When the ball hits the left side of the paddle, it goes left, the velocity doesn't matter
https://www.youtube.com/watch?v=fHX_2DLDp1w
You do't change it based on where it hit the paddle. To change the direction of the ball, all you have to do is to flip the numbers in the opposite direction. So multiplying x and y axis rigidBody value by -1 and applying it to the ball should do just that.
if ball collides with the paddle,multiply the velocity of the ball's rigidBody in all axis by -1, except for the z axis or the x-axis(Depends on how you setup your scene. It could ether z or x axis but the y-axis MUST be multiplied by -1 in other to go in the other direction.
You may have a speedVariable that increases the speed of the ball each time it hits the paddle.
void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.name == "ball")
{
Vector3 tempVect = new Vector3(collision.rigidbody.velocity.x * -1, collision.rigidbody.velocity.y * -1, collision.rigidbody.velocity.z);
collision.rigidbody.velocity = tempVect;
}
}
To detect where the ball hit, the easiest way I know about is to use tiny box colliders.
Create at-least 24 box colliders. Name them from detector1 to detector24
(detector1, detector2,detector3,detector4,detector5.....)
Mark isTrigger of 24 of the box colliders true so that they wont collide with the ball.
Create a layer called "detectorLayer" and make sure that those 24 box colliders are in the "detectorLayer" layer.
Create another layer called "ballLayer" and make sure that the ball's layer is set to "ballLayer".
Attach a collider to your paddle called. Change your paddle's GameObject name to "Paddle". Create a new layer called "PaddleLayer" and make sure that "Paddle" GameObject is in the layer called "PaddleLayer".
Position those 24 colliders from left to right in order close to the paddle but make sure they are not touching the paddle or the collider of the Paddle. The image below should help you understand what I am taking about.
Writing the code should be piece a piece of cake.
bool firstColision = false;
Check If ball collides with Paddle with OnCollisionEnter.
firstColision = true;
Immediately check which of the 24-colliders the ball is touching with the OnTriggerEnter function.
Inside the OnTriggerEnter function, make sure firstColision is true before checking which of the 24 box colliders is touching the ball.
The first box collider called "detector1" is positioned on left-most side while the last box collider called "detector24" is positioned to the right-most side of the padder. You can use if if statement or the switch statement todetect which box collider the ball triggered. 24/2 = 12 so box collider named "detector24" should be positioned in the middle and should be considered the middle of the paddle.
(ACTION) After detecting which box number the ball hit from with OnTriggerEnter, now you can do whatever you want. For example, make the ball go left or right. You may have constant values that dictates how far to move the ball for each box collider detected. That's up to you. You make your own rules from here now.
Set firstColision to false to reset it.
NOTE: It doesn't have to be 24 box colliders. It can be 15 but the more you have have, the better.
If everything works as expected, you can do the final part which is to go to Edit->Project Settings->Physics and make sure that the following is true:
A. detectorLayer cannot collide with detectorLayer.
B. PaddleLayer cannot collide with detectorLayer.
This will improve performance on mobile devices.

Making object move to a touch position - Corona SDK

I'm trying to get a game object to move to a touch location. I've tried numerous different ways of doing it, but all to no avail. I'm updating the objects y position every frame to keep it moving constantly forward, and I'm not sure if this is affecting adding a force or not.
Here is where I move the crow (inside an update function):
crow:translate((platformSpeed),0)
And here is where I'm trying to get the crow to move to the touch position:
local function attack(xPos,yPos)
--get magnitude of touch vector
local magnitude = math.sqrt(xPos*2 + yPos*2)
--normalize vector
xPos = xPos / magnitude
yPos = yPos / magnitude
print(xPos..yPos)
local forceMag = 0.1 -- change this value to apply more or less force
--now apply the force
crow:setLinearVelocity(xPos*forceMag, yPos*forceMag)
--crow:applyLinearImpulse(xPos*forceMag, yPos*forceMag, crow.x, crow.y)
end
local function touchHandler(event)
if (event.phase == "began") then
end
if (event.phase == "ended") then
if (event.yStart>event.y+10) then
jump()
else attack(event.x,event.y) end
end
end
As you can see, I've been trying linear velocity and linear impulse, but the direction is always wrong!
Any help would be great, Thanks!
Alan.
Try this:
local cow = display.newRect(0,0,50,50) -- create your object
local trans
local function moveObject(e)
if(trans)then
transition.cancel(trans)
end
trans = transition.to(cow,{time=200,x=e.x,y=e.y}) -- move to touch position
end
Runtime:addEventListener("tap",moveObject)
Keep Coding.............. :)
This is similar to the recent question.
I have an answer to the recent question, you can check and run my sample code on a blank project to see how it works.
My code uses physics and linear velocity.
https://stackoverflow.com/a/17847475/1605727