Day 18: Sprite collisions with circle shape. Pixel-perfect bullet trajectory.
Today I got fed up with the "moon hit" bug. Sometimes aliens would not get hit even if they are visible on the screen. I set up a bunch of tests with screen filled with aliens and semi-transparent moon to see what's going on. My coordinates for hit-test area were a bit off, but even when I fixed that it was not good. Box simply cannot cover the circled area well. You either miss aliens when you should hit them, or you shoot some that are completely hidden.
So I decided to switch to circle based checks. Since the moon in much larger than aliens, it would be enough to check whether all the 4 points of alien's bounding box are inside the moon's circle or not. To test this visually, I used libGDX builtin ShapeRenderer, like this:
shapeRenderer.setProjectionMatrix(camera.combined); shapeRenderer.begin(ShapeType.Circle); shapeRenderer.setColor(1, 1, 1, 1); shapeRenderer.circle(sMoon.getX() + 119, sMoon.getY() + 116, 167); shapeRenderer.end();
This code comes after SpriteBatch is complete. The white circle is drawn over the scene. I also draw all the bounding boxes of aliens using ShapeType.Box.
An efficient way to test whether point is inside the circle is not to use square root (slow) and just compare squared distances instead. libGDX builting function Circle.contains(x, y) does that properly, so I'm using that for checks. This works really nice. I extended the radius by a few pixels, because all the sprites have some padding and I'm really satisfied with the result:
Pixel-perfect bullet trajectory
In this game, bullets are fired some 50px below the bottom of the screen. I'm using atan2 function to rotate the bullet and hit the mark, but there were a couple of errors in my code, especially when you miss the target. To understand the rest of this post, please note that the game uses HitScan for all the shots in the game.
When player misses the target, the code extrapolates the trajectory to the end of the screen. Previous code that did this set the end point too far. Since the bullet is using tween engine to fly, this results in large jumps and bullet is only seen in 2-3 positions on the screen before it flies out. I fixed this code to set the end point at the edge of the screen, so now you can actually see the bullet flying.
This revealed another problem: the bullet sprite was sometimes 10-20 pixels off from the point where player touched the screen. There are three causes of this problem. First, I was using X and Y coordinates of the bullet sprite, which is the lower-bottom corner. I fixed this to use the bullet sprite center by adding half of width and height. But it was still off on some shots. Second problem was that I forgot to set the origin, so bullet was rotated around the bottom-left corner. I fixed that as well, but still some shots going to the left side of the screen were bad.
Then I realized that, when bullet rotates, width and height of the sprite changes, so the bullet center needs to be calculated after the rotation. With that fix, the bullet flies right through the point where player touched. The code looks like this:
// fly the bullet LaserBullet lb = new LaserBullet(tUI, 65, 64, 20, 40); lb.setPosition(0, -450); lb.setOrigin(10, 20); lb.setRotation( (float)(Math.atan2(-x, 450f+y) * 180f / Math.PI) ); Rectangle r = lb.getBoundingRectangle(); x = (int)(x - r.width * 0.5f); y = (int)(y - r.height * 0.5f); lb.target.set(x, y); bullets.add(lb); Tween.to(lb, SpriteTweenAccessor.POSITION_XY, delay) .target(x, y).start(tweenManager);read more... Tweet to @bigosaur Tweet Bigosaur, 2013-10-17