How can I manage collisions in slick ?
I created map and every tile which is wall have option blocked = true;
How to implement collision, that if I hit wall, I can move trough it?
Also if I reach edge of map, block movement.
package javagame;
import org.newdawn.slick.Animation;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.state.BasicGameState;
import org.newdawn.slick.state.StateBasedGame;
import org.newdawn.slick.tiled.TiledMap;
public class Play extends BasicGameState {
Animation movingUp, movingDown, movingLeft, movingRight;
Animation player;
int[] duration = {200, 200, 200};
float playerX = 0;
float playerY = 0;
float cameraX;
float cameraY;
float screenWidth;
float screenHeight;
private TiledMap map = null;
private static final float SPEED = 0.1f;
private boolean[][] blocked;
public Play(int state, int x, int y) {
playerX = (float) x;
playerY = (float) y;
}
private boolean blocked(float x, float y) {
return blocked[(int) x][(int) y];
}
#Override
public void init(GameContainer gc, StateBasedGame sbg) throws SlickException {
map = new TiledMap("map/map.tmx");
// build a collision map based on tile properties in the TileD map
blocked = new boolean[map.getWidth()][map.getHeight()];
for (int x = 0; x < map.getWidth(); x++) {
for (int y = 0; y < map.getHeight(); y++) {
int tileID = map.getTileId(x, y, 0);
String value = map.getTileProperty(tileID, "blocked", "false");
if ("true".equals(value)) {
blocked[x][y] = true;
}
}
}
Image[] walkUp = {
new Image("graphics/player/up0.png"),
new Image("graphics/player/up1.png"),
new Image("graphics/player/up2.png")
};
Image[] walkDown = {
new Image("graphics/player/down0.png"),
new Image("graphics/player/down1.png"),
new Image("graphics/player/down2.png")
};
Image[] walkLeft = {
new Image("graphics/player/left0.png"),
new Image("graphics/player/left1.png"),
new Image("graphics/player/left2.png")
};
Image[] walkRight = {
new Image("graphics/player/right0.png"),
new Image("graphics/player/right1.png"),
new Image("graphics/player/right2.png")
};
movingUp = new Animation(walkUp, duration, false);
movingDown = new Animation(walkDown, duration, false);
movingLeft = new Animation(walkLeft, duration, false);
movingRight = new Animation(walkRight, duration, false);
player = movingDown;
}
#Override
public void render(GameContainer gc, StateBasedGame sbg, Graphics g) throws SlickException {
screenWidth = gc.getWidth();
screenHeight = gc.getHeight();
cameraX = (screenWidth / 2) - (playerX / 2);
cameraY = (screenHeight / 2) - (playerY / 2);
map.render((int) playerX, (int) playerY);
player.draw(cameraX, cameraY);
g.drawString("X: " + playerX + "\nY: " + playerY, 520, 20);
g.resetTransform();
}
#Override
public void update(GameContainer gc, StateBasedGame sbg, int delta) throws SlickException {
Input input = gc.getInput();
if (input.isKeyDown(Input.KEY_UP) || input.isKeyDown(Input.KEY_W)) {
player = movingUp;
playerY += delta * SPEED;
player.update(delta);
} else if (input.isKeyDown(Input.KEY_DOWN) || input.isKeyDown(Input.KEY_S)) {
player = movingDown;
playerY -= delta * SPEED;
player.update(delta);
} else if (input.isKeyDown(Input.KEY_LEFT) || input.isKeyDown(Input.KEY_A)) {
player = movingLeft;
playerX += delta * SPEED;
player.update(delta);
} else if (input.isKeyDown(Input.KEY_RIGHT) || input.isKeyDown(Input.KEY_D)) {
player = movingRight;
playerX -= delta * SPEED;
player.update(delta);
}
}
}
Here is sample source code (NetBeans project):
https://www.box.com/s/9nicp0n067de632kcpj3
On Update, you can simply check the array. If it is a wall don't update. If it is the border, don't update.
#Override
public void update(GameContainer gc, StateBasedGame sbg, int delta) throws SlickException {
Input input = gc.getInput();
float yChange = 0, xChange=0;
boolean capturedInput = false;
if (input.isKeyDown(Input.KEY_UP) || input.isKeyDown(Input.KEY_W)) {
player = movingUp;
yChange += delta * SPEED;
capturedInput = true;
} else if (input.isKeyDown(Input.KEY_DOWN) || input.isKeyDown(Input.KEY_S)) {
player = movingDown;
yChange -= delta * SPEED;
capturedInput = true;
} else if (input.isKeyDown(Input.KEY_LEFT) || input.isKeyDown(Input.KEY_A)) {
player = movingLeft;
xChange += delta * SPEED;
capturedInput = true;
} else if (input.isKeyDown(Input.KEY_RIGHT) || input.isKeyDown(Input.KEY_D)) {
player = movingRight;
xChange -= delta * SPEED;
capturedInput = true;
}
if(capturedInput==true && !blocked(playerX+xChange,playerY+yChange)){
playerX += xChange;
playerY += yChange;
player.update(delta);
}
}
private boolean blocked(float x, float y) {
return blocked[(int) x][(int) y];
}
Pretty much, do not change the value until you check it first and then change the player value if all is okay.
EDIT: I added a variable capturedInput, this will let me know that we actually have a keyDown event. There are two comments about this. I consider this a "hack" to make your code work and not the most beautiful. I would personally try to add an event listener to the window to listen for key clicks, when a key is clicked I would change the position information, all the while the frame is refreshing and will update, or only refresh now that there was a change by calling repaint().
There are lots of tutorials out there for listening to onKeyDown. Your code should work better now and won't need the manual catch for the edge like your doing, you should be able to draw a wall around the edge in the block array of booleans, if not you can continue with your method.
Try creating a Rectangle of your blocks from X and Y position multiplied by tile dimensions and then verify with the intersects() method if it collides with your entity, such as explained in wiki Slick.
I hope that's useful to you.
Related
I want this code to effectively increase the smoothness of the transition between directions (it only works with one key at a time) so that I can use multiple keys. The problem is that whenever I change direction the "Player" stops and then continues in the new direction. I want the "Player" to smoothly transition between directions without having to fully release the active key before pressing the new one.
Main code:
Ball ball;
Player player1;
Player player2;
void setup() {
size(1368,768);
frameRate(60);
noStroke();
ball = new Ball(width/2, height/2, 30);
player1 = new Player(0, height/2, 30, 150);
player2 = new Player(width-30, height/2, 30, 150);
ball.speedX = -10;
ball.speedY = random(-5,5);
}
void draw() {
background(0);
ball.display();
ball.move();
player1.run();
player2.run();
//Collision
if (ball.top() < 0) {
ball.speedY = -ball.speedY;
}
if (ball.bottom() > height) {
ball.speedY = -ball.speedY;
}
if (ball.left() < 0) {
ball.speedX = 0;
ball.speedY = 0;
}
if (ball.right() > width) {
ball.speedX = 0;
ball.speedY = 0;
}
}
void keyPressed() {
player1.pressed((key == 'w' || key == 'W'), (key == 's' || key == 'S'));
player2.pressed((keyCode == UP), (keyCode == DOWN));
}
void keyReleased() {
player1.released((key == 'w' || key == 'W'), (key == 's' || key == 'S'));
player2.released((keyCode == UP), (keyCode == DOWN));
}
Player class code:
class Player {
float x, y;
int dy = 0;
float w, h;
float speedY = 5;
color c;
//Constructor
Player(float tempX, float tempY, float tempW, float tempH){
x = tempX;
y = tempY;
w = tempW;
h = tempH;
speedY = 0;
c = (255);
}
void run() {
display();
move();
}
void display() {
fill(c);
rect(x, y-h/2, w, h);
}
void move() {
y += dy * speedY;
}
void pressed(boolean up, boolean down) {
if (up) {dy = -1;}
if (down) {dy = 1;}
}
void released(boolean up, boolean down) {
if (up) {dy = 0;}
if (down) {dy = 0;}
}
}
Thanks in advance!
Add 2 attributes move_up and move_down to the class Player and set the attributes in
pressed respectively released:
class Player {
// [...]
boolean move_up = false, move_down = false;
void pressed(boolean up, boolean down) {
if (up) {move_up = true;}
if (down) {move_down = true;}
}
void released(boolean up, boolean down) {
if (up) {move_up = false;}
if (down) {move_down = false;}
}
}
Change speedY dependent on the attributes in move. Continuously reduce the speed if neither move_up not move_down is set (speedY = speedY * 0.95;). That causes that the player smoothly slows down if no key is pressed. If move_up or move_down is pressed the slightly change the speed dependent on the desired direction. Restrict the speed to a certain interval (speedY = max(-5.0, min(5.0, speedY));):
class Player {
// [...]
void move() {
if (!move_up && !move_down) {speedY *= 0.95;}
if (move_up) {speedY -= 0.1;}
if (move_down) {speedY += 0.1;}
speedY = max(-5.0, min(5.0, speedY));
y += speedY;
}
// [...]
}
Class Player:
class Player {
float x, y;
float w, h;
float speedY = 0.0;
color c;
boolean move_up = false, move_down = false;
//Constructor
Player(float tempX, float tempY, float tempW, float tempH){
x = tempX;
y = tempY;
w = tempW;
h = tempH;
c = (255);
}
void run() {
display();
move();
}
void display() {
fill(c);
rect(x, y-h/2, w, h);
println(y);
}
void move() {
if (!move_up && !move_down) {speedY *= 0.95;}
if (move_up) {speedY -= 0.1;}
if (move_down) {speedY += 0.1;}
speedY = max(-5.0, min(5.0, speedY));
y += speedY;
}
void pressed(boolean up, boolean down) {
if (up) {move_up = true;}
if (down) {move_down = true;}
}
void released(boolean up, boolean down) {
if (up) {move_up = false;}
if (down) {move_down = false;}
}
}
If you want smooth transitions, you're going to have to give up "adding a fixed integer distance" in the key handlers, and instead track which keys are down or not, and then accelerating/decelerating your player every time draw() runs. As simple illustration:
Box box;
boolean[] active = new boolean[256];
void setup() {
size(500,500);
box = new Box(width/2, height/2);
}
void draw() {
pushStyle();
background(0);
box.update(active); // First, make the box update its velocity,
box.draw(); // then, tell the box to draw itself.
popStyle();
}
void keyPressed() { active[keyCode] = true; }
void keyReleased() { active[keyCode] = false; }
With a simple box class:
class Box {
final float MAX_SPEED = 1, ACCELERATION = 0.1, DECELERATION = 0.5;
float x, y;
float dx=0, dy=0;
Box(float _x, float _y) { x=_x; y=_y; }
void draw() {
// We first update our position, based on current speed,
x += dx;
y += dy;
// and then we draw ourselves.
noStroke();
fill(255);
rect(x,y,30,30);
}
void update(boolean[] keys) {
if (keys[38]) { dy -= ACCELERATION ; }
else if (keys[40]) { dy += ACCELERATION ; }
else { dy *= DECELERATION; }
if (keys[37]) { dx -= ACCELERATION ; }
else if (keys[39]) { dx += ACCELERATION ; }
else { dx *= DECELERATION; }
dx = constrain(dx, -MAX_SPEED, MAX_SPEED);
dy = constrain(dy, -MAX_SPEED, MAX_SPEED);
}
}
The important part here is the update code, which updates the box's x and y velocity such that if a directional key is currently pressed, we increase the speeed (dx/dy) in that direction. Importantly, if no keys are pressed we also dampen the speed to that it returns to 0.
Finally, to make sure we don't end up with infinite speed, we cap the maximum allowed velocity.
Our restart button is only appearing by itself. Our two tanks disappear when we run the game. We haven't coded the function of the button, we're just trying to figure out how to make the button appear in the lower left corner
We moved the code into the game object class, but it does not change anything
This is our game object class. The button is at the bottom.
import java.awt.Graphics;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GameObject {
// Every moving object in our game is a separate GameObject. However, since there are different types of moving objects (eg. players and
// projectiles), each with their own variables and functions, we don't want to instantiate GameObjects (otherwise, we wouldn't be able
// to distinguish between a player and a projectile). Instead, we want to create derived (also known as child) classes to implement the
// separate functionality, and instantiate the derived/child classes instead. The purpose of this GameObject "parent" class is to
// provide the derived/child classes with a set of useful functions for handling physics interactions (eg. collisions with the terrain
// or the walls of the arena) between different GameObjects (eg. player/player, player/projectile, or even projectile/projectile). Note:
// depending on the features you implement, you may not end up using all of the functions provided in this class.
// Every GameObject contains its own velocity, position, size, and mass information (necessary for moving and handling physics
// interactions). We store this information in the GameObject class instead of the derived/child classes because every type of
// GameObject has the same set of velocity, position, size, and mass variables.
public double vX; // Velocity in the x direction. Positive if moving right. Negative if moving left.
public double vY; // Velocity in the y direction. Positive if moving down. Negative if moving up. Notice how it's flipped from the usual
// coordinate system in math!
public double posX; // Position along the x direction. Ranges from 0 (left edge) to maximumX (see below).
public double posY; // Position along the y direction. Ranges from 0 (top edge) to maximumY (see below).
public int width; // Width of the bounding box of the GameObject.
public int height; // Height of the bounding box of the GameObject.
public int mass; // Used in realistic physics collision calculations. Ignore it if you don't want to implement that feature.
public double radius; // Used for circular GameObjects only.
public int maximumX; // Maximum x position for a GameObject, equal to the arena width subtracted by the game object's width.
public int maximumY; // Maximum y position for a GameObject, equal to the arena height subtracted by the game object's height.
// Constructor. All derived (ie. child) classes call this constructor in their own constructors. The resulting derived/child class
// object can then call the other functions in this class.
public GameObject(int arenaWidth, int arenaHeight, double vX, double vY, double posX, double posY, int width, int height, int mass) {
this.vX = vX;
this.vY = vY;
this.posX = posX;
this.posY = posY;
this.width = width;
this.height = height;
this.mass = mass;
this.maximumX = arenaWidth - width;
this.maximumY = arenaHeight - height;
radius = Math.min(width, height) / 2.0;
}
// Note: No need to change this function since we're going to override it in the the child classes.
public boolean move(Map map, double translateX, double translateY) {
return false;
}
// Check if the calling GameObject currently intersects with the obj GameObject.
public boolean currentlyIntersects(GameObject obj) {
return (posX + width >= obj.posX && posY + height >= obj.posY && obj.posX + obj.width >= posX && obj.posY + obj.height >= posY);
}
// Check if the calling GameObject will intersect with the obj GameObject, after both have moved according to their velocities. A note
// of caution: what might go wrong if either player moves too fast?
public boolean willIntersect(GameObject obj) {
double nextX = posX + vX;
double nextY = posY + vY;
double nextObjX = obj.posX + obj.vX;
double nextObjY = obj.posY + obj.vY;
return (nextX + width >= nextObjX && nextY + height >= nextObjY && nextObjX + obj.width >= nextX && nextObjY + obj.height >= nextY);
}
// Clip the calling GameObject to within the arena's x bounds, if it has moved outside the arena along the x direction.
public boolean xClip() {
if (posX < 0) {
posX = 0;
return true;
} else if (posX > maximumX) {
posX = maximumX;
return true;
}
return false;
}
// Clip the calling GameObject to within the arena's y bounds, if it has moved outside the arena along the y direction.
public boolean yClip() {
if (posY < 0) {
posY = 0;
return true;
} else if (posY > maximumY) {
posY = maximumY;
return true;
}
return false;
}
// If the calling GameObject will move outside the arena along either direction (after moving according to its velocity), this function
// tells you which of the four edges of the arena it hit. If the calling GameObject will stay within the bounds of the arena, this
// function returns null.
public Direction hitEdgeDirection() {
if (posX + vX < 0) {
return Direction.LEFT;
} else if (posX + vX > maximumX) {
return Direction.RIGHT;
}
if (posY + vY < 0) {
return Direction.UP;
} else if (posY + vY > maximumY) {
return Direction.DOWN;
} else {
return null;
}
}
// If the calling GameObject will intersect with the "other" GameObject (after both move according to their velocities), this function
// tells you which of the four sides of the calling GameObject that the "other" GameObject hit. If the calling GameObject will not
// intersect with the "other" GameObject, this function returns null. Note: this function is great for figuring out when and where two
// rectangles intersect, but is it a good choice for handling circle/rectangle or circle/circle intersections?
public Direction hitObjectDirection(GameObject other) {
if (this.willIntersect(other)) {
double dx = other.posX + other.width / 2.0 + other.vX - (posX + width / 2.0 + vX);
double dy = other.posY + other.height / 2.0 + other.vY - (posY + height / 2.0 + vY);
double theta = Math.acos(dx / (Math.sqrt(dx * dx + dy * dy)));
double diagTheta = Math.atan2(height / 2.0, width / 2.0);
if (theta <= diagTheta) {
return Direction.RIGHT;
} else if (theta <= Math.PI - diagTheta) {
if (dy > 0) {
return Direction.DOWN;
} else {
return Direction.UP;
}
} else {
return Direction.LEFT;
}
} else {
return null;
}
}
// Change the calling GameObject's velocity (to simulate a "bouncing" effect) based on which direction it intersected another GameObject
// or the edge of the arena. If the passed in direction is null, this function does nothing (why is this a good idea?). This function is
// best used with the hitEdgeDirection and hitObjectDirection functions above.
public void bounce(Direction d) {
if (d == null) {
return;
}
// Note: We probably should use a "switch" statement here instead. But for pedagogical purposes it's left as a simple if/else
// conditional.
if (d == Direction.UP) {
vY = Math.abs(vY);
} else if (d == Direction.DOWN) {
vY = -Math.abs(vY);
} else if (d == Direction.LEFT) {
vX = Math.abs(vX);
} else if (d == Direction.RIGHT) {
vX = -Math.abs(vX);
}
}
// TODO: (Challenge!) If you want to implement realistic sphere-sphere collisions that take into account the laws of physics, do so in
// the function below.
public boolean bounceWith(GameObject otherObj, Map map, long frames, double[] actualVelocities) {
return false;
}
// Calculate the distance from (pointX, pointY)---perhaps representing the center of a circle---to the closest point on a rectangle
// bounded by minX (left), maxX (right), minY (top), and maxY (bottom). If the point is inside the rectangle, this function returns 0.
public double pointToRectSqrDist(double minX, double maxX, double minY, double maxY, double pointX, double pointY) {
double dx = Math.max(Math.max(minX - pointX, 0), pointX - maxX);
double dy = Math.max(Math.max(minY - pointY, 0), pointY - maxY);
return dx * dx + dy * dy;
}
// Rotate the point (x, y) "degrees" degrees around (centerX, centerY) in counterclockwise fashion, and return the resulting point in an
// array of length 2. If the returned array is "result", then (result[0], result[1]) is the final point.
public double[] rotatePoint(double centerX, double centerY, double degrees, double x, double y) {
double s = Math.sin(Math.toRadians(degrees));
double c = Math.cos(Math.toRadians(degrees));
x -= centerX;
y -= centerY;
double xNew = x * c - y * s;
double yNew = x * s + y * c;
double[] result = new double[2];
result[0] = xNew + centerX;
result[1] = yNew + centerY;
return result;
}
// Note: No need to change this function since we're going to override it in the the child classes.
public void draw(Graphics g) {
}
public static void main(String []args){
JButton b= new JButton("Reset");
JFrame f = new JFrame();
f.setSize(1200,800);
f.setVisible(true);
f.getDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel p = new JPanel();
f.add(p);
p.add(b);
b.setSize(50,50);
b.setVisible(true);
b.setLocation(50, 50);
}
}
This is our arena class:
// TODO: Feel free to import any other libraries that you need.
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import javax.swing.*;
#SuppressWarnings("serial")
public class Arena extends JPanel {
public int arenaWidth;
public int arenaHeight;
public Player player1;
public Player player2;
Timer timer;
public static int INTERVAL = 35;
public long lastTick;
// TODO: Add other variables to keep track of the game state or other game objects (eg. the map) that will be in your game. Don't forget
// to instantiate them in reset()!
// Constructor. Called inside Game.java for setting up the Arena on game start.
public Arena() {
// Create a timer that calls the tick() function every INTERVAL milliseconds. Every call of the tick() function is a "frame".
timer = new Timer(INTERVAL, new ActionListener() {
public void actionPerformed(ActionEvent e) {
tick();
}
});
lastTick = System.currentTimeMillis();
timer.start();
setFocusable(true);
// TODO: To recognize key presses, you need to fill in the following.
addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent butthole) {
if (butthole.getKeyCode() == KeyEvent.VK_W) {
player1.isWPressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_S) {
player1.isSPressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_A) {
player1.isAPressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_D) {
player1.isDPressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_SPACE) {
player1.isSpacePressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_UP) {
player2.isUpPressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_DOWN) {
player2.isDownPressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_LEFT) {
player2.isLeftPressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_RIGHT) {
player2.isRightPressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_ENTER) {
player2.isEnterPressed = true;
}
}
public void keyReleased(KeyEvent butthole) {
if (butthole.getKeyCode() == KeyEvent.VK_W) {
player1.isWPressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_S) {
player1.isSPressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_A) {
player1.isAPressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_D) {
player1.isDPressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_SPACE) {
player1.isSpacePressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_UP) {
player2.isUpPressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_DOWN) {
player2.isDownPressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_LEFT) {
player2.isLeftPressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_RIGHT) {
player2.isRightPressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_ENTER) {
player2.isEnterPressed = false;
}
}
});
}
// Resets the game to its initial state.
public void reset() {
this.removeAll();
this.setBackground(Color.WHITE);
this.setOpaque(true);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
arenaWidth = (int) screenSize.getWidth();
arenaHeight = (int) screenSize.getHeight();
player1 = new Player(arenaWidth, arenaHeight, 100, 100, 1);
player2 = new Player(arenaWidth, arenaHeight, arenaWidth - Player.INIT_SIZE - 100, arenaHeight - Player.INIT_SIZE - 100, 2);
requestFocusInWindow();
}
// Function called once per "frame".
void tick() {
// While tick() should be called once every INTERVAL amount of time, there's no guarantee of that, particularly if you have a lot
// of background apps running. Thus, we need to calculate the time difference (timeDelta) between every two calls of the tick()
// function. Note: 1 divided by this difference is commonly known as the "frames per second", or fps.
long currentTime = System.currentTimeMillis();
long timeDelta = (currentTime - lastTick)/35;
lastTick = currentTime;
if ((player1.isWPressed && player1.isSPressed) ||
(!player1.isWPressed && !player1.isSPressed)) {
player1.vX = 0;
player1.vY = 0;
} else if (player1.isWPressed) {
//System.out.println("Up");
player1.vX = Math.cos(player1.RotationDegree*Math.PI/180)*player1.speed * timeDelta;
player1.vY = Math.sin(player1.RotationDegree*Math.PI/180)*player1.speed * timeDelta;
//player1.posY -= player1.speed*timeDelta;
// MOVE FORWARD
} else if (player1.isSPressed) {
//System.out.println("Down");
player1.vX = -Math.cos(player1.RotationDegree*Math.PI/180)*player1.speed * timeDelta;
player1.vY = -Math.sin(player1.RotationDegree*Math.PI/180)*player1.speed * timeDelta;
//player1.posY += player1.speed*timeDelta;
// MOVE BACKWARD;
}
if (player1.isAPressed) {
//System.out.println("Left");
player1.RotationDegree -= player1.rotateSpeed* timeDelta;
// MOVE BACKWARD;
}
if (player1.isDPressed) {
//System.out.parintln("Right");
player1.RotationDegree += player1.rotateSpeed* timeDelta;
// MOVE BACKWARD;
}
if(player1.RotationDegree > 360) {
player1.RotationDegree -= 360;
}
else if(player1.RotationDegree < 0) {
player1.RotationDegree += 360;
}
player1.move(null, player1.vX, player1.vY);
if ((player2.isUpPressed && player2.isDownPressed) ||
(!player2.isUpPressed && !player2.isDownPressed)) {
player2.vX = 0;
player2.vY = 0; }
else if (player2.isUpPressed) {
//System.out.println("Up");
player2.vX = Math.cos(player2.RotationDegree*Math.PI/180)*player2.speed* timeDelta;
player2.vY = Math.sin(player2.RotationDegree*Math.PI/180)*player2.speed* timeDelta;
//player2.posY -= player2.speed*timeDelta;
// MOVE FORWARD
}
else if (player2.isDownPressed) {
//System.out.println("Down");
player2.vX = -Math.cos(player2.RotationDegree*Math.PI/180)*player2.speed * timeDelta;
player2.vY = -Math.sin(player2.RotationDegree*Math.PI/180)*player2.speed * timeDelta;
//player2.posY += player2.speed*timeDelta;
// MOVE BACKWARD;
}
if (player2.isLeftPressed) {
//System.out.println("Left");
player2.RotationDegree -= player2.rotateSpeed*timeDelta;
// MOVE BACKWARD;
}
if (player2.isRightPressed) {
//System.out.println("Right");
player2.RotationDegree += player2.rotateSpeed*timeDelta;
// MOVE BACKWARD;
}
if(player2.RotationDegree > 360) {
player2.RotationDegree -= 360;
}
else if(player2.RotationDegree < 0) {
player2.RotationDegree += 360;
}
player2.move(null, player2.vX, player2.vY);
player1.currentReload -= timeDelta;
if (player1.currentReload <= 0)
;
{
if (player1.isSpacePressed) {
// create bullet and fire
BasicWeapon newBullet = new BasicWeapon(arenaWidth, arenaHeight, player1.posX + player1.radius, player1.posY + player1.radius, player1);
player1.bullets.add(newBullet);
player1.currentReload = player1.MaxReload;
}
}
ArrayList<PlayerProjectile> bulletsToDelete1 = new ArrayList<PlayerProjectile>();
for (int i = 0; i < player1.bullets.size(); i++) {
PlayerProjectile bulletToChange = player1.bullets.get(i);
bulletsToDelete1.add(bulletToChange);
}
player1.bullets.removeAll(bulletsToDelete1);
// TODO: Update the game state each frame. This can be broken into the following steps:
// Step 1: Handle the keys pressed during the last frame by both players and calculate their resulting velocities/orientations.
// Step 2: Move the players and detect/handle player/player collisions and player/terrain collisions.
// Step 3: Decide whether a bullet should be fired for each player and create new bullet(s) if so. Also, handle reload mechanics.
// Step 4: Move all bullets via their calculated velocities (up to bullet range). Handle bullet/player & bullet/terrain collisions.
// Step 5: Decide whether the game has ended. If so, stop the timer and print a message to the screen indicating who's the winner.
// Note: If you implement other features (eg. weapon swapping, damage counters...etc.), you might also need to add more steps above.
// Update the display: this function calls paintComponent as part of its execution.
repaint(); }
// TODO: Draw all of the objects in your game.
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
//AffineTransform at = AffineTransoform.getTranslateInstance();
player1.draw(g);
player2.draw(g);
}
// Returns the dimensions of the Arena (for properly resizing the JPanel on the screen).
#Override
public Dimension getPreferredSize() {
return new Dimension(arenaWidth, arenaHeight);
}
}
THIS IS OUR PLAYER CLASS
// TODO: Feel free to import any other libraries that you need.
//import java.*;
import java.awt.*;
import java.util.ArrayList;
//import javax.swing.*;
//import java.awt.event.*;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Player extends GameObject {
public static void main(String []args){
}
// TODO: Set these values as you see fit. However, which variables should you not set as negative numbers? Which variables should you
// not set as zero? Which v'ariables should you not set as a very large positive number? Why?
public static final int INIT_SIZE = 30;
public static final int INIT_MASS = 0;
public static final int INIT_DAMAGE = 0;
public static final double INIT_SPEED = 10;
public static final double INIT_ROTATE_SPEED = 10;
public static final int INIT_HEALTH = 100;
// Member variables of the player that you can change over the course of the game.
public int damage = INIT_DAMAGE;
public double speed = INIT_SPEED;
public double rotateSpeed = INIT_ROTATE_SPEED;
public int health = INIT_HEALTH;
public double orientation = 0;
public int id;
// TODO: You may need to set up extra variables to store the projectiles fired by this player, the reload status/time of this player,
// the key press/release status of this player, and any other player-related features you decide to implement. Make sure to update the
// constructor appropriately as well!
long currentReload = 0;
long MaxReload = BasicWeapon.INIT_RELOAD;
ArrayList<PlayerProjectile> bullets = new ArrayList<PlayerProjectile>();
double RotationDegree = 0;
boolean isWPressed = false;
boolean isSPressed = false;
boolean isAPressed = false;
boolean isDPressed = false;
boolean isSpacePressed = false;
boolean isEnterPressed = false;
boolean isUpPressed = false;
boolean isDownPressed = false;
boolean isLeftPressed = false;
boolean isRightPressed = false;
boolean isLeftCLickPressed = false;
// Constructor that calls the super (ie. parent) class's constructor and instantiates any other player-specific variables.
public Player(int arenaWidth, int arenaHeight, double startPosX, double startPosY, int id) {
super(arenaWidth, arenaHeight, 0, 0, startPosX, startPosY, INIT_SIZE, INIT_SIZE, INIT_MASS);
this.id = id;
}
// TODO: This function should move the player and handle any player-terrain interactions.
#Override
public boolean move(Map map, double translateX, double translateY) {
posX += translateX;
posY += translateY;
xClip();
yClip();
return false;
}
//UPDATE PLAYER POSTION HERE
//}
public void draw(Graphics g) {
// TODO: Draw the barrel(s) for the player here
double xChords[] = new double[4];
double YChords[] = new double[4];
xChords[0] = posX + 0.6 * width;
xChords[1] = posX + 0.6 * width;
xChords[2] = posX + 1.5 * width;
xChords[3] = posX + 1.5 * width;
YChords[0] = posY + 0.4 * height;
YChords[1] = posY + 0.5 * height;
YChords[2] = posY + 0.5 * height;
YChords[3] = posY + 0.4 * height;
double[] point0 = rotatePoint(posX + 0.5 * width, posY + 0.5 * height, RotationDegree, xChords[0], YChords[0]);
double[] point1 = rotatePoint(posX + 0.5 * width, posY + 0.5 * height, RotationDegree, xChords[1], YChords[1]);
double[] point2 = rotatePoint(posX + 0.5 * width, posY + 0.5 * height, RotationDegree, xChords[2], YChords[2]);
double[] point3 = rotatePoint(posX + 0.5 * width, posY + 0.5 * height, RotationDegree, xChords[3], YChords[3]);
int rotatedPointsX[] = new int[4];
int rotatedPointsY[] = new int[4];
rotatedPointsX[0] = (int)Math.round(point0[0]);
rotatedPointsX[1] = (int)Math.round(point1[0]);
rotatedPointsX[2] = (int)Math.round(point2[0]);
rotatedPointsX[3] = (int)Math.round(point3[0]);
rotatedPointsY[0] = (int)Math.round(point0[1]);
rotatedPointsY[1] = (int)Math.round(point1[1]);
rotatedPointsY[2] = (int)Math.round(point2[1]);
rotatedPointsY[3] = (int)Math.round(point3[1]);
g.drawPolygon(rotatedPointsX, rotatedPointsY, 4);
g.setColor(Color.BLACK);
g.fillPolygon(rotatedPointsX, rotatedPointsY, 4);
if (id == 1) {
g.setColor(new Color(255, 215, 0));
} else if (id == 2) {
g.setColor(Color.RED);
}
// Body
g.fillOval((int) posX, (int) posY, width, height);
g.setColor(Color.BLACK);
g.drawOval((int) posX, (int) posY, width, height);
// TODO: Draw the health bar for the player here.
}
}
Only the button appears, and player1 and player2 don't.
I think what you are trying to achieve is something like :
public static void main(String []args){
JButton button= new JButton("Reset");
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
panel.add(button);
frame.add(panel, BorderLayout.NORTH);
frame.add(new Arena(), BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
For future question please do not post so much code. See: don't just copy in your entire program!
I am currently coding a game with Android Studio in the style of FlappyBird. I have two Cooldowns that give the user the possibility to use extra abilities.
In the main game view you can see 2 images that are grey and a timer under them. The timer goes from 10 to 0 and when zero is reached the number disappears, the image gets colored and the user knows he is able to activate the ability in the pause screen.
The problem:
When the game is starting the timer starts also and goes down as i wanted. But if the user enters the Pause Menu the Timer should stop and if he leaves it the Timer should run again.
I have a
long startTime = 0;
and a
long elapsedTime = (System.currentTimeMillis() - startTime) / 1000;
It all works but I cannot save the current time in a long and can't find any function for it!
I know the code is very long but the important part is in the drawWorld()-method in the if statement GameState.Running.
public class PlaneGame extends ApplicationAdapter{
private static final float PLANE_JUMP_IMPULSE = 350;
private static final float GRAVITY = -20;
private static final float PLANE_VELOCITY_X = 200;
private static final float PLANE_START_Y = 240;
private static final float PLANE_START_X = 50;
private static final int STATE_START = 0;
private static final int STATE_RUNNING = 1;
private static final int STATE_OVER = 2;
SpriteBatch batch;
OrthographicCamera camera;
OrthographicCamera uiCamera;
Texture background;
TextureRegion ground;
float groundOffsetX = 0;
TextureRegion ceiling;
TextureRegion rock;
TextureRegion rockDown;
TextureRegion planeSmall;
TextureRegion planeSmallBlack;
Animation plane;
TextureRegion ready;
TextureRegion gameOver;
TextureRegion pause;
BitmapFont font;
Vector2 planePosition = new Vector2();
Vector2 planeVelocity = new Vector2();
float planeStateTime = 0;
Vector2 gravity = new Vector2();
Array<Rock> rocks = new Array<Rock>();
GameState gameState = GameState.Start; /*neeeeeeewwwww*/
int score = 0;
long startTime = 0;
long startTime2;
long elapsedTime;
long savedTime;
boolean wantToSeeTime = true;
boolean wantToSeeTimePause = true;
float timeCdRock = 0;
float timeCdPlane = 0;
Rectangle rect1 = new Rectangle();
Rectangle rect2 = new Rectangle();
Music music;
Sound point;
Sound explode;
//this gets called repeatedly to run the game
#Override
public void render () {
//clear the screen so we can draw the next one
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
long saveTime = elapsedTime;
//update the position of the plane and rocks
updateWorld();
elapsedTime = saveTime;
//now draw the updated screen
drawWorld();
}
//initialize objects and load assets when game starts
#Override
public void create () {
startTime = TimeUtils.nanoTime(); /*Neeeewwwwwwwww !!!*/
Gdx.input.setInputProcessor(new GestureDetector(new MyGestureListener()));
batch = new SpriteBatch();
camera = new OrthographicCamera();
camera.setToOrtho(false, 800, 480);
uiCamera = new OrthographicCamera();
uiCamera.setToOrtho(false, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
uiCamera.update();
font = new BitmapFont(Gdx.files.internal("arial.fnt"));
background = new Texture("background.png");
ground = new TextureRegion(new Texture("ground.png"));
ceiling = new TextureRegion(ground);
ceiling.flip(true, true);
rock = new TextureRegion(new Texture("rock.png"));
rockDown = new TextureRegion(rock);
rockDown.flip(false, true);
Texture frame1 = new Texture("plane1.png");
frame1.setFilter(TextureFilter.Linear, TextureFilter.Linear);
Texture frame2 = new Texture("plane2.png");
Texture frame3 = new Texture("plane3.png");
planeSmall = new TextureRegion(new Texture("planeSmall.png"));
planeSmallBlack = new TextureRegion(new Texture("planeSmallBlack.png"));
ready = new TextureRegion(new Texture("ready.png"));
gameOver = new TextureRegion(new Texture("gameover.png"));
pause = new TextureRegion(new Texture("pause.png"));
plane = new Animation(0.05f, new TextureRegion(frame1), new TextureRegion(frame2), new TextureRegion(frame3), new TextureRegion(frame2));
plane.setPlayMode(PlayMode.LOOP);
music = Gdx.audio.newMusic(Gdx.files.internal("music.mp3"));
music.setLooping(true);
music.play();
point = Gdx.audio.newSound(Gdx.files.internal("point.ogg"));
explode = Gdx.audio.newSound(Gdx.files.internal("explode.wav"));
resetWorld();
}
//reset the state of the game
private void resetWorld() {
score = 0;
startTime = 0;
groundOffsetX = 0;
planePosition.set(PLANE_START_X, PLANE_START_Y);
planeVelocity.set(0, 0);
gravity.set(0, GRAVITY);
camera.position.x = 400;
//randomize the position and direction of the rocks
rocks.clear();
for(int i = 0; i < 5; i++) {
boolean isDown = MathUtils.randomBoolean();
rocks.add(new Rock(700 + i * 200, isDown?480-rock.getRegionHeight(): 0, isDown? rockDown: rock));
}
}
//use the time elapsed since the last call to render() to determine how much to update the game
private void updateWorld() {
float deltaTime = Gdx.graphics.getDeltaTime();
planeStateTime += deltaTime;
/*if(Gdx.input.justTouched()) {
if(gameState == STATE_START) {
gameState = STATE_RUNNING;
}
if(gameState == STATE_RUNNING) {
planeVelocity.set(PLANE_VELOCITY_X, PLANE_JUMP_IMPULSE);
}
if(gameState == STATE_OVER) {
gameState = STATE_START;
resetWorld();
}
}*/
if(gameState != GameState.Start) planeVelocity.add(gravity);
planePosition.mulAdd(planeVelocity, deltaTime);
camera.position.x = planePosition.x + 350;
if(camera.position.x - groundOffsetX > ground.getRegionWidth() + 400) {
groundOffsetX += ground.getRegionWidth();
}
rect1.set(planePosition.x + 20, planePosition.y, plane.getKeyFrames()[0].getRegionWidth() - 20, plane.getKeyFrames()[0].getRegionHeight());
for(Rock r: rocks) {
//if the rock is off the screen, give it a new location in front of the plane
if(camera.position.x - r.position.x > 400 + r.image.getRegionWidth()) {
boolean isDown = MathUtils.randomBoolean();
r.position.x += 5 * 200;
r.position.y = isDown?480-rock.getRegionHeight(): 0;
r.image = isDown? rockDown: rock;
r.counted = false;
}
rect2.set(r.position.x + (r.image.getRegionWidth() - 30) / 2 + 20, r.position.y, 20, r.image.getRegionHeight() - 10);
//check if the plane crashed
if(rect1.overlaps(rect2)) {
if(gameState != GameState.GameOver) explode.play();
gameState = GameState.GameOver;
planeVelocity.x = 0;
}
//award a point for not crashing into a rock
if(r.position.x < planePosition.x && !r.counted) {
score++;
r.counted = true;
point.play();
}
}
//check if the plane crashed
if(planePosition.y < ground.getRegionHeight() - 20 ||
planePosition.y + plane.getKeyFrames()[0].getRegionHeight() > 480 - ground.getRegionHeight() + 20) {
if(gameState != GameState.GameOver) explode.play();
gameState = GameState.GameOver;
planeVelocity.x = 0;
}
}
//draw the background, rocks, and plane to the screen and possibly some ui text
private void drawWorld() {
elapsedTime = (System.currentTimeMillis() - startTime) / 1000;
camera.update();
batch.setProjectionMatrix(camera.combined);
batch.begin();
batch.draw(background, camera.position.x - background.getWidth() / 2, 0);
for(Rock rock: rocks) {
batch.draw(rock.image, rock.position.x, rock.position.y);
}
batch.draw(ground, groundOffsetX, 0);
batch.draw(ground, groundOffsetX + ground.getRegionWidth(), 0);
batch.draw(ceiling, groundOffsetX, 480 - ceiling.getRegionHeight());
batch.draw(ceiling, groundOffsetX + ceiling.getRegionWidth(), 480 - ceiling.getRegionHeight());
batch.draw(plane.getKeyFrame(planeStateTime), planePosition.x, planePosition.y);
batch.end();
batch.setProjectionMatrix(uiCamera.combined);
batch.begin();
if(gameState == GameState.Start) {
batch.draw(ready, Gdx.graphics.getWidth() / 2 - ready.getRegionWidth() / 2, Gdx.graphics.getHeight() / 2 - ready.getRegionHeight() / 2);
}
if(gameState == GameState.GameOver) {
batch.draw(gameOver, Gdx.graphics.getWidth() / 2 - gameOver.getRegionWidth() / 2, Gdx.graphics.getHeight() / 2 - gameOver.getRegionHeight() / 2);
}
if(gameState == GameState.GameOver || gameState == GameState.Running) {
font.draw(batch, "" + score, Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() - 60);
}
if(gameState == GameState.Running || gameState == GameState.Pause) {
//font.draw(batch, "" + (10 - elapsedTime), (Gdx.graphics.getWidth() / 2) / 3, Gdx.graphics.getHeight() - 260);
if(wantToSeeTime){
font.draw(batch, "" + (5 - elapsedTime), (Gdx.graphics.getWidth() / 2) / 3, Gdx.graphics.getHeight() - 260);
}
if((5 -(elapsedTime) > 0)) {
batch.draw(planeSmallBlack, (Gdx.graphics.getWidth() / 2) / 3, Gdx.graphics.getHeight() - 250);
}else {
batch.draw(planeSmall, (Gdx.graphics.getWidth() / 2) / 3, Gdx.graphics.getHeight() - 250);
wantToSeeTime = false;
}
}
if(gameState == GameState.Pause){
batch.draw(pause, Gdx.graphics.getWidth() / 2 - pause.getRegionWidth() / 2, Gdx.graphics.getHeight() / 2 - pause.getRegionHeight() / 2);
if(wantToSeeTimePause){
font.draw(batch, "" + (savedTime), (Gdx.graphics.getWidth() / 2) / 3, Gdx.graphics.getHeight() - 260);
}
}
batch.end();
}
//object to hold all pertinent information for a rock
static class Rock {
Vector2 position = new Vector2();
TextureRegion image;
boolean counted;
public Rock(float x, float y, TextureRegion image) {
this.position.x = x;
this.position.y = y;
this.image = image;
}
}
// Neeeeeeeewwwww
static enum GameState {
Start, Running, GameOver, Pause
}
private class MyGestureListener implements GestureDetector.GestureListener {
#Override
public boolean touchDown(float x, float y, int pointer, int button) {
return false;
}
#Override
public boolean tap(float x, float y, int count, int button) {
if(gameState == GameState.Start) {
gameState = GameState.Running;
startTime = System.currentTimeMillis();
}
if(gameState == GameState.Running) {
planeVelocity.set(PLANE_VELOCITY_X, PLANE_JUMP_IMPULSE);
}
if(gameState == GameState.GameOver) {
gameState = GameState.Start;
resetWorld();
}
/*if((gameState == GameState.Pause) && (count == 2)) {
planeVelocity.set(PLANE_VELOCITY_X, PLANE_JUMP_IMPULSE);
gravity.set(0,GRAVITY);
gameState = GameState.Running;
}*/
return true;
}
#Override
public boolean longPress(float x, float y) {
return false;
}
#Override
public boolean fling(float velocityX, float velocityY, int button) {
if(gameState == GameState.Running) {
planePosition.set(planePosition.x, planePosition.y);
planeVelocity.set(0,0);
gravity.set(0, 0);
savedTime = System.currentTimeMillis() / 1000;
wantToSeeTime = false;
wantToSeeTimePause = true;
gameState = GameState.Pause;
} else {
planeVelocity.set(PLANE_VELOCITY_X, PLANE_JUMP_IMPULSE);
gravity.set(0,GRAVITY);
wantToSeeTime = true;
wantToSeeTimePause = false;
elapsedTime -= savedTime;
gameState = GameState.Running;
}
/*if(gameState == GameState.Pause) {
planeVelocity.set(PLANE_VELOCITY_X, PLANE_JUMP_IMPULSE);
gravity.set(0,GRAVITY);
gameState = GameState.Running;
}*/
return true;
}
#Override
public boolean pan(float x, float y, float deltaX, float deltaY) {
return false;
}
#Override
public boolean panStop(float x, float y, int pointer, int button) {
return false;
}
#Override
public boolean zoom(float initialDistance, float distance) {
return false;
}
#Override
public boolean pinch(Vector2 initialPointer1, Vector2 initialPointer2, Vector2 pointer1, Vector2 pointer2) {
return false;
}
public void pinchStop() {
}
}
}
Thanks for your help!
You can use joda time for advanced operations
Please check the link for your reference
http://www.joda.org/joda-time/userguide.html
I'm going to assume that your game runs as part of a loop. Ideally what you should have is a starting time when the ability is activated, which counts down during the cooldown based on how much time has elapsed since the last iteration of your loop. For example, for an ability with a 10 second cooldown:
final long abilityLength = 10 * 1000;
long cooldownRemaining = 0;
long lastTimestamp;
boolean isPaused = false;
private void doAbility() {
if (cooldownRemaining <= 0) {
cooldownRemaining = abilityLength;
}
}
public void main(int[] args) {
lastTimestamp = System.currentTimeMillis();
while(true) { // your main game loop
// Time (in ms) elapsed since the last iteration of this loop
long delta = System.currentTimeMillis() - lastTimestamp;
... // Other game code
if (cooldownRemaining > 0 && !isPaused) {
// Subtract the delta from the remaing cooldown time.
cooldownRemaining -= delta;
}
lastTimeStamp = System.currentTimeMillis();
}
}
Right, I have been learning Slick2D for the past couple of days and want to know why and how to fix my player getting stuck in the collision coordinates if statement.
This is my code for the main Play state, the first couple of lines of the Update void are the if conditions to see if the character (middle of window) is inside to coords...if player is then move em back basically.
package com.ohdanbo.game;
import org.newdawn.slick.*;
import org.newdawn.slick.geom.Rectangle;
import org.newdawn.slick.geom.Shape;
import org.newdawn.slick.state.*;
public class Play extends BasicGameState {
Animation dude, movingUp, movingDown, movingLeft, movingRight;
Image map;
boolean quit = false;
int[] duration = { 200, 200 };
float dudePositionX = 0;
float dudePositionY = 0;
float shiftX = (640 / 2) - (40 / 2);
float shiftY = (360 / 2) - (40 / 2);
Image pauseBG;
Shape rectangle;
float rectX = 0;
float rectY = 0;
public Play(int state) {
}
public void init(GameContainer gc, StateBasedGame sbg) throws SlickException {
//rectangle = new Rectangle(100,100);
pauseBG = new Image("res/bg.png");
map = new Image("res/worldTemp.png");
Image[] walkUp = { new Image("res/charBack.png"), new Image("res/charBack.png") };
Image[] walkDown = { new Image("res/charFront.png"), new Image("res/charFront.png") };
Image[] walkLeft = { new Image("res/charLeft.png"), new Image("res/charLeft.png") };
Image[] walkRight = { new Image("res/charRight.png"), new Image("res/charRight.png") };
movingUp = new Animation(walkUp, duration, false);
movingDown = new Animation(walkDown, duration, false);
movingLeft = new Animation(walkLeft, duration, false);
movingRight = new Animation(walkRight, duration, false);
dude = movingDown;
}
public void render(GameContainer gc, StateBasedGame sbg, Graphics g) throws SlickException {
map.draw(dudePositionX, dudePositionY);
dude.draw(shiftX, shiftY);
//g.draw(rectangle);
// g.setColor(Color.black);
g.drawString("Dude's X: " + dudePositionX + "\nDude's Y: " + dudePositionY, 400, 20);
if (quit == true) {
pauseBG.draw(0, 0);
g.drawString("Continue (C)", 250, 100);
g.drawString("Main Menu (M)", 250, 150);
g.drawString("Quit (Q)", 250, 200);
if (quit == false) {
g.clear();
}
}
}
public void update(GameContainer gc, StateBasedGame sbg, int delta) throws SlickException {
Input input = gc.getInput();
if ((dudePositionX > 100) && (dudePositionX < 267) && (dudePositionY > -68) && (dudePositionY < 156)) {
if(input.isKeyDown(Input.KEY_UP)){dudePositionY -= delta * .1f;}
if(input.isKeyDown(Input.KEY_DOWN)){dudePositionY += delta * .1f;}
if(input.isKeyDown(Input.KEY_LEFT)){dudePositionX -= delta * .1f;}
if(input.isKeyDown(Input.KEY_RIGHT)){dudePositionX += delta * .1f;}
}
if (input.isKeyDown(Input.KEY_UP)) {
dude = movingUp;
dudePositionY += delta * .1f; // increase the Y coordinates of bucky
// (move him up)
if (dudePositionY > 162) {
dudePositionY -= delta * .1f; // dont let him keep going up if
// he reaches the top
}
}
if (input.isKeyDown(Input.KEY_DOWN)) {
dude = movingDown;
dudePositionY -= delta * .1f;
if (dudePositionY < -600) {
dudePositionY += delta * .1f;
}
}
if (input.isKeyDown(Input.KEY_LEFT)) {
dude = movingLeft;
dudePositionX += delta * .1f;
if (dudePositionX > 324) {
dudePositionX -= delta * .1f;
}
}
if (input.isKeyDown(Input.KEY_RIGHT)) {
dude = movingRight;
dudePositionX -= delta * .1f;
if (dudePositionX < -840) {
dudePositionX += delta * .1f;
}
}
if (input.isKeyDown(Input.KEY_ESCAPE)) {
quit = true;
}
if (quit == true) {
if (input.isKeyDown(Input.KEY_C)) {
quit = false;
}
if (input.isKeyDown(Input.KEY_M)) {
sbg.enterState(0);
quit = false;
}
if (input.isKeyDown(Input.KEY_Q)) {
System.exit(0);
}
}
}
public int getID() {
return 1;
}
}
How you want to approach collision detection/collision response problems like this is to understand how collisions should typically play out in game loops.
Basically, it's all based on order of operation to get the right outcome:
Take the characters initial position into account, and save it.
Poll the controller input from the player, and update the player coordinates.
If the player is "out of bounds" or colliding with anything you don't want it to, return it to the previous position (e.g. the initial position you saved in step 1)
Try an approach like that, rather than integrating into your controller code directly as I point out in a snippet of your code below:
if (input.isKeyDown(Input.KEY_RIGHT)) {
dude = movingRight;
dudePositionX -= delta * .1f;
// Do these lines LATER after all the
// controls of movement have been seen and the player updated
if (dudePositionX < -840) {
dudePositionX += delta * .1f;
// Instead of this line, you would have it be something like:
// dudePositionX = previousDudePositionX;
}
}
My scroller is working but I can't move player.
Player is centering to middle of window screen. And map is centering to player's position.
Window (program) size is 640 x 640 px
Map map.tmx is orthogonal (zelda/pokemon style game).
Using only UP, DOWN, RIGHT, LEFT keys (no diagonal moves).
what I am calculating or doing bad ?
public class Play extends BasicGameState {
Animation movingUp, movingDown, movingLeft, movingRight;
Animation player;
String playerName = "Test";
boolean quit = false;
int[] duration = {200, 200, 200};
int playerX = 0;
int playerY = 0;
int cameraX;
int cameraY;
int screenWidth;
int screenHeight;
private TiledMap map = null;
private static final float SPEED = 0.1f;
public Play(int state, float x, float y) {
playerX = (int) x;
playerY = (int) y;
}
#Override
public void init(GameContainer gc, StateBasedGame sbg) throws SlickException {
map = new TiledMap("map/map.tmx");
Image[] walkUp = {
new Image("graphics/player/up0.png"),
new Image("graphics/player/up1.png"),
new Image("graphics/player/up2.png")
};
Image[] walkDown = {
new Image("graphics/player/down0.png"),
new Image("graphics/player/down1.png"),
new Image("graphics/player/down2.png")
};
Image[] walkLeft = {
new Image("graphics/player/left0.png"),
new Image("graphics/player/left1.png"),
new Image("graphics/player/left2.png")
};
Image[] walkRight = {
new Image("graphics/player/right0.png"),
new Image("graphics/player/right1.png"),
new Image("graphics/player/right2.png")
};
movingUp = new Animation(walkUp, duration, false);
movingDown = new Animation(walkDown, duration, false);
movingLeft = new Animation(walkLeft, duration, false);
movingRight = new Animation(walkRight, duration, false);
player = movingDown;
}
#Override
public void render(GameContainer gc, StateBasedGame sbg, Graphics g) throws SlickException {
screenWidth = gc.getWidth();
screenHeight = gc.getHeight();
cameraX = (screenWidth / 2) - (playerX / 2);
cameraY = (screenHeight / 2) - (playerY / 2);
map.render(playerX, playerY);
player.draw(cameraX, cameraY);
g.drawString("X: " + playerX + "\nY: " + playerY, 520, 20);
g.resetTransform();
}
#Override
public void update(GameContainer gc, StateBasedGame sbg, int delta) throws SlickException {
Input input = gc.getInput();
if (input.isKeyDown(Input.KEY_UP)) {
player = movingUp;
player.update(delta);
playerY += delta * SPEED;
} else if (input.isKeyDown(Input.KEY_DOWN)) {
player = movingDown;
player.update(delta);
playerY -= delta * SPEED;
} else if (input.isKeyDown(Input.KEY_LEFT)) {
player = movingLeft;
player.update(delta);
playerX += delta * SPEED;
} else if (input.isKeyDown(Input.KEY_RIGHT)) {
player = movingRight;
player.update(delta);
playerX -= delta * SPEED;
}
}
}
Basic idea:
screenWidth = gc.getWidth();
screenHeight = gc.getHeight();
cameraX = (screenWidth / 2) - (playerX / 2);
cameraY = (screenHeight / 2) - (playerY / 2);
map.render(playerX, playerY);
player.draw(cameraX, cameraY);
Working in my example