Help needed for bullet collisions and enemy deaths.

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
User avatar
conman420
Prole
Posts: 33
Joined: Sun Aug 31, 2008 8:46 pm

Help needed for bullet collisions and enemy deaths.

Post by conman420 »

Hi, I've been working on a side scrolling game, it has dynamic terrain and enemy spawning and bullet firing.

However, in the bullet collision I know how to detect if a bullet has hit an enemy but I cannot then perform actions on that enemy to kill it because there is no way of getting the body from the callback. I have tried setting the shapes data to its body which works but then I have no way of knowing what shape that body belongs to, therefore I cannot detect whether the body is a bullet or an enemy etc.

Here is my code:

Code: Select all

enemies = {}

terrain = {}

ply = {}

bullets = {}

function load()
	font = love.graphics.newFont(love.default_font, 12)
	love.graphics.setFont(font)
	
	world = love.physics.newWorld( 1000, 1000 )
	
	ground = love.physics.newBody(world,0,0,0)
	
	world:setGravity(0,50)
	world:setCallback(collide)
	
	enem_img = love.graphics.newImage("Downs.png")
	
	ply.bod = love.physics.newBody(world, 400, 20, 20)
	ply.cir = love.physics.newCircleShape(ply.bod, 16)
	ply.img = love.graphics.newImage("Funny.png")
	ply.gun = love.graphics.newImage("gun.gif")
	ply.gunang = 0
	ply.cir:setData(ply.cir)
	ply.cir:setCategory(1)
end

function collide(shp, shp2, con)

end

function update(dt)
	world:update(dt)
	ground:setVelocity(-500, 0)
	local x2, y2 = love.mouse.getPosition()
	local x1, y1 = ply.bod:getPosition()
	if x2 <= x1 then x2 = x1 + 5 end
	local ang = math.atan2(y2 - y1, x2 - x1) * 180 / math.pi
	if ang <= -80 then ang = -80 elseif ang >= 80 then ang = 80 end
	ply.gunang = ang
end

lastupdate = 0
lastenemy = 0

function draw()
	local time = love.timer.getTime()
	if lastenemy + 2 <= time then
		newenemy()
		lastenemy = time
	end
	if lastupdate + 0.1 <= time then
		updateground()
		lastupdate = time
	end
	love.graphics.setColor( 255,255,255 )
	love.graphics.draw("ply Y: "..ply.bod:getY(), 10, 10)
	love.graphics.draw("bullets: "..#bullets, 10, 22)
	love.graphics.draw("last_y: "..last_y.." enemy: "..(600 - last_y) - 10, 10, 34)
	for k, v in pairs(terrain) do
		love.graphics.setColor( v.col )
		love.graphics.polygon(2, v.sqr:getPoints())
	end
	for k,v in pairs(enemies) do
		love.graphics.draw(enem_img, v.bod:getX(), v.bod:getY())
		v.bod:applyForce(-1500,0)
	end
	for k,v in pairs(bullets) do
		love.graphics.setColor( 255,255,255 )
		love.graphics.circle(2, v.bod:getX(), v.bod:getY(), 2)
		v.bod:setVelocity(v.dirx,v.diry)
	end
	love.graphics.draw(ply.img, ply.bod:getX(), ply.bod:getY(), 0, 2 )
	love.graphics.draw(ply.gun, ply.bod:getX() + 15, ply.bod:getY() + 5	, ply.gunang, 0.2 )
end

function mousepressed(x, y, button)
	if button == love.mouse_left then
		createbullet()
	end
end

last_y = 100
last_change = -1
target_y = 100

function updateground()
	if #terrain > 100 then table.remove(terrain, 1) end
	--shift them all
	for k,v in ipairs(terrain) do
		v.bod:setPosition(v.bod:getX() - 10, v.bod:getY())
	end
	local min = 0
	local max = 0
	if last_change < 0 then -- encourage variation of terrain
		min = -10
		max = 1
	else
		min = -1
		max = 10
	end
	last_change = math.random(min,max)
	local new_y = last_y + last_change
	if new_y < 10 then new_y = 10 last_change = 1 end
	if new_y > 400 then new_y = 400 last_change = -1 end
	last_y = new_y
	local g = {}
	g.bod = love.physics.newBody(world,000,000,0)
	g.sqr = love.physics.newRectangleShape( g.bod, 800, 600, 10, new_y )
	g.col = love.graphics.newColor( 0, new_y / 2, 0) 
	table.insert(terrain, g)
end

function createbullet()
	local dat = {}
	dat.bod = love.physics.newBody(world, ply.bod:getX() + 20, ply.bod:getY() + ply.gunang, 100)
	dat.circ = love.physics.newCircleShape(dat.bod, 2)
	local x1, y1 = love.mouse.getPosition()
	local x2, y2 = ply.bod:getPosition()
	if x1 <= x2 then x1 = x2 + 5 end
	local dirx = math.atan2(y2 - y1, x2 - x1) * 180 / math.pi
	local diry = math.atan2(y1 - y2, x1 - x2) * 180 / math.pi
	if dirx < 0 then dirx = dirx * -1 end
	dat.dirx = dirx
	dat.diry = diry
	dat.bod:setBullet( true )
	table.insert(bullets, dat)
end

function newenemy()
	local dat = {}
	dat.bod = love.physics.newBody(world, 780, (600 - last_y), 50)
	dat.circ = love.physics.newCircleShape(dat.bod, 8)
	table.insert(enemies, dat)
end
User avatar
rude
Administrator
Posts: 1052
Joined: Mon Feb 04, 2008 3:58 pm
Location: Oslo, Norway

Re: Help needed for bullet collisions and enemy deaths.

Post by rude »

First of all: there's only one callback now. That might not be sufficient. I have some ideas for improvements in that department, but I'll leave details for another thread.

What you want to do is create a table which contains all the information you need and set that as the shape data. For instance a table which contains a reference to the shape in question, and its parent body.

Code: Select all

-- This
ply.cir:setData(ply) 

-- Instead of 
-- ply.cir:setData(ply.cir)
User avatar
conman420
Prole
Posts: 33
Joined: Sun Aug 31, 2008 8:46 pm

Re: Help needed for bullet collisions and enemy deaths.

Post by conman420 »

Ah I see. So for the bullets and enemies I should add the table they were created with?

Also how do I delete bodies manually? That code causes love to exit itself after about 40 seconds and I'm pretty sure it is because the bodies are never deleted.
User avatar
rude
Administrator
Posts: 1052
Joined: Mon Feb 04, 2008 3:58 pm
Location: Oslo, Norway

Re: Help needed for bullet collisions and enemy deaths.

Post by rude »

There's a hard limit of 2048 bodies (blame Box2D) ... or it might be 2048 contacts, I don't know. Box2D's response to an exceeded limit is a complete crash.

Which isn't great.

To remove bodies, do this:

Code: Select all

-- Assuming an enemy looks like this:
-- enemy = { b = body,  s = shape }
-- And we have a table containing enemies:
-- enemies = {}

-- We want to remove enemy 5

-- This removes the enemy (eventually).
enemies[5].s:setMaskBits(0) -- Collides with nothing.
table.remove(enemies, 5) -- Remove it from the table. This should also cause it to not be drawn on screen.

-- The object will eventually be removed from Box2D by the garbage collector. 
-- If you can't wait, use collectgarbage(). If you decide to use collectgarbage(), 
-- you don't need to set the mask bits.
Make sure there are no other references to the table, though. Otherwise it will not be removed. Use World:getBodyCount() to check how many bodies Box2D "sees" at any time.

I eventually decided to do it like this, because I wanted to avoid World:removeBody( body ) at all cost.
User avatar
conman420
Prole
Posts: 33
Joined: Sun Aug 31, 2008 8:46 pm

Re: Help needed for bullet collisions and enemy deaths.

Post by conman420 »

Ahhh...

Perhaps you could make a convienence function? Like shape:remove()

Would be more intuitive and faster :)

Lovely support here though :D
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot], Bing [Bot], Google [Bot], Majestic-12 [Bot], Semrush [Bot] and 10 guests