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. ;)
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)
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 |
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]