For some reason there are two player speeds in my game Orange Quest. What I mean by this is when I test my game in eclipse and my headphones are off. The game goes twice as fast as when my headphones are on.
I sent the game to my friend and he also experiences the same problems. I've tried to debug the code to see if there was a something wrong with it, but I didn't see any problem. I also tried to adjust the player speed but that didn't help either, as it was still going twice as fast when my headphones were off.
Here is the code for the game panel
Oh yah, by the way the frame is 700 by 700
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.awt.*;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
public class GamePanel extends JPanel implements ActionListener {
/**
*
*/
private static final long serialVersionUID = 1L;
Player player;
boolean running = true;
CopyOnWriteArrayList<Wall> walls = new CopyOnWriteArrayList<>();
CopyOnWriteArrayList<LavaWall> lavaWalls = new CopyOnWriteArrayList<>();
CopyOnWriteArrayList<Enemy> enemies = new CopyOnWriteArrayList<>();
CopyOnWriteArrayList<Orange> oranges = new CopyOnWriteArrayList<>();
java.util.Timer gameTimer;
int cameraX;
int offset;
int count = 0;
int index = 0;
int set;
int s = 50;
int r = 75;
BufferedImage dirt;
BufferedImage grass;
BufferedImage lava;
BufferedImage cloud;
BufferedImage spike;
BufferedImage undergroundLava;
BufferedImage character;
BufferedImage thumbnail;
JLabel timeLabel = new JLabel();
JLabel orangeLabel = new JLabel(": 0/7");
double orangeCount = 0;
int elapsedTime = 0;
int minutes = 0;
int seconds = 0;
int ms = 0;
String minutesString = String.format("%02d", minutes);
String secondsString = String.format("%02d", seconds);
String msString = String.format("%02d", ms);
java.util.Timer timer = new java.util.Timer();
TimerTask task = new TimerTask() {
#Override
public void run() {
elapsedTime += 10;
minutes = (elapsedTime / 60000);
seconds = (elapsedTime / 1000) % 60;
ms = (elapsedTime / 10) % 100;
minutesString = String.format("%02d", minutes);
secondsString = String.format("%02d", seconds);
msString = String.format("%02d", ms);
timeLabel.setText(minutesString + ":" + secondsString + ":" + msString);
}
};
GamePanel() {
if (running) {
player = new Player(400, 300, this);
reset();
setLayout(null);
gameTimer = new java.util.Timer();
add(player.death);
player.death.setBounds(550, 15, 300, 100);
player.death.setFont(new Font("Verdana", Font.PLAIN, 20));
add(orangeLabel);
orangeLabel.setBounds(320, 15, 200, 50);
orangeLabel.setFont(new Font("Verdana", Font.PLAIN, 20));
gameTimer.schedule(new TimerTask() {
#Override
public void run() {
try {
if (walls.get(walls.size() - 1).x < 800) {
offset += 700;
makeWalls(offset);
}
player.set();
for (Wall wall : walls)
wall.set(cameraX);
for (int i = 0; i < walls.size(); i++) {
if (walls.get(i).x < -800)
walls.remove(i);
}for(Enemy enemy : enemies)
enemy.set(cameraX);
for (LavaWall lava : lavaWalls)
lava.set(cameraX);
for (int i = 0; i < lavaWalls.size(); i++) {
if (lavaWalls.get(i).x < -800)
lavaWalls.remove(i);
}
for(Orange orange : oranges)
orange.set(cameraX);
for(int i = 0; i < oranges.size(); i++) {
if(oranges.get(i).x < -800)
oranges.remove(i);
}
repaint();
} catch (Throwable e) {
e.printStackTrace();
}
}
}, 0, 17);
add(timeLabel);
timeLabel.setText(minutesString + ":" + secondsString + ":" + msString);
timeLabel.setBounds(10, 15, 300, 100);
timeLabel.setFont(new Font("Verdana", Font.PLAIN, 25));
timer.scheduleAtFixedRate(task, 0, 10);
} else if (!running) {
System.exit(0);
}
}
public void makeWalls(int offset) {
Random rnd = new Random();
try {
dirt = ImageIO.read(getClass().getResourceAsStream("/art/dirt block.png"));
grass = ImageIO.read(getClass().getResourceAsStream("/art/grass block.png"));
lava = ImageIO.read(getClass().getResourceAsStream("/art/lava block.png"));
cloud = ImageIO.read(getClass().getResourceAsStream("/art/cloud block.png"));
undergroundLava = ImageIO.read(getClass().getResourceAsStream("/art/underground lava.png"));
thumbnail = ImageIO.read(getClass().getResourceAsStream("/art/thumbnail.png"));
} catch (Exception e) {
e.printStackTrace();
}
if (count == 7) {
oranges.add(new Orange(offset + 50, 500, s, s, thumbnail));
for (int i = 1; i <= 14; i++) {
walls.add(new Wall(offset + i * 50, 600, s, s, grass));
walls.add(new Wall(offset + i * 50, 650, s, s, dirt));
}
}
else if(count != 10) {
if (index == 0) {
for (int i = 1; i <= 14; i++) {
walls.add(new Wall(offset + i * 50, 600, s, s, grass));
walls.add(new Wall(offset + i * 50, 650, s, s, dirt));
if(i == 6) {
enemies.add(new Enemy(offset + 350, 550, r, s));
}
}
set = 0;
} else if (index == 1) {
for (int j = 1; j <= 14; j++) {
for (int i = 650; i >= 350; i -= 50) {
if (i == 350)
walls.add(new Wall(offset, i, s, s, grass));
else
walls.add(new Wall(offset, i, s, s, dirt));
}
offset += 50;
set = 1;
}
} else if (index == 2) {
if(set == 0) {
walls.add(new Wall(offset - 50, 500, s, s, cloud));
walls.add(new Wall(offset - 200, 400, s, s, cloud));
walls.add(new Wall(offset, 300, s, s, cloud));
for (int i = 0; i < 9; i++) {
walls.add(new Wall(offset + 300 + i * 50, 600, s, s, grass));
walls.add(new Wall(offset + 300 + i * 50, 650, s, s, dirt));
}
}else {
for (int i = 1; i <= 14; i++) {
walls.add(new Wall(offset + i * 50, 600, s, s, grass));
walls.add(new Wall(offset + i * 50, 650, s, s, dirt));
if(i == 6) {
enemies.add(new Enemy(offset + 350, 550, r, s));
}
}
}
} else if (index == 3) {
for (int i = 1; i <= 2; i++) {
walls.add(new Wall(offset + i * 50, 600, s, s, grass));
walls.add(new Wall(offset + i * 50, 650, s, s, dirt));
}
lavaWalls.add(new LavaWall(offset + 150, 600, s, s, lava));
lavaWalls.add(new LavaWall(offset + 200, 600, s, s, lava));
lavaWalls.add(new LavaWall(offset + 250, 600, s, s, lava));
lavaWalls.add(new LavaWall(offset + 300, 600, s, s, lava));
lavaWalls.add(new LavaWall(offset + 350, 600, s, s, lava));
lavaWalls.add(new LavaWall(offset + 150, 650, s, s, undergroundLava));
lavaWalls.add(new LavaWall(offset + 200, 650, s, s, undergroundLava));
lavaWalls.add(new LavaWall(offset + 250, 650, s, s, undergroundLava));
lavaWalls.add(new LavaWall(offset + 300, 650, s, s, undergroundLava));
lavaWalls.add(new LavaWall(offset + 350, 650, s, s, undergroundLava));
for (int i = 6; i <= 12; i++) {
walls.add(new Wall(offset + 100 + i * 50, 600, s, s, grass));
walls.add(new Wall(offset + 100 + i * 50, 650, s, s, dirt));
}
}
for(Wall wall : walls) {
for(LavaWall lava : lavaWalls) {
if(lava.hitBox.getBounds() == wall.hitBox.getBounds()) {
lavaWalls.remove(lava);
}
}
}
count++;
index = rnd.nextInt(4);
}
}
public void reset() {
count = 0;
index = 0;
player.x = 200;
player.y = 150;
cameraX = 150;
player.xspeed = 0;
player.yspeed = 0;
walls.clear();
enemies.clear();
lavaWalls.clear();
oranges.clear();
offset = -150;
makeWalls(offset);
player.isDed = false;
}
public void paint(Graphics g) {
try {
super.paint(g);
Graphics2D gtd = (Graphics2D) g;
player.draw(gtd);
for(int i = 0; i < walls.size(); i++) {
try {
walls.get(i).draw(gtd);
} catch (Throwable e) {
}
}for(Enemy enemy : enemies)
enemy.draw(gtd);
for(LavaWall lava : lavaWalls)
lava.draw(gtd);
g.drawImage(thumbnail, 270, 15, 50, 50, null);
for(Orange orange : oranges)
orange.draw(gtd);
} catch (Throwable e) {
e.printStackTrace();
}
}
#Override
public void actionPerformed(ActionEvent e) {
}
public void keyPressed(KeyEvent e) {
if (e.getKeyChar() == 'a')
player.keyLeft = true;
if (e.getKeyChar() == 'w' || e.getKeyCode() == 32)
player.keyUp = true;
if (e.getKeyChar() == 's')
player.keyDown = true;
if (e.getKeyChar() == 'd')
player.keyRight = true;
if (e.getKeyChar() == 'h')
Player.keyGlide = true;
if (e.getKeyChar() == 'j')
player.keyMega = true;
if (e.getKeyChar() == 'l')
player.keyRush = true;
}
public void keyReleased(KeyEvent e) {
if (e.getKeyChar() == 'a')
player.keyLeft = false;
if (e.getKeyChar() == 'w' || e.getKeyCode() == 32)
player.keyUp = false;
if (e.getKeyChar() == 's')
player.keyDown = false;
if (e.getKeyChar() == 'd')
player.keyRight = false;
if (e.getKeyCode() == 27)
System.exit(0);
if (e.getKeyChar() == 'h')
Player.keyGlide = false;
if (e.getKeyChar() == 'j')
player.keyMega = false;
if (e.getKeyChar() == 'l')
player.keyRush = false;
}
}
and here is the player code
import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.swing.*;
public class Player {
GamePanel panel;
int x;
int y;
int width;
int height;
double xspeed;
double yspeed;
boolean keyLeft;
boolean keyRight;
boolean keyUp;
static boolean keyGlide;
boolean keyDown;
boolean keyMega;
boolean keyRush;
boolean falling;
boolean isDeathSound;
boolean isDed;
Graphics2D gtd;
long time = 0;
Timer timer;
long start;
long stop;
double deaths = 0;
JLabel death = new JLabel("Deaths: 0");
Rectangle hitBox;
long waitTime;
static Clip clip;
static boolean onGround;
Player(){
}
Player(int x, int y, GamePanel panel){
this.panel = panel;
this.x = x;
this.y = y;
width = 50;
height = 100;
hitBox = new Rectangle(x, y, width, height);
}
public void set() throws Throwable{
Iterator<Orange> orangeItr = panel.oranges.iterator();
if(keyLeft && keyRight || !keyLeft && !keyRight) xspeed *= 0.8;
else if(keyLeft && !keyRight)xspeed--;
else if(keyRight && !keyLeft)xspeed++;
if(keyUp && keyGlide) {keyUp = false;}
if(yspeed > 0)onGround = false;
if(keyMega && keyGlide) {keyMega = false;}
if(onGround && keyGlide)keyGlide = false;
if(xspeed > 0 && xspeed < 0.75)xspeed = 0;
if(xspeed < 0 && xspeed > -0.75)xspeed = 0;
if(xspeed > 7 && !keyRush)xspeed = 7;
if(xspeed < -7 && !keyRush)xspeed = -7;
if(xspeed > 12 && keyRush)xspeed = 12;
if(xspeed < -12 && keyRush)xspeed = -12;
if(yspeed < -12.75) yspeed = -12.75;
if(keyUp && onGround) {
jumpSound();
hitBox.y++;
onGround = false;
for(Wall wall : panel.walls) {
if(wall.hitBox.intersects(hitBox)) {
yspeed = -8;
}
}hitBox.y--;
}if(keyMega && onGround) {
megaSound();
onGround = false;
hitBox.y++;
for(Wall wall : panel.walls) {
if(wall.hitBox.intersects(hitBox)) yspeed = -12.75;
}hitBox.y--;
}
yspeed += 0.3;
if(keyGlide && !onGround && yspeed > 1.2) {
glideSound();
yspeed -= 0.25;
}if(keyDown && !onGround) {
yspeed += 0.4;
}
//horizontal collision
hitBox.x += xspeed;
for(Wall wall : panel.walls) {
if(hitBox.intersects(wall.hitBox)) {
hitBox.x -= xspeed;
while(!wall.hitBox.intersects(hitBox))hitBox.x += Math.signum(xspeed);
hitBox.x -= Math.signum(xspeed);
panel.cameraX += x - hitBox.x;
xspeed = 0;
hitBox.x = x;
}
}
for(Orange orange : panel.oranges){
isDed = false;
if(hitBox.intersects(orange.hitBox)) {
isDed = false;
if(panel.orangeCount != 6) {
isDed = false;
keyGlide = false;
hitBox.x -= xspeed;
while(!orange.hitBox.intersects(hitBox))hitBox.x += Math.signum(xspeed);
hitBox.x -= Math.signum(xspeed);
panel.cameraX += x - hitBox.x;
xspeed = 0;
x = hitBox.x;
panel.orangeCount++;
panel.reset();
panel.orangeLabel.setText(": " +(int)panel.orangeCount + "/7");
onGround = true;
isDed = false;
deathSound();
}
else {
keyGlide = false;
hitBox.x -= xspeed;
while(!orange.hitBox.intersects(hitBox))hitBox.x += Math.signum(xspeed);
hitBox.x -= Math.signum(xspeed);
xspeed = 0;
x = hitBox.x;
onGround = true;
panel.orangeCount++;
panel.orangeLabel.setText(": " + (int)panel.orangeCount + "/7");
orangeSound();
System.out.println("Well done");
Thread.sleep(1100);
System.out.print("your time is ");
System.out.println(panel.timeLabel.getText());
System.out.println("you died " + (int)deaths + " time(s)");
System.exit(0);
}
}
}
//vertical collision
hitBox.y += yspeed;
for(Wall wall : panel.walls) {
if(hitBox.intersects(wall.hitBox)) {
keyGlide = false;
hitBox.y -= yspeed;
while(!wall.hitBox.intersects(hitBox))hitBox.y += Math.signum(yspeed);
hitBox.y -= Math.signum(yspeed);
yspeed = 0;
y = hitBox.y;
onGround = true;
}
}
try {
for(LavaWall lava : panel.lavaWalls) {
if(hitBox.intersects(lava.hitBox)) {
isDed = true;
isDeathSound = true;
keyGlide = false;
hitBox.y -= yspeed;
while(!lava.hitBox.intersects(hitBox))hitBox.y += Math.signum(yspeed);
hitBox.y -= Math.signum(yspeed);
yspeed = 0;
y = hitBox.y;
onGround = true;
isDed = true;
die();
}
}
for(Orange orange : panel.oranges){
isDed = false;
if(hitBox.intersects(orange.hitBox)) {
isDed = false;
if(!(panel.orangeCount == 7)) {
keyGlide = false;
hitBox.y -= yspeed;
while(!orange.hitBox.intersects(hitBox))hitBox.y += Math.signum(yspeed);
hitBox.y -= Math.signum(yspeed);
yspeed = 0;
y = hitBox.y;
onGround = true;
panel.orangeCount++;
panel.reset();
panel.orangeLabel.setText(": " +(int)panel.orangeCount + "/7");
isDed = false;
deathSound();
}
else if(panel.orangeCount == 7) {
keyGlide = false;
hitBox.y -= yspeed;
while(!orange.hitBox.intersects(hitBox))hitBox.y += Math.signum(yspeed);
hitBox.y -= Math.signum(yspeed);
yspeed = 0;
y = hitBox.y;
onGround = true;
panel.orangeCount++;
panel.orangeLabel.setText(": " + (int)panel.orangeCount + "/7");
orangeSound();
System.out.println("Well done");
Thread.sleep(1100);
System.out.print("your time is ");
System.out.println(panel.timeLabel.getText());
System.out.println("you died " + (int)deaths + " time(s)");
Thread.sleep(4500);
System.out.println("press v to exit");
}
}
}
}catch(Throwable e) {
}
panel.cameraX -= xspeed;
y += yspeed;
hitBox.x = x;
hitBox.y = y;
//death code
if(y > 800) {
isDed = true;
onGround = true;
die();
}enemyScript();
}void enemyScript() {
for(Enemy enemy : panel.enemies) {
if(hitBox.intersects(enemy.hitBox)) {
isDed = true;
isDeathSound = true;
keyGlide = false;
hitBox.y -= yspeed;
while(!enemy.hitBox.intersects(hitBox))hitBox.y += Math.signum(yspeed);
hitBox.y -= Math.signum(yspeed);
yspeed = 0;
y = hitBox.y;
onGround = true;
isDed = true;
die();
}
}
}
void jumpSound(){
soundEffect("res\\musicAndSoundEffects\\jump.wav");
}void megaSound(){
soundEffect("res\\musicAndSoundEffects\\megajump.wav");
}void glideSound(){
soundEffect("res\\musicAndSoundEffects\\shorterGlide.wav");
}void deathSound(){
if(isDed) {
soundEffect("res\\musicAndSoundEffects\\death.wav");
}else if(!isDed) {
soundEffect("res\\musicAndSoundEffects\\orangeSound.wav");
}
}void notDie(){
stopSound();
}
void orangeSound(){
soundEffect("res\\musicAndSoundEffects\\orangeSound.wav");
}void stopSound(){
clip.stop();
}
void die() {
deathSound();
panel.reset();
deaths++;
death.setText("Deaths: " + (int)deaths);
onGround = true;
}
static void soundEffect(String filePath) {
try {
File file = new File(filePath);
AudioInputStream sound = AudioSystem.getAudioInputStream(file);
clip = AudioSystem.getClip();
if(onGround && !filePath.equals("res\\musicAndSoundEffects\\shorterGlide.wav")) {
clip.open(sound);
clip.setFramePosition(0);
clip.start();
}else if(filePath.equals("res\\musicAndSoundEffects\\shorterGlide.wav")) {
clip.open(sound);
clip.setFramePosition(0);
clip.start();
}
}catch(Exception e) {
JOptionPane.showMessageDialog(null, e.getStackTrace());
}
}
public void draw(Graphics2D g) {
g.setColor(Color.BLACK);
g.fillRect(x, y, width, height);
}
}
for anyone who knows the answer to this. Please comment the problem and not the answer so that I can learn. thanks
In broad strokes:
Because you're using a java.util.Timer and it is just the wrong tool for the job.
Let's say you want a game to run smoothly, but not slam the CPU of even the most powerful machine imaginable, and also to run as well as it can if the system it runs on is insufficient (that last one is important; even if a system is plenty powerful to run the game, sometimes you get a 'blip' of underperformance because of other stuff the computer was doing, such as when you plug in a USB stick and all sorts of driver shenanigans occurs in the background).
To accomplish these things, you need two crucial properties:
Timing related to the gameplay (such as, if one of the players is moving northbound, the amount of movement that occurs) should be based entirely on 'time passed', and be unaffected by the amount of time required to actually redraw parts of the screen, nor should it be affected by how many 'loops' of screen refresh you manage to squeeze into a single second.
Timing related to rendering needs to go fast as possible, but needs a breaking system: BEFORE you begin the task of 'update game state then render the state to the screen', you should calculate the timing at which that should all be done. Then, once you are done with one loop of the game (adjust game state and rendering all that), you check whether you 'beat the clock'. If you did, THEN wait. In other words, instead of asking a timer to wait a fixed amount of time before doing the next game loop, the amount of time to wait needs to take into account you took some time to adjust the game state and render things.
Here's some example code of a basic game loop. Just use it for inspiration, it doesn't fit verbatim into the code you already have.
class Example {
private static final long NANOS_PER_MILLI =
TimeUnit.MILLISECONDS.toNanos(1);
private static final long NANOS_PER_FRAME =
TimeUnit.SECONDS.toNanos(1) / 60;
private static final long ADJUST_EXPECTATIONS_TRESHOLD =
NANOS_PER_FRAME * 5;
private long start = System.nanoTime();
private long frameCounter = 0;
public void run() {
while (running) {
gameLoop();
}
}
void gameLoop() {
frameCounter++;
long target = startTime + NANOS_PER_FRAME * frameCounter;
// target now holds the exact time at which point we should
// be done with this game loop.
recalcGameState();
redrawStuff();
long end = System.nanoTime();
long delta = target - System.nanoTime();
if (delta > 0) {
// We had time to spare!
// Let's sleep most of that away, giving us
// a little bit of leeway (5msec worth) in case the system is
// going to blip on us.
// Also, CPU waiting is generally granularized at millis,
// so let's convert to millis first, and be 5 millis 'early'
long millisToWait = (delta / NANOS_PER_MILLI) - 5;
if (millisToWait > 0) sleep(millisToWait);
else if (-delta > READJUST_EXPECTATIONS_TRESHOLD) {
// Rats - we missed the boat, and by a lot!
// we are over 5 frames behind, let's readjust
// expectations and slow down the game logic, too:
start = end;
frameCounter = 0;
}
}
}
}
The above code will reliably render at 60 gameloops every second, (that 5msec leeway will adjust itself on the next loop, as that would just mean you get there even faster and thus wait longer) - and will 'catch up' on a blip for at most 5 frames (catching up a ton of frames after a long blip is usually not a good idea, so we limit the catchup mechanism).
Usually when you're forced into readjustment you may want to adjust the game logic's clocks as well (it is a bit unfair for a player to slam into a wall that was way ahead of them because their CPU just decided to doze off for a second, unlikely as that is, whilst they couldn't steer or even see anything!) - so, have a ball with gameStart.
NB: nanoTime is used and not System.currentTimeMillis(), because System.CTM represents the system clock, which means it will drastically change when the user resets their time - modern computers update their clocks by way of a network time server, so this happens all the time (heh), and means that looking at it for frame timing is a bad idea. From time to time the game will do mad glitching when the NTP daemon adjusted the clocks. nanoTime is more or less reflecting 'CPU uptime' and does not get modified when the system clock is adjusted.
NB2: If you need 'frameCounter' to represent game turns, you can't just reset it like this; instead, figure out how many frames you want to adjust by, and then add that many NANOS_PER_FRAME to 'start' instead.
Related
For my class I need to create a one player pong game. We need to use what we've learned so its simple code and the problem is, while the "while loop" is running, my Keydown thread doesn't seem to work. My teacher isn't helpful at all so that's why i'm here. Please help by only using methods and such that are used in my program.
I've tried moving the ball code into a run thread but i can never seem to actually get that thread running.
public class PongGame extends Applet {
int startup = 0;
int x = 400;
int y = 500;
int x2 = (int) (Math.random() * (600 + 1));
int y2 = (int) (Math.random() * (600 + 1));
int xVelocity = 1;
int yVelocity = 1;
int x2Velocity = 1;
int y2Velocity = 1;
int curry = 700;
int currx = 600;
int counter = 2;
#Override
public void init() {
setSize(1440, 900);
setBackground(Color.black);
setFont(new Font("Helvetica", Font.BOLD, 36));
}
#Override
public boolean keyDown(Event evt, int key) {
startup = startup + 1;
repaint();
if (key == Event.RIGHT) {
currx += 10;
}
if (key == Event.LEFT) {
currx -= 10;
}
if (currx == 1400) {
currx = 10;
}
if (currx == 20) {
currx = 1399;
}
repaint();
return false;
}
public void run(Graphics g) {
startup = 0;
while (counter > 0) {
try {
Thread.sleep(30);
} catch (InterruptedException e) {
}
x += xVelocity;
y += yVelocity;
g.setColor(Color.blue);
g.fillRect(currx, curry, 300, 25);
g.setColor(Color.red);
g.fillOval(x, y, 30, 30);
for (int j = 0; j < 20000000; j++);
g.setColor(Color.black);
g.fillOval(x, y, 30, 30);
//Bounce the ball off the wall or paddle
if (x >= 1400 || x <= 0) {
xVelocity = -xVelocity;
}
if (y >= 800 || y <= 0) {
yVelocity = -yVelocity;
}
if (((y == 675)) && ((currx <= x) && (currx + 300) >= x)) {
yVelocity = -yVelocity;
y = y - 10;
repaint();
}
}
}
public void paint(Graphics g) {
if (startup == 0) {
g.setColor(Color.white);
g.drawString("Welcome To PONG", 0, 100);
g.drawString("Created by Caden Anton", 0, 200);
g.drawString("copyright May 3rd, 2019 ©", 0, 300);
g.drawString("Press any key to continue", 0, 400);
}
if (startup == 1) {
g.setColor(Color.white);
g.drawString("RULES:", 0, 100);
g.drawString("Press the arrwow keys to move the paddle", 0, 200);
g.drawString("Your objective is to keep the ball from touching the ground", 0, 300);
g.drawString("Once the ball hits the ground you lose the game", 0, 400);
}
if (startup >= 2) {
//Input Run thread start here.
}
}
}
I'm trying to write my first game in Java. I followed some tutorials and learned how to load and update a background using a Canvas and how to load and move a player sprite. I did these two separately and they worked fine, but when I put the two together and try to move the player, the game slows down to the point that it is unplayable. This only happens when I hold down an arrow key to move the player; the game actually runs "smoothly" if I rapidly tap the arrow key. After quite a bit of testing, I'm convinced that the problem occurs when the background is redrawn each frame. Any other improvements would also be appreciated.
Code (All of it):
Game.Java:
package Game;
import Level.Level;
import Player.Player;
import Sprites.SpriteSheetLoader;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import javax.swing.JFrame;
public class Game extends Canvas implements Runnable {
// Set dimensions of the game.
public static final int HEIGHT = 320;
public static final int WIDTH = 480;
public static final int SCALE = 2;
public static Dimension GAME_DIM = new Dimension(WIDTH * SCALE, HEIGHT * SCALE);
private BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
private int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
public SpriteSheetLoader loader;
public Screen screen;
public Level level;
public InputHandler input = new InputHandler(this);
public Player player = new Player();
private boolean running = false;
private boolean moving = true;
private int FPS = 60;
private long targetTime = 1000 / FPS;
// Set character's starting position at the center. I have no idea why I had to add the "- 50" to each value.
public int x = GAME_DIM.width / 2 - 50;
public int y = GAME_DIM.height / 2 - 50;
public int xScroll = 0;
public int yScroll = 0;
public int col = 0;
public int row = 0;
public int ticks = 0;
public int frame = 0;
public static void main(String[] args) {
Game game = new Game();
game.setPreferredSize(new Dimension(GAME_DIM));
game.setMaximumSize(new Dimension(GAME_DIM));
game.setMinimumSize(new Dimension(GAME_DIM));
JFrame frame = new JFrame("Valkyrie Game");
frame.add(game);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setResizable(true);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
game.start();
}
public void start() {
running = true;
new Thread(this).start();
}
public Game() {
}
public void init() {
loader = new SpriteSheetLoader();
screen = new Screen(WIDTH, HEIGHT);
level = new Level(16, 16);
}
public void run() {
init();
long start, elapsed, wait;
while (running) {
start = System.nanoTime();
render();
tick();
elapsed = System.nanoTime() - start;
//System.out.println("Elapsed: " + elapsed);
wait = targetTime - elapsed / 1000000;
if(wait < 0) {
wait = 5;
}
try {
Thread.sleep(wait);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void stop() {
running = false;
}
public void tick() {
// Movement
if (input.right) {
xScroll++;
player.setAnimation(player.walkRight);
//x++;
row = 2;
ticks++;
if(ticks < 10) {
frame = 1;
} else if(ticks == 10) {
frame = 2;
} else if(ticks == 20) {
frame = 3;
} else if(ticks == 30) {
frame = 2;
} else if(ticks == 40) {
frame = 1;
} else if(ticks == 50) {
ticks = 0;
frame = 0;
}
moving = true;
} else if (input.left) {
xScroll--;
player.setAnimation(player.walkLeft);
//x--;
row = 1;
ticks++;
if(ticks < 10) {
frame = 1;
} else if(ticks == 10) {
frame = 2;
} else if(ticks == 20) {
frame = 3;
} else if(ticks == 30) {
frame = 2;
} else if(ticks == 40) {
frame = 1;
} else if(ticks == 50) {
ticks = 0;
frame = 0;
}
moving = true;
} else if (input.up) {
yScroll--;
player.setAnimation(player.walkUp);
//y--;
row = 3;
ticks++;
if(ticks < 10) {
frame = 1;
} else if(ticks == 10) {
frame = 2;
} else if(ticks == 20) {
frame = 3;
} else if(ticks == 30) {
frame = 2;
} else if(ticks == 40) {
frame = 1;
} else if(ticks == 50) {
ticks = 0;
frame = 0;
}
moving = true;
} else if (input.down) {
yScroll++;
player.setAnimation(player.walkDown);
//y++;
row = 0;
ticks++;
if(ticks < 10) {
frame = 1;
} else if(ticks == 10) {
frame = 2;
} else if(ticks == 20) {
frame = 3;
} else if(ticks == 30) {
frame = 2;
} else if(ticks == 40) {
frame = 1;
} else if(ticks == 50) {
ticks = 0;
frame = 0;
}
moving = true;
}
if (!input.down && !input.left && !input.right && !input.up) {
player.setAnimation(player.stand);
frame = 0;
ticks = 1;
moving = false;
}
//System.out.println("Tick: " + ticks);
}
public void render() {
BufferStrategy bs = getBufferStrategy();
if (bs == null) {
createBufferStrategy(3);
requestFocus();
return;
}
do {
Graphics g = bs.getDrawGraphics();
try {
for (int i = 0; i < ticks; i++) {
g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
g.drawImage(player.Player(frame, row), x, y, null);
level.renderBackground(xScroll, yScroll, screen);
for (int y = 0; y < this.screen.h; y++) {
for (int x = 0; x < screen.w; x++) {
pixels[x + (y * WIDTH)] = screen.pixels[x + (y * screen.w)];
}
}
}
} finally {
g.dispose();
}
bs.show();
this.update(bs.getDrawGraphics());
} while (bs.contentsLost());
// Graphics g = bs.getDrawGraphics();
//
// g.dispose();
// bs.show();
}
}
InputHandler.Java:
package Game;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class InputHandler implements KeyListener {
public boolean up = false;
public boolean down = false;
public boolean left = false;
public boolean right = false;
public InputHandler(Game game) {
game.addKeyListener(this);
}
public void toggle(KeyEvent ke, boolean pressed) {
int keyCode = ke.getKeyCode();
if(keyCode == KeyEvent.VK_UP) up = pressed;
if(keyCode == KeyEvent.VK_DOWN) down = pressed;
if(keyCode == KeyEvent.VK_LEFT) left = pressed;
if(keyCode == KeyEvent.VK_RIGHT) right = pressed;
}
public void keyTyped(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
toggle(e, true);
}
public void keyReleased(KeyEvent e) {
toggle(e, false);
}
}
Screen.Java:
package Game;
import Sprites.Sprite;
public class Screen {
public int w, h;
int xOffset = 0;
int yOffset = 0;
public int[] pixels;
public Screen(int w, int h) {
this.w = w; // 480
this.h = h; // 320
pixels = new int[w * h]; // 153600
}
public void renderSprite(int xPos, int yPos, Sprite sprite) {
int height = sprite.h;
int width = sprite.w;
xPos -= xOffset;
yPos -= yOffset;
for(int y = 0; y < height; y++) {
if(yPos + y < 0 || yPos + y >= h) continue;
for(int x = 0; x < width; x++) {
if(xPos + x < 0 || xPos + x >= w) continue;
int col = sprite.pixels[x + (y * height)];
if(col != -65281 && col < 0) pixels[(x + xPos) + (y + yPos) *w]= col;
}
}
}
public void setOffs(int xOffs, int yOffs) {
xOffset = xOffs;
yOffset = yOffs;
}
}
Level.Java:
package Level;
import Game.Screen;
import Sprites.Sprite;
import Sprites.Sprites;
import Tiles.Tile;
public class Level {
int w, h;
public int[] tiles;
public Level(int w, int h) {
this.w = w;
this.h = h;
tiles = new int[w * h];
loadMap(0, 0, 0, 0);
}
public void renderBackground(int xScroll, int yScroll, Screen screen) {
int xo = xScroll >> 4;
int yo = yScroll >> 4;
int w = (screen.w + 15) >> 4;
int h = (screen.h + 15) >> 4;
screen.setOffs(xScroll, yScroll);
for(int y = yo; y <= h + yo; y++) {
for(int x = xo; x <= w + xo; x++) {
getTile(x, y).render(x, y, screen);
}
}
screen.setOffs(0, 0);
}
public Tile getTile(int x, int y) {
if(x < 0 || y < 0 || x >= w || y >= h) return Tile.rockTile;
return Tile.tiles[tiles[x + y * w]];
}
public void loadMap(int x0, int y0, int x1, int y1) {
Sprite sprite = Sprites.level[x0][y0];
for(int y = 0; y < sprite.h; y++) {
for(int x = 0; x < sprite.w; x++) {
if(sprite.pixels[x + y * sprite.h] == -9276814) {
tiles[x + x1 + (y + y1) * h] = Tile.rockTile.id;
} else {
tiles[x + x1 + (y + y1) * h] = Tile.grassTile.id;
}
}
}
}
}
Player.Java:
package Player;
import Animation.Animation;
import Sprites.Sprite;
import java.awt.image.BufferedImage;
public class Player {
// Images for each animation
private BufferedImage[] walkingLeft = {Sprite.getSprite(0, 1), Sprite.getSprite(1, 1), Sprite.getSprite(2, 1)}; // Gets the upper left images of my sprite sheet
private BufferedImage[] walkingRight = {Sprite.getSprite(0, 2), Sprite.getSprite(1, 2), Sprite.getSprite(2, 2)};
private BufferedImage[] walkingUp = {Sprite.getSprite(0, 3), Sprite.getSprite(1, 3), Sprite.getSprite(2, 3)};
private BufferedImage[] walkingDown = {Sprite.getSprite(0, 0), Sprite.getSprite(1, 0), Sprite.getSprite(2, 0)};
private BufferedImage[] standing = {Sprite.getSprite(1, 0)};
// These are animation states.
public Animation walkLeft = new Animation(walkingLeft, 10);
public Animation walkRight = new Animation(walkingRight, 10);
public Animation walkUp = new Animation(walkingUp, 10);
public Animation walkDown = new Animation(walkingDown, 10);
public Animation stand = new Animation(standing, 10);
// This is the actual animation
public Animation animation = stand;
public BufferedImage Player(int x, int y) {
BufferedImage player = Sprite.getSprite(x, y);
return player;
}
public void update() {
animation.update();
}
public void render() {
}
public void setAnimation(Animation animation) {
this.animation = animation;
}
}
Sprite.Java:
package Sprites;
import Game.Game;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Sprite {
public int w, h;
public int[] pixels;
public static BufferedImage sprite = null;
public Sprite(int w, int h) {
this.w = w;
this.h = h;
pixels = new int[w * h];
}
public void clear(int color) {
for(int i = 0; i < pixels.length; i++) {
pixels[i] = color;
}
}
private static BufferedImage spriteSheet;
private static final int TILE_SIZE = 80;
public static BufferedImage loadSprite() {
try {
sprite = ImageIO.read(Game.class.getResource("/valkyrie.png"));
} catch (IOException e) {
e.printStackTrace();
}
return sprite;
}
public static BufferedImage getSprite(int xGrid, int yGrid) {
if(spriteSheet == null) {
spriteSheet = loadSprite();
}
// xGrid and yGrid refer to each individual sprite
return spriteSheet.getSubimage(xGrid * TILE_SIZE, yGrid * TILE_SIZE, TILE_SIZE, TILE_SIZE);
}
}
This is going to require double buffering. Any game with a lot going on needs double buffering.
How do you double buffer in java for a game?
Although I couldn't go through the code completely, it seems you do not do double buffering which affect performance drastically.
Try this in the relevant part of your program:
JFrame frame = new JFrame("Valkyrie Game");
frame.add(game);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setResizable(true);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setDoubleBuffered(true); //added line, rest is the same
game.start();
You really should use Timer. It will solve all your problems.
Every tick, you redraw all what you need.
And every tick, you should just check, which keys are pressed and which are not, instead of adding listeners. To keep tracking this, you always have to remember the keys pressed "before".
You can even create two Timers, one for graphic redraw and one for game logic.
Even timers can be delayed or something, the usual approach is to find out, how much time elapsed (System.nanoTime for example) and count how much of game logic you should forward to keep game always unlaggy and fluent.
I am making a tile based platformer game in java. I render a map which is stored in a 2 dimensional array but when this array is very big my game starts to become slow. I realised that I had to only render the part of the map that is viewable, I tried to do that but i wrote very hacky code that only worked partly so I removed it. How can I do this properly? Here is my code (without the hacky stuff). Also how could I use System.nanoTime() rather than System.currentTimeMillis()?
package sexy_robot_from_another_dimension;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.TexturePaint;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class Game extends JPanel
{
int playerX = 50;
int playerY = 50;
static boolean up = false;
static boolean down = false;
static boolean right = false;
static boolean left = false;
int playerSpeed = 1;
String[][] map;
int blockSize = 20;
int jumpLoop = 0;
int maxJumpLoop = 280;
static BufferedImage block, player;
int playerWidth = 20;
int playerHeight = 35;
int cameraX = 0;
int cameraY = 0;
long nextSecond = System.currentTimeMillis() + 1000;
int frameInLastSecond = 0;
int framesInCurrentSecond = 0;
public Game()
{
super();
try
{
map = load("/maps/map1.txt");
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
Timer timer = new Timer();
TimerTask task = new TimerTask()
{
#Override
public void run()
{
if(up)
{
if((!playerIsOnBlock(playerX, playerY).equals("0")) || (!playerIsOnBlock(playerX + (playerWidth - 1), playerY).equals("0")))
{
timeToJump();
}
}
if(down)
{
}
if(right)
{
if((playerIsLeftBlock(playerX, playerY).equals("0")) && (playerIsLeftBlock(playerX, playerY + (playerHeight/2 - 1)).equals("0")) && (playerIsLeftBlock(playerX, playerY + (playerHeight - 1)).equals("0")))
{
playerX += playerSpeed;
}
}
if(left)
{
if((playerIsRightBlock(playerX, playerY).equals("0")) && (playerIsRightBlock(playerX, playerY + (playerHeight/2 - 1)).equals("0")) && (playerIsRightBlock(playerX, playerY + (playerHeight - 1)).equals("0")))
{
playerX -= playerSpeed;
}
}
repaint();
}
};
timer.scheduleAtFixedRate(task, 0, 10);
Timer timerGrav = new Timer();
TimerTask taskGrav = new TimerTask()
{
#Override
public void run()
{
if((playerIsOnBlock(playerX, playerY).equals("0")) && (playerIsOnBlock(playerX + (playerWidth - 1), playerY).equals("0")))
{
playerY += playerSpeed;
repaint();
}
}
};
timerGrav.scheduleAtFixedRate(taskGrav, 0, 6);
}
void timeToJump()
{
if(jumpLoop == 0)
{
jumpLoop = 1;
Timer timer = new Timer();
TimerTask task = new TimerTask()
{
#Override
public void run()
{
if((playerIsBelowBlock(playerX, playerY).equals("0")) && (playerIsBelowBlock(playerX + (playerWidth - 1), playerY).equals("0")))
{
playerY -= playerSpeed;
jumpLoop++;
repaint();
}
else
{
jumpLoop = maxJumpLoop;
}
if(jumpLoop == maxJumpLoop)
{
jumpLoop = 0;
cancel();
}
}
};
timer.scheduleAtFixedRate(task, 0, 3);
}
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
long currentTime = System.currentTimeMillis();
if (currentTime > nextSecond)
{
nextSecond += 1000;
frameInLastSecond = framesInCurrentSecond;
framesInCurrentSecond = 0;
}
framesInCurrentSecond++;
g.drawString(frameInLastSecond + " fps", 10, 20);
cameraX = -playerX + getWidth()/2;
cameraY = -playerY + getHeight()/2;
g.translate(cameraX, cameraY);
for (int x = 0; x < map.length; x++)
{
for (int y = 0; y < map[0].length; y++)
{
switch(map[x][y])
{
case "0":
break;
case "1":
if(block != null)
{
TexturePaint tp0 = new TexturePaint(block, new Rectangle(0, 0, blockSize, blockSize));
g2.setPaint(tp0);
}
g.fillRect(y*blockSize, x*blockSize, 20, 20);
break;
}
}
}
g.setColor(Color.BLACK);
if(player != null)
{
TexturePaint tp0 = new TexturePaint(player, new Rectangle(playerX, playerY, playerWidth, playerHeight));
g2.setPaint(tp0);
}
g.fillRect(playerX, playerY, playerWidth, playerHeight);
g.setColor(Color.black);
g.setFont(new Font("Droid Sans Mono", Font.PLAIN, 12));
g.drawString("Sexy!", playerX - 5, playerY - 10);
}
boolean outOfMap(int x, int y)
{
y -= blockSize - 1;
x -= blockSize - 1;
if((y/blockSize <= map.length - 2) && (y/blockSize >= 0) && (x/blockSize <= map[0].length-2) && (x/blockSize >= 0))
{
return false;
}
return true;
}
String playerIsOnBlock(int x, int y)
{
y += playerHeight;
if(!outOfMap(x, y))
{
if(map[y/blockSize][x/blockSize] != "0")
{
return map[y/blockSize][x/blockSize];
}
}
return "0";
}
String playerIsBelowBlock(int x, int y)
{
y -= playerSpeed;
if(!outOfMap(x, y))
{
if(map[y/blockSize][x/blockSize] != "0")
{
return map[y/blockSize][x/blockSize];
}
}
return "0";
}
String playerIsLeftBlock(int x, int y)
{
x += playerWidth;
if(!outOfMap(x, y))
{
if(map[y/blockSize][x/blockSize] != "0")
{
return map[y/blockSize][x/blockSize];
}
}
return "0";
}
String playerIsRightBlock(int x, int y)
{
x -= playerSpeed;
if(!outOfMap(x, y))
{
if(map[y/blockSize][x/blockSize] != "0")
{
return map[y/blockSize][x/blockSize];
}
}
return "0";
}
String[][] load(String file) throws IOException
{
BufferedReader br = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream(file)));
int lines = 1;
int length = br.readLine().split(" ").length;
while (br.readLine() != null) lines++;
br.close();
br = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream(file)));
String[][] map = new String[lines][length];
for (int i = 0; i < lines; i++)
{
String line = br.readLine();
String[] parts = line.split(" ");
for (int y = 0; y < length; y++)
{
map[i][y] = parts[y];
}
}
br.close();
return map;
}
}
Thank you!
It seems your camera is centered on the player, then there are two ways of doing this, I like the first way, it is a bit cleaner:
1th: Create a rectangle that bounds your cameras view, and check if the map x,y is within this view, render only if true.
Rectangle cameraView = new Rectangle(playerX - getWidth() / 2, playerY - getHeight() / 2, getWidth(), getHeight());
for (int x = 0; x < map.length; x++) {
for (int y = 0; y < map[0].length; y++) {
if (!cameraView.contains(x*blockSize, y*blockSize))
continue;
switch (map[x][y]) {
case "0":
break;
case "1":
if (block != null) {
TexturePaint tp0 = new TexturePaint(block, new Rectangle(0, 0, blockSize, blockSize));
g2.setPaint(tp0);
}
g.fillRect(y * blockSize, x * blockSize, 20, 20);
break;
}
}
}
The second option is to simply calculate the distance to the center of the screen (playerX,playerY) from each map[x][y] and skip all map[x][y] that falls outside your viewing bounds, this is a bit uglier to code and I really don't recommend this, the rectangle option above should be fast enough.
Edit:
#JasonC That is true, I didn't consider for instance when an x value is definitely outside the view, it will still go into the y loop through all the y values. One can simply create a dummy variable in the x-loop and do the following check
for (int x = 0; x < map.length; x++) {
int dummyY = playerY
if(!cameraView.contains(x,dummyY))
continue;
....
//rest of code ommitted
Another optimization you can do is considering not setting a TexturePaint (expensive operation) but instead simply drawing the image of the block:
g.fillRect(y * blockSize, x * blockSize, 20, 20);
Replaced with
g.drawImage(block, y*blockSize, x*blockSize, null);
The same with the playerimage.
Set the clipping region to the visible area with Graphics.setClip(), that will prevent most rendering operations from taking effect outside that region.
For drawing operations where this isn't sufficient (perhaps you also want to avoid doing calculations or something for objects outside the clipping area), test your objects bounds against the clipping rectangle and skip the object if it doesn't intersect.
See Graphics.setClip().
A further optimization can be done by, for example, calculating the range of blocks on your map that is definitely outside of the visible area, and excluding that from your for loop (no sense testing blocks against the clipping region if you know they are outside already). Take the clipping region, transform it to map index coordinates, then you will know where in your map the visible area is and you can just iterate over that subsection of the map.
I hope this makes sense, but I'm trying to make a snake-type game in Java and if you press two directions at the same time/ too fast the snake goes on top of itself making you lose.
For example if you you're going downwards, and hit right then up very fast, you get the snake going straight up on the same column and killing itself, but it should go right one then up one. If anyone can help me that'd be great, thanks!
package tk.sketchistgames.Snake;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.IOException;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Board extends JPanel implements ActionListener{
/**
* Main graphical area for Snake
*/
private static final long serialVersionUID = 4085437479211945011L;
private final int WIDTH = 600;
private final int HEIGHT = 600;
private final int DOT_SIZE = 10;
private final int ALL_DOTS = 1200;
private final int RAND_POS = 59;
public static int DELAY = 90;
private int x[] = new int[ALL_DOTS];
private int y[] = new int[ALL_DOTS];
private int dots, food_x, food_y, pdown_x, pdown_y, rdouble_x, rdouble_y, powerUp_x, powerUp_y, half_x, half_y;
private boolean left = false;
private boolean right = true;
private boolean up = false;
private boolean down = false;
private boolean inGame = true;
private int score = 0;
private int fruitEaten = 0;
private boolean Bonus = false;
private boolean RDouble = false;
private boolean bpower = false;
private boolean halfpower = false;
private Timer timer;
private Image food;
private Image head;
private Image body;
private Image pdown;
private Image rdouble;
private Image powerUp;
private Image half;
public Board() {
addKeyListener(new TAdapter());
setBackground(Color.decode("0x3F919E"));
ImageIcon iid = new ImageIcon(this.getClass().getResource("/images/body.png"));
body = iid.getImage();
ImageIcon iia = new ImageIcon(this.getClass().getResource("/images/food.png"));
food = iia.getImage();
ImageIcon iih = new ImageIcon(this.getClass().getResource("/images/head.png"));
head = iih.getImage();
ImageIcon iipd = new ImageIcon(this.getClass().getResource("/images/pdown.png"));
pdown = iipd.getImage();
ImageIcon iird = new ImageIcon(this.getClass().getResource("/images/pup2.png"));
rdouble = iird.getImage();
ImageIcon iipu1 = new ImageIcon(this.getClass().getResource("/images/pup1.png"));
powerUp = iipu1.getImage();
ImageIcon iihd = new ImageIcon(this.getClass().getResource("/images/halfDown.png"));
half = iihd.getImage();
setFocusable(true);
initGame();
}
public void initGame() {
dots = 5;
for (int z = 0; z < dots; z++) {
x[z] = 50 - z*10;
y[z] = 50;
}
locateFood();
timer = new Timer(DELAY, this);
timer.start();
}
public void checkApple() throws UnsupportedAudioFileException, IOException, LineUnavailableException {
if ((x[0] == pdown_x) && (y[0] == pdown_y)){
dots -= 1;
score -= 50;
AudioInputStream audioIn = AudioSystem.getAudioInputStream(Board.class.getResource("/sounds/powerdown.wav"));
Clip clip = AudioSystem.getClip();
clip.open(audioIn);
clip.start();
bpower = false;
}
if ((x[0] == half_x) && (y[0] == half_y)){
dots = dots /2;
score = score /2;
AudioInputStream audioIn = AudioSystem.getAudioInputStream(Board.class.getResource("/sounds/powerdown.wav"));
Clip clip = AudioSystem.getClip();
clip.open(audioIn);
clip.start();
clip.start();
halfpower = false;
}
if ((x[0] == powerUp_x) && (y[0] == powerUp_y)){
dots += 4;
score += 100;
AudioInputStream audioIn = AudioSystem.getAudioInputStream(Board.class.getResource("/sounds/powerup1.wav"));
Clip clip = AudioSystem.getClip();
clip.open(audioIn);
clip.start();
Bonus = false;
}
if ((x[0] == rdouble_x) && (y[0] == rdouble_y)){
dots = dots * 2;
score += 1000;
AudioInputStream audioIn = AudioSystem.getAudioInputStream(Board.class.getResource("/sounds/powerup2.wav"));
Clip clip = AudioSystem.getClip();
clip.open(audioIn);
clip.start();
RDouble = false;
}
if ((x[0] == food_x) && (y[0] == food_y)) {
dots++;
long r = Math.round(Math.random() * 10);
if(r == 4){
locatePowerUp();
Bonus = true;
}
long half = Math.round(Math.random() * 175);
System.out.println(half);
if(half == 89){
locateHalfDown();
halfpower = true;
}
long rdouble = Math.round(Math.random() * 100);
if(rdouble == 50){
locateDoubleUp();
RDouble = true;
}
long badpower = Math.round(Math.random() * 25);
if(badpower == 25 || badpower == 20 || badpower == 15|| badpower == 10 || badpower == 5|| badpower == 0){
locatePowerDown();
bpower = true;
}
score += (50 + fruitEaten);
AudioInputStream audioIn = AudioSystem.getAudioInputStream(Board.class.getResource("/sounds/eat.wav"));
Clip clip = AudioSystem.getClip();
clip.open(audioIn);
clip.start();
fruitEaten++;
locateFood();
}
}
public void paint(Graphics g) {
super.paint(g);
if (inGame) {
if(halfpower){
g.drawImage(half, half_x, half_y, this);
}
if(Bonus){
g.drawImage(powerUp, powerUp_x, powerUp_y, this);
}
if(RDouble){
g.drawImage(rdouble, rdouble_x, rdouble_y, this);
}
if(dots <= 0) gameOver(g);
g.setColor(Color.white);
Font small1 = new Font("arcadepix", Font.PLAIN, 20);
g.setFont(small1);
g.drawString("Score: " + score + " Food Eaten: " + fruitEaten + " Length: " + dots, 15, 15);
g.drawImage(food, food_x, food_y, this);
if(bpower){
g.drawImage(pdown, pdown_x, pdown_y, this);
}
for (int z = 0; z < dots; z++) {
if (z == 0)
g.drawImage(head, x[z], y[z], this);
else g.drawImage(body, x[z], y[z], this);
}
if(Menu.pause){
g.drawString("Paused! 'P' To unpause!", 20, 100);
for (int z = 0; z < dots; z++) {
x[z] = 50 - z*10;
y[z] = 50;
}
}
Toolkit.getDefaultToolkit().sync();
g.dispose();
}else{
gameOver(g);
}
}
public void gameOver(Graphics g) {
if(dots >= 300){
String msg = "You won!";
Font small = new Font("arcadepix", Font.PLAIN, 20);
FontMetrics metr = this.getFontMetrics(small);
g.setColor(Color.white);
g.setFont(small);
g.drawString(msg, (WIDTH - metr.stringWidth(msg)) / 2, HEIGHT / 2);
g.drawString("Total Score: " + score +"!", (WIDTH - metr.stringWidth(msg)) /2 - 12, (HEIGHT / 2) - 18);
g.drawString("Total Food Eaten: " + dots + "!", (WIDTH - metr.stringWidth(msg)) /2 - 72, (HEIGHT / 2) - 38);
g.drawString("Press Space to play again!", (WIDTH - metr.stringWidth(msg)) /2 - 77, (HEIGHT / 2) + 18);
setBackground(Color.red);
}else{
String msg = "Game Over";
Font small = new Font("arcadepix", Font.PLAIN, 20);
FontMetrics metr = this.getFontMetrics(small);
g.setColor(Color.white);
g.setFont(small);
g.drawString(msg, (WIDTH - metr.stringWidth(msg)) / 2, HEIGHT / 2);
g.drawString("Total Score: " + score, (WIDTH - metr.stringWidth(msg)) /2 - 12, (HEIGHT / 2) - 18);
g.drawString("Press Space to Continue", (WIDTH - metr.stringWidth(msg)) /2 - 77, (HEIGHT / 2) + 18);
setBackground(Color.decode("0x3F919E"));
}
}
public void move() {
for (int z = dots; z > 0; z--) {
x[z] = x[(z - 1)];
y[z] = y[(z - 1)];
}
if (left) {
x[0] -= DOT_SIZE;
}
if (right) {
x[0] += DOT_SIZE;
}
if (up) {
y[0] -= DOT_SIZE;
}
if (down) {
y[0] += DOT_SIZE;
}
}
public void checkCollision() {
for (int z = dots; z > 0; z--) {
if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) {
inGame = false;
}
}
if (y[0] > HEIGHT) {
inGame = false;
}
if (y[0] < 0) {
inGame = false;
}
if (x[0] > WIDTH) {
inGame = false;
}
if (x[0] < 0) {
inGame = false;
}
}
public void locateFood() {
int r = (int) (Math.random() * RAND_POS);
food_x = ((r * DOT_SIZE));
r = (int) (Math.random() * RAND_POS);
food_y = ((r * DOT_SIZE));
}
public void locatePowerDown() {
int r = (int) (Math.random() * RAND_POS);
pdown_x = ((r * DOT_SIZE));
r = (int) (Math.random() * RAND_POS);
pdown_y = ((r * DOT_SIZE));
}
public void locateDoubleUp() {
int r = (int) (Math.random() * RAND_POS);
rdouble_x= ((r * DOT_SIZE));
r = (int) (Math.random() * RAND_POS);
rdouble_y = ((r * DOT_SIZE));
}
public void locatePowerUp() {
int r = (int) (Math.random() * RAND_POS);
powerUp_x= ((r * DOT_SIZE));
r = (int) (Math.random() * RAND_POS);
powerUp_y = ((r * DOT_SIZE));
}
public void locateHalfDown() {
int r = (int) (Math.random() * RAND_POS);
half_x= ((r * DOT_SIZE));
r = (int) (Math.random() * RAND_POS);
half_y = ((r * DOT_SIZE));
}
public void actionPerformed(ActionEvent e) {
if (inGame) {
if(Menu.pause){
}
try {
checkApple();
} catch (UnsupportedAudioFileException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
} catch (LineUnavailableException e1) {
e1.printStackTrace();
}
checkCollision();
move();
}
repaint();
}
public void reset(){
left = false;
right = true;
up = false;
down = false;
inGame = true;
score = 0;
fruitEaten = 0;
for (int z = 0; z < dots; z++) {
x[z] = 50 - z*10;
y[z] = 50;
}
dots = 5;
bpower = false;
locatePowerDown();
RDouble = false;
locateDoubleUp();
locatePowerUp();
locateFood();
repaint();
}
private class TAdapter extends KeyAdapter {
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if(key == KeyEvent.VK_SPACE){
if(inGame){
if(Menu.pause){
Menu.pause = false;
}else if(!Menu.pause){
Menu.pause = true;
}
}
if(!inGame){
reset();
}
}
if(key == KeyEvent.VK_P){
if(Menu.pause){
Menu.pause = false;
}else if(!Menu.pause){
Menu.pause = true;
}
}
if ((key == KeyEvent.VK_LEFT || key == KeyEvent.VK_A) && (!right)) {
left = true;
up = false;
down = false;
}
if ((key == KeyEvent.VK_RIGHT ||key == KeyEvent.VK_D) && (!left)) {
right = true;
up = false;
down = false;
}
if ((key == KeyEvent.VK_UP || key == KeyEvent.VK_W) && (!down)) {
up = true;
right = false;
left = false;
}
if ((key == KeyEvent.VK_DOWN || key == KeyEvent.VK_S) && (!up)) {
down = true;
right = false;
left = false;
}
}
}
}
Based on your description, your code is changing the boolean variables before the move occurs. A simple solution would be to store all moves in a Queue, and process them by removing them, that way you ensure you won't overwrite a move.
In other words, every time you record a key event that would change the booleans, store some sort of signal (an int, a String, an enum, etc.) in a Queue, and in your move method, simply remove the signal from the front of the Queue and process it like you process the boolean variables. If you were to use an enum for UP DOWN RIGHT LEFT it would be fairly readable and you could use a switch-case to process each movement.
ex.
switch (movement) {
case UP: /* up code */ break;
case LEFT: /* left code */ break;
case RIGHT: /* right code */ break;
case DOWN: /* down code */ break;
}
where movement is the signal you removed from the queue, and UP DOWN RIGHT LEFT are enum's (for that matter they could be int constants, but as Bloch recommends in Effective Java, prefer enum types to int constants.)
private enum Movement { UP, DOWN, RIGHT, LEFT }
This allows you to refer to these types in the switch above, and instantiate the Queue as follows:
Queue<Movement> movementQueue = new ArrayDeque<Movement>();
Which in turn means you can add whichever movement you need simply by doing the following:
movementQueue.offer(UP); // or DOWN or RIGHT or LEFT, whichever you want.
And when you're ready to use them, access them as follows:
Movement movement = movementQueue.poll();
For more info on Queues: http://docs.oracle.com/javase/6/docs/api/java/util/Queue.html
For more info on enums: http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html
Also because you said you're new to Java, I'd recommend Head First: Java for an overhead view, and Effective Java to learn a good amount of best practices.
So I'm trying to implement collision detection in my game and for some reason the collision isnt working properly.
public class ArenaKeys extends KeyAdapter {
arenaScreenBuild arena;
int xPos = 0, playerFace = 4;
int xPPos = 200, yPPos = 150;
int pX = 40, pY = 30;
AttackAshe aAtk = new AttackAshe();
int[][] mask = new int[400][92];
#SuppressWarnings("static-access")
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();// Get key pressed
if (keyCode == e.VK_RIGHT) {
playerFace = 4;
xPos += 5;
pX = (xPos + xPPos) / 5;
if (checkBoundary(pX, pY) == (false))
xPos -= 5;
} else if (keyCode == e.VK_LEFT) {
playerFace = 3;
xPos -= 5;
pX = (xPos + xPPos) / 5;
if (checkBoundary(pX, pY) == (false))
xPos += 5;
} else if (keyCode == e.VK_UP) {
playerFace = 2;
yPPos -= 5;
pY = yPPos / 5;
if (checkBoundary(pX, pY) == (false))
yPPos += 5;
} else if (keyCode == e.VK_DOWN) {
playerFace = 1;
yPPos += 5;
pY = yPPos / 5;
if (checkBoundary(pX, pY) == (false))
yPPos -= 5;
}
if (keyCode == e.VK_SPACE) {
aAtk.regArrow(arena.xPosition(), arena.yPosition());
arena.shoot(playerFace);
arena.xArrow = xPPos;
arena.yArrow = yPPos;
} else if (keyCode == e.VK_ESCAPE)
System.exit(0);
arena.moveArena(xPos);
arena.turnPlayer(playerFace);
arena.updateScreen(xPPos, yPPos);
}
public boolean checkBoundary(int x, int y) {
Rectangle t1 = new Rectangle(Turret.x, Turret.y, Turret.WIDTH,
Turret.HEIGHT);
Rectangle p = new Rectangle(pX, pY, Player.WIDTH, Player.HEIGHT);
if (t1.intersects(p))
// if (mask[x][y] == 0)
return false;
else
return true;
}
public static class Turret {
static int x = 168;
static int y = 40;
static final int WIDTH = 50;
static final int HEIGHT = 50;
}
public static class Player {
static final int WIDTH = 25;
static final int HEIGHT = 25;
}
public ArenaKeys(arenaScreenBuild arena) throws Exception {
this.arena = arena;
}
}
Approximately 20 units before the actual turret, the sprite stops being able to move any further. The sprite cannot go above or below the turret even if you go really high or really low.
What seems to be going wrong is that the sprite is colliding into the turret rectangle too early but I don't understand how that it possible. I draw the turret exactly 50 wide, 50 high at 168,40. The player is moving so it's x, y is different everytime but it's dimensions are 25 wide and 25 high.
The original turret is 126x111 approximately but I draw it as 50x50
25x25
public class arenaScreenBuild extends JPanel implements ActionListener {
String picPath = "pictures/";
String[] fileName = { "stageBridge.png", "turret.png", "Ashe.png",
"regArrow.png", "arenaScreen.png" };
ClassLoader cl = arenaScreenBuild.class.getClassLoader();
URL imgURL[] = new URL[5];
Toolkit tk = Toolkit.getDefaultToolkit();
Image imgBG, imgTurret, imgPlayer, imgRegArrow, imgBorder;
Boolean[] ptFunc = new Boolean[3];
int PLAYER_INITIAL_X = 200, PLAYER_INITIAL_Y = 150;
int xPos = 0, xPFace = 150, yPFace = 0;
int xPPos = 200, yPPos = 150;
int xVal, yVal, xArrow, yArrow, xTemp, yTemp;
Timer space;
int counter, facePosition = 1;
public arenaScreenBuild() throws Exception {
for (int x = 0; x < 5; x++) {
imgURL[x] = cl.getResource(picPath + fileName[x]);
}
imgBG = tk.createImage(imgURL[0]);
imgTurret = tk.createImage(imgURL[1]);
imgPlayer = tk.createImage(imgURL[2]);
imgRegArrow = tk.createImage(imgURL[3]);
imgBorder = tk.createImage(imgURL[4]);
for (int x = 0; x < 3; x++)
ptFunc[x] = true;
space = new Timer(100, this);
}
public void updateScreen() {
repaint();
}
public void moveArena(int x) {
xPos = x;
}
public void updateScreen(int x, int y) {
xPPos = x;
yPPos = y;
repaint();
}
public boolean arrow() {
return (true);
}
public void turnPlayer(int face) {
if (face == 4) {
xPFace = 150;
} else if (face == 3) {
xPFace = 100;
} else if (face == 2) {
xPFace = 50;
} else if (face == 1) {
xPFace = 0;
}
if (yPFace == 50)
yPFace = 0;
else if (yPFace == 0)
yPFace = 50;
}
public void paintComponent(Graphics g) {
g.drawImage(imgBG, 10, 30, 610, 490, xPos, 0, xPos + 600, 460, this);
g.drawImage(imgTurret, 850 - xPos, 200, 950 - xPos, 300, 0, 0, 126,
110, this);
g.drawImage(imgTurret, 1350 - xPos, 200, 1450 - xPos, 300, 0, 0, 126,
110, this);
g.drawImage(imgPlayer, xPPos, yPPos, 50 + (xPPos),
50 + (yPPos), xPFace, yPFace, xPFace + 50, yPFace + 50, this);
if (counter <= 5000 && counter > 0)
g.drawImage(imgRegArrow, xArrow, yArrow, this);
g.drawImage(imgBorder, 0, 0, 620, 600, 0, 0, 620, 600, this);
g.setColor(Color.WHITE);
g.drawString("x:" + (xPPos + xPos), 535, 525);
g.drawString("y:" + yPPos, 535, 545);
}
public int xPosition() {
xVal = xPPos + xPos;
return (xVal);
}
public int yPosition() {
yVal = yPPos;
return (yVal);
}
public void shoot(int i) {
facePosition = i;
xTemp = xPosition();
yTemp = yPosition();
space.start();
}
public void actionPerformed(ActionEvent e) {
counter++;
if (facePosition == 4) {
if (counter <= 5000) {
xArrow += 50;
}
}
else if (facePosition == 3) {
if (counter <= 5000) {
xArrow -= 50;
}
}
else if (facePosition == 2) {
if (counter <= 5000) {
yArrow -= 50;
}
}
else if (facePosition == 1) {
if (counter <= 5000) {
yArrow += 50;
}
}
if (xArrow == (xTemp + 100)) {
counter = 0;
space.stop();
}
updateScreen();
}
}
Turns out that my values for the x position were wrong. Also due to my previous boundary code, there were still remenants of it which messed me up therefore not allowing me to see the problem sooner.
For any one looking for how to make the collision detection boundary, just make 2 rectangles and use the
Rectangle rect1 = new Rectangle(topLeftX, topLeftY, rectangleWidth,rectangleHeight);
Rectangle rect2 = new Rectangle(topLeftX, topLeftY, rectangleWidth, rectangleHeight);
if (rect1.intersects(rect2))
//enter code to do if it intersects here