So, I'm creating a simple 2D video game. I noticed recently that the player can move off the screen, so I added this code:
if (newX <= SIZE * TagsCanvas.SCALE) {
newX = SIZE * TagsCanvas.SCALE;
} else if (newX >= TagsCanvas.CANVAS_WIDTH - (SIZE * TagsCanvas.SCALE) - getAABB().getRadX()) {
newX = TagsCanvas.CANVAS_WIDTH - (SIZE * TagsCanvas.SCALE) - getAABB().getRadX();
}
if (newY <= SIZE * TagsCanvas.SCALE) {
newY = SIZE * TagsCanvas.SCALE;
} else if (newY >= TagsCanvas.CANVAS_HEIGHT - (SIZE * TagsCanvas.SCALE) - getAABB().getRadY()) {
newY = TagsCanvas.CANVAS_HEIGHT - (SIZE * TagsCanvas.SCALE) - getAABB().getRadY();
}
TagsCanvas.CANVAS_WIDTH is the width of the canvas object. Height is the height of the canvas object. Size is the size of the player, and scale is the scale of the game (At the moment it's 2). getAABB().getRadX() returns the radius from the center of the player to the edge (The 'player' is a square box).
Now, the x works fine, but the Y part doesn't. It'll block the player from moving up off the map (The first statement), but it will let the player go about 20 points too far down. Any help would be appreciated!
What you can do is have it wraparound by using the Modulo operator (i.e. remainder after integer division).
Say you want to move 5 units:
Have a method:
public void move(int amt){
pos = (pos + amt) % TagsCanvas.CANVAS_WIDTH.
}
The Modulo operator makes sure that the value will never be equal to or greater than TagsCanvas.CANVAS_WIDTH.
Related
I am looking at a source code of a brick breaker game created by r3ticuli(an user of GitHub). The below method checks whether two objects intersect each other or not, and if they do, the method checks and returns which part of the main object is touched by the other.
The method is part of GameObject class (paddle, brick, and ball are all sub-classes of this class) The class object's x and y coordinates start at the object's upper left corner. The programmer, who made this code, only calls this method with paddle or brick and passes ball as an argument in main method.
I spent two days to understand how this logic works. I understand that the variable theta calculates the angle of vector between two objects' centers. However, I can not understand what is the purpose of the variable, diagTheta, and how the method determines which part of the object is interconnected to the other.
Thank you for your support.
*If my question has a problem, I'll fix it.
/**
* Compute whether two GameObjects intersect.
*
* #param other
* The other game object to test for intersection with.
* #return NONE if the objects do not intersect. Otherwise, a direction
* (relative to <code>this</code>) which points towards the other
* object.
*/
public Intersection intersects(GameObject other) {
if ( other.x > x + width
|| other.y > y + height
|| other.x + other.width < x
|| other.y + other.height < y)
return Intersection.NONE;
// compute the vector from the center of this object to the center of
// the other
double dx = other.x + other.width /2 - (x + width /2);
double dy = other.y + other.height/2 - (y + height/2);
double theta = Math.atan2(dy, dx); // In here, theta means the angle between objects. from original to the other object (center to center).
double diagTheta = Math.atan2(height, width); // This is angle from (x,y) to its opposite tip side of the shape.
if ( -diagTheta <= theta && theta <= diagTheta )
return Intersection.RIGHT;
if ( diagTheta <= theta && theta <= Math.PI - diagTheta )
return Intersection.DOWN;
if ( Math.PI - diagTheta <= theta || theta <= diagTheta - Math.PI )
return Intersection.LEFT;
// if ( diagTheta - Math.PI <= theta && theta <= diagTheta)
return Intersection.UP;
}
In this picture, (dx dy) is the RED arrow and it points from this to the other. And (width, height) is the blue arrow, which is the diagonal of this. The atan2 function will return the angle of the arrow, from -pi to +pi, where zero is pointing right, positive up, negative down.
In the picture you can see that the blue arrow is more positive that the red arrow, i.e it is closer to pi. Therefore diagTheta is about 2.9 and theta is about 2.5. Does that make sense?
When you run through the logic, you can see it returns UP, which is to say that other box is above this box.
As you move the other box to different locations, the RED arrow will change, and you get different theta values. Try out several possibilities and confirm for yourself that the logic returns the correct result.
I am programming a game in java with reference to youtube videos and have come across some code and algorithms that I don't quite understand.
private float x, y;
public Camera(float x, float y) {
this.x = x;
this.y = y;
}
public void tick(GameObject object) {
x += ((object.getX() - x) - 1000 / 2) * 0.05f;
y += ((object.getY() - y) - 563 / 2) * 0.05f;
System.out.println(x);
if (x <= 0)
x = 0;
if (x >= 1032)
x = 1032;
if (y <= 0)
y = 0;
if (y >= 563 + 48)
y = 563 + 48;
}
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
}
Now, I understand everything here except this
x += ((object.getX() - x) - 1000 / 2) * 0.05f;
y += ((object.getY() - y) - 563 / 2) * 0.05f;
which is within the tick method. The method is what updates the camera to follow the player around the screen. The GameObject parameter is parsed in as the player object. How do the cameras x and y follow the player with this algorithm? Why do we subtract players x/y by the cameras x/y and then all multiplied by a float. I get the subtracting the windows width / 2 and height / 2 centers the player in the middle of the screen part.
The float is what gives the camera a smoother feel according to RealtutsGML. It's not strictly fixed on the players position, it slides along a little. But why does * 0.05 make this happen?
Then in the game class inside the main rendering method the cameras getX and getY method alongside the graphics2D's translate method are to determine where the camera moves within the game.
Graphics g = bs.getDrawGraphics();
Graphics2D g2d = (Graphics2D) g;
g.setColor(Color.BLUE);
g.fillRect(0, 0, 1000, 563);
g2d.translate(-camera.getX(), -camera.getY());
handler.render(g);
g2d.translate(camera.getX(), camera.getY());
Apparently, the screen center will be at the coordinates (x + 1000 / 2, y + 563 / 2). With this in mind, let's take a closer look at the formula (only the x part; y is exactly the same):
x += ((object.getX() - x) - 1000 / 2) * 0.05f;
Re-arranging the parenthesis, we get:
x += (object.getX() - (x + 1000 / 2)) * 0.05f;
We see that the screen center is right there. Let us substitute it with the variable c to make things clearer:
x += (object.getX() - c) * 0.05f;
The first term object.getX() - c is the difference between the object's position and the screen center. Hence, if the object is centered, this difference is zero. This is exactly the amount that we need to move the camera to exactly center the object. And if we had
x += (object.getX() - c);
... we would always keep the object at the screen center. But we have a factor of 0.05 there. Hence, we are not moving the camera by the entire distance. Only by a fraction of it. I.e., we are moving the object closer to the screen center. If it is far away, it will move fast (because the difference is large). If it is already close, it will move slowly (because the difference is small). The factor of 0.05 is setting how immediate the centering will be. A factor of 0 would not move the object at all, a factor of 1 would immediately center the object. Everything in between represents a smooth motion with different accelerations. You can imagine this factor as the stiffness of a spring between the screen center and the object.
Im trying to build a simple game, built on the bases of RealTutsGML wave game(I assume some here knows it so it might help). Anyway, Im trying to make a "bot player" that will avoid the enemies and I believe that everything should be right, but for some reason it works only on one enemy at a time(Im adding enemies to the game every few seconds and the new enemy is the only enemy that works). The weird thing is, I created a circle around my enemy, and with that circle I detect nearby enemies, I have the following code: if (circle.intersects(rectangle)), the circle recieves the player position and updates all the time, and the rectangle gets the enemies position through a for loop:
for (int i = 0; i < handler.object.size(); i++) {
GameObject tempObject = handler.object.get(i);
if (tempObject.getId() == ID.basicEnemy) {
rectangle = new Rectangle((int) tempObject.getX(), (int) tempObject.getY(), 16, 16); }
I set a print line if a collision occures, just to test and see, and whenever enemies hit the circle around the player, it prints "collision", it works with all enemies. but the following code that causes the player to move, only occures when the last created enemy object hits.
Thats how the whole method looks like:
public void AImove() {
for (int i = 0; i < handler.object.size(); i++) {
circle = new Ellipse2D.Double((int) player.getX() - 48, (int) player.getY() - 48, 130, 130);
GameObject tempObject = handler.object.get(i);
if (tempObject.getId() == ID.basicEnemy) {
rectangle = new Rectangle((int) tempObject.getX(), (int) tempObject.getY(), 16, 16);
}
if (rectangle != null && circle != null) {
if (circle.intersects(rectangle)) {
float diffX = rectangle.x - player.getX() - 8;
float diffY = rectangle.y - player.getY() - 8;
float distance = (float) Math.sqrt((rectangle.x - player.getX()) * (rectangle.x - player.getX())
+ ((rectangle.y - player.getY()) * (rectangle.y - player.getY())));
float newX = ((-1 / distance) * diffX * 10);
float newY = ((-1 / distance) * diffY * 10);
player.setVelX(newX);
player.setVelY(newY);
} else if (player.getX() != WIDTH / 2 - 32 && player.getY() != HEIGHT / 2 - 32) {
float diffX = player.getX() - 368 + 10;
float diffY = player.getY() - 268 + 10;
float distance = (float) Math.sqrt((player.getX() - 368) * (player.getX() - 368)
+ ((player.getY() - 268) * (player.getY() - 268)));
player.setVelX((-1 / distance) * diffX * 5);
player.setVelY((-1 / distance) * diffY * 5);
}
}
}
}
(Just a short exlanation, newX/Y algorithm is not setting the player in a new position, it just sets the velocityX/Y, which moves the player to the oopsite direction of the collided enemy, if there is no collision, the player will try to go back to the center of the screen using the same algorithm).
I've also tested with a prints of the "supposed to be" new velX and velY, like so:
System.out.println((-1 / distance) * diffX * 10);
System.out.println((-1 / distance) * diffY * 10);
And again, it gives the right value no matter which enemy collides with the player's circle, but for some reason it just doesn't move, and works ONLY when the last created enemy object hits the circle.
Im kinda lost, as I really don't see a reason it wouldn't work, the condition occures, the code that needs to be executed works, but it just doesn't.
Sorry for the long post, I tried to be as specific and short as I can, if more code is needed please let me know, I just don't want to add too much code. Thanks for the help!
edit - https://github.com/pardovot/MyProjects/tree/master/AImove/Pong - link to the project in github, so you'll have all the needed information about it.
I assume that what Dezigo said about the last iteration replating the values is corret, does anyone has any ideas what I can do to fix that? Because overall I don't see a real reason for that to happen, as the actual code should run in each iteration, and actually work(which doesn't obviously....)
I need to change Ball Direction after collision with another ball or with a edge of the window.
I managed to do something like that:
y += yMove;
x += xMove;
//if the ball moves to the right edge of the window, turn around.
if(x > width - size)
{
x = width - size;
xMove *= -1;
if (xMove > 0) {
xSpeed = xMove + (Math.random() * (1));
}
if (xMove <= 0) {
xSpeed = xMove - (Math.random() * (1));
}
if (yMove > 0) {
ySpeed = yMove + (Math.random() * (1));
}
if (yMove <= 0) {
ySpeed = yMove - (Math.random() * (1));
}
}
And same for another edges.
I'm trying to use same method for changing direction of balls after they collide with each other, but it's just not working / it's weird. Can anyone help me?
When balls collide, make vector connecting ball centers (N) and normalize it (uN)
Components of velocities parallel to N (normal) are exchanged (due to impulse law)
Components of velocities perpendicular to N (tangential) remain the same
To get components in given local system, use scalar and cross product:
V1t = dot(V1, uN)
V2t = dot(V2, uN)
V1n = cross(V1, uN)
V2n = cross(V2, uN)
after collision
V1t' = V2t
V2t' = V1t
V1n' = V1n
V2n' = V2n
To return into global system (I did not checked signs thoroughly):
V1x = V1t` * uN.X + V2n` * uN.Y
V1y = -V1t` * uN.Y + V2n` * uN.X
(This is essentially dot and cross products again, but I expanded expressions to show different bases)
Note that this approach is like to ball-edge collision, when N is normal to the border and you reverse only one component of velocity vector.
For your BouncingBall class, you can have a method like flipDirection(), but you can have a finer directional control by splitting it into 2 methods which filps the direction of the ball vertically and horizontally.
class BouncingBall{
public void horizontalFlip(){
moveX *= -1;
}
public void verticalFlip(){
moveY *= -1;
}
//To have move control over each direction, you can have a method for each direction.
public void moveNorth(){
moveY = Math.abs(moveY) * -1;
}
public void moveSouth(){
moveY = Math.abs(moveY);
}
public void moveWest(){
moveX = Math.abs(moveX) * -1;
}
public void mpveEast(){
moveX = Math.abs(moveX);
}
}
Depending on how you want the ball to bounce off. In a simple bounce off, the balls can bounce towards 4 possible directions:
North West
North East
South West
South East
The direction of the ball to bounce off will be relative to the position of the ball it is colliding with and you do not want 2 collided balls which move in the same direction to switch direction just because they collided. Hence you need to check the positions of the 2 balls, and flipDirection() becomes insufficinet to achieve that.
if(b1.intersects(b2)){
if(b1.getX() < b2.getX()){ // b1 above b2
b1.moveNorth();
b2.moveSouth();
}
else{
b1.moveSouth();
b2.moveNorth();
}
if(b1.getY() < b2.getY()){ // b1 at left side of b2
b1.moveWest();
b2.moveEast();
}
else{
b1.moveEast();
b2.moveWest();
}
}
For example, to change direction when hitting the walls on the left and right:
if(ball.getPosX() <= 0 || ball.getPosX() >= PNL_WIDTH-Ball.SIZE)
ball.horizontalReverse();
Same logic for verticalReverse.
I'm currently working on a Top-Down-Shooter and having some issues with collision.
My world is made of tiles (64x64). The tiles and the entities are rectangles. The player moves with a speed of e.g 2.74 (and not in pixels for smoother movement). But when it comes to the collision between the player (an entity) and a wall i have some issues. To check if there is a collision i take the current position of my player and his movement speed to calculate where his next position would be and if there is any collision. But i check every pixel on the way, so i cant skip an obstacle even if the movement speed is very high. Let's just say the players current position is X:200 Y:200 and he moves 2.74 Pixels a tick in the x direction. My game now checks if there is any collision at X:201 Y:200, X:202 Y:200 or X:202.74 Y:200 and if not moves the player to that position. If I now try to move the player further in the x direction and there is a wall 0.26 Pixels away the player wont move and leave a tiny gap. I tried to calculate the distance between player and wall and add this amount to the players position but for that I need to know which side of the wall the player hits. Also I want the player to be able to move up and down when the wall he hits is in front of him and the other way around.
Here is my collision method (in Java):
public static boolean collision(float ex, float ey, int width, int height) { // ex, ey would be the next position of the player
if (ex < 0 || ex + width > worldWidth || ey < 0 || ey + height > worldHeight) return true; // checks if this position is in the world
int firstTileX = (int) (ex / Tile.TILE_SIZE); // calculates tiles he could possible collide width
int firstTileY = (int) (ey / Tile.TILE_SIZE);
int lastTileX = (int) ((ex + width - 1) / Tile.TILE_SIZE);
int lastTileY = (int) ((ey + height - 1) / Tile.TILE_SIZE);
for (int y = firstTileY; y <= lastTileY; y++) {
if (y < 0) continue; // checks for out of bounds
if (y >= worldTileHeight) break;
for (int x = firstTileX; x <= lastTileX; x++) {
if (x < 0) continue;
if (x >= worldTileWidth) break;
if (tiles[y][x].solid) return true; // if the tile is solid -> collision found
}
}
return false; // no collision found
}
And my movement method:
public void move(float xa, float ya) {
float nx, ny;
while (xa != 0 || ya != 0) {
nx = x;
ny = y;
if (xa != 0) {
if (Math.abs(xa) > 1) { // if the x-speed is greater than 1
nx = x + MathUtil.abs(xa); // returns -1 for negative numbers and 1 for positiv
xa -= MathUtil.abs(xa);
} else { // less than 1
nx = x + xa;
xa = 0;
}
}
if (ya != 0) { // same here
if (Math.abs(ya) > 1) {
ny = y + MathUtil.abs(ya);
ya -= MathUtil.abs(ya);
} else {
ny = y + ya;
ya = 0;
}
}
if (!Level.collision(nx, ny, width, height)) setPosition(nx, ny); // checks if there is an collision and sets the new position if not
else if (!Level.collision(nx, y, width, height)) x = nx; // if there was a collision check if the player can walk in x direction
else if (!Level.collision(x, ny, width, height)) y = ny; // or in y direction
}
}
My problem is the pretty much the same as CoderMusgrove's problem in his post (Pixel-perfect collision and doubles):
Summary & Question
I have a problem where if the speed of an entity isgreater thanthe distance from the tile it is going into, it will leave at least a pixel in between itself and the tile, and I really don't like this. What kind of algorithm could I use that will find the tiniest difference between the entity and the tile?
If you need any additional information, I will be glad to add it.
Thanks for your help!
Easily resolvable by changing your interpretation.
You are retaining a fractional position for the purpose of fine grained speed. Ignore the fraction for the purpose of collision detection and display (if you were to do sub-pixel rendering, do the collision on the subpixel rendering accurarcy level).
int screenX = (int) Math.round(objX);
int screenY = (int) Math.round(objY);
// rendering and collision detection based on rounded position