I have a stationary object firing bullets at foe(enemy object). The problem is the game is running at 30fps, and the bullets are travelling at a pretty fast speed, so fast that they sometimes endup on the other side of the foe, but not so fast that they passthrough foe. I'm using circular collision and the foe have a circle of radius 4 with origin placed at the center of their circular-sprite. The bullets are small with radius of 1 or 2. Everytime a bullet is colliding within the foe's circle the game takes the angle between the bullet and the foe (the bullet-foe-angle) and applies a force in that direction onto the foe. Normally this would mean any bullets fired at the foe push the foe in a 'general direction away' from the bullet's point of origin but not necessarily in the direction of the bullet's projectory(unless it's a dead-on hit). But a bullet which first appears on the other side of the foe's origin... is going to push it forwards, towards the bullet's point of origin. Also of note is that the last bullet to hit the foe before it hits 0 hp determines the direction of the foe's ragdoll/corpse effect(any small bullet bump basically gets pronounced for a second or two). Each object has a dx,dy variable tabulating all the forces applies on it that frame then added to their x,y.
My main consideration is finding a fix that has a low cpu cost, but don't let it limit the discussion.
So the OP's issue is caused primarily by two things since spd/fps changes are off the table:
a) the bullet first appearing in the wrong side of the foe's circle-Hitbox
b) the fact I use the direction from the bullet to the center of the foe (bullet-foe-angle) to determine the dx+= dy+= that's applied when the bullet hits the foe.
Ideas to remedy:
-
Since the object firing bullets is stationary I could take the angle from the object to the foe, rather than the bullet to the foe. This works...but it's the same effect regardless of where a stationary foe is hit(dead-center/left/right/etc). And if something is dx+=1 and dy+=0, aka sidestepping a player's position, and they get hit by bullets from that player...should the bullet bump them slightly in the direction of the bullet's projectory? If it's a deadon hit...ya...but if it's grazing their side its usually a bit weird looking to have them be bumped with the bullet projectory a bit. The dx are all added up so the sidestep movement is still the dominant force but still. But with the original bullet-foe-angle method the graze would send them uniquely upwards a bit instead and the deadon hit would send them with the bullet projectory which seems overall better/unique looking from my testing. There's also the consideration of the ragdolling where any seemingly small and weird bump direction gets pronounced via a multiplier for a brief period of time.
-
I keep the bullet-foe-angle method and give the foe a second circle of larger radius, say 8. Once the bullet enters this larger circle the angle between the two is taken and recorded for that specific bullet-foe relationship. The larger circle then never runs again for that specific relationship, and when the smaller radius 4 circle is triggered I use that recorded angle. This seems to be a best of both worlds approach....it will still suffer a bit from (1)'s methodology...but it would solve the OP. Personally I don't like this method, cause it makes it so every bullet has to have a little bit of the (1) problem as a cost to fix the few bullets that fall under the OP's problem.
-
If the foe is always facing the player when they are fired at...then I can maybe identify when bullets are 'on the wrong side' of the foe's circle. Then I can maybe do some kind of reflection(like how when u make a circle u make a small ~45 degree arc then reflect it 8 ways to make the full circle) to, in-effect, teleport the bullet a few pixels backwards along it's projectory enough so it sends the foe in generally the correct direction as intended with the bullet-foe angle method.
- Something with the dot product I could use....? Not sure. Apparently it can tell when two objects are facing eachother...I made a demo a while back proving to myself it does...maybe something there could help...I'm thinking it would not work right here, but just an idea.
Would appreciate any ideas/methods that could be thought of to help solve this.
classic idea: instead of directly checking the bullet’s current position, you sweep from previous to current (incrementing by some pixels at a time), and check each position. this avoids the jumping issue when speed is higher than enemy/obstacle width.
for rectangular collisions this technique is called swept aabb and is easy to search for; you should be able to apply it for circles too!
what about fixing the root cause?
compute intersection between a segment (start/end position of bullet) vs a circle (foe radius), that will give the exact point of impact (eg always in front of foe).
you can also simplify the response by always applying
velocity of the bullet - the actual position of the bullet doesn’t really matter after a hit.
@freds72 What would be the process to this? Do basic circle-circle collision...and if I get a hit I then mentally draw the trajectory line of the bullet and calculate the point it would first make contact with the outside of the circle? Is 'raycasting after a collision' the intended idea? Or is there a different order I should be looking at
plain maths: http://kylehalladay.com/blog/tutorial/math/2013/12/24/Ray-Sphere-Intersection.html
but tbh, suggest to simplify and take the bullet direction as the nudge vector
@freds72 Okay this is what I ended up with after going through that post. They referred to needing a ray consisting of a point of origin and a 'direction vector'. And then they used a formula of "Origin + Direction * t = Point" to represent any point on the ray(the bullet's trajectory line) over time. I didn't get what they meant by direction vector though. The goal was to determine the length of the line from the bullet's origin to the first hypothetical point of overlap with the circle (aka the first big red dot in the gif below). That formula basically takes that line's length and multiplies it by the 'direction vector'...so that seems to heavily indicate they are referring to you needing a unit vector when they say direction vector? Anyways that was my understanding and I have this so far which seems to work assuming we have a ray moving in a linear fashion from some predefined origin point.
function ray_circ(ray,circ) --ray needs an origin ox, oy i.e. ray_circ({cx=,cy=,dx=,dy=,ox=,oy=},{cx=,cy=,r=}) local cx,cy=circ.cx,circ.cy local ox,oy=ray.ox,ray.oy --take the unit-vector of the vector <ray.dx,ray.dy> where dx and dy are the change in movement each frame. local rdx,rdy=ray.dx,ray.dy --convert the change in bullet movement to a unit vector: local udx,udy=rdx/sqrt(rdx*rdx+rdy*rdy),rdy/sqrt(rdx*rdx+rdy*rdy) --mbe dont need...but need for diagonals --so now we just need x=ox+udx*hypotenus...so goal is to find the hypotenus to the point on the outer circ. --start with hypotenus b/w origin and center of circ local l=sqrt((cx-ox)^2+(cy-oy)^2) --project this hypotenus line against the bullet's trajectory udx udy local ldx,ldy=cx-ox,cy-oy --will need the dx/dy of the hypotenus line --<ldx,ldy> dot <udx,udy> local tc=(ldx*udx)+(ldy*udy) --tc is the dist from origin to perpendicular point from center of circle if tc<0 then return end --bailout early cause opposite direction --now solve for length of d since we have the other two parts local d=sqrt(l^2-tc^2) --the perp distance b/w center and tc if d>circ.r then return end --bailout early --so now that we have d we can make a right triangle to p1, knowing d and that h=radius: local t1c=sqrt(circ.r^2-d^2) --we have at this point --t1c: point from p1 along trajectory to spot on trajectory that is perp to center of circ --tc: point from origin to this same spot along trajectory that is perp to center of circ local t1=tc-t1c --length of line from origin to p1 (red dots below) local t2=tc+t1c --length of line from origin to p2 p1x=ox+udx*t1 p1y=oy+udy*t1 p2x=ox+udx*t2 p2y=oy+udy*t2 return true -- return p1x,p1y,p2x,p2y end |
This is how they compare when the bullet teleports through the object and first procs on the wrong side. The origin method seems sort of fine, but I did that in an earlier game and it definitely was not fine...I might have made a mistake in implementation but I'm wary of it.
I optimized cpu down to the below, where it's about 2-3x the cpu of a normal collision. And this assumes it would only have to run once per collision, likely within the normal point-circle/circle-circle check. Not sure if cpu can be optimized further, or if there's a better method altogether. Tokens also an issue.
function ray_circ(ray,circ) local cx,cy,r=circ.cx,circ.cy,circ.r local ox,oy=ray.ox,ray.oy local rdx,rdy=ray.dx,ray.dy local udx,udy=rdx/sqrt(rdx*rdx+rdy*rdy),rdy/sqrt(rdx*rdx+rdy*rdy) local ldx,ldy=cx-ox,cy-oy local l = ldx*ldx + ldy*ldy --squared both sides local tc = ldx*udx + ldy*udy if tc<=0 then return end local d = l - tc*tc --squared both sides if d>r*r then return end local t1=tc-sqrt(r*r - d) p1x,p1y=ox+udx*t1,oy+udy*t1 return true end -- if ray_circ(bullet,foe) then --calculate and use the angle from p1x,p1y to foe's centerpoint... |
Hi @Cerb043_4:
A simple solution would be to check the collision of the bullets without updating the screen if the speed is >8.
So let's say the speed of the bullet is 9 which can definitely skip an 8x8 tile. To check a collision you could have a FOR/END comparison of each pixel that exceeds the speed you know it can miss, in this case, 9.
You could even have it step by 4 or so to make it faster knowing your collision routine would catch it every time.
An even simpler solution would be to check to see if the bullet in one cycle is suddenly the opposite of its previous cycle in X1+Y1 @ X2+Y2, where X1+Y1 is the player's shot and X2+Y2 is the prospective target.
So let's say that x1<x2 and Y1=y2.
The next cycle if x1>x2 and y1=y2, that would register a collision since now x1 > x2 and previously x1 was < x2.
The simplest solution of all though would be to adjust your hit-box to be >8 if the speed of the shot is >8.
So for instance your vertical speed of your shot is zero and the horizontal speed of the shot is 9. Then it is simply:
a=8 if speedofshotx>8 then a=speedofshotx end b=8 if speedofshoty>8 then b=speedofshoty end if abs(shotx-enemyx)<a and abs(shoty-enemyy)<b then { target hit } end |
[Please log in to post a comment]