I am making a 2D Java game and i'm trying to figure out how to add basic "gravity"
my current code is this:
public void checkCollision() {
Rectangle player_rectangle = new Rectangle(player.getX(),player.getY(),32,32);
for(Wall wall : walls) {
Rectangle wall_rectangle = new Rectangle(wall.getX(), wall.getY(), 32,32);
if(player_rectangle.intersects(wall_rectangle)) {
player.yspeed = 0;
} else {
player.yspeed = 1;
}
}
For some reason my code just goes straight through the walls even if it is touching it. I want it to hit the wall if one is present beneath it and if there isnt then keep falling.
you're iterating over all your walls. If the wall intersected is not the last wall in the list, subsequent walls may reset your speed to 1. Break your loop when you detect intersection. Specifically:
if(player_rectangle.intersects(wall_rectangle)) {
player.yspeed = 0;
break;
Related
I am trying to write something that will take a X and Y coordinate and a value that represents the available movement points the selected actor has. It should then output a list of the reachable locations so that I can highlight those tiles when the actor is choosing where to move.
I managed to use a function from my pathfinding library (https://github.com/xaguzman/pathfinding) that gives me the neighbouring tiles of a tile as a List of grid cells. It can also check my Tile-map and see if the tile is walkable.
What I can't really get my head around is how I would be able to set this up so that it would run as many times as the movement points.
List<GridCell> neighbours;
NavigationTiledMapLayer navLayer;
public void getMovementPossibilities(int tileX, int tileY) {
GridCell cell1;
GridCell cell2;
cell1 = navLayer.getCell(tileX, tileY);
GridFinderOptions opt = new GridFinderOptions();
opt.allowDiagonal = true;
opt.dontCrossCorners = false;
neighbours = navLayer.getNeighbors(cell1, opt);
for (int i = 0; i < neighbours.size(); i++) {
int nX = neighbours.get(i).getX();
int nY = neighbours.get(i).getY();
cell2 = navLayer.getCell(nX, nY);
neighbours.addAll(navLayer.getNeighbors(cell2, opt));
}
}
Sounds like a case for recursion. I can't see how you're keeping track of movement points but your current method finds all tiles 1 distance away. Inside this method, you need to recall the same method, but with each of those neighbours as your new start point and with the movement points reduced by 1.
This in turn generates all the second neighbours, and then the method will be called recursively and so on...
You will want a check at the top of the method so that if the distance points left are 0, it just immediately adds the current tile to neighbours and then returns (to stop the recursive chain going on forever).
In my game the character is constantly moving from left to right, so there will never be a situation where he hits the right side of a platform. I had a very brief talk with somebody and he suggested me to model my platform as 4 rectangles so that I would only need 3 cases in my algorithm (i.e. Player can only collide with the rectangles shaded green in the image below). Here's a visual description of what I mean:
I gave it a try but the collision detection is still messed up. It doesn't go through the side of the platform (this is good news since we want this to happen) but you can't glide on top of it or bounce under it. Can anyone see where I may have gone wrong?
public void checkCollisions(ArrayList<GameObject> list) {
for (int i = 0; i < list.size(); i++) {
for (int j = i + 1; j < list.size(); j++) {
GameObject go1 = list.get(i);
GameObject go2 = list.get(j);
boolean playerPlatformCollision = go1.getType()
.equalsIgnoreCase("player") && go2.getType().equalsIgnoreCase("platform");
if (playerPlatformCollision) {
Rectangle playerRect = go1.getRect();
Rectangle platformRect = go2.getRect();
if (playerRect.overlaps(platformRect)) {
float platformRectX, platformRectY, platformRectWidth, platformRectHeight;
platformRectX = go2.getRect().getX(); // x position of the platform
platformRectY = go2.getRect().getY(); // y position of the platform
platformRectWidth = go2.getRect().getWidth(); // width of platform
platformRectHeight = go2.getRect().getHeight(); // height of platform
Rectangle caseA = new Rectangle(platformRectX,
platformRectY, platformRectX + 1,
platformRectHeight);
Rectangle caseB = new Rectangle(platformRectX + 1,
(platformRectY + platformRectHeight) - 1,
platformRectWidth - 1, platformRectHeight);
Rectangle caseC = new Rectangle(platformRectX + 1,
platformRectY, platformRectWidth, 1);
if (playerRect.overlaps(caseA)) {
go1.setxSpeed(0);
} else if (playerRect.overlaps(caseB)) {
go1.setySpeed(0);
} else if (playerRect.overlaps(caseC)) {
go1.setySpeed(-3f);
}
}
}
}
}
}
Please let me know if you are not clear with any parts of the code.
I can't be sure that this is the issue, but it seems that this problem is not addressed in your code, and I have had similar problems myself when ignoring this. When your player collides with the platform, it means that its rectangle is already intersecting the platform, by anywhere between 1 and how fast you are going number of pixels. Just adjusting the x and y speed is not enough. You must also remove the player rectangle from the platform, or else the player will get stuck. This can be done by using a loop and checking if your player is still colliding. if so, move 1 pixel over in the direction you came from, until no longer colliding.
I've been working for a while on a clone of space invaders. The biggest problem I've faced by far is getting the aliens to move. After a lot of effort, I've developed n algorithm to move them. The algorithm behaves as expected, except that when the aliens have finished moving to the left/right and then move down, the left or rightmost aliens begin to converge on the two next to them, overlapping their sprites. Monitoring their X coordinates, I discovered that they move 10 pixels to the left/right every time they move down. I can't figure out why they do this, and am hoping you can help me. Here is my movement algorithm (the aliens are arranged in 2 rows of 10 and this method is called every ten frames. movestate dictates which direction the aliens are moving in.):
public void alienMove()
{
boolean movedown = false;
if(movestate)
{
for(int i = 0;i<aliens.length;i++)
{
if(aliens[i].getX()==950)
{
movestate = false;
movedown = true;
}
else
{
aliens[i].setX(aliens[i].getX()+10);
}
}
if(movedown)
{
for(int j = 0;j<aliens.length;j++)
{
aliens[j].setY(aliens[j].getY()+20);
}
movedown = false;
}
}
else
{
for(int i = 0;i<aliens.length;i++)
{
if(aliens[i].getX()==50)
{
movestate = true;
movedown = true;
}
else
{
aliens[i].setX(aliens[i].getX()-10);
}
}
if(movedown)
{
for(int j = 0;j<aliens.length;j++)
{
aliens[j].setY(aliens[j].getY()+20);
}
movedown = false;
}
}
}
Suppose the aliens are about to hit the right edge. The first 9 (reading left-to-right) don't have X = 950, so move 10 pixels right. Then the rightmost does have X=950, so doesn't move right; instead, it triggers the direction change and downshift.
Similarly, at the left edge, the first alien doesn't move, but triggers the (later) downshift and direction reversal, then the other nine aliens move left by 10 pixels.
You need to treat all the aliens the same way. Either move the edge alien as well, or don't move any aliens when the edge is reached.
Before trying to debug this code, I would propose you to improve and simplify it. First, if you look at your code, there are many parts completely similar that could be easily factorized.
Plus, you may be more comfortable if you had an enum Direction with some methods like move(Point p), the code would be clearer. Plus, I get that aliens[i].getX() == 50 is actually always false except for the leftmost alien, so this test should be out of the loop.
It may also be the reason why your code doesn't work, because your first loop moves the aliens from aliens[0] to aliens[aliens.length - 1]. Only the last alien can verify aliens[i].getX() == 950 so when this condition is reached, all the aliens from aliens[0] to aliens[aliens.length - 2] will be moved to the right and to the bottom, whereas aliens[aliens.length - 1] will only be moved to the bottom.
Normally in Space Invaders, the aliens move as a block. So when they are moving left, you wouldn't want to test that all of them have reached coordinate 50. Just the leftmost one. So perhaps:
if(aliens[i].getX()==50)
should be:
if(aliens[0].getX()==50)
etc. And correspondingly:
if(aliens[aliens.length - 1].getX()==950)
when they are going right?
Here is a bug, last one didn't move, but all before it has moved in left/right direction;
if(aliens[i].getX()==950)
{
movestate = false;
movedown = true;
}
else
{
aliens[i].setX(aliens[i].getX()+10);
}
this check if(aliens[i].getX()==950) should be after increment.
In last iteration you have moved all aliens to left/right but last one detects edge and its position it isn't moved, so all movement effect from last iteration should be reverted.
I am working on a platformer game that will use tile maps, which I don't know if is a good idea!
I've made a neat tile map editor with tools for setting a spawn point etc. but now that I want to be able to test play the game after editing map and for future use I need of course integrate physics which I've done with Box2D which comes with LibGDX!
I am creating a method to create a collision map from tile map which has data if tile is collideable or not!
So I came up with this great idea:
loop through the map and if we find a colliding tile loop through its neighbor tiles and see if they're colliding too, and do this til noncolliding tile is found when we set width and height for the colliding rectangle
after we got bunch of rectangle I sort them in order from biggest square to smallest so we get biggest pieces and I add the rectangles to final list and check against the final rect if any of them overlaps with current body so I don't have overlapping bodys
But you know, code tells more than 1000 words, right?
public void createBody() {
List<Rectangle> allRects = new ArrayList<Rectangle>();
for(int x = 0; x < info.getWidth(); x++) {
for(int y = 0; y < info.getHeight(); y++) {
if(tiles[x][y].getInfo().isColliding()) {
int width = 1;
int height = 1;
//loop through neighbors horizontally
for(int i = 0; i < info.getWidth() - x; i++) {
if(!tiles[x + i][y].getInfo().isColliding()) {
//if tile is not clipped, we set width to i which is current x offset
width = i;
break;
}
}
//only if width is bigger than zero can the rect have any tiels..
if(width > 0) {
boolean breakingBad = false;
//loop through neighbors horizontally
for(int j = 0; j < info.getHeight() - y; j++) {
//loop though neigbors vertizally
for(int i = 0; i < width; i++) {
//check if tile is not colliding
if(!tiles[x + i][y + j].getInfo().isColliding()) {
//and if so, we set height to j which is current y offset
height = j;
//breaking bad aka leaving both loops
breakingBad = true;
break;
}
}
if(breakingBad) {
break;
}
}
}
if(width * height > 0)
allRects.add(new Rectangle(x, y, width, height));
}
}
}
Collections.sort(allRects, new Comparator<Rectangle>() {
#Override
public int compare(Rectangle o1, Rectangle o2) {
Integer o1Square = o1.width * o1.height;
Integer o2Square = o2.width * o2.height;
return o2Square.compareTo(o1Square);
}
});
List<Rectangle> finalRects = new ArrayList<Rectangle>();
mainloop:
for(Rectangle rect: allRects) {
for(Rectangle finalRect: finalRects) {
if(finalRect.contains(rect)) {
continue mainloop;
}
}
finalRects.add(rect);
}
for(Rectangle rect: finalRects) {
PolygonShape polyShape = new PolygonShape();
polyShape.setAsBox((float)rect.getWidth() / 2, (float)rect.getHeight() / 2, Vector2.tmp.set((float)rect.getCenterX(), (float)rect.getCenterY()), 0f);
mapBody.createFixture(polyShape, 1);
polyShape.dispose();
}
}
however this sill seems pretty inefficient because for some reasons its still creating smaller fixtures than it could be possible, for example in upper right corner
also its creating single fixtures in the corners of the center rectangle and I can't figure out why!
Is the whole idea all inefficient, and should I use other method or manually create collision maps or what could be the best idea?
Originally each tile was its own fixture which caused weird bugs on their edges as expected
First off, a custom tile mapping tool is a great idea on the surface, but you're reinventing the wheel.
libGDX has built-in support for TMX maps.
http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/maps/tiled/TmxMapLoader.html
Instead of using your homebrew editor, you can use a full featured editor such as this Tiled - http://www.mapeditor.org/
So once you have a better system in place for your maps, I would look at this from an object oriented perspective. Since you want to use box2d physics, each collidableTile HAS A body. So all you need to do is assign a physics body to each collidableTile, and set the size according to your standard tile size.
Don't forget that there is a difference between the box2d world and your game screen, where box2d is measured in metric units, and your screen is measured in pixels. So you need to do some math to set positions and size properly. If you want a set of tiles to share a body, you may want to pass in the body as a parameter when you construct each collidableTile, and then adjust the size of the body based on how many adjacent tiles you can find. More complex shapes for the physics body may be more complex.
You can also save resources by setting those tiles to 'sleep', where box2d does a reduced simulation on those bodies until it detects a collision. If you're only using box2d for collision detection on terrain, you may want to consider other options, like using shape libraries to detect intersections, and then setting the box2d physics on your player characters body to stop downward acceleration while there is contact, or something.
Here is my code:
/*
Scott Landau
Robot Lab Assignment 1
*/
// Standard Java Libs
import java.io.*;
// Player/Stage Libs
import javaclient2.*;
import javaclient2.structures.*;
import javaclient2.structures.sonar.*;
// Begin
public class SpinningRobot
{
public static Position2DInterface pos = null;
public static LaserInterface laser = null;
public static void main(String[] args)
{
PlayerClient robot = new PlayerClient("localhost", 6665);
laser = robot.requestInterfaceLaser(0, PlayerConstants.PLAYER_OPEN_MODE);
pos = robot.requestInterfacePosition2D(0,PlayerConstants.PLAYER_OPEN_MODE);
robot.runThreaded (-1, -1);
pos.setSpeed(0.5f, -0.25f);
// end pos
float x, y;
x = 46.0f;
y = -46.0f;
boolean done = false;
while( !done ){
if(laser.isDataReady()) {
float[] laser_data = laser.getData().getRanges();
System.out.println("== IR Sensor ==");
System.out.println("Left Wall Distance: "+laser_data[360]);
System.out.println("Right Wall Distance: " +laser_data[0]);
// if laser doesn't reach left wall, move to detect it
// so we can guide using left wall
if ( laser_data[360] < 0.6f ) {
while ( laser_data[360] < 0.6f ) {
pos.setSpeed(0.5f, -0.5f);
}
} else if ( laser_data[0] < 0.6f ) {
while(laser_data[0<0.6f) { pos.setSpeed(0.5f, 0.5f);
}
}
pos.setSpeed(0.5f, -0.25f);
// end pos?
done = ( (pos.getX() == x) && (pos.getY() == y) );
}
}
}
} // End
I was trying to have the Roomba go continuously at a slight right curve, quickly turning away from each wall it came to close to if it recognized it with it's laser. I can only use laser_data[360] and laser_data[0] for this one robot. I think this would eventually navigate the maze.
However, I am using the Player Stage platform, and Stage freezes when the Roomba comes close to a wall using this code, I have no idea why.
Also, if you can think of a better maze navigation algorithm, please let me know.
Thank you!
It seems like you get stuck against a wall because you are oscillating around distance 0.6.
For instance:
Drive angling toward wall, eventually reaching < 0.6f
Rotate away from wall, stop when > 0.6f. In other words 0.6000001f (for instance)
Resume normal trajectory.
Almost immediately < 0.6f
Goto 2
You will get stuck angling slightly toward and slightly away from the wall.
You need a buffer zone. Once you get within MIN_DIS distance from the wall you need to rotate away until you reach some BUFFER_DIS from the wall where BUFFER_DIS > MIN_DIS by a fair amount.
EDIT:
Looks like you have a few issues. First you are checking if the laser_data is close to a wall, and if it is you are rotating toward the wall. You need to check if it is NOT close to a wall, and rotate toward it.
Second, you are in a very tight while loop, adjusting wheel speeds. This will probably result in bad things. You need to call a trackWall() function when you are too far from the wall, which will poll laser.isDataReady() in a separate loop. Either that, or you need a state-machine in your main polling loop. If not, you are pointlessly adjusting wheel speed with stale laser data.
Third, can you guarantee that the wall will always be within 0.6f? If not, your robot will spin infinitely if it gets to far from the wall.