Why isn't the LinkedList going through all the entities? - java

I am trying to develop collision detection. For some reason, the collision is working for the last entity in the LinkedList. I have tried my best to debug it using the console to see where it's stopping, but I had no luck. All the other entities are not working, only the last one is. Here is the code :
public class Player implements Entity {
Image player;
public float x = 100f;
public float y = 100f;
boolean canGoLeft = true;
boolean canGoRight = true;
boolean canGoUp = true;
boolean canGoDown = true;
public float speed = 0.15f;
public Rectangle leftRect;
public Rectangle rightRect;
public Rectangle topRect;
public Rectangle bottomRect;
int i = 0;
Entities entities = new Entities();
public Player() {
}
public void update(GameContainer game, int delta) {
if(Keyboard.isKeyDown(Keyboard.KEY_D)) {
if(canGoRight) {
x += speed * delta;
}
}
if(Keyboard.isKeyDown(Keyboard.KEY_A)) {
if(canGoLeft) {
x -= speed * delta;
}
}
if(Keyboard.isKeyDown(Keyboard.KEY_W)) {
if(canGoUp) {
y -= speed * delta;
}
}
if(Keyboard.isKeyDown(Keyboard.KEY_S)) {
if(canGoDown) {
y += speed * delta;
}
}
for(Entity entity : Game.entities.entities) {
checkCollisions(entity);
}
}
public void render(GameContainer game, Graphics g) {
leftRect = new Rectangle(x, y + 5, 2, 80);
rightRect = new Rectangle(x + 45, y + 5, 2, 80);
topRect = new Rectangle(x + 6, y, 36, 2);
bottomRect = new Rectangle(x + 6, y + 90, 36, 2);
//rect = new Rectangle(200, 100, 60, 88);
try {
player = new Image("res/Player.png");
player.setFilter(Image.FILTER_NEAREST);
} catch (SlickException e) {
e.printStackTrace();
}
player.draw(x, y, 60, 88);
//g.draw(leftRect);
//g.draw(rightRect);
//g.draw(topRect);
//g.draw(bottomRect);
}
public void checkCollisions(Entity entity) {
// Collision Detection
canGoLeft = !leftRect.intersects(entity.getRect());
canGoRight = !rightRect.intersects(entity.getRect());
canGoDown = !bottomRect.intersects(entity.getRect());
canGoUp = !topRect.intersects(entity.getRect());
}
public Rectangle getRect() {
return null;
}
}

It is iterating through your entire list, but checkCollisions overwrites the value of canGoLeft, canGoRright, etc. every time it is called.
You should do something like
canGoLeft = canGoLeft && !leftRect.intersects(entity.getRect());

Related

How to detect collisions between objects in LibGDX

This is my first post on stack overflow so I apologize in advance if I'm breaking any rules about posting, etc. I have been working on a an asteroids-esque shooting game and I can't figure out how to get the collision detection working between the rocks and the laser.
The source code can be found here. I had to make some changes to the update method of LevelScreen because the original code is dependent on using the BlueJ IDE. I found a fix in this post and got the collision working between the spaceship and the rocks.
The LevelScreen class
package com.mygdx.game;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
import java.util.ArrayList;
public class LevelScreen extends BaseScreen {
private Spaceship spaceship;
private boolean gameOver;
private boolean rocksNeedRemoved;
private Rock toRemove;
private ArrayList<Rock> rocks;
private ArrayList<Laser> lasers;
public void initialize() {
gameOver = false;
toRemove = null;
rocksNeedRemoved = false;
BaseActor space = new BaseActor(0, 0, mainStage);
space.loadTexture("space.png");
space.setSize(800, 600);
BaseActor.setWorldBounds(space);
spaceship = new Spaceship(400, 300, mainStage);
rocks = new ArrayList<Rock>();
lasers = new ArrayList<Laser>();
rocks.add(new Rock(600, 500, mainStage));
rocks.add(new Rock(600, 300, mainStage));
rocks.add(new Rock(600, 100, mainStage));
rocks.add(new Rock(400, 100, mainStage));
rocks.add(new Rock(200, 100, mainStage));
rocks.add(new Rock(200, 300, mainStage));
rocks.add(new Rock(200, 500, mainStage));
rocks.add(new Rock(400, 500, mainStage));
lasers.add(new Laser(400, 500, mainStage));
}
public void update(float dt) {
//Code from book(Throws class not found error)
/*
for (BaseActor rockActor : BaseActor.getList(mainStage,
"Rock")) {
if (rockActor.overlaps(spaceship)) {
if (spaceship.shieldPower <= 0) {
Explosion boom = new Explosion(0, 0,
mainStage);
boom.centerAtActor(spaceship);
spaceship.remove();
spaceship.setPosition(-1000, -1000);
BaseActor messageLose = new BaseActor(0, 0,
uiStage);
messageLose.loadTexture("message-
lose.png");
messageLose.centerAtPosition(400, 300);
messageLose.setOpacity(0);
messageLose.addAction(Actions.fadeIn(1));
gameOver = true;
}
else {
spaceship.shieldPower -= 34;
Explosion boom = new Explosion(0, 0,
mainStage);
boom.centerAtActor(rockActor);
rockActor.remove();
}
}
for (BaseActor laserActor :
BaseActor.getList(mainStage, "Laser")) {
if (laserActor.overlaps(rockActor)) {
}
Explosion boom = new Explosion(0, 0,
mainStage);
boom.centerAtActor(rockActor);
laserActor.remove();
rockActor.remove();
}
}
if (!gameOver && BaseActor.count(mainStage, "Rock") ==
0) {
BaseActor messageWin = new BaseActor(0, 0,
uiStage);
messageWin.loadTexture("message-win.png");
messageWin.centerAtPosition(400, 300);
messageWin.setOpacity(0);
messageWin.addAction(Actions.fadeIn(1));
gameOver = true;
}
}
*/
// loop I used to get collision working between rocks
and spaceship
for (Rock each : rocks)
if (spaceship.overlaps(each) && !each.crashed &&
spaceship.shieldPower <= 0) {
Explosion boom = new Explosion(0, 0,
mainStage);
Explosion boom2 = new Explosion(0, 0,
mainStage);
boom.centerAtActor(spaceship);
boom2.centerAtActor(each);
spaceship.remove();
spaceship.setPosition(-1000, -1000);
each.crashed = true;
each.clearActions();
each.addAction(Actions.fadeOut(1));
each.addAction(Actions.after(Actions.removeActor()));
rocksNeedRemoved = true;
toRemove = each;
} else if (spaceship.overlaps(each) &&
!each.crashed) {
Explosion boom = new Explosion(0, 0,
mainStage);
boom.centerAtActor(each);
spaceship.shieldPower -= 34;
each.crashed = true;
each.clearActions();
each.addAction(Actions.fadeOut(1));
each.addAction(Actions.after(Actions.removeActor()));
rocksNeedRemoved = true;
toRemove = each;
}
//check for collision between rocks and lasers (Not
working correctly)
for (int i = rocks.size() - 1; i >= 0; i--) {
Rock rock = rocks.get(i);
for (int j = lasers.size() - 1; j >= 0; j--) {
Laser laser = lasers.get(j);
if(rock.getBounds().overlaps(laser.getBounds())) {
Explosion boom = new Explosion(0, 0,
mainStage);
boom.centerAtActor(rock);
rock.crashed = true;
rock.clearActions();
rock.addAction(Actions.fadeOut(1));
rock.addAction(Actions.after(Actions.removeActor()));
rocksNeedRemoved = true;
toRemove = rock;
}
}
}
}
//override default InputProcessor method
public boolean keyDown(int keycode) {
if (keycode == Keys.X)
spaceship.warp();
if (keycode == Keys.SPACE)
spaceship.shoot();
return false;
}
}
The Spaceship class.
package com.mygdx.game;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.math.MathUtils;
public class Spaceship extends BaseActor {
private Thrusters thrusters;
private Shield shield;
public int shieldPower;
public Spaceship(float x, float y, Stage s) {
super(x, y, s);
loadTexture("spaceship.png");
setBoundaryPolygon(8);
setAcceleration(100);
setMaxSpeed(100);
setDeceleration(10);
thrusters = new Thrusters(0, 0, s);
addActor(thrusters);
thrusters.setPosition(-thrusters.getWidth(),
getHeight() / 2 - thrusters.getHeight() / 2);
shield = new Shield(0, 0, s);
addActor(shield);
shield.centerAtPosition(getWidth() / 2, getHeight() /
2);
shieldPower = 100;
}
public void act(float dt) {
super.act(dt);
float degreesPerSecond = 160; //rotation speed
if (Gdx.input.isKeyPressed(Keys.LEFT))
rotateBy(degreesPerSecond * dt);
if (Gdx.input.isKeyPressed(Keys.RIGHT))
rotateBy(-degreesPerSecond * dt);
if (Gdx.input.isKeyPressed(Keys.UP)) {
accelerateAtAngle(getRotation());
thrusters.setVisible(true);
}
else {
thrusters.setVisible(false);
}
shield.setOpacity(shieldPower / 100f);
if (shieldPower <= 0)
shield.setVisible(false);
applyPhysics(dt);
wrapAroundWorld();
}
public void warp() {
if(getStage() == null)
return;
Warp warp1 = new Warp(0, 0, this.getStage());
warp1.centerAtActor(this);
setPosition(MathUtils.random(800),
MathUtils.random(600));
Warp warp2 = new Warp(0, 0, this.getStage());
warp2.centerAtActor(this);
}
public void shoot() {
if (getStage() == null)
return;
Laser laser = new Laser(0, 0, this.getStage());
laser.centerAtActor(this);
laser.setRotation(this.getRotation());
laser.setMotionAngle(this.getRotation());
}
}
The Laser class.
package com.mygdx.game;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.Action;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
public class Laser extends BaseActor {
Rectangle bounds;
public Laser(float x, float y, Stage s) {
super(x, y, s);
bounds = new Rectangle((int)getX(), (int)getY(),
(int)getWidth(), (int)getHeight());
loadTexture("laser.png");
addAction(Actions.delay(1));
addAction(Actions.after(Actions.fadeOut(0.5f)));
addAction(Actions.after(Actions.removeActor()));
setSpeed(400);
setMaxSpeed(400);
setDeceleration(0);
}
public void act(float dt) {
super.act(dt);
applyPhysics(dt);
wrapAroundWorld();
}
public Rectangle getBounds() {
return bounds;
}
private void setXY(float pX,float pY) {
setPosition(pX, pY);
bounds.setX((int)pX);
bounds.setY((int)pY);
}
}
The Rock class
package com.mygdx.game;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.Action;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
import com.badlogic.gdx.math.MathUtils;
import com.mygdx.game.BaseActor;
public class Rock extends BaseActor {
public boolean crashed;
Rectangle bounds;
public Rock(float x, float y, Stage s) {
super(x, y, s);
bounds = new Rectangle((int)getX(), (int)getY(),
(int)getWidth(), (int)getHeight());
loadTexture("rock.png");
float random = MathUtils.random(30);
addAction(Actions.forever(Actions.rotateBy(30 + random,
1)));
setSpeed(50 + random);
setMaxSpeed(50 + random);
setDeceleration(0);
setMotionAngle(MathUtils.random(360));
crashed = false;
}
public void act(float dt) {
super.act(dt);
applyPhysics(dt);
wrapAroundWorld();
}
public boolean isCrashed() {
return crashed;
}
public void crashed() {
crashed = true;
clearActions();
addAction(Actions.fadeOut(1));
addAction(Actions.after(Actions.removeActor()));
}
public Rectangle getBounds() {
return bounds;
}
private void setXY(float pX,float pY) {
setPosition(pX, pY);
bounds.setX((int)pX);
bounds.setY((int)pY);
}
}
Boundary and overlap methods from BaseActor class
public void setBoundaryPolygon(int numSides) {
float w = getWidth();
float h = getHeight();
float[] vertices = new float[2 * numSides];
for(int i = 0; i < numSides; i ++) {
float angle = i * 6.28f / numSides;
//x coordinate
vertices[2 * i] = w / 2 * MathUtils.cos(angle) + w / 2;
//y coordinate
vertices[2 * i + 1] = h / 2 * MathUtils.sin(angle) + h / 2;
}
boundaryPolygon = new Polygon(vertices);
}
public Polygon getBoundaryPolygon() {
boundaryPolygon.setPosition(getX(), getY());
boundaryPolygon.setOrigin(getOriginX(), getOriginY());
boundaryPolygon.setRotation(getRotation());
boundaryPolygon.setScale(getScaleX(), getScaleY());
return boundaryPolygon;
}
public boolean overlaps(BaseActor other) {
Polygon poly1 = this.getBoundaryPolygon();
Polygon poly2 = other.getBoundaryPolygon();
//initial text to improve performance
if(!poly1.getBoundingRectangle().overlaps(poly2.getBoundingRectangle()))
return false;
return Intersector.overlapConvexPolygons(poly1, poly2);
}
So I guess the real question would be how would I go about checking for collisions between the rocks ArrayList and the lasers fired by the player? At this point I just want to finish the game even if it's not using best practices. I tried using the method described here and received no errors but also no collision between lasers and rock. Even if I manually add a laser to the lasers ArrayList. This last post I found leads me to believe I need something like a getAllLasers() but I'm not 100% sure how to go about that. Would it be easier to just learn Box2D or Quadtree?
I realize this is a complex problem and want to thank you in advance for taking taking the time to read it. I'm happy to provide any more information you need.
You already have Rectangle bounds for both of this entities, this is all you need. You can use Rectangle.overlaps()
for(Laser laser: lasers){
for(Rock rock: rocks){
if(laser.getBounds().overlaps(rock.getBounds())){
//collided!
}
}
}
Make sure you are getting a updated rectangle/bounds. Add this extra line in both Rock and Laser getBounds() method:
public Rectangle getBounds() {
bounds.set(getX(),getY(),getWidth(),getHeight());
return bounds;
}
if your actors scales or rotate you should update bounds accordingly
I think problem may be with update your actors bounds, i can't find where you update it. I wrote similiar game and i change Bounds of actors on each update step and all works well in some lines...
public void update() {
...
// changing bounds
bounds = new Rectangle(position.x,position.y,
actorWidth,
actorHeight);
}
Or if you use other approach check that your bounds changing in time

Rectangle intersects not working while jumping

I am making a game with android. The player is a rectangle that has to jump over obstacles. When i jump i decrease the height and the y coordinates. When i run the game the rectangle is jumping but when i try to jump over an obstacle its still hitting it. So i think i forget to change a value but i have no idea what value. Sorry for my bad english and i am a beginner at coding.
This is my Player class:
package com.example.niek.speelveld;
import android.graphics.Canvas;
import android.graphics.Paint;
/**
* Created by Niek on 15-8-2017.
*/
public class Player extends GameObject {
private int score;
private boolean up;
private boolean playing;
private long startTime;
private int h = 0;
private boolean jump;
public Player(int x , int y){
super.x = x;
super.y = y;
width = 100;
height = GamePanel.HEIGHT;
score = 0;
startTime = System.nanoTime();
}
public void setUp(boolean b){up = b;}
public void update(){
long elapsed = (System.nanoTime()-startTime)/1000000;
if(elapsed>100){
score++;
startTime = System.nanoTime();
}
if(up && h == 0){
jump = true;
up = false;
}
if (jump) {
if (h < 120) {
h = h + 7;
height = height - 7;
y = y - 7;
} else {
jump = false;
}
} else {
if (h > 0) {
h = h - 7;
height = height + 7;
y = y + 7;
}
}
}
public void draw(Canvas canvas){
Paint myPaint = new Paint();
myPaint.setStyle(Paint.Style.STROKE);
myPaint.setStrokeWidth(7);
canvas.drawRect(x, y, width + x, height, myPaint );
myPaint.setStrokeWidth(1);
canvas.drawText("HIGHSCORE " + score, GamePanel.WIDTH - 100 , 0+myPaint.getTextSize(), myPaint);
}
public int getScore(){return score;}
public boolean getPlaying(){return playing;}
public void setPlaying(boolean b){playing = b;}
//public void resetScore(){score = 0;}
public boolean getJump() {
return jump;
}
}
And in my Gamepanel class i use these 2 methods. When i call the class obstacle.get(i).update(); its only updating the x value so its moving towards the player.
public void update(){
if(player.getPlaying()) {
bg.update();
player.update();
//Add obstacles with timer
long obstacleElapsed = (System.nanoTime()-obstacleStartTime)/1000000;
if(obstacleElapsed >(2000 - player.getScore()/4)){
obstacles.add(new Obstacle(BitmapFactory.decodeResource(getResources(), R.drawable.rsz_spike1), WIDTH + 10, HEIGHT - 51, 14, 51, player.getScore()));
//reset timer
obstacleStartTime = System.nanoTime();
}
}
//Loop through every obstacle and check collision
for(int i = 0; i<obstacles.size(); i++){
obstacles.get(i).update();
if (collision(obstacles.get(i), player)) {
obstacles.remove(i);
player.setPlaying(false);
Intent intent = new Intent(c, Result.class);
intent.putExtra("SCORE", player.getScore());
c.startActivity(intent);
break;
}
//Remove obstacles that are out of the screen
if (obstacles.get(i).getX() < -100) {
obstacles.remove(i);
break;
}
}
}
public boolean collision(GameObject o, GameObject p){
if(Rect.intersects(o.getRectangle(), p.getRectangle())){
return true;
}
return false;
}
#Override
public void draw(Canvas canvas){
//Get Width and Height from screen
final float scaleFactorX = (float)getWidth()/WIDTH;
final float scaleFactorY = (float)getHeight()/HEIGHT;
if(canvas!=null) {
final int savedState = canvas.save();
canvas.scale(scaleFactorX, scaleFactorY);
bg.draw(canvas);
player.draw(canvas);
//Draw obstacles
for(Obstacle o : obstacles ){
o.draw(canvas);
}
canvas.restoreToCount(savedState);
}
}
And finally the Player and the Obstacle class extends Gameobject where i have the getRectangle method.
public Rect getRectangle(){
return new Rect(x, y, x+width, y+height);
}

Java Eclipse game - restart game

I have made a game in Eclipse neon with the language Java, but I was wondering how I can make the game so that if the user presses r he or she could go back to the first screen where it says press spacebar to start. here are my 3 files of code if you need it to figure out the solution.
//This is the game file (main)
import processing.core.PApplet;
public class Game extends PApplet{
public static void main(String[] args) {
// TODO Auto-generated method stub
PApplet.main("Game");
}
James james1;
Wall wall1;
Wall wall2;
Wall wall3;
public void settings() {
size(500, 700);
}
public void setup() {
frameRate(40);
james1 = new James(this, 250, 650, 50);
wall1 = new Wall(this, 0);
wall2 = new Wall(this, 250);
wall3 = new Wall(this, -250);
}
public void draw() {
if(key == ' ') {
clear();
background(0, 255, 255);
boolean loser = wall1.col(james1.getX(), james1.getY()) || wall2.col(james1.getX(), james1.getY()) ||wall3.col(james1.getX(), james1.getY());
james1.show();
move();
if (!loser){
wall1.update();
wall2.update();
wall3.update();
}
else {
reset();
}
wall1.display();
wall2.display();
wall3.display();
}
else {
background(255, 0, 255);
textSize(45);
text("Press Space to Start!", 50, 350);
}
}
public void reset() {
clear();
frameRate(0);
textSize(40);
text("You Lose, R to restart", 25, 550);
}
public void restart() {
}
public void move() {
james1.setX(mouseX);
if (mouseX <= 25) {
james1.setX(25);
}
else if (mouseX >= 475) {
james1.setX(475);
}
}
}
//This is the character file (James)
import processing.core.PApplet;
public class James {
PApplet p;
private float x;
private float y;
private float size;
public James(PApplet np, float nx, float ny, float nsize) {
p = np;
x = nx;
y = ny;
size = nsize;
}
public void show() {
p.fill(255,20,147);
p.ellipse(x, y, size, size);
p.fill(128,128,0);
p.ellipse(x - 10, y - 10, size - 30, size - 30);
p.ellipse(x + 10, y - 10, size -30, size - 30);
}
public float getX() {
return x;
}
public float getY() {
return y;
}
public float getSize() {
return size;
}
public void setX(float gx) {
x = gx;
}
public void setY(float gy) {
y = gy;
}
public void setSize(float gSize) {
size = gSize;
}
}
//This is obstacle class file (walls)
import processing.core.PApplet;
public class Wall {
PApplet p;
private float x;
private float y;
private float w;
private float xw;
private float yw;
private float h;
private float wh;
private float xspeed;
private float yspeed;
public Wall (PApplet np, float ny) {
p = np;
x = 0;
y = ny;
wh = 0;
xw = p.random(0, 450);
yw = p.random(0, 450);
w = p.random(0, 450);
h = 30;
yspeed = 10;
}
public void display() {
p.rect(0, y, w, h);
p.rect(w + 100, y, 500-(w+100), h);
}
public void update() {
if(y > p.height) {
y = 0;
w = p.random(0,450);
xw = p.random(0, 450);
yw = p.random(0, 450);
}
y = yspeed + y;
}
public float getX() {
return x;
}
public float getY() {
return y;
}
public float getW() {
return w;
}
public float getXW() {
return xw;
}
public float getYW() {
return yw;
}
public float getWH() {
return wh;
}
public float getH() {
return h;
}
public float getxSpeed() {
return xspeed;
}
public float getySpeed() {
return yspeed;
}
public void setX(float gx) {
x = gx;
}
public void setYW(float gyw) {
yw = gyw;
}
public void setW(float gw) {
w = gw;
}
public void setY(float gy) {
y = gy;
}
public void setH(float gh) {
h = gh;
}
public void setxSpeed(float gxSpeed) {
xspeed = gxSpeed;
}
public void setySpeed(float gySpeed) {
yspeed = gySpeed;
}
public boolean col(float ballX, float ballY) {
if (y + h >= ballY - 25 && y <= ballY + 25 ) {
if(ballX - 25 <= w || ballX+25 >= w+100) {
return true;
}
}
return false;
}
}
Hope that wasn't too long for just one goal ;)
Copy everything from method setup to method restart and then call restart from method setup.
You get something like this:
public void setup()
{
restart();
}
public void restart()
{
frameRate(40);
james1 = new James(this, 250, 650, 50);
wall1 = new Wall(this, 0);
wall2 = new Wall(this, 250);
wall3 = new Wall(this, -250);
}
Now just call restart whenever you want the game to restart. In your example: when the user presses the 'r' button.
In games, it really is a good idea to have a Map class that handles these kind of things.
You can just basically copy setup and paste it with a new method name, or even use that same method to make it so when pressed "r", it just calls that method back again. But just for it to be cleaner and much more readable. you can do this.
public void setup() {
if(key.r) { //of course this key.r will have to be created as a KeyListener.
restart();
}
}
public void restart() {
frameRate(40);
james1 = new James(this, 250, 650, 50);
wall1 = new Wall(this, 0);
wall2 = new Wall(this, 250);
wall3 = new Wall(this, -250);
}
You can either create a new class for the KeyInput or do it in the same class, but it should be a little like this:
public boolean[] keys = new boolean[120];
public boolean r;
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
f3 = (key == KeyEvent.VK_F3);
}
public void keyReleased(KeyEvent e) {
keys[e.getKeyCode()] = false;
}
public void keyTyped(KeyEvent e) {
keys[e.getKeyCode()] = false;
}
}
Have a nice day!
:D

Collision issue with Java Game

I am making a little Java Game for practice. Since changing up sprites and what not I am having problems with collision between Enemy and Projectiles. I will provide the Projectile class and can provide more if anyone would like to look! Help would much greatly be appreciated here as I've literally been trying to fix this for hours. Essentially each enemy should take 5 hits then disappear but I can't seem to get it like so. I think the problem lies within the bounds but I really have no idea at the minute what's up.
dn and dn2 are the enemy sprites. r is the projectile fired by my character
public class Projectile {
private int x, y, speedX;
private boolean visible;
private Rectangle r;
public Projectile(int startX, int startY) {
x = startX;
y = startY;
speedX = 7;
visible = true;
r = new Rectangle(0, 0, 0, 0);
}
public void update() {
x += speedX;
r.setBounds(x, y, 10, 5);
if (x > 800) {
visible = false;
r = null;
}
if (x < 800) {
checkCollision();
}
}
private void checkCollision() {
if (r.intersects(StartingClass.dn.r)) {
visible = false;
if (StartingClass.dn.health > 0) {
StartingClass.dn.health -= 1;
}
if (StartingClass.dn.health == 0) {
StartingClass.dn.setCenterX(-100);
StartingClass.score += 5;
}
}
if (r.intersects(StartingClass.dn2.r)) {
visible = false;
if (StartingClass.dn2.health > 0) {
StartingClass.dn2.health -= 1;
}
if (StartingClass.dn2.health == 0) {
StartingClass.dn2.setCenterX(-100);
StartingClass.score += 5;
}
}
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getSpeedX() {
return speedX;
}
public boolean isVisible() {
return visible;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void setSpeedX(int speedX) {
this.speedX = speedX;
}
public void setVisible(boolean visible) {
this.visible = visible;
}
}
public class Enemy {
private int power, centerX, speedX, centerY;
private Background bg = StartingClass.getBg1();
private Ninja ninja = StartingClass.getNinja();
public static boolean hit = false;
public Rectangle r = new Rectangle(0, 0, 0, 0);
public int health = 5;
private int movementSpeed;
// Behavioral Methods
public void update() {
follow();
centerX += speedX;
speedX = bg.getSpeedX() * 5 + movementSpeed;
r.setBounds(centerX - 25, centerY - 25, 50, 60);
if (r.intersects(Ninja.yellowRed)) {
checkCollision();
}
}
private void checkCollision() {
if (r.intersects(Ninja.rect) || r.intersects(Ninja.rect2) || r.intersects(Ninja.rect3) || r.intersects(Ninja.rect4)){
System.out.println("collision");
hit = true;
}
}
public void follow() {
if (centerX < -95 || centerX > 810) {
movementSpeed = 0;
}
else if (Math.abs(ninja.getCenterX() - centerX) < 5) {
movementSpeed = 0;
}
else {
if (ninja.getCenterX() >= centerX) {
movementSpeed = 1;
} else {
movementSpeed = -1;
}
}
}
public class Ninja {
// Constants are Here
final int JUMPSPEED = -20;
final int MOVESPEED = 5;
private int centerX = 100;
private int centerY = 377;
private boolean jumped = false;
private boolean movingLeft = false;
private boolean movingRight = false;
private boolean ducked = false;
private boolean readyToFire = true;
private int speedX = 0;
private int speedY = 0;
public static Rectangle rect = new Rectangle(0, 0, 0, 0);
public static Rectangle rect2 = new Rectangle(0, 0, 0, 0);
public static Rectangle rect3 = new Rectangle(0, 0, 0, 0);
public static Rectangle rect4 = new Rectangle(0, 0, 0, 0);
public static Rectangle yellowRed = new Rectangle(0, 0, 0, 0);
public static Rectangle footleft = new Rectangle(0, 0, 0, 0);
public static Rectangle footright = new Rectangle(0, 0, 0, 0);
private Background bg1 = StartingClass.getBg1();
private Background bg2 = StartingClass.getBg2();
public Rectangle r = new Rectangle(0, 0, 0, 0);
private ArrayList<Projectile> projectiles = new ArrayList<Projectile>();
public void update() {
// Moves Character or Scrolls Background accordingly.
if (speedX < 0) {
centerX += speedX;
}
if (speedX == 0 || speedX < 0) {
bg1.setSpeedX(0);
bg2.setSpeedX(0);
}
if (centerX <= 200 && speedX > 0) {
centerX += speedX;
}
if (speedX > 0 && centerX > 200) {
bg1.setSpeedX(-MOVESPEED / 5);
bg2.setSpeedX(-MOVESPEED / 5);
}
// Updates Y Position
centerY += speedY;
// Handles Jumping
speedY += 1;
if (speedY > 3) {
jumped = true;
}
// Prevents going beyond X coordinate of 0
if (centerX + speedX <= 60) {
centerX = 61;
}
rect.setRect(centerX - 34, centerY - 30, 67, 62);
rect2.setRect(rect.getX(), rect.getY() + 63, 68, 63);
rect3.setRect(rect.getX() - 26, rect.getY() + 32, 26, 20);
rect4.setRect(rect.getX() + 68, rect.getY() + 32, 26, 20);
yellowRed.setRect(centerX - 100, centerY - 110, 180, 180);
footleft.setRect(centerX - 50, centerY + 20, 50, 15);
footright.setRect(centerX, centerY + 20, 50, 15);
}
public void moveRight() {
if (ducked == false) {
speedX = MOVESPEED;
}
}
public void moveLeft() {
if (ducked == false) {
speedX = -MOVESPEED;
}
}
public void stopRight() {
setMovingRight(false);
stop();
}
public void stopLeft() {
setMovingLeft(false);
stop();
}
private void stop() {
if (isMovingRight() == false && isMovingLeft() == false) {
speedX = 0;
}
if (isMovingRight() == false && isMovingLeft() == true) {
moveLeft();
}
if (isMovingRight() == true && isMovingLeft() == false) {
moveRight();
}
}
public void jump() {
if (jumped == false) {
speedY = JUMPSPEED;
jumped = true;
}
}
public void shoot() {
if (readyToFire) {
Projectile p = new Projectile(centerX + 40, centerY + 80);
projectiles.add(p);
}
}
Problem solved . It lay within my enemy class in r.setBounds(). Just painted that rectangle to screen and adjusted coordinates and all was good

Threads with Key Bindings

I'm new to Java graphics and threads, and I'm trying to make a game (specifically, Pong). The idea is that two people can play on the same keyboard (i.e. there are two paddles that are controlled through different keys). Currently, both players can't move their paddle at the same time.
Is there a solution to this? Are separate threads the answer?
If possible, I'd like the paddles to be able to move (at least seemingly) at the same time.
Update: It seems like using a Set<Integer> to store pressed keys is the best option. I've done that (and it works), but I'm wondering if any of this code is not on the Event Dispatching Thread (EDT) and if I need to use SwingUtilities.invokeLater();. Here's the necessary code:
private Set<Integer> keysDown = Collections.synchronizedSet(new HashSet<Integer>());
public void keyPressed(KeyEvent e)
{
keysDown.add(e.getKeyCode());
}
public void keyReleased(KeyEvent e)
{
keysDown.remove(e.getKeyCode());
}
public void updatePaddlePositions()
{
if (keysDown.contains(KeyEvent.VK_W))
paddleOne.move(-PADDLE_MOVE_INCREMENT);
if (keysDown.contains(KeyEvent.VK_S))
paddleOne.move(PADDLE_MOVE_INCREMENT);
if (keysDown.contains(KeyEvent.VK_UP))
paddleTwo.move(-PADDLE_MOVE_INCREMENT);
if (keysDown.contains(KeyEvent.VK_DOWN))
paddleTwo.move(PADDLE_MOVE_INCREMENT);
try {
Thread.sleep(DELAY);
} catch (InterruptedException e) {
System.out.println("You Interrupted the game!");
}
canvas.repaint();
}
Here's the paintComponent method of the canvas object:
public void paintComponent(Graphics g)
{
super.paintComponent(g);
paddleOne.paint(g);
paddleTwo.paint(g);
updatePaddlePositions(); // Does this need to be SwingUtilities.invokeLater(this)?
// And should updatePaddlePositions() be run() as a result?
}
And here's the paint method of the paddleOne and paddleTwo objects:
public void paint(Graphics g)
{
g.setColor(Color.BLACK);
g.fillRect(x, y, PADDLE_WIDTH, PADDLE_HEIGHT);
}
Feel free to comment on my design if anything pops out as "bad" to you. Lastly, what does Collections.synchronizedSet(new HashSet<Integer>()) mean/do?
Update #2: It seems like key bindings are the way to go (even though they take more code, in this case). What make key bindings better? Is it that they work separately from everything else (and don't need window focus like key listeners)?
Also, I understand that HashSet<Integer> is just a subclass of Set<Integer>, but what is the purpose of Collections.synchronizedSet(...)? I'm assuming it has to do with threads, but I don't know why it's needed in this program (if it is at all).
First off, use Swing KeyBindings Also I see no real need for multi-threadung unless you want your game multi-threaded.
To clarify the problem is you need to be able for 2 players to press different keys?
If so:
Solution:
Simply use booleans to flag whether or not a key is pressed down, you would than of course have to reset the flag when the key is released.
In your game logic you would check the states if the booleans and act appropriately.
See my below example (both paddles can move independently using W and S and UP and DOWN keys):
public class GameLogic {
public GameLogic() {
initComponents();
}
private void initComponents() {
JFrame frame = new JFrame("Game Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//create the gamepanel
final GamePanel gp = new GamePanel(600, 500);
//create panel to hold buttons to start pause and stop the game
JPanel buttonPanel = new JPanel();
buttonPanel.setOpaque(false);
final JButton startButton = new JButton("Start");
final JButton pauseButton = new JButton("Pause");
final JButton stopButton = new JButton("Stop");
pauseButton.setEnabled(false);
stopButton.setEnabled(false);
//add listeners to buttons (most of the actions - excuse the pun - takes palce here :)
startButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
//clear enitites currently in array
gp.clearEntities();
ArrayList<BufferedImage> ballEntityImages = new ArrayList<>();
ArrayList<Long> ballEntityTimings = new ArrayList<>();
ballEntityImages.add(createColouredImage("white", 50, 50, true));
ballEntityTimings.add(500l);
Entity ballEntity = new Entity(gp.getWidth() / 2, gp.getHeight() / 2, ballEntityImages, ballEntityTimings);
ballEntity.RIGHT = true;
//create images for entities
ArrayList<BufferedImage> advEntityImages = new ArrayList<>();
ArrayList<Long> advEntityTimings = new ArrayList<>();
advEntityImages.add(createColouredImage("orange", 10, 100, false));
advEntityTimings.add(500l);
advEntityImages.add(createColouredImage("blue", 10, 100, false));
advEntityTimings.add(500l);
//create entities
AdvancedSpritesEntity player1Entity = new AdvancedSpritesEntity(0, 100, advEntityImages, advEntityTimings);
ArrayList<BufferedImage> entityImages = new ArrayList<>();
ArrayList<Long> entityTimings = new ArrayList<>();
entityImages.add(createColouredImage("red", 10, 100, false));
entityTimings.add(500l);//as its the only image it doesnt really matter what time we put we could use 0l
//entityImages.add(createColouredImage("magenta", 100, 100));
//entityTimings.add(500l);
Entity player2Entity = new Entity(gp.getWidth() - 10, 200, entityImages, entityTimings);
gp.addEntity(player1Entity);
gp.addEntity(player2Entity);//just a standing still Entity for testing
gp.addEntity(ballEntity);//just a standing still Entity for testing
//create Keybingings for gamepanel
GameKeyBindings gameKeyBindings = new GameKeyBindings(gp, player1Entity, player2Entity);
GamePanel.running.set(true);
//start the game loop which will repaint the screen
runGameLoop(gp);
startButton.setEnabled(false);
pauseButton.setEnabled(true);
stopButton.setEnabled(true);
}
});
pauseButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
boolean b = GamePanel.paused.get();
GamePanel.paused.set(!b);//set it to the opposite of what it was i.e paused to unpaused and vice versa
if (pauseButton.getText().equals("Pause")) {
pauseButton.setText("Un-pause");
} else {
pauseButton.setText("Pause");
}
}
});
stopButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
GamePanel.running.set(false);
GamePanel.paused.set(false);
/* if we want enitites to be cleared and a blank panel shown
gp.clearEntities();
gp.repaint();
*/
if (!pauseButton.getText().equals("Pause")) {
pauseButton.setText("Pause");
}
startButton.setEnabled(true);
pauseButton.setEnabled(false);
stopButton.setEnabled(false);
}
});
//add buttons to panel
buttonPanel.add(startButton);
buttonPanel.add(pauseButton);
buttonPanel.add(stopButton);
//add gamepanel to jframe and button panel
frame.add(gp);
frame.add(buttonPanel, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
//Simply used for testing (to simulate sprites) can create different colored images
public static BufferedImage createColouredImage(String color, int w, int h, boolean circular) {
BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
switch (color.toLowerCase()) {
case "green":
g2.setColor(Color.GREEN);
break;
case "magenta":
g2.setColor(Color.MAGENTA);
break;
case "red":
g2.setColor(Color.RED);
break;
case "yellow":
g2.setColor(Color.YELLOW);
break;
case "blue":
g2.setColor(Color.BLUE);
break;
case "orange":
g2.setColor(Color.ORANGE);
break;
case "cyan":
g2.setColor(Color.CYAN);
break;
case "gray":
g2.setColor(Color.GRAY);
break;
default:
g2.setColor(Color.WHITE);
break;
}
if (!circular) {
g2.fillRect(0, 0, img.getWidth(), img.getHeight());
} else {
g2.fillOval(0, 0, img.getWidth(), img.getHeight());
}
g2.dispose();
return img;
}
//Starts a new thread and runs the game loop in it.
private void runGameLoop(final GamePanel gp) {
Thread loop = new Thread(new Runnable() {
#Override
public void run() {
gp.gameLoop();
}
});
loop.start();
}
//Code starts here
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {//attempt to set look and feel to nimbus Java 7 and up
for (UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
GameLogic gameLogic = new GameLogic();
}
});
}
}
class GameKeyBindings {
public GameKeyBindings(JComponent gp, final Entity entity, final Entity entity2) {
gp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, false), "W pressed");
gp.getActionMap().put("W pressed", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent ae) {
entity.UP = true;
}
});
gp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, true), "W released");
gp.getActionMap().put("W released", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent ae) {
entity.UP = false;
}
});
gp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, false), "S pressed");
gp.getActionMap().put("S pressed", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent ae) {
entity.DOWN = true;
}
});
gp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, true), "S released");
gp.getActionMap().put("S released", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent ae) {
entity.DOWN = false;
}
});
gp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), "down pressed");
gp.getActionMap().put("down pressed", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent ae) {
entity2.DOWN = true;
}
});
gp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), "down released");
gp.getActionMap().put("down released", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent ae) {
entity2.DOWN = false;
}
});
gp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), "up pressed");
gp.getActionMap().put("up pressed", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent ae) {
entity2.UP = true;
}
});
gp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true), "up released");
gp.getActionMap().put("up released", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent ae) {
entity2.UP = false;
}
});
}
}
class AdvancedSpritesEntity extends Entity {
public AdvancedSpritesEntity(int x, int y, ArrayList<BufferedImage> images, ArrayList<Long> timings) {
super(x, y, images, timings);
}
void setAnimation(ArrayList<BufferedImage> images, ArrayList<Long> timings) {
reset();//reset variables of animator class
setFrames(images, timings);//set new frames for animation
}
}
class Entity extends Animator {
private int speed = 5;
public boolean UP = false, DOWN = false, LEFT = false, RIGHT = false, visible;
private Rectangle2D.Double rect;
public Entity(int x, int y, ArrayList<BufferedImage> images, ArrayList<Long> timings) {
super(images, timings);
UP = false;
DOWN = false;
LEFT = false;
RIGHT = false;
visible = true;
rect = new Rectangle2D.Double(x, y, getCurrentImage().getWidth(), getCurrentImage().getHeight());
}
public void setSpeed(int speed) {
this.speed = speed;
}
public int getSpeed() {
return speed;
}
public boolean isVisible() {
return visible;
}
public void setVisible(boolean visible) {
this.visible = visible;
}
public HashSet<String> getMask(Entity e) {
HashSet<String> mask = new HashSet<>();
int pixel, a;
BufferedImage bi = e.getCurrentImage();//gets the current image being shown
for (int i = 0; i < bi.getWidth(); i++) { // for every (x,y) component in the given box,
for (int j = 0; j < bi.getHeight(); j++) {
pixel = bi.getRGB(i, j); // get the RGB value of the pixel
a = (pixel >> 24) & 0xff;
if (a != 0) { // if the alpha is not 0, it must be something other than transparent
mask.add((e.getX() + i) + "," + (e.getY() - j)); // add the absolute x and absolute y coordinates to our set
}
}
}
return mask; //return our set
}
// Returns true if there is a collision between object a and object b
public boolean checkPerPixelCollision(Entity b) {
// This method detects to see if the images overlap at all. If they do, collision is possible
int ax1 = (int) getX();
int ay1 = (int) getY();
int ax2 = ax1 + (int) getWidth();
int ay2 = ay1 + (int) getHeight();
int bx1 = (int) b.getX();
int by1 = (int) b.getY();
int bx2 = bx1 + (int) b.getWidth();
int by2 = by1 + (int) b.getHeight();
if (by2 < ay1 || ay2 < by1 || bx2 < ax1 || ax2 < bx1) {
return false; // Collision is impossible.
} else { // Collision is possible.
// get the masks for both images
HashSet<String> maskPlayer1 = getMask(this);
HashSet<String> maskPlayer2 = getMask(b);
maskPlayer1.retainAll(maskPlayer2); // Check to see if any pixels in maskPlayer2 are the same as those in maskPlayer1
if (maskPlayer1.size() > 0) { // if so, than there exists at least one pixel that is the same in both images, thus
return true;
}
}
return false;
}
public void move() {
if (UP) {
rect.y -= speed;
}
if (DOWN) {
rect.y += speed;
}
if (LEFT) {
rect.x -= speed;
}
if (RIGHT) {
rect.x += speed;
}
}
#Override
public void update(long elapsedTime) {
super.update(elapsedTime);
getWidth();//set the rectangles height accordingly after image update
getHeight();//set rectangles height accordingle after update
}
public boolean intersects(Entity e) {
return rect.intersects(e.rect);
}
public double getX() {
return rect.x;
}
public double getY() {
return rect.y;
}
public double getWidth() {
if (getCurrentImage() == null) {//there might be no image (which is unwanted ofcourse but we must not get NPE so we check for null and return 0
return rect.width = 0;
}
return rect.width = getCurrentImage().getWidth();
}
public double getHeight() {
if (getCurrentImage() == null) {
return rect.height = 0;
}
return rect.height = getCurrentImage().getHeight();
}
}
class Animator {
private ArrayList<BufferedImage> frames;
private ArrayList<Long> timings;
private int currIndex;
private long animationTime;
private long totalAnimationDuration;
private AtomicBoolean done;//used to keep track if a single set of frames/ an animtion has finished its loop
public Animator(ArrayList<BufferedImage> frames, ArrayList< Long> timings) {
currIndex = 0;
animationTime = 0;
totalAnimationDuration = 0;
done = new AtomicBoolean(false);
this.frames = new ArrayList<>();
this.timings = new ArrayList<>();
setFrames(frames, timings);
}
public boolean isDone() {
return done.get();
}
public void reset() {
totalAnimationDuration = 0;
done.getAndSet(false);
}
public void update(long elapsedTime) {
if (frames.size() > 1) {
animationTime += elapsedTime;
if (animationTime >= totalAnimationDuration) {
animationTime = animationTime % totalAnimationDuration;
currIndex = 0;
done.getAndSet(true);
}
while (animationTime > timings.get(currIndex)) {
currIndex++;
}
}
}
public BufferedImage getCurrentImage() {
if (frames.isEmpty()) {
return null;
} else {
try {
return frames.get(currIndex);
} catch (Exception ex) {//images might have been altered so we reset the index and return first image of the new frames/animation
currIndex = 0;
return frames.get(currIndex);
}
}
}
public void setFrames(ArrayList<BufferedImage> frames, ArrayList< Long> timings) {
if (frames == null || timings == null) {//so that constructor super(null,null) cause this to throw NullPointerException
return;
}
this.frames = frames;
this.timings.clear();
for (long animTime : timings) {
totalAnimationDuration += animTime;
this.timings.add(totalAnimationDuration);
}
}
}
class GamePanel extends JPanel {
private final static RenderingHints textRenderHints = new RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
private final static RenderingHints imageRenderHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
private final static RenderingHints colorRenderHints = new RenderingHints(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
private final static RenderingHints interpolationRenderHints = new RenderingHints(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
private final static RenderingHints renderHints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
public static AtomicBoolean running = new AtomicBoolean(false), paused = new AtomicBoolean(false);
private int width, height, frameCount = 0, fps = 0;
private ArrayList<Entity> entities = new ArrayList<>();
private final Random random = new Random();
public GamePanel(int w, int h) {
super(true);//make sure double buffering is enabled
setIgnoreRepaint(true);//mustnt repaint itself the gameloop will do that
width = w;
height = h;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(width, height);
}
public void addEntity(Entity e) {
entities.add(e);
}
void clearEntities() {
entities.clear();
}
//Only run this in another Thread!
public void gameLoop() {
//This value would probably be stored elsewhere.
final double GAME_HERTZ = 30.0;
//Calculate how many ns each frame should take for our target game hertz.
final double TIME_BETWEEN_UPDATES = 1000000000 / GAME_HERTZ;
//At the very most we will update the game this many times before a new render.
//If you're worried about visual hitches more than perfect timing, set this to 1.
final int MAX_UPDATES_BEFORE_RENDER = 5;
//We will need the last update time.
double lastUpdateTime = System.nanoTime();
//Store the last time we rendered.
double lastRenderTime = System.nanoTime();
//If we are able to get as high as this FPS, don't render again.
final double TARGET_FPS = 60;
final double TARGET_TIME_BETWEEN_RENDERS = 1000000000 / TARGET_FPS;
//Simple way of finding FPS.
int lastSecondTime = (int) (lastUpdateTime / 1000000000);
//store the time we started this will be used for updating map and charcter animations
long currTime = System.currentTimeMillis();
while (running.get()) {
if (!paused.get()) {
double now = System.nanoTime();
long elapsedTime = System.currentTimeMillis() - currTime;
currTime += elapsedTime;
int updateCount = 0;
//Do as many game updates as we need to, potentially playing catchup.
while (now - lastUpdateTime > TIME_BETWEEN_UPDATES && updateCount < MAX_UPDATES_BEFORE_RENDER) {
updateGame(elapsedTime);//Update the entity movements and collision checks etc (all has to do with updating the games status i.e call move() on Enitites)
lastUpdateTime += TIME_BETWEEN_UPDATES;
updateCount++;
}
//If for some reason an update takes forever, we don't want to do an insane number of catchups.
//If you were doing some sort of game that needed to keep EXACT time, you would get rid of this.
if (now - lastUpdateTime > TIME_BETWEEN_UPDATES) {
lastUpdateTime = now - TIME_BETWEEN_UPDATES;
}
drawGame();//draw the game by invokeing repaint (which will call paintComponent) on this JPanel
lastRenderTime = now;
//Update the frames we got.
int thisSecond = (int) (lastUpdateTime / 1000000000);
if (thisSecond > lastSecondTime) {
//System.out.println("NEW SECOND " + thisSecond + " " + frameCount);
fps = frameCount;
frameCount = 0;
lastSecondTime = thisSecond;
}
//Yield until it has been at least the target time between renders. This saves the CPU from hogging.
while (now - lastRenderTime < TARGET_TIME_BETWEEN_RENDERS && now - lastUpdateTime < TIME_BETWEEN_UPDATES) {
//allow the threading system to play threads that are waiting to run.
Thread.yield();
//This stops the app from consuming all your CPU. It makes this slightly less accurate, but is worth it.
//You can remove this line and it will still work (better), your CPU just climbs on certain OSes.
//FYI on some OS's this can cause pretty bad stuttering. Scroll down and have a look at different peoples' solutions to this.
//On my OS(Windows 7 x64 intel i3) it does not allow for time to make enityt etc move thus all stands still
try {
Thread.sleep(1);
} catch (Exception e) {
}
now = System.nanoTime();
}
}
}
fps = 0;//no more running set fps to 0
}
private void drawGame() {
//Both revalidate and repaint are thread-safe — you need not invoke them from the event-dispatching thread. http://docs.oracle.com/javase/tutorial/uiswing/layout/howLayoutWorks.html
repaint();
}
private void updateGame(long elapsedTime) {
updateEntityMovements(elapsedTime);
checkForCollisions();
}
private void checkForCollisions() {
if (entities.get(0).intersects(entities.get(2)) || entities.get(1).intersects(entities.get(2))) {
if (entities.get(2).LEFT) {
entities.get(2).RIGHT = true;
entities.get(2).LEFT = false;
} else {
entities.get(2).LEFT = true;
entities.get(2).RIGHT = false;
}
System.out.println("Intersecting");
} /*
//This is best used when images have transparent and non-transparent pixels (only detects pixel collisions of non-transparent pixels)
if (entities.get(0).checkPerPixelCollision(entities.get(2)) | entities.get(1).checkPerPixelCollision(entities.get(2))) {
if (entities.get(2).LEFT) {
entities.get(2).RIGHT = true;
entities.get(2).LEFT = false;
} else {
entities.get(2).LEFT = true;
entities.get(2).RIGHT = false;
}
System.out.println("Intersecting");
}
*/
}
private void updateEntityMovements(long elapsedTime) {
for (Entity e : entities) {
e.update(elapsedTime);
e.move();
}
}
#Override
protected void paintComponent(Graphics grphcs) {
super.paintComponent(grphcs);
Graphics2D g2d = (Graphics2D) grphcs;
applyRenderHints(g2d);
drawBackground(g2d);
drawEntitiesToScreen(g2d);
drawFpsCounter(g2d);
frameCount++;
}
public static void applyRenderHints(Graphics2D g2d) {
g2d.setRenderingHints(textRenderHints);
g2d.setRenderingHints(imageRenderHints);
g2d.setRenderingHints(colorRenderHints);
g2d.setRenderingHints(interpolationRenderHints);
g2d.setRenderingHints(renderHints);
}
private void drawEntitiesToScreen(Graphics2D g2d) {
for (Entity e : entities) {
if (e.isVisible()) {
g2d.drawImage(e.getCurrentImage(), (int) e.getX(), (int) e.getY(), null);
}
}
}
private void drawFpsCounter(Graphics2D g2d) {
g2d.setColor(Color.WHITE);
g2d.drawString("FPS: " + fps, 5, 10);
}
private void drawBackground(Graphics2D g2d) {
g2d.setColor(Color.BLACK);
g2d.fillRect(0, 0, getWidth(), getHeight());
//thanks to trashgod for lovely testing background :) http://stackoverflow.com/questions/3256269/jtextfields-on-top-of-active-drawing-on-jpanel-threading-problems/3256941#3256941
g2d.setColor(Color.BLACK);
for (int i = 0; i < 128; i++) {
g2d.setColor(new Color(random.nextInt(256), random.nextInt(256), random.nextInt(256)));//random color
g2d.drawLine(getWidth() / 2, getHeight() / 2, random.nextInt(getWidth()), random.nextInt(getHeight()));
}
}
}
This may be a legitimate use case for the low-level access afforded to a KeyListener. See How to Write a Key Listener and KeyEventDemo; don't forget to requestFocusInWindow(). Also consider offering keyboard control for one player and mouse control for the other.
Addendum: #David Kroukamp has adduced an appealing counter-example using key bindings. For reference, the example cited here illustrates one approach to managing key preferences.

Categories