I'm working on a little 2D platformer/fighting game with C++ and SDL, and I'm having quite a bit of trouble with the collision detection.
The levels are made up of an array of tiles, and I use a for loop to go through each one (I know it may not be the best way to do it, and I may need help with that too). For each side of the character, I move it one pixel in that direction and check for a collision (I also check to see if the character is moving in that direction). If there is a collision, I set the velocity to 0 and move the player to the edge of the tile.
My problem is that if I check for horizontal collisions first, and the player moves vertically at more than one pixel per frame, it handles the horizontal collision and moves the character to the side of the tile even if the tile is below (or above) the character. If I handle vertical collision first, it does the same, except it does it for the horizontal axis.
How can I handle collisions on both axes without having those problems? Is there any better way to handle collision than how I'm doing it?
XNA's 2D platformer example uses tile-based collision as well. The way they handle it there is pretty simple and may useful for you. Here's a stripped down explanation of what's in there (removing the specific-to-their-demo stuff):
After applying movement, it checks for collisions.
It determines the tiles the player overlaps based on the player's bounding box.
It iterates through all of those tiles...
If the tile being checked isn't passable:
It determines how far on the X and Y axes the player is overlapping the non-passable tile
Collision is resolved only on the shallow axis:
If Y is the shallow axis (abs(overlap.y) < abs(overlap.x)), position.y += overlap.y; likewise if X is the shallow axis.
The bounding box is updated based on the position change
Move on to the next tile...
It's in player.cs in the HandleCollisions() function if you grab the code and want to see what they specifically do there.
Yes. Vector based collision will be much better than tile based. Define each edge of a tile as lines (there are short cuts, but ignore them for now.) Now to see if a collision has occured, find the closest horizontal and vertical line. if you take the sign of lastPos.x * LineVector.y - lastPos.y * LineVector.x and compare that with thisTurnsPos.x * LineVector.y - ThisTurnsPos.y * LinePos.x. If the signs of those two values differ, you have crossed that line this tic. This doesn't check if you've crossed the end of a line segment though. You can form a dot product between the same lineVector and your curPosition (a little error here, but good enough probably) and it is either negative or greater than the line's magnitude squared, you aren't within that line segment and no collision has occured.
Now this is farily complex and you could probably get away with a simple grid check to see if you've crossed into another square's area. But! The advantage of doing it with vectors is it solves the moving faster than the size of the collision box problem and (more importantly), you can use non axis aligned lines for your collisions. This system works for any 2D vectors (and with a little massaging, 3D as well.) It also allows you slide your character along the edge of the collision box rather easily as well because you've already done 99% of the math needed to find where you are supposed to be after a collision.
I've glossed over a couple of implementation details, but I can tell that I've used the above method in countless commercial video games and it has worked like a charm. Good Luck!
Related
I'm building a server-side(!) for online game and I need to check if bullet hit a target.
Client sends me position and angle of bullet fired, I know all the enemies coordinates and sizes. How can I calculate if bullet hit an enemy after a bullet fired? Enemies have different sizes, bullet is 1px size.
For example, I have player shooting angle of 45 degrees from 0, 0 coordinates, and an enemy on 200, 200 coordinates with 10 width, 15 height. How do I calculate that bullet hit the enemy?
UPDATE: The game is 2d dhooter from the top, like Crimsonland. Projectiles have start speed which decreases slowly.
You should get a glance at the power of a point.
It will allow you to know the minimal distance between a line and a point.
So if the distance is lower than the width of the target, it's a hit !
The easiest way is to just check if the bullet is inside the rectangle of the enemy. If it is, dispose the bullet and the enemy.
basically:
while(game.is_on()){
for(enemy = enemyCollection.begin(); enemy != enemyCollection.end(); ++enemy){
if(enemy.contains(bullet)){
enemy.dead();
bullet.hit();
}
}
If you need to check whether line intersects rectangle, then apply orientation test based on cross product - if sign of cross product is the same for all rectangle vertices - line does not intersect it.
Line L0-L1, point P
Orientation = Cross(P - L0, L1 - L0)
(instead of L1-L0 components you can use Cos(Fi) and Sin(Fi) of shooting angle)
If you need to get intersection points, use some algorithm for line clipping - for example, Liang-Barsky one.
It's not quite as easy as it looks. To get a full solution, you need to extrude your objects along their paths of travel. Then you do object to object intersection tests.
That's difficult, as extrusion of an arbitrary shape by an arbitrary path is hard. So it's easier to cheat a little bit. Bullets can have no area, so they are just line segments. Make sure the step size is low enough for the line segment to be relatively short. Then make the ships rectangles, again with relatively short lines. Now it's lots of short line to line intersection tests. You can do these quickly with the planesweep algorithm. The nice thing about planesweep is that you can also extend it to curves or shapes, by returning possible intersections rather than actual intersections.
Once you have an intersection between a bullet and a ship's collision rectangle, you might need to run a timestep to check that a hit actually takes place rather than a narrow miss. You can also determine whether the hit was on front, left, right or back at this point.
I'm trying to create OBB's around objects and then collide them using DirectX 11 and C++. It all works fine until I rotate one (or more) of the boxes and then the collision detection returns true too early. It seems to still "correctly" collide but just too early (as in bounding box is too big). I think I'm going wrong on the fundamentals so here's a quick breakdown of the steps I've done:
Create a OBB struct that contains a Vector mCentre, Float[3] mExtents and Vector[3] mLocalAxes.
I derive the centre by finding the "left" adding the half of the width, rinse and repeat for other axes.
The extents are the "half widths" so width, height and depth / 2
The local axes is taken from the top 3 rows of the matrix.
I then use a function to check all 15 axes to test for a separating axis which returns false if one is found.
If anyone can see a flaw in my logic and point it out that'd be great! (I have a feeling my centre calculations are wrong)
Normally I would put up code but this is coursework so I'd much rather just find where my train of thought is wrong!
Thanks in advance (sorry for the wall of text)
I have a small application I have built where there are a few balls on a blank background. They all start flying through the air and use the physics I wrote to bounce accurately and have realistic collision responses. I am satisfied with how it looks except I have an issue where when my balls land directly on top of each other, the attach together and float directly up.
Here are the functions involved
https://gist.github.com/anonymous/899d6fb255a85d8f2102
Basically if the Collision function returns true, I use the ResolveCollision to change their velocities accordingly.
I believe the issue is from the slight re-positioning I do in ResolveCollision(). If they collide I bring them a frame or so backwards in location so that they are not intersecting still the next frame. However, when they are directly on top they bounce off eachother at such small bounces that eventually stepping back a frame isn't enough to unhook them.
I'm unsure if this is the problem and if it is, then what to do about it.
Any help would be awesome!
The trick is to ignore the collision if the circles are moving away from each other. This works so long as your timestep is small enough relative to their velocities (i.e. the circles can't pass through each other in a single frame).
When the circles first collide, you will adjust their velocity vectors so their relative velocity vector pushes them apart (because a collision will do that). After that, any further collisions are spurious because the circles will be moving apart, and will eventually separate completely. So, just ignore collisions between objects that are moving apart, and you should be fine.
(I've implemented such an algorithm in a 3D screensaver I wrote, but the algorithm is entirely dimension-agnostic and so would work fine for 2D circles).
As seen in the image
I draw set of contours (polygons) as GL_LINE_STRIP.
Now I want to select curve(polygon) under the mouse to delete,move..etc in 3D .
I am wondering which method to use:
1.use OpenGL picking and selection. ( glRenderMode(GL_SELECT) )
2.use manual collision detection , by using a pick-ray and check whether the ray is inside each polygon.
I strongly recommend against GL_SELECT. This method is very old and absent in new GL versions, and you're likely to get problems with modern graphics cards. Don't expect it to be supported by hardware - probably you'd encounter a software (driver) fallback for this mode on many GPUs, provided it would work at all. Use at your own risk :)
Let me provide you with an alternative.
For solid, big objects, there's an old, good approach of selection by:
enabling and setting the scissor test to a 1x1 window at the cursor position
drawing the screen with no lighting, texturing and multisampling, assigning an unique solid colour for every "important" entity - this colour will become the object ID for picking
calling glReadPixels and retrieving the colour, which would then serve to identify the picked object
clearing the buffers, resetting the scissor to the normal size and drawing the scene normally.
This gives you a very reliable "per-object" picking method. Also, drawing and clearing only 1 pixel with minimal per-pixel operation won't really hurt your performance, unless you are short on vertex processing power (unlikely, I think) or have really a lot of objects and are likely to get CPU-bound on the number of draw calls (but then again, I believe it's possible to optimize this away to a single draw call if you could pass the colour as per-pixel data).
The colour in RGB is 3 unsigned bytes, but it should be possible to additionally use the alpha channel of the framebuffer for the last byte, so you'd get 4 bytes in total - enough to store any 32-bit pointer to the object as the colour.
Alternatively, you can create a dedicated framebuffer object with a specific pixel format (like GL_R32UI, or even GL_RG32UI if you need 64 bits) for that.
The above is a nice and quick alternative (both in terms of reliability and in implementation time) for the strict geometric approach.
I found that on new GPUs, the GL_SELECT mode is extremely slow. I played with a few different ways of fixing the problem.
The first was to do a CPU collision test, which worked, but wasn't as fast as I would have liked. It definitely slows down when you are casting rays into the screen (using gluUnproject) and then trying to find which object the mouse is colliding with. The only way I got satisfactory speeds was to use an octree to reduce the number of collision tests down and then do a bounding box collision test - however, this resulted in a method that was not pixel perfect.
The method I settled on was to first find all the objects under the mouse (using gluUnproject and bounding box collision tests) which is usually very fast. I then rendered each of the objects that have potentially collided with the mouse in the backbuffer as a different color. I then used glReadPixel to get the color under the mouse, and map that back to the object. glReadPixel is a slow call, since it has to read from the frame buffer. However, it is done once per frame, which ends up taking a negligible amount of time. You can speed it up by rendering to a PBO if you'd like.
Giawa
umanga, Cant see how to reply inline... maybe I should sign up :)
First of all I must apologize for giving you the wrong algo - i did the back face culling one. But the one you need is very similar which is why I got confused... d'oh.
Get the camera position to mouse vector as said before.
For each contour, loop through all the coords in pairs (0-1, 1-2, 2-3, ... n-0) in it and make a vec out of them as before. I.e. walk the contour.
Now do the cross prod of those two (contour edge to mouse vec) instead of between pairs like I said before, do that for all the pairs and vector add them all up.
At the end find the magnitude of the resulting vector. If the result is zero (taking into account rounding errors) then your outside the shape - regardless of facing. If your interested in facing then instead of the mag you can do that dot prod with the mouse vector to find the facing and test the sign +/-.
It works because the algo finds the amount of distance from the vector line to each point in turn. As you sum them up and you are outside then they all cancel out because the contour is closed. If your inside then they all sum up. Its actually Gauss's Law of electromagnetic fields in physics...
See:http://en.wikipedia.org/wiki/Gauss%27s_law and note "the right-hand side of the equation is the total charge enclosed by S divided by the electric constant" noting the word "enclosed" - i.e. zero means not enclosed.
You can still do that optimization with the bounding boxes for speed.
In the past I've used GL_SELECT to determine which object(s) contributed the pixel(s) of interest and then used computational geometry to get an accurate intersection with the object(s) if required.
Do you expect to select by clicking the contour (on the edge) or the interior of the polygon? Your second approach sounds like you want clicks in the interior to select the tightest containing polygon. I don't think that GL_SELECT after rendering GL_LINE_STRIP is going to make the interior responsive to clicks.
If this was a true contour plot (from the image I don't think it is, edges appear to intersect) then a much simpler algorithm would be available.
You cant use select if you stay with the lines because you would have to click on the line pixels rendered not the space inside the lines bounding them which I read as what you wish to do.
You can use Kos's answer but in order to render the space you need to solid fill it which would involve converting all of your contours to convex types which is painful. So I think that would work sometimes and give the wrong answer in some cases unless you did that.
What you need to do is use the CPU. You have the view extents from the viewport and the perspective matrix. With the mouse coord, generate the view to mouse pointer vector. You also have all the coords of the contours.
Take the first coord of the first contour and make a vector to the second coord. Make a vector out of them. Take 3rd coord and make a vector from 2 to 3 and repeat all the way around your contour and finally make the last one from coord n back to 0 again. For each pair in sequence find the cross product and sum up all the results. When you have that final summation vector keep hold of that and do a dot product with the mouse pointer direction vector. If its +ve then the mouse is inside the contour, if its -ve then its not and if 0 then I guess the plane of the contour and the mouse direction are parallel.
Do that for each contour and then you will know which of them are spiked by your mouse. Its up to you which one you want to pick from that set. Highest Z ?
It sounds like a lot of work but its not too bad and will give the right answer. You might like to additionally keep bounding boxes of all your contours then you can early out the ones off of the mouse vector by doing the same math as for the full vector but only on the 4 sides and if its not inside then the contour cannot be either.
The first is easy to implement and widely used.
How would you store game level for a Dizzy-like adventure game? How would you specify walkable areas and graphics? Is it tile-based, pixel-based or walkable surfaces can be described by vectors?
Very old adventure games (like Sierra's quests from the 80s) used to actually maintain a separate bitmap of the entire screen that represented z-depth and materials to determine where your character could go and where it would be hidden. They would use pixel sampling to check where their small sprites would go.
Though current machines are faster, long side scrolling levels make this sort of approach impractical, IMHO. You have to go with more sparse representations.
One option is to "reduce" your game into invisible tiles, which are easier to represent. The main problem with this is that it can constrain your design (e.g., difficult to do diagonal platforms), and it can make the animations very shoddy (e.g., your characters' feet not actually touching the platform). This option can work IMHO for zelda-like adventure games, but not for action games.
A second option is to represent the game world via some vector representation and implement some collision detection. I would personally go with the second solution, especially if you can be smart about how you organize your data structures to minimize access time (e.g., have faster access to a subset of world elements close to your characters current position).
I wouldn't be surprised if there are available 2D game engines that provide this sort of capability, as there are definitely 3D engines that do it. In fact, you may find it easier to use an existing 3D game engine and use it to render 2D.
The Dizzy game is probably using a tile-based system. The game world is made up of a palette of tiles that are repeated throughout the level. Each tile would have three elements - the image drawn to the screen, the z-buffer to allow the main character to walk behind parts of the image and a collision map. The latter two would be implemented as monochrome images such that:
colour | z map | collision
-------|--------------------|---------------
black | draw dizzy infront | collide
white | draw dizzy behind | don't collide
Storing these are monochrome images save a lot of ram too.
You would have an editor to build level that would display a grid where tiles can be dragged and dropped.
That specific game is a tile-based game, with pixel perfect collision. The collision was controlled by a single bit in the colour byte of the tile (the brightness) and you could also mirror the tile by setting the flashing bit of the colour.
The tiles, however, could only be placed at even x,y coordinates (I suspect this was done to help the collision system a bit.)
The collision system involved a persistent check around the hero. The rules were roughly:
- If it finds a non-collision pixel row below the hero, it dropped the hero by 1 pixel.
- If there is a collision intersection with the hero, it raised the hero by 1 pixel.
- When moving left or right, it checks in that direction and if:
- if finds a wall (collision height more than 4 pixels), deny movement in that direction;
- if it finds a climbable box (collision height up to 4 pixels), allow movement in that direction.
- If there is enough headroom allow jumping, else, stop at the last possible free position.
When combining these simple rules, you obtained a very smooth collision negotiation able to walk even arbitrary slopes without extra taxing.
In creating such a game, I would use individual tiles, I would assemble them on layers (background, foreground etc...) and render them. I would assemble a separate collision map from the tiles attributes that indicate a collision tile, and I would use that separate map to negotiate the hero's collision and movement.
Similar to this demo: http://games.catevaclickuri.ro/