当您在 Lua 中加载/运行文件时,Lua 会按顺序查看整个文件一次,因此您检查冲突的行仅在 main.lua 加载时发生,永远不会再被查看。
就目前的代码而言,它只检查敌人和子弹的碰撞一次
if CheckCollision(enemy.x,enemy.y,enemy.w,enemy.h,bullets.x,bullets.y,bullets.w,bullets.h) then enemy = nil
end
如果你把这个放到love.update(dt)方法中,就会达到你想要的效果。
我想指出,一旦 enemy 设置为 nil(发生冲突),您将在尝试索引 nil 值时抛出错误,因为您的 enemy 变量不再是表。
同样值得注意的是,这些行
bullets.x = o.x
bullets.y = o.y
在for循环中
for i, o in ipairs(bullets) do
导致您的子弹行为不正常(至少,我假设您不希望它们的行为)每次发射新子弹时,它都会与代码一起添加到 bullets 表中
table.insert(bullets, {
x = player.x,
y = player.y,
dir = direction,
speed = 400
})
这会将每个新表放入bullets 的#bullets + 1(表的最后一个索引+1)索引中。由于您的 for 循环遍历 bullets 表中的每个项目符号对象,因此发生的最后一个分配总是在表中的最后一个项目符号上。
让我试着更简单地解释一下。
假设一名玩家发射了两颗子弹。第一次子弹发射将调用我之前提到的table.insert(...) 调用。因此,我们的bullets 表将如下所示
bullets = {
x = 100,
y = 100, -- This is what you set player.x and player.y to in the start.
w = 15,
h = 15,
-- This is that new table we added - the 1st bullet fired.
{
-- This will all be inside it according to the table.insert(...) call.
x = 100, -- What player.x is equal to
y = 100, -- What player.y is equal to
dir = ... -- Who knows what it is, just some radians that you calculated.
speed = 400
}
}
现在,您使用了 ipairs(...) 调用,这意味着我们的 for 循环只会查看 bullets 中的表格 - 聪明的想法。但是它的实现存在问题。
-- With our new table inside bullets, we will only have that table to look at for the entire loop. So, lets jump right into the loop implementation.
local i, o
for i, o in ipairs(bullets) do
-- This is fine. These lines look at the new table's x and y values and move them correctly.
o.x = o.x + math.cos(o.dir) * o.speed * dt
o.y = o.y + math.sin(o.dir) * o.speed * dt
-- This is the problem.
bullets.x = o.x -- Now, those x and y values in the bullets table are set to the new table's x and y values.
bullets.y = o.y
-- The rest of the loop works fine.
...
end
现在,对于一个新子弹,它可以正常工作。随着新子弹的传播,每次更新都会正确更新bullets.x 和bullets.y。但现在让我们考虑一下我们的玩家发射的第二颗子弹。
bullets的新造型是这样的
bullets = {
x = 150, -- These are equal to the first bullet's values - for now, at least.
y = 150,
w = 15,
h = 15,
-- This 1st bullet is still here.
{
x = 150, -- Lets say the bullet has moved 50 pixels in both directions.
y = 150,
dir = ...
speed = 400
},
-- This is the new second bullet.
{
x = 100, -- Remember player.x and player.y are both 100
y = 100,
dir = ...
speed = 400
}
}
看看这是怎么回事?让我们在第一次迭代时跳转到 for 循环。
-- First iteration occurs. We're looking at the first bullet.
for i, o in ipairs(bullets) do
o.x = o.x + math.cos(o.dir) * o.speed * dt -- Lets say o.x = 160 now
o.y = o.y + math.sin(o.dir) * o.speed * dt -- and o.y = 160
bullets.x = o.x -- bullets.x = o.x, so bullets.x = 160
bullets.y = o.y -- bullets.y = o.y, so bullets.y = 160
...
end
然后我们进入第二个项目符号......
-- Second iteration, second bullet.
for i, o in ipairs(bullets) do
-- Since it's the new table, o.x and o.y start at 100
o.x = o.x + math.cos(o.dir) * o.speed * dt -- Lets say o.x = 110
o.y = o.y + math.sin(o.dir) * o.speed * dt -- Lets say o.y = 110 as well
bullets.x = o.x
bullets.y = o.y
-- But now our bullets.x and bullets.y have moved to the new bullet!
-- The position of the 1st bullet is completely forgotten about!
...
end
这就是问题所在。目前编写循环的方式,程序只关心最近发射的子弹,因为它将被放在bullets 表中的最后 - 它被检查并最后分配给bullets.x 和bullets.y。这会导致碰撞检查只关心最近发射的子弹是否触及敌人,而不关心其他子弹。
有两种方法可以解决此问题。要么在碰撞时分别评估每个子弹的位置,然后在它们的表格中添加宽度和高度,就像这样
-- When you add a bullet
table.insert(bullets, {
x = player.x,
y = player.y,
w = bullets.w,
h = bullets.h,
dir = direction,
speed = 400
})
-- When looking for collisions
local i, o
for i, o in ipairs(bullets) do
o.x = o.x + math.cos(o.dir) * o.speed * dt
o.y = o.y + math.sin(o.dir) * o.speed * dt
-- This will destroy an enemy correctly
if CheckCollision(enemy.x,enemy.y,enemy.w,enemy.h, o.x, o.y, o.w, o.h) then enemy = nil end
if (o.x < -10) or (o.x > love.graphics.getWidth() + 10)
or (o.y < -10) or (o.y > love.graphics.getHeight() + 10) then
table.remove(bullets, i)
end
end
这样您只需将碰撞检查器的位置移动到循环内部并更改其参数。
另一种方法是制作可以“实例化”的类类表,其对象的元表指向类类表。更难,但更好的实践,更容易编写方法和什么不适合。也使对多个玩家、敌人、子弹等的通用检查和评估变得更加容易。