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.
Related
So, my problem is that I'm currently working on a path-finding technique in an open world with tiles of various sizes. The object needs to find an optimal path to a destination inside an infinite world (it generates on the fly), which is filled with tiles of various sizes (which are not located on a set grid - they can have any location and size - and neither have to be integers). (The object has access to the data of all the tiles via and ArrayList). Now some factors that make this problem more difficult:
The objects itself has a size and cannot move through tiles. Therefore, it is possible for a path to exist that is too narrow for the object to move through.
The target destination may itself be a moving object.
It is possible for there to be dozens of such objects at the same time - so it is necessary for the algorithm to either be light on the system or for the path to be calculated in a few separate ticks of the program.
I tried implementing solutions for maze-solving techniques, but the main problem is the in most mazes, the tiles can only have very specific coordinates (such as whole numbers) and are always the same size.
I also tried rendering the scene as a giant conventional maze where tiles are actually pixels of tiles (so if i have a 20x40 tile it becomes a 20x40 block of 1x1 tiles), but ran into performance issues and the still didn't solve the issue with a path potentially being to narrow for the object to fit through.
EDIT:
Terribly sorry for my poor wording before, that happens when I'm trying to rush to a solution without fully understanding the question. So what I'm using the algorithm for at the moment is for NPC enemies to find their way to the player around obstacles. Here is an example of a scene:
The black circle with an arrow is the player, the black bush-like blobs are the NPC enemies. So this my my current algorithm I'm using for the enemy AI:
void move() { //part of the Enemy class, this function is called once each tick for every enemy
PVector velocity = new PVector(speed*game.dt, 0); //speed is a pre-set float denoting the enemy's speed, game.dt is deltatim
velocity.rotate(atan2(game.player.location.y-location.y, game.player.location.x-location.x)); //game.player.location is a PVector of the player's position, location is a PVector of this enemy's position
boolean init_collision = getTileCollision(); //getTileCollision is a boolean of whether this enemy is colliding with any tile
location.add(velocity);
boolean collision = getTileCollision();
if (!init_collision && collision) { //if the enemy happens to spawn inside a tile, let is move out of it before checking for collision
location.sub(velocity);
if (desired_heading != -1) { //desired heading is the angle, in radians, of which 90-degree angle the enemy wants to move in, by default set to -1 (see my descrition of this algorithm below)
velocity = new PVector(speed*game.dt, 0);
velocity.rotate(desired_heading);
location.add(velocity);
if (getTileCollision()) {
location.sub(velocity);
velocity = new PVector(speed*game.dt, 0);
velocity.rotate(current_heading); //current heading the an angle, in radians, of which 90-degree angle the enemy is currently moving in. set to -1 by default but can not equal -1 if desired_heading is not -1
location.add(velocity);
if (getTileCollision()) {
location.sub(velocity);
desired_heading = -1;
current_heading = -1;
}
} else {
desired_heading = -1;
current_heading = -1;
}
} else {
float original_heading = velocity.heading();
desired_heading = radians(round(degrees(velocity.heading())/90.0)*90.0); //round to the nearest 90 degrees
velocity = new PVector(speed*game.dt, 0);
velocity.rotate(desired_heading);
location.add(velocity);
if (getTileCollision()) {
location.sub(velocity);
}
float turn = radians(90);
while (true) { //if it cant move, try rotating 90 degrees and moving
velocity.rotate(turn);
location.add(velocity);
if (!getTileCollision() && abs(round(degrees(current_heading)) - round(degrees(velocity.heading()))) != 180) {
current_heading = velocity.heading();
break;
} else {
location.sub(velocity);
}
}
}
} else {
desired_heading = -1;
current_heading = -1;
}
}
So what my terrible code hopes to accomplish is the the enemy first tries to move directly at the player. If it encounters an obstacle, it will round its angle to the nearest 90 degrees, set desired_heading to this and try to move through. If it cant, it will rotate another 90 degrees and so forth, always keeping the original rounded angle in mind.
This doesn't work remotely well as first of all, rotating 90 degrees has a 50% chance to go in the exact wrong diretion, so I tried adding
if (abs(original_heading - velocity.heading()+turn) < abs(original_heading - velocity.heading()-turn)) {
turn = radians(-90);
}
right before the while (true) but that broke the algorithm completely (sometimes the enemy will freeze in deep thought and not move ever again).
What am I doing terribly wrong? Should I try a different algorithm or does this one have potential?
I hope this is a better question now...
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.
I am currently making a 2D Platformer in Java. I am a beginner so take it easy.
I have a problem with the gravity in the game. I am using different variables for falling and jumping. I am using a tiled map. So lets get to the point.
My fall method works like this -
if(collisionDown == false) {
characterY += fall;
fall ++;
{
fall is equal to 4. and If collisionDown is true it resets back to 4.
My jump method is almost the same:
if(key.E == true && collisionDown == true) {
characterY -= jump;
jump --;
}
jump is equal to 16. and If collisonDown is true it resets back to 16.
Now problem is: imagine the character is jumping. its in the air and while going down characterY += fall; lets say characterY = 250 and fall is equal - 15 at that exact moment. The next solid tile below the character starts at Y position 255. character is at 250 and does NOT detect collision so next frame it adds 15 to characterY which is 250 + 15 = 265. At that point the character has "entered" the solid tile which was at positionY 255.
I have "fixed" that so the character gets back ON TOP of the solid tile (and that is visible and annoying.) That is not the perfect solution because it slows the character 1 frame each time it enters a solid tile(which is because it detects left and right collision and the character can't move). The character visibly stutters if I can say it like that.
I need a solution for that problem but can't think of any.
So if you make a suggestion I would be happy. Thank you.
I would probably use something like the following.
if(collisionDown == false) {
characterYnext = characterY + fall; //Get Next Position
if(NextMovementCollides()){ //Basically if next position is too far.
characterYnext += difference_between(CharacterY,Ground); //This should move the character to the ground state.
fall = 0; //No longer falling so reset the value.
}
else{characterY += fall; fall++;} //Otherwise continue falling like normal.
}
DISCLAIMER: I'm not a java programmer so my syntax might be a bit off.
This should work, just plug in your game logic where it would make sense.
The way I usually handle this, is to pre-check movement:
private int moveDown(int fall){
if (isSolidBlock(characterX, characterY + fall)){
//Knowing the height of your block, calculate some kind of reduction. If your block height is 40, it's probably something like (characterY + fall)%40
fall = 4;
collisionDown = true;
return maxFallUntilSolidBlockReached();
}
fall++;
return fall;
}
private boolean isSolidBlock(int x, int y){
//Implement some kind of check if at (x,y) there's a solid block.
}
Then just do this for the fall calculation:
if(collisionDown == false) {
characterY += moveDown(fall);
}
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;
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.