In my maze game applet I'm having a little bug. Collision detection works but sometimes my character can go into walls. More precisely it detects collision and moves the character into the wall. It happens very rare but still pretty annoying. It happens if I keep pressing the arrow keys too fast alternately (for example down and right in a corner). The thread sleeps 20ms if that matters and a tile is 20px*20px.
I think the bug is somewhere here.
Rect is around the character 20*20px and bigRect is 60*60px to check collision in advance and r is around the wall tiles also 20*20px.
public void checkCollision() {
if (r.intersects(Hero.getBigRect()) && Hero.getRect().intersects(r)) {
if (hero.isMovingUp() == true) {
hero.stopUp();
hero.setHeroY(tileY + 20);
}
if (hero.isMovingDown() == true) {
hero.stopDown();
hero.setHeroY(tileY - 20);
}
if (hero.isMovingLeft() == true) {
hero.stopLeft();
hero.setHeroX(tileX + 20);
}
if (hero.isMovingRight() == true) {
hero.stopRight();
hero.setHeroX(tileX - 20);
}
}
}
Related
I´m building my own Space Invaders in Java with Greenfoot and I have a Spaceship which shoots the Aliens, but the Bullets stop at the top of the map and stay there so I wrote this method which should remove a bullet if it hits the top of the but it doesn't work. What´s the problem?
public void disappearIfOnTop() {
Actor Bullet = getOneIntersectingObject(Actor.class);
getY();
if(getY() == 0) {
World world;
world = getWorld();
world.removeObject(Bullet);
}
}
Edit: they are getting removed if they hit another bullet which is stuck on the top.
The method getOneIntersectingObject() returns a null if there is no other actor.
You might want to check this to be sure:
public void disappearIfOnTop() {
if (getY() == 0) {
Actor bullet = getOneIntersectingObject(Actor.class);
if (bullet == null) {
setLocation(getX(), 50); // move down if no other is around
} else {
setLocation(getX(), 100); // move further down if another is around
}
}
}
If the method getOneIntersectingObject() returns a reference to an actor, your current method is removing that one, not the one who is at Y=0. (BTW, don't use variable names starting with uppercase letters. By convention, this is reserved for classes.)
You can simplify your method to:
public void disappearIfOnTop() {
if (getY() == 0) {
getWorld().removeObject(this);
}
}
I'm working on a game in java, based on the Atari game adventure. I got the basic KeyListener part working fine, but then I added another if statement, using another class, to test if if the player was going to hit a wall, and stopping movement if that was the case. The method I used also used if statements, and when I ran the code, it had MAJOR lag. I tried a while loop first, but that made it lag even worse. Anyway to make this not lag so much? It doesn't seem that complex a program to run, and I still have to add yet another if statement to make be able to move into another room, so I have to do something to massively cut down on the lag.
Here is the class:
class Player extends JPanel implements KeyListener{
private char c = 'e';
int x = 400;
int y = 400;
int mapX = 0;
int mapY = 0;
public Player() {
this.setPreferredSize(new Dimension(800, 500));
addKeyListener(this);
}
public void addNotify() {
super.addNotify();
requestFocus();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Environment Layout = new Environment();
Layout.drawRoom(mapX,mapY,g);
g.fillRect(x , y , 20, 20);
}
public void keyPressed(KeyEvent e) { }
public void keyReleased(KeyEvent e) { }
public void keyTyped(KeyEvent e) {
c = e.getKeyChar();
repaint();
Environment Layout = new Environment();
if(Layout.isWall(x,y,c)){}
else{
if (c == 'a'){
x = x - 3;
}
else if (c == 'w'){
y = y - 3;
}
else if (c == 's'){
y = y + 3;
}
else if (c == 'd'){
x = x + 3;
}
}
}
public static void main(String[] s) throws IOException{
JFrame f = new JFrame();
f.getContentPane().add(new Player());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
}
}
The draw room method I used in this was just to put the background of the room into place.
Here is the isWall method from the Environment class:
public boolean isWall(int moveX, int moveY, char let){
BufferedImage room = null;
try {
room = ImageIO.read(new File(xNum + "," + yNum + ".png"));
}
catch (IOException e) {
}
int[][] walls = convertImage(room);
boolean blocked = false;
if(let == 'w') {
if(walls[moveY-8][moveX] == -3584){blocked = true;}
}
else if(let == 's') {
if(walls[moveY+20][moveX] == -3584){blocked = true;}
}
else if(let == 'a') {
if(walls[moveY][moveX-5] == -3584){blocked = true;}
}
else if(let == 'd') {
if(walls[moveY][moveX+20] == -3584){blocked = true;}
}
return blocked;
}
the convertImage method just converts the image of the room into an int array, for the value of the colors. -3584 is the color of the walls. It's possible this is what's lagging it, but this seemed like the best way for each room to have the walls done automatically.
I also tried a timer, but either I did that wrong, or it just didn't help.
I can give more of my code if that's needed, but help with this would be much appreciated. I'm relatively new to this kind of stuff, so it's likely I'm missing something big. Thanks.
The lag here is almost certainly not from the if statements. Those are really fast. I think the bigger issue is in isWall. Notice that any time you want to check for whether a wall is present, you
Open a file,
read the file contents,
convert the file contents from an image to a grid of pixels, and
read exactly one pixel.
Reading files from disk is extremely slow compared to looking at values in memory. For example, a regular magnetic hard drive works at around 7200 RPM, so the seek time is measured in milliseconds. On the other hand, your processor can do about a billion operations per second, so other operations take nanoseconds. That means that a disk read is roughly a million times slower than other operations, which is almost certainly where you're getting the lag from!
To fix this, consider rewriting your isWall code so that you only read the file and do the conversion once and, having done that, then just look up the part of the image you need. This converts doing tons of (glacially slow) file reads to one single (slow but inevitable) file read followed by tons of fast memory reads.
You appear to be moving your walls further than you are moving your player.
Is it possible that your player object is getting stuck in a wall there by producing "blocked = true" continuously?
Your character gets +- 3 in every direction, however your walls seem inconsistent and range from 8 up to 20 down to 5 left to 20 right.
This is an extension to #templatetypedef's answer.
Instead of loading the image files upon calling the isWall method, you might want to consider caching all of the walls on game start.
So I am thinking;
have a HashMap data structure keyed by <String, Integer>. Where String is your coordinates. E.g. coordinate string = "100,238"
parse all the .png image files in the directories and store the coordinates as key and the value can just be any dummy value like 1 or 2.
Then when isWall() is invoked. Given the X and Y coordinate, build the coordinate string as mentioned in point 1 and check if the key exists. If it does then we know it is a piece of wall else not.
This should drastically reduce the I/O disk contention.
In future, if you would like to extend the solution to incorporate APIs like isTreasureChest() or isMonster(). It can be extended by building a immutable class call "Room" or "Tile" to represent the object. Then modify the HashMap to take in <String, Room>.
I am trying to get the collision in my game to be exactly perfect. What I am testing is if you hit a wall with the player, you come to a stop. I only implemented the collision code for when the player hits the left side of a wall(when the wall is on the right side of the player). Here is the code.
if(entityOnRight){
if(player.getPositionCorner(SquareMapTuples.BOTTOM_RIGHT).x -
ent.getPositionCorner(SquareMapTuples.BOTTOM_LEFT).x > -.9f)
player.setMovementBooleans(false, false, false, false);
else
player.setMovementBooleans(true, false, false, false);
}
Note: If I go very slow, it will stop the player where I desire it to be stopped, but going fast, it won't do the collision the way I want
Essentially, the code states if the wall is on the right side, it will check the bottom right corner of the rectangle of the player, subtract the bottom left corner of the wall, and check if the distance between the two is 0.001. 0.001 is almost an unnoticeable distance, hence why I used that value. Here is the code for player.setMovementBooleans
public void setMovementBooleans(boolean canMoveRight, boolean canMoveLeft, boolean canMoveUp, boolean canMoveDown){
this.canMoveRight = canMoveRight;
if(canMoveRight == false && moveRight)
vel.x = 0;
}
The canMoveRight boolean in the Player class (not in parameters) is what allows you to be able to move, moveRight is when you are trying to move right. Here is some code that will better explain how these booleans interact:
//If you clicked right arrow key and you're not going
//Faster then the max speed
if(moveRight && !(vel.x >= 3)){
vel.x += movementSpeed;
}else if(vel.x >= 0 && !moveRight){
vel.x -= movementSpeed * 1.5f;
System.out.println("stopping");
//Make sure it goes to rest
if(vel.x - movementSpeed * 1.5f < 0)
vel.x = 0;
}
and:
if(Gdx.input.isKeyPressed(Keys.D) && canMoveRight)
moveRight = true;
else
moveRight = false;
So to give a summary, if you click the "D" key, it allows you to start moving. However if the boolean canMoveRight is false, it won't move you. Here is an image showing what happens (The player is yellow, the wall is green)
As you can see, the player goes much further then I want it to. It should stop at this point:
Any help with figuring out how to accomplish this is extremely appreciated!
Maybe the way you tried it is a bit too complicated :-). I suggest a simpler way from scratch: make the map and the player a com.badlogic.gdx.math.Rectangle instance. Now, in the following part of the code you make a check, whether after the move the player would still be inside the map, if yes then allow the move, if not, then don't allow:
if(Gdx.input.isKeyPressed(Keys.D){
float requestedX, requestedY;
//calculate the requested coordinates
Rectangle newPlayerPositionRectangle = new Rectangle(requestedX, requestedY, player.getWidth(), player.getHeight());
if (newPlayerPositionRectangle.overlaps(map) {
//move the player
} else {
//move the player only to the edge of the map and stop there
}
}
The best way to handle those collisions would be to use a physics engine like Box2D which already comes packed with Libgdx. When a collision occurs in Box2D a event gets fired and you can easly handle that event. So you should probably take a look here.
Another alternative to achieve this without physics would be to use logical rectanlges representing the player and walls (can also be polyline) and use Intersector class of libgdx.
which is here.
I'll try to explain what i need to achieve.
My player starts at 0, 0 position.
When I touch the screen, the player starts to move up.
And when the player is at the specific position in the Y axis (Let's say 200), I want him to stop moving.
When I touch the screen this method is called:
public void move() {
if (player.getPosition().y < 200) {
player.velocity.set(0, 100);
}
And in my player's update method I have:
if (player.getPosition().y > 200) {
player.position.set(0, 200);
player.velocity.set(0, 0);
}
The problem with this is that if I set the velocity in the move method to something like 1000, there is one frame rendered with the position of the player above 200. Then it changes it to 200 and stays there.
I tried this in the update method:
if (player.getPosition().y == 200) {
player.velocity.set(0, 0);
}
But this statement is never true because the position of the player is always 200 and some random numbers like 200.1257 or 200.54578
I will appreciate all your answers!
You can cast it to an integer like this:
if ((int)player.getPosition().y == 200) {
player.velocity.set(0, 0);
}
But i will not recommend it because if your player is at high speed it could skip the 200 and go on, i think the right thing to do is
if (player.getPosition().y > 200) {
player.velocity.set(0, 0);
player.setPosition(new Vector2(player.getX,200));
}
You can predict where your player are going and calculate the deacceleration as well for a smooth feel for the player..
Now if you want multiple vectors on screen the best is to calculate a distance between the player and the points on screen like this:
//If the player is 20 units closer than the point 200,200 do something
if(player.getPosition().dst2(new Vector2(200,200) < 20){
//do something
}
I'm working on a tile 2d-side platformer game. I have done some stuff so far.
I'm working on a basic collision detection using rectangles of libgdx so considering
I have only grass block for now I made a single block world in Java (file reader is not ready)
the problem is my detection only works the first time in other words if I spawn my colliding
to a block it detects collision and do so. Although if i spawn my player top of the block
with out colliding player falls forever.
Here is the code world.update(); =>
public void update() {
Iterator<block> cb = Blocks.iterator();
while (cb.hasNext()) {
block b = cb.next();
if (b.getBounds().overlaps(player.getBounds())) {
if (player.getPosition().x >= b.getPosition().x + 32) {
//RIGHT
player.getVelocity().x = 0;
} else if (player.getPosition().x + 32 <= b.getPosition().x) {
//Left
player.getVelocity().x = 0;
}
//All Y
player.getVelocity().y = 0;
}
if (!b.getBounds().overlaps(player.getBounds())) {
player.getVelocity().y = -gravity;
}
}
}
Your while loop is applying gravity for every block your player does not intersect with. So if you have 10 blocks, and the player intersects just 1, you'll still apply gravity 9 times. You should only apply the gravity change to the player once.
Set a flag (boolean hitSomething = false) before your loop, then set it to true (hitSomething = true) if the player hits any block. Then, after the loop, if hitSomething is false, apply gravity to the player.
Stepping through the update method with a debugger is a good way to figure out what your code is doing in cases like this. It should be quicker than waiting for Stack Overflow to debug your code, too.