Page 1 of 1

Help needed for bullet collisions and enemy deaths.

Posted: Mon Sep 01, 2008 12:32 pm
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

Re: Help needed for bullet collisions and enemy deaths.

Posted: Mon Sep 01, 2008 1:31 pm
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)

Re: Help needed for bullet collisions and enemy deaths.

Posted: Mon Sep 01, 2008 2:05 pm
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.

Re: Help needed for bullet collisions and enemy deaths.

Posted: Mon Sep 01, 2008 3:47 pm
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.

Re: Help needed for bullet collisions and enemy deaths.

Posted: Mon Sep 01, 2008 4:28 pm
by conman420
Ahhh...

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

Would be more intuitive and faster :)

Lovely support here though :D