I have a GUI Maze game that I am creating and I am having trouble implementing walls that prevent the player from moving. The walls are rectangles but the player is a circle so I had to create a collision detection method between a rectangle and a circle. The problem I am having is that my collidesWith() method is in the player class and it takes a single wall as a parameter and returns a string that tells you which side of the wall is the player intersecting with. This means I can only check if the player is colliding with one wall at a time. Here is the code for the collidesWith() method. x and y are the x and y coordinates of the player since this method is in the player class:
I should mention that the code above doesn't exactly work how I wanted it to since it only works when the player is coming from the sides. If the player is coming from the top or bottom, the player just goes through the wall.
The reason I need this method to return a string to tell me where the player is coming from is so that I can explicitly restrict the movement of the player when the up, down, left, right keys are pressed. This is in another class where all the GUI components are. Here is the code for that. I have created a wall object on top so that it can be passed as a parameter here when checking for intersection:
In my view, it is better to avoid to iterate through the whole array of wallsArraylist. If I understood correctly, it is necessary to explore neighbour cells, if yes, then you can explore neighbours in a two-dimensional array using this approach:
(pseudo-code)
row_limit = count(array);
if(row_limit > 0){
column_limit = count(array[0]);
for(x = max(0, i-1); x <= min(i+1, row_limit); x++){
for(y = max(0, j-1); y <= min(j+1, column_limit); y++){
if(x != i || y != j){
print array[x][y];
}
}
}
}
In addition, try to avoid magic strings. Replace "comingFromRight" and etc to constant string:
public static final String COMING_FROM_RIGHT = "comingFromRight";
Related
I'm working on a collision system for my game, however I can't get it to work, if I add more than one wall (which is the object I'm rendering) the collision system doesn't work and I can get through the block.
However if I leave only one wall the collision works correctly, or if at the end of the loop I add a break;
the collision works but only on the first wall of the map, the others don't get the collision.
Would anyone know how to solve this? I've been trying to solve it for 2 days and I couldn't.
public boolean checkCollisionWall(int xnext, int ynext){
int[] xpoints1 = {xnext+3,xnext+3,xnext+4,xnext+3,xnext+3,xnext+4,xnext+10,xnext+11,xnext+11,xnext+10,xnext+11,xnext+11};
int[] ypoints1 = {ynext+0,ynext+8,ynext+9,ynext+11,ynext+12,ynext+15,ynext+15,ynext+12,ynext+11,ynext+9,ynext+8,ynext+0};
int npoints1 = 12;
Polygon player = new Polygon(xpoints1,ypoints1,npoints1);
Area area = new Area(player);
for(int i = 0; i < Game.walls.size(); i++){
Wall atual = Game.walls.get(i);
int[] xpoints2 = {atual.getX(),atual.getX(),atual.getX()+16,atual.getX()+16};
int[] ypoints2 = {atual.getY(),atual.getY()+16,atual.getY()+16,atual.getY()};
int npoints2 = 4;
Polygon Wall = new Polygon(xpoints2,ypoints2,npoints2);
area.intersect(new Area(Wall));
if(area.isEmpty()){
return true;
}
}
return false;
}
I'm pretty sure the problem is this call:
area.intersect(new Area(Wall));
Here's the JavaDoc for that method:
public void intersect(Area rhs)
Sets the shape of this Area to the intersection of its current shape
and the shape of the specified Area. The resulting shape of this Area
will include only areas that were contained in both this Area and also
in the specified Area.
So area, which represents the shape of your player, is going to be modified by each test with a wall, which is why it's only working with one wall.
You could fix the issue by simply making the player Area the argument of the call, as in:
Area wallArea = new Area(Wall);
wallArea.intersect(area);
if(wallArea.isEmpty()){
return true;
}
By the way, this logic is reversed isn't it. Don't you want to check that the resulting area is not empty, i.e. there was an overlap between the player and the wall?
The other option, if each Wall is actually a rectangle, would be to use the this Area method instead:
public boolean intersects(double x,
double y,
double w,
double h)
Tests if the interior of the Shape intersects the interior of a
specified rectangular area. The rectangular area is considered to
intersect the Shape if any point is contained in both the interior of
the Shape and the specified rectangular area.
Something like this:
if(area.intersects(atual.getX(), atual.getY(), 16, 16)) {
return true;
}
As this avoids the creation of an Area object for each wall, and the intersection test is going to be much simpler, and faster.
I'm having some difficulty implementing very basic collision within libGDX. The update code for the "player" is as so:
private void updatePosition(float _dt)
{
Vector2 _oldPos = _pos;
_pos.add(_mov.scl(_dt*50));
_bounds.setPosition(_pos.x-8, _pos.y-12);
if(_game.getMap().checkCollision(_bounds))
{
System.out.println("Collision Detected");
_pos = _oldPos;
_bounds.setPosition(_pos.x-8, _pos.y-12);
}
}
So, initially _oldPos is stored as the values of _pos position vector before applying any movement.
Movement is then performed by adding the movement vector _mov (multiplied by the delta-time * 50) to the position vector, the player's bounding rectange _bounds is then updated to this new position.
After this, the player's new bounding Rectangle is checked for intersections against every tile in the game's "map", if an intersection is detected, the player cannot move in that direction, so their position is set to the previous position _oldPos and the bounding rectangle's position is also set to the previous position.
Unfortunately, this doesn't work, the player is able to travel straight through tiles, as seen in this image:
So what is happening here? Am I correct in thinking this code should resolve the detected collision?
What is strange, is that replacing
_pos = _oldPos;
with (making the same move just made in reverse)
_pos.sub(_mov.scl(_dt*50));
Yields very different results, where the player still can travel through solid blocks, but encounters resistance.
This is very confusing, as the following statement should be true:
_oldPos == _pos.sub(_mov.scl(_dt*50));
A better solution for collision detection would be to have a Vector2 velocity, and every frame add velocity to position. You can have a method that tests if Up arrow key is pressed, add 1 (or whatever speed you would like) to velocity. And if down is pressed, subtract 1 (or whatever speed). Then you can have 4 collision rectangles on player, 1 on top of player, bottom, left, and right. You can say
if(top collides with bounds){
if(velocity.y > 0){
set velocity.y = 0.
}
}
And do the same for down, left and right (eg... for bottom, make sure to test if(velocity.y < 0) instead of if(velocity.y > 0).
EDIT: You code is not working because you set oldPos = pos without instantiating a new Vector2. Which means when you add onto pos, it also changes oldPos. So say oldPos = new Vector2(pos);
try to test future position before move. If collision, don't move.
So I am making a tetris game and one of the problems I am running into is piece rotation. I know I can just hard code it but thats not the right way to do it. The way the system works is I have a 2d array of an object 'Tile' the 'Tile' object has x, y coords, boolean isActive, and color. The boolean isActive basically tells the computer which tiles are actually being used (Since tetris shapes are not perfect quadrilaterals).
Here is how I would make a shape in my system:
public static Tile[][] shapeJ() {
Tile[][] tile = new Tile[3][2];
for (int x = 0; x < 3; x++) {
for (int y = 0; y < 2; y++) {
tile[x][y] = new Tile(false, Color.blue);
}
}
tile[0][0].setActive(true);
tile[0][0].setX(0);
tile[0][0].setY(0);
tile[1][0].setActive(true);
tile[1][0].setX(50);
tile[1][0].setY(0);
tile[2][0].setActive(true);
tile[2][0].setX(100);
tile[2][0].setY(0);
tile[2][1].setActive(true);
tile[2][1].setX(100);
tile[2][1].setY(50);
return tile;
}
Now I need to rotate this object, I do not know how to do that without hard coding the positions. There has to be an algorithm for it. Can anyone offer some help?
A good way that I used when writing a tetris clone is to use rotational matrices:
http://en.wikipedia.org/wiki/Rotation_matrix
So the coordinates (x',y') of the point (x,y) after rotation are:
x' = x*cos(theta) - y*sin(theta);
y' = x*sin(theta) + y*cos(theta);
Where theta is the angle of rotation(+-90 degrees or +-PI/2 radians for the java functions that I know)
In this case the blocks are rotated around the origin (0, 0) so you either have to have the coordinates of the block in special "block space" that then gets transposed onto "field space" or you take away the offset of the block so that it is centered at the origin every iteration.
I hope that helps, I am happy to answer specific questions in the comments.
In the game i'm building, I have made a basic collision detection system.
My current method is explained below:
I workout where the player will be in the next step of the game:
double checkforx = x+vx;
double checkfory = y+vy;
I then check for a collision with blocks (1) in mapArray.
public static Boolean checkForMapCollisions(double character_x,double character_y){
//First find our position in the map so we can check for things...
int map_x = (int) Math.round((character_x-10)/20);
int map_y = (int) Math.round((character_y-10)/20);
//Now find out where our bottom corner is on the map
int map_ex = (int) Math.round((character_x+10)/20);
int map_ey = (int) Math.round((character_y+10)/20);
//Now check if there's anything in the way of our character being there...
try{
for(int y = map_y; y <= map_ey; y++){
for(int x = map_x; x <= map_ex; x++){
if (levelArray[y][x] == 1){
return true;
}
}
}
}catch (Exception e){
System.out.println("Player outside the map");
}
return false;
}
If true is returned {nothing}
If false is returned {Player physics}
I need the player to be able to land on a block and then be able to walk around but I cannot find and adequate tutorial for this.
Can someone give me an idea on how to run my collision detection and/or movement?
There are 2 parts to this question. Collision detection, meaning determining whether a volume is touching or intersecting another volume. The second is collision response. Collision response is the physics portion.
I'll cover collision detection here as that's primarily what you asked about.
Ddefine a class for the map like so:
int emptyTile = 0;
//this assumes level is not a ragged array.
public boolean inBounds(int x, int y){
return x>-1 && y>-1 && x<levelArray[0].length && y<levelArray.length;
}
public boolean checkForCollisions(Rectangle rectangle){
boolean wasCollision = false;
for(int x=0;x<rectangle.width && !wasCollision;x++){
int x2 = x+rectangle.x;
for(int y=0;y<rectangle.height && !wasCollision;y++){
int y2 = y+rectangle.y;
if(inBounds(x2,y2) && levelArray[y2][x2] != emptyTile){
//collision, notify listeners.
wasCollision=true;
}
}
}
}
Do not make your methods static. You probably want more than one instance of a level right? Static is for when you need to share state which remains constant across multiple instances of a class. Level data will surely not remain constant for every level.
Instead of passing in a coordinate, try passing in an entire rectangle. This rectangle will be the bounding box of your character (the bounding box is also sometimes referred to as AABB, which means Axis-aligned bounding box, just FYI in case you're reading tutorials online for this sort of thing.) Let your Sprite class decide what its bounding rectangle is, that's not the map class's responsibility. All the map should be used for is maybe rendering, and whether a rectangle is overlapping tiles which are not empty.
I am sorry for a very shitty explanation but here is my github code and it will help better.
https://github.com/Quillion/Engine
Just to explain what I do. I have character object (https://github.com/Quillion/Engine/blob/master/QMControls.java) and it has vectors and a boolean called standing. Every time boolean standing is false. Then we pass it to the engine to check for collision, if collision happens then standing is true and y vector is 0. As to x vector whenever you press any arrow keys you make the xvector of the object to whatever value you want. And in the update loop you displace the given box by the amount of speed.
I've got a ball that I can move around on a map consisting of equally sized tiles. The player should not be able to walk over the tiles that are darker and have a black border. I've got a multidimensional array of the tiles that I use to check which tiles are solid.
I would like the player to slide against the wall if he is moving both horizontally and vertically into it. The problem is that if he does that he sticks to the wall. I managed to get it working perfectly on each axis, but separately. Here is my code for the horizontal collision checking:
if (vx < 0) {
// checks for solid tiles left of the player
if (level.isBlocked(i, j) || level.isBlocked(i, jj)) {
x = side * (i + 1); // moves player to left side of tile
vx = 0;
}
} else if (vx > 0) {
// checks for solid tiles right of the player
if (level.isBlocked(ii, j) || level.isBlocked(ii, jj)) {
x = (ii * side) - getWidth(); // moves player to right side of tile
vx = 0;
}
}
The level.isBlocked() method checks if that index of the array is occupied by a solid tile. The i and j variables is which index in the array the player's top right corner is located on. The ii and jj variables is which index in the array the player's bottom right corner is located on.
This works fine, but then if I add the same chunk of code beneath but replacing x with y, vx with vy and so on the problem occurs. So I can add either the horizontal or vertical collision handling and it works, but not at the same time. I've seen a few articles explaining I have to separate them or something, but I didn't understand much of them. How can I check collision on both axes and keep the sliding effect?
I finally got it to work. Angelatlarge's answer was helpful in understanding the problem, but I decided to start from scratch. I ended up first calculating the new x and y position and storing them in separate variables. Then I checked the tile under the middle left of the player and the same with the middle right. I then set a boolean to true if the player was standing on a tile because of his horizontal speed. If there was no collision I set the real x variable to the new one I calculated earlier. I then repeated the same thing for the vertical collision.
This is for the horizontal checking:
float newX = x + vx * delta;
boolean xCollision = false;
if (vx < 0) {
int i = level.toIndex(x);
int j = level.toIndex(y + getHeight() / 2);
xCollision = level.isBlocked(i, j);
} else if (vx > 0) {
int i = level.toIndex(x + getWidth());
int j = level.toIndex(y + getHeight() / 2);
xCollision = level.isBlocked(i, j);
}
if (!xCollision) x = newX;
The problem is that with the setup you have, given a block and the player position, and also given the fact that they overlap, you don't know whether the player collided with a vertical or a horizontal wall of the block. So see this more clearly consider the following block and two collision paths
The top path will collide with the left wall, and requires a vx=0; (cessation of horizontal movement), while the bottom path collides with the bottom wall and will require vy=0;, or stopping of the vertical movement.
I think in order to do the kind of collision detection you want, you will want to compute intersections of the player path and the walls of the blocks, not just checking whether the player overlaps a block. You could hack the desired behavior by computing the overlapping rectange of the player rectangle and the block rectangle. Consider the following situation:
where the red seqare represents your player. The fact that the overlap rectangle (the small rectangle occupied where the player is on top of the block) is more wide than it is tall suggests that it was the vertical collision that happened, not a horizontal. This is not foolproof, however. And it still requires you to be able to access the shape of the block, rather than just stesting if a part of the player rectangle overlaps a block.