Thread not behaving correctly
- by ivor
Hello, I wonder if anyone can help me to understand where I could be going wrong with this code; Basically I'm working on a turorial and calling the class below from another class - and it is getting the following error;
Exception in thread "Thread-1" java.lang.NullPointerException
at org.newdawn.spaceinvaders.TCPChat.run(TCPChat.java:322)
at java.lang.Thread.run(Unknown Source)
I realise the error is beibg flagged in another class- but I have tested the other class with a small class which sets up a separate thread - and it works fine, but as soon as I try and implement a new thread in this class - it causes all sorts of problems. Am I setting up the thread correctly in this class?
Basically I can set up a thread in this class, with a test loop and it's fine, but when I bring in the functionality of the rest of the game it sometimes hangs, or does not display at all.
Any suggestions on where I could be going wrong would be greatly appreciated.
Thanks for looking.
package org.newdawn.spaceinvaders;
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferStrategy;
import java.util.ArrayList;
import java.util.Scanner;
import java.awt.*;//maybe not needed
import javax.swing.*;//maybenot needed
import java.util.Random;
//import java.io.*;
/**
* The main hook of our game. This class with both act as a manager
* for the display and central mediator for the game logic.
*
* Display management will consist of a loop that cycles round all
* entities in the game asking them to move and then drawing them
* in the appropriate place. With the help of an inner class it
* will also allow the player to control the main ship.
*
* As a mediator it will be informed when entities within our game
* detect events (e.g. alient killed, played died) and will take
* appropriate game actions.
*
* @author Kevin Glass
*/
public class Game extends Canvas implements Runnable{
/** The stragey that allows us to use accelerate page flipping */
private BufferStrategy strategy;
/** True if the game is currently "running", i.e. the game loop is looping */
private boolean gameRunning = true;
/** The list of all the entities that exist in our game */
private ArrayList entities = new ArrayList();
/** The list of entities that need to be removed from the game this loop */
private ArrayList removeList = new ArrayList();
/** The entity representing the player */
private Entity ship;
/** The speed at which the player's ship should move (pixels/sec) */
private double moveSpeed = 300;
/** The time at which last fired a shot */
private long lastFire = 0;
/** The interval between our players shot (ms) */
private long firingInterval = 500;
/** The number of aliens left on the screen */
private int alienCount;
/** The number of levels progressed */
private double levelCount;
/** high score for the user */
private int highScore;
/** high score for the user */
private String player = "bob";
//private GetUserInput getPlayer;
/** The list of entities that need to be removed from the game this loop */
/** The message to display which waiting for a key press */
private String message = "";
/** True if we're holding up game play until a key has been pressed */
private boolean waitingForKeyPress = true;
/** True if the left cursor key is currently pressed */
private boolean leftPressed = false;
/** True if the right cursor key is currently pressed */
private boolean rightPressed = false;
/** True if we are firing */
private boolean firePressed = false;
/** True if game logic needs to be applied this loop, normally as a result of a game event */
private boolean logicRequiredThisLoop = false;
//private Thread cThread = new Thread(this);
//public Thread t = new Thread(this);
//private Thread g = new Thread(this);
void setHighscore(int setHS) {
highScore = setHS;
}
public int getHighscore() {
return highScore;
}
public void setPlayer(String setPlayer) {
player = setPlayer;
}
public String getPlayer() {
return player;
}
public void run() {
//setup();
System.out.println("hello im running bob");
/*int count = 1;
do {
System.out.println("Count is: " + count);
count++;
try{Thread.sleep(1);}
catch(InterruptedException e){}
} while (count <= 2000000);*/
//Game g =new Game();
//Game g = this;
// Start the main game loop, note: this method will not
// return until the game has finished running. Hence we are
// using the actual main thread to run the game.
//setup();
//this.gameLoop();
//try{thread.sleep(1);}
//catch{InterruptedException e}
}
/**
* Construct our game and set it running.
*/
public Game () {
//Thread t = new Thread(this);//set up new thread for invaders game
//t.run();//run the run method of the game
//Game g =new Game();
//setup();
//Thread t = new Thread(this);
//thread.start();
//SwingUtilities.invokeLater(this);
Thread er = new Thread(this);
er.start();
}
public void setup(){
//initialise highscore
setHighscore(0);
// create a frame to contain our game
JFrame container = new JFrame("Space Invaders 101");
// get hold the content of the frame and set up the resolution of the game
JPanel panel = (JPanel) container.getContentPane();
panel.setPreferredSize(new Dimension(800,600));
//panel.setLayout(null);
// setup our canvas size and put it into the content of the frame
setBounds(0,0,800,600);
panel.add(this);
// Tell AWT not to bother repainting our canvas since we're
// going to do that our self in accelerated mode
setIgnoreRepaint(true);
// finally make the window visible
container.pack();
container.setResizable(false);
container.setVisible(true);
// add a listener to respond to the user closing the window. If they
// do we'd like to exit the game
container.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
//cThread.interrupt();
System.exit(0);
}
});
// add a key input system (defined below) to our canvas
// so we can respond to key pressed
addKeyListener(new KeyInputHandler());
// request the focus so key events come to us
requestFocus();
// create the buffering strategy which will allow AWT
// to manage our accelerated graphics
createBufferStrategy(2);
strategy = getBufferStrategy();
// initialise the entities in our game so there's something
// to see at startup
initEntities();
}
/**
* Start a fresh game, this should clear out any old data and
* create a new set.
*/
private void startGame() {
// clear out any existing entities and intialise a new set
entities.clear();
initEntities();
//initialise highscore
setHighscore(0);
// blank out any keyboard settings we might currently have
leftPressed = false;
rightPressed = false;
firePressed = false;
}
/**
* Initialise the starting state of the entities (ship and aliens). Each
* entitiy will be added to the overall list of entities in the game.
*/
//private void initEntities() {
public void initEntities() {
Random randomAlien = new Random();
// create the player ship and place it roughly in the center of the screen
//ship = new ShipEntity(this,"sprites/ship.gif",370,550);//orignal
ship = new ShipEntity(this,"sprites/ship.gif",700,300);//changed postioning to right hand side
entities.add(ship);
// create a block of aliens (5 rows, by 12 aliens, spaced evenly)
alienCount = 0;
levelCount = 1.02;
for (int row=0;row<7;row++) {//altered number of rows
for (int x=0;x<5;x++) {
int r = randomAlien.nextInt(100);//loop added to produce random aliens
if (r < 50){
//Entity alien = new AlienEntity(this,"sprites/alien.gif",/*100+*/(x*50),(50)+row*30);
Entity alien = new AlienEntity(this,"sprites/alien.gif",100+(x*90),(12)+row*85);
entities.add(alien);
alienCount++;
}
}
}
}
//private void initEntities() {
public void initAlienEntities() {
Random randomAlien = new Random();
// create the player ship and place it roughly in the center of the screen
//ship = new ShipEntity(this,"sprites/ship.gif",370,550);//orignal
//ship = new ShipEntity(this,"sprites/ship.gif",700,300);//changed postioning to right hand side
//entities.add(ship);
// create a block of aliens (5 rows, by 12 aliens, spaced evenly)
alienCount = 0;
levelCount = levelCount + 0.10;//this increases the speed on every level
for (int row=0;row<7;row++) {//altered number of rows
for (int x=0;x<5;x++) {
int r = randomAlien.nextInt(100);//loop added to produce random aliens
if (r < 50){//randome check to show alien
//Entity alien = new AlienEntity(this,"sprites/alien.gif",/*100+*/(x*50),(50)+row*30);
Entity alien = new AlienEntity(this,"sprites/alien.gif",-250+(x*90),(12)+row*85);
entities.add(alien);
alienCount++;
}
}
}
advanceAlienSpeed(levelCount);
}
/**
* Notification from a game entity that the logic of the game
* should be run at the next opportunity (normally as a result of some
* game event)
*/
public void updateLogic() {
logicRequiredThisLoop = true;
}
/**
* Remove an entity from the game. The entity removed will
* no longer move or be drawn.
*
* @param entity The entity that should be removed
*/
public void removeEntity(Entity entity) {
removeList.add(entity);
}
/**
* Notification that the player has died.
*/
public void notifyDeath() {
message = "Oh no! They got you, try again?";
waitingForKeyPress = true;
}
/**
* Notification that the player has won since all the aliens
* are dead.
*/
public void notifyWin() {
message = "Well done! You Win!";
waitingForKeyPress = true;
}
/**
* Notification that an alien has been killed
*/
public void notifyAlienKilled() {
// reduce the alient count, if there are none left, the player has won!
alienCount--;
if (alienCount == 0) {
//notifyWin();win not relevant here...
this.initAlienEntities();//call fresh batch of aliens
}
// if there are still some aliens left then they all need to get faster, so
// speed up all the existing aliens
advanceAlienSpeed(1.30);
}
public void advanceAlienSpeed(double speed) {
// if there are still some aliens left then they all need to get faster, so
// speed up all the existing aliens
for (int i=0;i<entities.size();i++) {
Entity entity = (Entity) entities.get(i);
if (entity instanceof AlienEntity) {
// speed up by 2%
entity.setHorizontalMovement(entity.getHorizontalMovement() * speed);
//entity.setVerticalMovement(entity.getVerticalMovement() * 1.02);
}
}
}
/**
* Attempt to fire a shot from the player. Its called "try"
* since we must first check that the player can fire at this
* point, i.e. has he/she waited long enough between shots
*/
public void tryToFire() {
// check that we have waiting long enough to fire
if (System.currentTimeMillis() - lastFire < firingInterval) {
return;
}
// if we waited long enough, create the shot entity, and record the time.
lastFire = System.currentTimeMillis();
ShotEntity shot = new ShotEntity(this,"sprites/shot.gif",ship.getX()+10,ship.getY()-30);
entities.add(shot);
}
/**
* The main game loop. This loop is running during all game
* play as is responsible for the following activities:
* <p>
* - Working out the speed of the game loop to update moves
* - Moving the game entities
* - Drawing the screen contents (entities, text)
* - Updating game events
* - Checking Input
* <p>
*/
public void gameLoop() {
long lastLoopTime = System.currentTimeMillis();
// keep looping round til the game ends
while (gameRunning) {
// work out how long its been since the last update, this
// will be used to calculate how far the entities should
// move this loop
long delta = System.currentTimeMillis() - lastLoopTime;
lastLoopTime = System.currentTimeMillis();
// Get hold of a graphics context for the accelerated
// surface and blank it out
Graphics2D g = (Graphics2D) strategy.getDrawGraphics();
g.setColor(Color.black);
g.fillRect(0,0,800,600);
// cycle round asking each entity to move itself
if (!waitingForKeyPress) {
for (int i=0;i<entities.size();i++) {
Entity entity = (Entity) entities.get(i);
entity.move(delta);
}
}
// cycle round drawing all the entities we have in the game
for (int i=0;i<entities.size();i++) {
Entity entity = (Entity) entities.get(i);
entity.draw(g);
}
// brute force collisions, compare every entity against
// every other entity. If any of them collide notify
// both entities that the collision has occured
for (int p=0;p<entities.size();p++) {
for (int s=p+1;s<entities.size();s++) {
Entity me = (Entity) entities.get(p);
Entity him = (Entity) entities.get(s);
if (me.collidesWith(him)) {
me.collidedWith(him);
him.collidedWith(me);
}
}
}
// remove any entity that has been marked for clear up
entities.removeAll(removeList);
removeList.clear();
// if a game event has indicated that game logic should
// be resolved, cycle round every entity requesting that
// their personal logic should be considered.
if (logicRequiredThisLoop) {
//g.drawString("Press any key",(800-g.getFontMetrics().stringWidth("Press any key"))/2,300);
for (int i=0;i<entities.size();i++) {
Entity entity = (Entity) entities.get(i);
entity.doLogic();
}
logicRequiredThisLoop = false;
}
// if we're waiting for an "any key" press then draw the
// current message
//show highscore at top of screen
//show name at top of screen
g.setColor(Color.white);
g.drawString("Player : "+getPlayer()+" : Score : "+getHighscore(),20,20);
if (waitingForKeyPress) {
g.setColor(Color.white);
g.drawString(message,(800-g.getFontMetrics().stringWidth(message))/2,250);
g.drawString("Press any key",(800-g.getFontMetrics().stringWidth("Press any key"))/2,300);
}
// finally, we've completed drawing so clear up the graphics
// and flip the buffer over
g.dispose();
strategy.show();
// resolve the movement of the ship. First assume the ship
// isn't moving. If either cursor key is pressed then
// update the movement appropraitely
ship.setVerticalMovement(0);//set to vertical movement
if ((leftPressed) && (!rightPressed)) {
ship.setVerticalMovement(-moveSpeed);//**took out setHorizaontalMOvement
} else if ((rightPressed) && (!leftPressed)) {
ship.setVerticalMovement(moveSpeed);//**took out setHorizaontalMOvement
}
// if we're pressing fire, attempt to fire
if (firePressed) {
tryToFire();
}
// finally pause for a bit. Note: this should run us at about
// 100 fps but on windows this might vary each loop due to
// a bad implementation of timer
try { Thread.sleep(10); } catch (Exception e) {}
}
}
/**
* A class to handle keyboard input from the user. The class
* handles both dynamic input during game play, i.e. left/right
* and shoot, and more static type input (i.e. press any key to
* continue)
*
* This has been implemented as an inner class more through
* habbit then anything else. Its perfectly normal to implement
* this as seperate class if slight less convienient.
*
* @author Kevin Glass
*/
private class KeyInputHandler extends KeyAdapter {
/** The number of key presses we've had while waiting for an "any key" press */
private int pressCount = 1;
/**
* Notification from AWT that a key has been pressed. Note that
* a key being pressed is equal to being pushed down but *NOT*
* released. Thats where keyTyped() comes in.
*
* @param e The details of the key that was pressed
*/
public void keyPressed(KeyEvent e) {
// if we're waiting for an "any key" typed then we don't
// want to do anything with just a "press"
if (waitingForKeyPress) {
return;
}
// if (e.getKeyCode() == KeyEvent.VK_LEFT) {
////leftPressed = true;
///}
//// if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
//rightPressed = true;
if (e.getKeyCode() == KeyEvent.VK_UP) {
leftPressed = true;
}
if (e.getKeyCode() == KeyEvent.VK_DOWN) {
rightPressed = true;
}
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
firePressed = true;
}
}
/**
* Notification from AWT that a key has been released.
*
* @param e The details of the key that was released
*/
public void keyReleased(KeyEvent e) {
// if we're waiting for an "any key" typed then we don't
// want to do anything with just a "released"
if (waitingForKeyPress) {
return;
}
if (e.getKeyCode() == KeyEvent.VK_UP) {//changed from VK_LEFT
leftPressed = false;
}
if (e.getKeyCode() == KeyEvent.VK_DOWN) {//changed from VK_RIGHT
rightPressed = false;
}
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
firePressed = false;
}
}
/**
* Notification from AWT that a key has been typed. Note that
* typing a key means to both press and then release it.
*
* @param e The details of the key that was typed.
*/
public void keyTyped(KeyEvent e) {
// if we're waiting for a "any key" type then
// check if we've recieved any recently. We may
// have had a keyType() event from the user releasing
// the shoot or move keys, hence the use of the "pressCount"
// counter.
if (waitingForKeyPress) {
if (pressCount == 1) {
// since we've now recieved our key typed
// event we can mark it as such and start
// our new game
waitingForKeyPress = false;
startGame();
pressCount = 0;
} else {
pressCount++;
}
}
// if we hit escape, then quit the game
if (e.getKeyChar() == 27) {
//cThread.interrupt();
System.exit(0);
}
}
}
/**
* The entry point into the game. We'll simply create an
* instance of class which will start the display and game
* loop.
*
* @param argv The arguments that are passed into our game
*/
//public static void main(String argv[]) {
//Game g =new Game();
// Start the main game loop, note: this method will not
// return until the game has finished running. Hence we are
// using the actual main thread to run the game.
//g.gameLoop();
//}
}