[Noob here] Collision not working

General discussion about LÖVE, Lua, game development, puns, and unicorns.
Post Reply
L0idser
Prole
Posts: 1
Joined: Wed Jun 23, 2021 2:29 am

[Noob here] Collision not working

Post by L0idser »

It's been 2 and a half days since I was learning love2d on youtube. And I don't know why my collision code doesn't work, I don't mind giving my source code since It's just for learning.

So let's get to the point, my real problem is my collision works but when my bullet collides with the enemy sometimes it deletes the enemy and sometimes not. I'm really confuse and I don't know what to do, I have been fixing and searching some solutions online like non stop but I can't find a working one so I decided to register on the forum and find some help.

Code: Select all

--Requirements Importer
local Chrono = require("utils/chrono")

--Main
function love.load()
    timer = Chrono()
  
    player = {}
    player.x = 250
    player.y = 590
    player.cooldown = 15
    player.width = 0.1
    player.height = 0.1
    player.image = love.graphics.newImage("images/spaceship.png")
    player.bullets = {}

    enemies = {}
    enemies.army = {}

    function player.shoot()
        if player.cooldown < 0 then
            player.cooldown = 15

            bullet = {}
            bullet.x = player.x - 30
            bullet.y = player.y - 50

            table.insert(player.bullets, bullet)
        end
    end

    function enemies.spawn()
        enemy = {}
        enemy.x = math.random(0, 700)
        enemy.y = 10
        enemy.width = 1
        enemy.height = 1
        enemy.image = love.graphics.newImage("images/enemy.png")

        table.insert(enemies.army, enemy)
    end
    
    timer:every(2, function()
      enemies.spawn()
    end)
end

function love.update(dt)
    timer:update(dt)
  
    player.cooldown = player.cooldown - 1

    if love.keyboard.isDown("escape") then
        love.event.quit()
    elseif love.keyboard.isDown("left") then
        if player.x > 50 then
            player.x = player.x - 8
        end
    elseif love.keyboard.isDown("right") then
        if player.x < 800 then
            player.x = player.x + 8
        end
    end

    for i,v in pairs(player.bullets) do
        v.y = v.y - 10
    end

    for i,v in pairs(enemies.army) do
        v.y = v.y + 3
    end
    
    for i, e in pairs(enemies.army) do
      for _, b in pairs(player.bullets) do
        if CheckCollision(b.x, b.y, 10, 10, e.x, e.y, enemy.width, enemy.height) then
          table.remove(enemies.army, i)
        end
      end
    end
end

function CheckCollision(x1,y1,w1,h1, x2,y2,w2,h2)
  return x1 < x2+w2 and
         x2 < x1+w1 and
         y1 < y2+h2 and
         y2 < y1+h1
end

function love.draw()
    love.graphics.draw(player.image, player.x, player.y, math.pi, player.width, player.height)
    love.graphics.print("Player location:"..player.x.." | "..player.y)

    for i,v in pairs(player.bullets) do
        if v.y < 0 then
            table.remove(player.bullets, i)
        end

        love.graphics.setColor(255, 255, 255)
        love.graphics.rectangle("fill", v.x, v.y, 10, 10)
    end

    for i,v in pairs(enemies.army) do
        love.graphics.setColor(77, 255, 0)
        love.graphics.draw(v.image, v.x, v.y, 1, 0.1)
    end
end

function love.keypressed(key, scancode, isrepeat)
    if key == "space" then
        player.shoot()
    end
end
Attachments
Learning.rar
(61.61 KiB) Downloaded 127 times
User avatar
milon
Party member
Posts: 472
Joined: Thu Jan 18, 2018 9:14 pm

Re: [Noob here] Collision not working

Post by milon »

Hi L0idser,

Nice artwork, and a really decent start for a beginner!!

This is the danger of hard-coding values. You've defined your enemies to be 1x1 pixel size. Very small hit window.
L0idser wrote: Wed Jun 23, 2021 2:35 am

Code: Select all

    function enemies.spawn()
        enemy = {}
        enemy.x = math.random(0, 700)
        enemy.y = 10
        enemy.width = 1
        enemy.height = 1
        enemy.image = love.graphics.newImage("images/enemy.png")

        table.insert(enemies.army, enemy)
    end
Normally, I'd set the enemy size to the size of the image (see example below). But your image is huge (512 x 512), so you need to either use a smaller image, or else specify your scale value when you load the image, and apply it to the enemy size. Try something like this:

Code: Select all

    function enemies.spawn()
        enemy = {}
        enemy.x = math.random(0, 700)
        enemy.y = 10
        enemy.image = love.graphics.newImage("images/enemy.png")
        enemy.scale = 0.1
        enemy.width = enemy.image:getWidth() * enemy.scale
        enemy.height = enemy.image:getHeight() * enemy.scale
        

        table.insert(enemies.army, enemy)
    end
It still seems like the hitbox is off (adjusted to the right), but I don't have time to dig into that just now.

But I hope this helps, and keep coding! :)

EDIT:
Okay, I got the hitbox figured out too. Ultimately, the issue is because you rotated the enemy graphic, but you were checking the un-rotated enemy co-ordinates to determine a hit. It's much more involved to check for a hit on a non-rectangular (or rotated rectangle) shape. It's much easier to just rotate the image source and save it the way you want it before using it (un-rotated) in-game.

Here's some other thoughts:
* Color isn't defined as an integer, unless you're using an old version of Love. It's a decimal ranging from 0-1 instead now. You don't even need to specify a color in your example - drawing images has nothing to do with what color is selected. You're only actually using that for the bullet, which defaults to white anyway.
* Cooldown being decremented in love.update() is problematic because love.update() will happen more frequently on faster systems and slower on slower systems - and this will impact how often a player can shoot. Instead, base the decrement off of the dt variable.
* There's a bunch of code style issues (defining functions within other functions, etc), but I won't go into detail there unless you ask.

Here's what I'm currently playing with on my system:

Code: Select all

--Requirements Importer
local Chrono = require("utils/chrono")

--Main
function love.load()
    timer = Chrono()
  
    player = {}
    player.x = 250
    player.y = 500
    player.cooldown = 15
    player.scale = 0.1
    player.image = love.graphics.newImage("images/spaceship.png")
    player.bullets = {}

    enemies = {}
    enemies.army = {}

    function player.shoot()
        if player.cooldown < 0 then
            player.cooldown = 15

            bullet = {}
            bullet.width = 10
            bullet.height = 10
            bullet.x = player.x + 20
            bullet.y = player.y - 10

            table.insert(player.bullets, bullet)
        end
    end

    function enemies.spawn()
        enemy = {}
        enemy.x = math.random(0, 700)
        enemy.y = 10
        enemy.image = love.graphics.newImage("images/enemy.png")
        enemy.scale = 0.1
        enemy.width = enemy.image:getWidth() * enemy.scale
        enemy.height = enemy.image:getHeight() * enemy.scale

        table.insert(enemies.army, enemy)
    end
    
    timer:every(2, function()
      enemies.spawn()
    end)
end

function love.update(dt)
    timer:update(dt)
  
    player.cooldown = player.cooldown - dt * 20

    if love.keyboard.isDown("escape") then
        love.event.quit()
    elseif love.keyboard.isDown("left") then
        if player.x > 50 then
            player.x = player.x - 8
        end
    elseif love.keyboard.isDown("right") then
        if player.x < 800 then
            player.x = player.x + 8
        end
    end

    for i,v in pairs(player.bullets) do
        v.y = v.y - 10
    end

    for i,v in pairs(enemies.army) do
        v.y = v.y + 3
    end
    
    for i, e in pairs(enemies.army) do
      for _, b in pairs(player.bullets) do
        if CheckCollision(b.x, b.y, b.width, b.height, e.x, e.y, enemy.width, enemy.height) then
          table.remove(enemies.army, i)
        end
      end
    end
end

function CheckCollision(x1,y1,w1,h1, x2,y2,w2,h2)
  return x1 < x2+w2 and
         x2 < x1+w1 and
         y1 < y2+h2 and
         y2 < y1+h1
end

function love.draw()
    love.graphics.setColor(1, 1, 1)
    love.graphics.draw(player.image, player.x, player.y, 0, player.scale)
    love.graphics.print("Player location:"..player.x.." | "..player.y)

    for i,v in pairs(player.bullets) do
        if v.y < 0 then
            table.remove(player.bullets, i)
        end

        love.graphics.rectangle("fill", v.x, v.y, v.width, v.height)
    end

    for i,v in pairs(enemies.army) do
        love.graphics.draw(v.image, v.x, v.y, 0, v.scale)
    end
end

function love.keypressed(key, scancode, isrepeat)
    if key == "space" then
        player.shoot()
    end
end

Any code samples/ideas by me should be considered Public Domain (no attribution needed) license unless otherwise stated.
Post Reply

Who is online

Users browsing this forum: No registered users and 59 guests