Sometimes bullets go through the walls despite of the fact that both have colliders and bullets are meant to be destroyed once they enter the wall's collider.
Bullets are being added to list_of_bullets {} list on each player or enemy shot and later get updated in love.update()
for i,v in ipairs(list_of_bullets) do
if not v.alive then
table.remove(list_of_bullets, i)
end
v:update(dt)
end
In 99.99% of cases bullets behave correctly according to the behaviour set in function Bullet:new – once they contact a 'static' wall:
self.collider:setPreSolve(function(col, other, contact)
if other.collision_class == 'static' then
self.alive = false
end
end)
Then the first thing that Bullet:update(dt) does is a check whether the bullet is alive and destroys the collider if it is not:
function Bullet:update(dt)
if not self.alive then
self.collider:destroy()
And here is the way bullets and their colliders move after the check for being alive:
self.x = self.x + self.speed * math.cos(self.angle) * dt
self.y = self.y + self.speed * math.sin(self.angle) * dt
self.collider:setPosition(self.x, self.y)
self.collider:setAngle(self.angle)
However sometimes this happens:
image 1 - the bullet is about to hit the wall
image 2 - the bullet is right inside the wall, colliders clearly overlapping
image 3 - the bullet goes outside of the wall and it's collider with no collision detected
Extreme case in in image 4 with all colliders being drawn. Under specific angle all of the bullets just go through.
What surprises me the most is the fact that extremely low speeds for bullets (three seconds to travel from enemy to player) cause almost as much "passes through colliders" as extremely high speeds but relatively high speeds decrease these probability significantly.
Extremely high speeds make bullets travel larger distance than the wall width per frame however the issue with slow speeds seems to be different.
p.s. I was considering raycasting but bullets are supposed to be not so fast, player must have a change to dodge it so I would rather keep drawing colliders for each bullets.
p.s.s. Yes, this is windfield and I know that it is outdated but I am almost on the finish line of the project and changing it would be too painful
Collision not being registered
-
- Prole
- Posts: 8
- Joined: Tue Nov 14, 2023 4:01 pm
Re: Collision not being registered
Without having looked much, this looks like the typical case of deleting elements from a table that is being iterated. At least the first loop looks like that.
When you're removing an element from a table, all elements after it are renumbered. If you're deleting element 4 in a table of 6 elements, then element 5 becomes 4, and element 6 becomes 5. When i increases, it will check element 5, but the element that was 5 is now 4 and hasn't been checked! So the previous element 5, which is now element 4, isn't checked in this iteration.
To solve it, just iterate in reverse order:
By the way, please use [code] tags instead of [i] tags for code snippets. That will help with clarity.
When you're removing an element from a table, all elements after it are renumbered. If you're deleting element 4 in a table of 6 elements, then element 5 becomes 4, and element 6 becomes 5. When i increases, it will check element 5, but the element that was 5 is now 4 and hasn't been checked! So the previous element 5, which is now element 4, isn't checked in this iteration.
To solve it, just iterate in reverse order:
Code: Select all
for i = list_of_bullets, 1, -1 do
local v = list_of_bullets[i]
if not v.alive then
table.remove(list_of_bullets, i)
end
v:update(dt)
end
-
- Prole
- Posts: 8
- Joined: Tue Nov 14, 2023 4:01 pm
Re: Collision not being registered
Thank you for advice, but unfortunately this did not fix the issue (and also you are missing '#' sign). Maybe a bit more information could be useful:pgimeno wrote: ↑Thu Nov 30, 2023 5:45 pm Without having looked much, this looks like the typical case of deleting elements from a table that is being iterated. At least the first loop looks like that.
When you're removing an element from a table, all elements after it are renumbered. If you're deleting element 4 in a table of 6 elements, then element 5 becomes 4, and element 6 becomes 5. When i increases, it will check element 5, but the element that was 5 is now 4 and hasn't been checked! So the previous element 5, which is now element 4, isn't checked in this iteration.
To solve it, just iterate in reverse order:
By the way, please use [code] tags instead of [i] tags for code snippets. That will help with clarity.Code: Select all
for i = list_of_bullets, 1, -1 do local v = list_of_bullets[i] if not v.alive then table.remove(list_of_bullets, i) end v:update(dt) end
Code: Select all
-- bullet.lua
Bullet = Object:extend()
function Bullet:new(x, y, angle, shooter)
self.image = love.graphics.newImage("bullet.png")
self.x = x
self.y = y
self.width = self.image:getWidth()
self.height = self.image:getHeight()
self.angle = angle
self.speed = 50
self.origin_x = self.image:getWidth() / 2
self.origin_y = self.image:getHeight() / 2
self.scale = 3
self.alive = true
self.shooter = shooter
self.collider = world:newRectangleCollider(self.x, self.y, self.width*self.scale, self.height*self.scale)
self.collider:setCollisionClass('bullet')
self.collider:setObject(self)
self.collider:setPreSolve(function(col, other, contact)
if other.collision_class == 'static' then
self.alive = false
bullet_hits_wall:play()
--print("self.alive set to false by PreSolve")
end
end)
end
Here is the part of enemy.lua code that is responsible for creating bullets shot by enemies, but I doubt that the issue is here:
Code: Select all
local player_x, player_y = player.collider:getPosition()
self.angle = math.atan2(player_y - self.y, player_x - self.x)
if self.can_shoot then
local shot_sound = enemy_shot:clone()
shot_sound:play()
local bullet_spawn_offset = (self.width * self.scale)
local enemy_x, enemy_y = self.collider:getPosition()
local bullet_start_x = enemy_x + math.cos(self.angle) * bullet_spawn_offset
local bullet_start_y = enemy_y + math.sin(self.angle) * bullet_spawn_offset
table.insert(list_of_bullets, Bullet(bullet_start_x, bullet_start_y, self.angle, scaler, self))
self.can_shoot = false
self.time_since_last_shot = 0
end
Re: Collision not being registered
Just some random thoughts...as first step try to figure out what exactly goes wrong.
Is it the collision detection? Or deleting the bullet?
Is it the collision detection? Or deleting the bullet?
Maybe because with low speeds more bullets are alive at the same time? That might point to some error in updating/deleting bullets.As I said before, low speeds increase the probability of the issue.
-
- Prole
- Posts: 8
- Joined: Tue Nov 14, 2023 4:01 pm
Re: Collision not being registered
Deleting the bullets works just fine, it is the collision that is not being detected for some reason. And it is not being detected under some very specific circumstances.knorke wrote: ↑Thu Nov 30, 2023 9:11 pm Just some random thoughts...as first step try to figure out what exactly goes wrong.
Is it the collision detection? Or deleting the bullet?
Maybe because with low speeds more bullets are alive at the same time? That might point to some error in updating/deleting bullets.As I said before, low speeds increase the probability of the issue.
The amount of bullets is not the issue, I have tested that. Adding 15 enemies shooting with almost not reloading time at speed of 500 causes maybe 0.1% of bullets to go through, but even one enemy with speed of 50 and long reloading under specific angle will send almost all if not all of his bullets right through the wall.
I suspect that either windfield or love.physics itself has some issue with handling collisions when angle is involved. Size of colliders is fine, I have made bullets and walls gigantic, taking almost half of the screen, but the issue persisted: specific angles and bullet just goes through the wall.
I was thinking of changing the bullet's collider shape to circle or putting another smaller collider inside the bullet that won't rotate itself. Does that sound reasonable?
Re: Collision not being registered
I've had a mess around with Windfield and while I managed get a similar behaviour as you describe, I believe it was result of using the API incorrectly. It would be best if you could share your .love file so we can properly see how your code works altogether.
-
- Prole
- Posts: 8
- Joined: Tue Nov 14, 2023 4:01 pm
Re: Collision not being registered
Hopefully I am sharing the project with all the files correctlymarclurr wrote: ↑Fri Dec 01, 2023 10:21 am I've had a mess around with Windfield and while I managed get a similar behaviour as you describe, I believe it was result of using the API incorrectly. It would be best if you could share your .love file so we can properly see how your code works altogether.
Best spot for testing is the wall between first room and second enemy, it is easy to find an angle there that lets the bullets to go through the walls.
Currently the speed of bullets is set to slow value, you can change it in bullet.lua, function Bullet:new(x, y, angle, shooter). Currently a lot of bullets go through the values but if you increase the speed to ~400-500 then it gets way better, yet not perfect. Too high speed obviously lets the bullets go through the wall since the bullet travels the distance way longer than the width of the wall. But why reasonable and extremely slow speeds do not register collision, the slower the worse?
Collision classes, types and import from tileMap are handled in main.lua, function resetGame().
I have already tried changing bullet's collider to other shapes, sizes, prevent it from rotating or putting two colliders for 'double-check' but nothing solved the issue – collision is simply not being registered.
Thank you for so much help and support, let me know if I can improve the way of sharing information and details with all of you guys!
- Attachments
-
- project.zip
- (47.45 MiB) Downloaded 229 times
Re: Collision not being registered
I think the problem is the call to setPosition. This seems to cause some weirdness in the Box2D internals. I'm not sure exactly why it sometimes works and other times doesn't but I suspect it's not meant to be used to move things into a colliding state. I remove the setPosition and setAngle calls from the bullet update method and added the below to the constructor and everything works perfectly:
Code: Select all
local vx = math.cos(angle) * self.speed
local vy = math.sin(angle) * self.speed
self.collider:setLinearVelocity(vx, vy)
self.collider:setAngle(self.angle)
-
- Prole
- Posts: 8
- Joined: Tue Nov 14, 2023 4:01 pm
Re: Collision not being registered
I was thinking about this part of code but for some reason decided that this could not be it)marclurr wrote: ↑Fri Dec 01, 2023 2:46 pm I think the problem is the call to setPosition. This seems to cause some weirdness in the Box2D internals. I'm not sure exactly why it sometimes works and other times doesn't but I suspect it's not meant to be used to move things into a colliding state. I remove the setPosition and setAngle calls from the bullet update method and added the below to the constructor and everything works perfectly:
Code: Select all
local vx = math.cos(angle) * self.speed local vy = math.sin(angle) * self.speed self.collider:setLinearVelocity(vx, vy) self.collider:setAngle(self.angle)
Indeed this approach fixed the issue, thank you so much! However I have put this code into bullet.update (changing angle to self.angle). Otherwise trajectory is affected by enemies the bullet has already hit. Enemy collider velocity is set to 0,0 after being hit by bullet to prevent any movement.
Anyway, to sum up, moving colliders by collider:setPosition() seems like a poor practice and should not be used unless truly intended, right?
Also, it feels like too much, but how do I check the Box2D internals to monitor things like this issue?
Thank you so much once again!
Re: Collision not being registered
From what I can tell, you're right. Best to let the physics engine perform all movement, but I'm no expert on Box2D. I had a read around and play with Windfield because your problem interested me. There may be a way to use setPosition in a reliable way but I didn't find anything in my cursory search.DannyMcWopper wrote: ↑Fri Dec 01, 2023 4:27 pm Anyway, to sum up, moving colliders by collider:setPosition() seems like a poor practice and should not be used unless truly intended, right?
Also, it feels like too much, but how do I check the Box2D internals to monitor things like this issue?
On a side note, and while I wouldn't recommend changing your approach this far down the line, I personally wouldn't bother using a full-blown physics engine for a game like this, you end up banging your head against the wall over really fiddly problems. In the past I've used Bump which only handles AABB (rectangles) colliders, with a few choices for how to resolve collisions (i.e., slide along surface, stop at contact point, bounce or do nothing). it's also not too difficult to implement a really simple grid aligned tilemap collision system, coupled with a basic AABB overlap routine and you can implement pretty much any 2D game. That's not to take anything away from what you've done, it's not bad at all especially if this is your first project.
Who is online
Users browsing this forum: No registered users and 0 guests