Log In  


Hi there,
I'm making a scorched earth-style game and it's all coming together pretty well, but I'm having some real problems with collision. Right now what I've got is pretty rudimentary but I can't work out how to do it any better. Basically when the tank fires a shot, the shot moves in a parabola with variables yspeed and xspeed that get added on to the y and x positions of the shot each frame. The shot cannot collide with anything for two frames (to prevent collision with the tank itself) but after that, the shot should explode on contact with any pixel that isn't the colour of the background (i.e. black). However, if moving at a decent speed it will usually move through walls for a frame or so before exploding.

I've attached the cartridge and the relevant collision code.

function shot:update()
 if self.timeout > 0 then
  self.x = flr(self.x + self.xspeed)
  self.y = flr(self.y + self.yspeed)
  self.yspeed = self.yspeed + gravity
  self.timeout -= 1
 else 
  if self.x>=127 then
   self.x = 127
   fired = false
  end
  if self.x<=0 then
   self.x = 0
   fired = false
  end
  if self.y>=127 then
   self.y = 127
   fired = false
   explode = true
   sfx(1,1)
  end
  if pget(self.x,self.y+3)!=0 
  and pget(self.x,self.y+3)!=textclr then
   fired = false
   explode = true
   sfx(1,1)
  end
  self.x = flr(self.x + self.xspeed)
  self.y = flr(self.y + self.yspeed)
  self.yspeed = self.yspeed + gravity
 end
end


Test each point from the last position to the current. As soon as it hits anything stop and explode. Depending on your movement implementation you could either use sth like bresenham or simply divide the shot's dx and dy by some factor depending on its current speed.
Good luck!


I would go with the latter @johanp suggestion. Since the bullet is the only thing moving really, you might as well just waste a lot of cycles updating it's position at 10x or 100x the actual framerate. All you need to do is add a loop and divide the speed by that amount each step.

Not efficient maybe, but neither is wasting all those cycles doing nothing. ;)


I have to ask the obvious question. Isn't there a collision detection routine in PICO ?

Function CollideImage:Object


Nope, it's just that bare. The only things you get are the bank of sounds/"songs", and the bank of "sprites"/map. Just like the songs just point to a series of sounds, the map just references the sprites. (and the sprites can have flags set, but they aren't objects or anything at all. you have to manually deal with tracking, collision, iterating through for whatever updates, etc)


1

Well, if we're going old-school, sounds like we are. The easiest way to detect collision between two points I've used in the past for my games is:

if abs(x1-x2)<8 and abs(y1-y2)<8 then POW

where x1&y1 is the location of sprite#1 and x2&y2 are the location for sprite#2, both sized at 8x8 pixels.


what johanp & slembcke said.
use the max number of pixels travelled on x or y as steps

something like that:

dx=nx-ox -- new_pos-old_pos
dy=ny-oy
steps=max(abs(dx),abs(dy))
sdx=dx/steps 
sdy=dy/steps
for s=1,steps do
	x=ox+sdx*s
	y=oy+sdy*s
	c=pget(x,y)
	if (c!=air) boom(x,y) break
end

this will test some (air) pixels multiple times, but that's a really negligible overhead.


@ultrabrite: this works for shots fired left, but shots fired right explode in mid-air. Any idea why this may be?

EDIT: Realised this isn't particularly helpful. Here's the implemented code:

function shot:collision()
 local dx = self.x-self.lastx
 local dy = self.y-self.lasty
 local steps = max(abs(dx),abs(dy))
 local sdx = dx/steps
 local sdy = dy/steps
 for s=1,steps do
  testx = self.lastx+sdx*s
  testy = self.lasty+sdy*s
  c = pget(testx,testy)
  if c!=0 and c!= textclr then
   self.x=testx
   self.y=testy
   fired = false
   explode = true
   sfx(1,1)
   break
  end
 end
end

Bump


sorry I didn't notice!

the missile might be testing itself, try adding its color alongside air & text. hard to say why it wouldn't happen on the left though.


I've fixed it by changing the colour check to just check the floor colour - it means the shots won't collide directly with tanks now, but it fixes the issue, so it's better than before :) Thanks for the help!


but you'll get the same problem, missile moving thru tanks at high speed.
though you could test distances from missile to tanks at every testx/testy.

glad to help!


late to the party and might be unrelated,
but this works great for circle-circle continuous collision,

returns:
the exact timestep (0-1)
<0 if already penetrating
false if not colliding.

https://www.gamasutra.com/view/feature/131424/pool_hall_lessons_fast_accurate_.php

function poolhall(a,b)
 local relativecenter=b.center-a.center
 local relativevelocity=a.linearvelocity-b.linearvelocity

 local mrc=magnitude(relativecenter)
 local mrv=magnitude(relativevelocity)

 local radii=(a.radius+b.radius)

 if mrv<mrc-radii then
 	return false
 end

 local u=unitvector(relativevelocity)
 local d=dotproduct(relativecenter,u)

 if d<=0 then 
 	return false
 end

 local f=mrc^2-d^2
 local radiisquared=radii^2

 if f>=radiisquared then
 	return false
 end

 local t=radiisquared-f 

 if t<0 then
 	return false
 end

 local distance=d-sqrt(t)

 if mrv<distance then
 	return false
 end

 return distance/mrv
end

(NOTE: using 2d-vector meta tables!)



[Please log in to post a comment]