Character States and Animation

Hi, I’m ChouChou, I’ve been lurking around this great forum for a while and this is my first post.

I have a question regarding the character states and animation, I’ ve read some great posts from
http://www.java-gaming.org/index.php/topic,21788.0.html
and
http://www.java-gaming.org/index.php/topic,16829.0.html

I’m doing a side scroller game like rockmanX4(using Zero’s light saber as weapon) and hopefully the control/animation will be smooth like it in the future, but I felt like I coded my character states and animation class wrong as I need a bunch of if statements just to get things working. Following is the code and I’m seeking advice on how to improve my state/animation system to make things better, thanks in advance!

P.S I will post additional code if required.

States Class


public class States {
	/** Character actions */
	public static final int IDLE = 0;
	public static final int RUNNING = 1;
	public static final int JUMPING = 2;
	public static final int HIT = 3;
	public static final int ATTACKING = 4;
	
	/** Directoin player is facing */
	public static final int LEFT = -1;
	public static final int RIGHT = -2;
}

Animation Class

public class Animation {
	private BufferedImage img;
	private int frameDuration = 60;
	private int frameNumberY, frameNumberX, frameX, frameY, charWidth, charHeight, sheetWidth, sheetHeight, playStyle;
	private long lastFrameChange;
	private long lastLoopTime = System.currentTimeMillis();
	/** Boolean to check if an animation has finished playing */
	private boolean isFinished = false;
	
	/** Play animation once */
	public static final int PLAY_ONCE = 0;
	/** Loop animation */
	public static final int LOOPING = 1;
	
	/** Entity's animation current state */
	private int currentState = States.IDLE;
	/** Current playing animation state */
	private int animState = States.IDLE;
	
	
	
	
	
	/** Creates animation for entity*/
	public void createAnimation(BufferedImage img, int x, int y, int offsetX, int offsetY, int charWidth, int charHeight,
			 int sheetWidth, int sheetHeight, int frameDuration, int startFrame, int playStyle) {
	
		if(currentState > animState || currentState == States.IDLE) {
			animState = currentState;
			frameNumberX = 0;
			lastFrameChange = 0;
			this.img = img;
			this.charWidth = charWidth;
			this.charHeight = charHeight;
			this.sheetWidth = sheetWidth;
			this.sheetHeight = sheetHeight;
			this.frameDuration = frameDuration;
			this.playStyle = playStyle;
		}
	}
	 
	public void update() {
		if(currentState == animState && !isFinished) {
			/** Difference between current time and last loop time */
			long delta = System.currentTimeMillis() - lastLoopTime;
			
			/** Last loop time */
			lastLoopTime = System.currentTimeMillis();
			
			/** Variable to check if frame > duration */
			lastFrameChange += delta;
			
			/** Change frame */
			if (lastFrameChange > frameDuration) {
				/** Reset frame */
				lastFrameChange = 0;
				
				/** update the frame X */
				frameNumberX++;
				if (frameNumberX >= sheetWidth) {
					if(playStyle == PLAY_ONCE) {
						isFinished = true;
						currentState = States.IDLE;
						animState = States.IDLE;
					} else if(playStyle == LOOPING) {
						frameNumberX = 0;
					}
				}
				
				/** update the frame Y */
				frameNumberY++;
				if (frameNumberY >= sheetHeight) {
					frameNumberY = 0;
				}
			}
			/** Current frame X */
			frameX = (frameNumberX % sheetWidth) * charWidth;
			
			/** Current frame Y */
			frameY = (frameNumberY % sheetHeight) * charHeight;
		}
		
		/** When animation finished change states to IDLE*/
		if(isFinished) {
			isFinished = false;
		}
	}
	
	/** Get current image */
	public BufferedImage getCurrentImage() {
		return img.getSubimage(frameX, frameY, charWidth, charHeight);
	}
	
	/** Set playing animation state */
	public int getAnimState() {
		return animState;
	}
	
	/** Get current entity animation state */
	public int getCurrentState() {
		return currentState;
	}
	
	/** Set entity animation state */
	public void setCurrentState(int state) {
		this.currentState = state;
	}
	
	/** Set entity animation state */
	public void setAnimState(int state) {
		this.animState = state;
	}
	
	/** Return if animation has finished playing or not */
	public boolean isFinished() {
		return isFinished;
	}
	
	public void setFinished(boolean isFin) {
		isFinished = isFin;
	}
}

Part of the Player class

int key = -1;
	/** Update player */
	public void update(long elapsedTime) {
		float dx = 0, dy = 0;
		
		
		/** Set player states */
		setPlayerState(dx, dy);
		
		/** Check player collision with enemies */
		enemyCollision();
		
		/** Change player status */
		changeStatus(elapsedTime);
		
		/** check if player is falling */
		fallCheck();
		
		/** Make player jump */
		jump();
		
		
		/** Offset X for player */
		offsetX = (int)((GameCore.mapPosX + GameCore.fixX)* Map.TILE_SIZE);
		/** Offset Y for player */
		offsetY = (int)((GameCore.mapPosY + GameCore.fixY) * Map.TILE_SIZE);
		
		/** Current pos of player*/
		xp = (int) (Map.TILE_SIZE * px);
		yp = (int) (Map.TILE_SIZE * py);
		
		/** Update animation */
		updateAnim();
	}
	
	/** Changes player status */
	long totalDuration = 0;
	private void changeStatus(long elapsedTime) {
		if(status == Status.INVINCIBLE) {
			totalDuration += elapsedTime;
			if(totalDuration > 1000) {
				status = Status.NORMAL;
				totalDuration = 0;
			}
		}
	}

	/** Makes player jump */
	private void jump() {
		/** Check if player is jumping */
		if(jumping) {
			idle = false;
			/** Increase gravity */
			jumpDistance += gravity;
			//jumping up
			if(jumpDistance < 0 && space) {
				if(validLocation(px, py - moveSPeed)) {
					moveY(-moveSPeed);
				} else {
					space = false;
				}
			// Falling down
			} else if(jumpDistance > 0 || !space) {
				moveY(moveSPeed);
			}
			anim.setCurrentState(States.JUMPING);
		} 
	}

	/** Check if player is falling */
	private void fallCheck() {
		if(!jumping) {
			if(validLocation(px, py + moveSPeed)) {
				jumpDistance = 0;
				jumping = true;
			} 
		}
	}
	
	/** Set player state */
	private void setPlayerState(float dx, float dy) {
		/** Player getting hit */
		if(hit) {
			if(status != Status.INVINCIBLE) {
				idle = false;
				left = false;
				right = false;
				jumping = false;
				hp -= 1;
				anim.setCurrentState(States.HIT);
				status = Status.INVINCIBLE;
			}
			hit = false;
		}
		
		/** Player attacking */
		if(attacking) {
			if(States.ATTACKING > anim.getCurrentState()) {
				idle = false;
				anim.setCurrentState(States.ATTACKING);
			}
//			attacking = false;
		}
		
		/** Player jumping*/
		if(space && !jumping) {
			if(anim.getAnimState() == States.ATTACKING || anim.getAnimState() == States.HIT) {
			} else {
				idle = false;
				jumping = true;
				jumpDistance = maxJumpHeight;
			}
		}
		
		/** Player moving left */
		if(left) {
			idle = false;
			faceDir = States.LEFT;
			if(anim.getAnimState() == States.ATTACKING || anim.getAnimState() == States.HIT) {
				/** When hit while running, don't change state */
			} else if(jumping) {
				dx = -moveSPeed;
			} else {
				if(States.RUNNING >= anim.getCurrentState()) {
					anim.setCurrentState(States.RUNNING);
					dx = -moveSPeed;
				} else if(attacking) {
				} else {
					idle = true;
				}
			}
		} 
		
		/** Player moving right */
		if(right) {
			idle = false;
			faceDir = States.RIGHT;
			/** When hit while running, don't change state */
			if(anim.getAnimState() == States.ATTACKING || anim.getAnimState() == States.HIT) {
			} else if(jumping) {
				dx = moveSPeed;
			} else {
				if(States.RUNNING >= anim.getCurrentState()) {
					anim.setCurrentState(States.RUNNING);
					dx = moveSPeed;
				} else if(attacking) {
				} else {
					idle = true;
				}
			}
		}
		
		/** Player Standing */
		if(idle) {
			anim.setCurrentState(States.IDLE);
		}
		
		/** Move player and shift map in the opposite direction */
		if(moveX(dx)) {
		}
	}
	
	public void keyPressed(KeyEvent e) {
		/** Attacking */
		if (e.getKeyCode() == KeyEvent.VK_C && !pressAttack) {
			attacking = true;
			pressAttack = true;
		}
		/** Jumping */
		if (e.getKeyCode() == KeyEvent.VK_SPACE && !jumping && !attacking && !pressSpace) {
			space = true;
			pressSpace = true;
		}
		/** Left */
		if (e.getKeyCode() == KeyEvent.VK_LEFT) {
			left = true;
		}
		/** Right */
		if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
			right = true;
		}
		/** Up */
		if (e.getKeyCode() == KeyEvent.VK_UP) {
		}
		/** Down */
		if (e.getKeyCode() == KeyEvent.VK_DOWN) {
		}
	}
	
	public void keyReleased(KeyEvent e) {
		/** Moving left */
		if (e.getKeyCode() == KeyEvent.VK_LEFT) {
			left = false;
			idle = true;
		}
		/** Moving left */
		if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
			right = false;
			idle = true;
		}
		if (e.getKeyCode() == KeyEvent.VK_UP) {
		}
		if (e.getKeyCode() == KeyEvent.VK_DOWN) {
		}
		/** Jumping */
		if(e.getKeyCode() == KeyEvent.VK_SPACE) {
			space = false;
			pressSpace = false;
		}
		/** Attacking */
		if (e.getKeyCode() == KeyEvent.VK_C) {
			attacking = false;
			pressAttack = false;
		}
	}
	
	
	/** Update animation */
	private void updateAnim() {
		if(anim.getCurrentState() == States.HIT) {
			if(faceDir == States.LEFT) {
				anim.createAnimation(img.getPlayerAnim()[img.GET_HIT_L], xp, yp, offsetX, offsetY, 56, 53, 6, 1, 80, 0, Animation.PLAY_ONCE);
			} else {
				anim.createAnimation(img.getPlayerAnim()[img.GET_HIT_R], xp, yp, offsetX, offsetY, 56, 53, 6, 1, 80, 0, Animation.PLAY_ONCE);
			}
		} else if(anim.getCurrentState() == States.ATTACKING) {
			if(faceDir == States.LEFT) {
				anim.createAnimation(img.getPlayerAnim()[img.ATTACK_L], xp, yp, offsetX, offsetY - 20, 103, 71, 5, 1, 60, 0, Animation.PLAY_ONCE);
			} else {
				anim.createAnimation(img.getPlayerAnim()[img.ATTACK_R], xp, yp, offsetX, offsetY - 20, 103, 71, 5, 1, 60, 0, Animation.PLAY_ONCE);
			}
		} else if(anim.getCurrentState() == States.RUNNING) {
			if(faceDir == States.LEFT) {
				anim.createAnimation(img.getPlayerAnim()[img.RUN_L], xp, yp, offsetX, offsetY, 54, 45, 15, 1, 60, 0, Animation.PLAY_ONCE);
			} else {
				anim.createAnimation(img.getPlayerAnim()[img.RUN_R], xp, yp, offsetX, offsetY, 54, 45, 15, 1, 60, 0, Animation.PLAY_ONCE);
			}
		} else if(anim.getCurrentState() == States.JUMPING) { 
			if(faceDir == States.LEFT) {
				anim.createAnimation(img.getPlayerAnim()[img.JUMP_L], xp, yp, offsetX, offsetY, 54, 58, 5, 1, 60, 0, Animation.PLAY_ONCE);
			} else {
				anim.createAnimation(img.getPlayerAnim()[img.JUMP_R], xp, yp, offsetX, offsetY, 54, 58, 5, 1, 60, 0, Animation.PLAY_ONCE);
			}
		} else if(anim.getCurrentState() == States.IDLE) {
			if(faceDir == States.LEFT) {
				anim.createAnimation(img.getPlayerAnim()[img.IDLE_L], xp, yp, offsetX, offsetY, 54, 45, 1, 1, 60, 0, Animation.LOOPING);
			} else {
				anim.createAnimation(img.getPlayerAnim()[img.IDLE_R], xp, yp, offsetX, offsetY, 54, 45, 1, 1, 60, 0, Animation.LOOPING);
			}
		}
		
		anim.update();
	}

You should probably just use the same update signature on the animation as you do for the player, then you can pass the elapsed time instead of recalculating it.

In your animation update function it looks like you increase frameNumberY every time instead of just if(frameNumberX >= sheetWidth), basically traversing diagonally across the sprite sheet.

I’m not sure how your sprite sheets are setup but the way I usually do it is to have a separate sheet for each action. The animation class has no player logic in it all it does is update and draw. The player class has an animation object for each action and the player class chooses what animation to update/draw based on its current state.

It seems like every time you update you create a new animation, you should try loading the animations once and then just setting the currentAnimation to whichever animation is happening at the time. For example have a jumping animation, running, etc, etc and a currentAnimation. Then when you check if the character is “running” set the currentAnimation to running.

But as you can tell by the first post you linked to, this method may not be the best method or even a correct method.

Thanks for the replies! My sprite sheet is set up with image after image horizontally(but each action has a separate sheet ), so Y never increases( keep it simple for now), only X will need to change. something like this: [0 1 2 3 4 5 6 7] with numbers representing each image.

I also have a LoadImage class to load the sprite sheet and store them into an array, so that the images are only loaded once. I can play most of the animations correctly but when I switch from left button to right button really quickly( I have a faceDir boolean in the player class to show which direction the player is currently facing ) I have a problem that the correct running animation wont be played unless the previous direction animation has finished playing, is that the problem of my animation class or did i somehow set up the character states wrong in the player class?

I’m going to implement that next .

After I implemented elapsedTime(delta for some people) to my moveSpeed variables I start seeing multiple images for my running animation, is there a reason for that? :-\

Do you mean multiple images off the sprite sheet? drawing the same image multiple places? Regardless it’s hard to say what the problem is without seeing the code.

Yea, mutiple images drawing near where the sprite is, do you want me to send you the code?

Sure you can send it to me or just post it some public place so more people can help. Multiple images off the sprite sheet probably means your sub image is just too big.

This is my new Animation class, changed delta to elapsedTime as you suggested :slight_smile:


	/** Creates animation for entity*/
	public void createAnimation(BufferedImage img, int x, int y, int offsetX, int offsetY, int charWidth, int charHeight,
			 int sheetWidth, int sheetHeight, int frameDuration, int startFrame, int playStyle) {
	
		animState = currentState;
		this.img = img;
		this.charWidth = charWidth;
		this.charHeight = charHeight;
		this.sheetWidth = sheetWidth;
		this.sheetHeight = sheetHeight;
		this.frameDuration = frameDuration;
		this.playStyle = playStyle;
	}
	 
	public void update(long elapsedTime) {
		if(currentState == animState && !isFinished) {
			/** Difference between current time and last loop time */
			lastFrameChange += elapsedTime;
			
			/** Change frame */
			if (lastFrameChange > frameDuration) {
				/** Reset frame */
				lastFrameChange = 0;
				
				/** update the frame X */
				frameNumberX++;
				if (frameNumberX >= sheetWidth) {
					if(playStyle == PLAY_ONCE) {
						isFinished = true;
						frameNumberX = 0;
						lastFrameChange = 0;
						currentState = States.IDLE;
						animState = States.IDLE;
					} else if(playStyle == LOOPING) {
						frameNumberX = 0;
					}
				}
				
				/** update the frame Y */
				frameNumberY++;
				if (frameNumberY >= sheetHeight) {
					frameNumberY = 0;
				}
			}
			/** Current frame X */
			frameX = (frameNumberX % sheetWidth) * charWidth;
			
			/** Current frame Y */
			frameY = (frameNumberY % sheetHeight) * charHeight;
		}
		
		/** When animation finished change states to IDLE*/
		if(isFinished) {
			isFinished = false;
		}
	}

This is part of the player class, I’ve made some modification and trying things out, I moved the setting animation methods into here, they were originally in the updateAnim method:


/** Set player state 
	 * @param elapsedTime */
	private void setPlayerState(float dx, float dy, long elapsedTime) {
		/** Player getting hit */
		if(hit) {
			if(status != Status.INVINCIBLE) {
				idle = false;
				left = false;
				right = false;
				jumping = false;
				hp -= 1;
				anim.setCurrentState(States.HIT);
				status = Status.INVINCIBLE;
				
				/** Hit animation */
				if(faceDir == States.LEFT) {
					anim.createAnimation(img.getPlayerAnim()[img.GET_HIT_L], xp, yp, offsetX, offsetY, 56, 53, 6, 1, 80, 0, Animation.PLAY_ONCE);
				} else {
					anim.createAnimation(img.getPlayerAnim()[img.GET_HIT_R], xp, yp, offsetX, offsetY, 56, 53, 6, 1, 80, 0, Animation.PLAY_ONCE);
				}
			}
			hit = false;
		}
		
		/** Player attacking */
		if(attacking) {
			if(States.ATTACKING > anim.getCurrentState()) {
				idle = false;
			}
			/** Set attack animation*/
			anim.setCurrentState(States.ATTACKING);
			
			if(faceDir == States.LEFT) {
				anim.createAnimation(img.getPlayerAnim()[img.ATTACK_L], xp, yp, offsetX, offsetY - 20, 103, 71, 5, 1, 60, 0, Animation.PLAY_ONCE);
			} else {
				anim.createAnimation(img.getPlayerAnim()[img.ATTACK_R], xp, yp, offsetX, offsetY - 20, 103, 71, 5, 1, 60, 0, Animation.PLAY_ONCE);
			}
			
			if(book != null) {
				book = null;
			}
			book = new SpellBook(px, py, faceDir);
			book.enemyCollision();
			attacking = false;
		}
		
		/** Player jumping*/
		if(space && !jumping) {
			if(anim.getAnimState() == States.ATTACKING || anim.getAnimState() == States.HIT) {
			} else {
				idle = false;
				jumping = true;
				jumpDistance = maxJumpHeight;
			}
		}
		
		/** Player moving left */
		if(left) {
			idle = false;
			faceDir = States.LEFT;
			dx = -moveSPeed * elapsedTime * 0.05f;
			/** Set running animation */
			if(States.RUNNING >= anim.getCurrentState()) {
				anim.setCurrentState(States.RUNNING);
				anim.createAnimation(img.getPlayerAnim()[img.RUN_L], xp, yp, offsetX, offsetY, 54, 45, 15, 1, 60, 0, Animation.PLAY_ONCE);
			}
		} 
		
		/** Player moving right */
		if(right) {
			idle = false;
			faceDir = States.RIGHT;
			dx = moveSPeed * elapsedTime * 0.05f;
			if(States.RUNNING >= anim.getCurrentState()) {
				/** Set running animation */
				anim.setCurrentState(States.RUNNING);
				anim.createAnimation(img.getPlayerAnim()[img.RUN_R], xp, yp, offsetX, offsetY, 54, 45, 15, 1, 60, 0, Animation.PLAY_ONCE);
			}
		}
		
		/** Player Standing */
		if(idle) {
			anim.setCurrentState(States.IDLE);
			
			if(faceDir == States.LEFT) {
				anim.createAnimation(img.getPlayerAnim()[img.IDLE_L], xp, yp, offsetX, offsetY, 54, 45, 1, 1, 60, 0, Animation.LOOPING);
			} else {
				anim.createAnimation(img.getPlayerAnim()[img.IDLE_R], xp, yp, offsetX, offsetY, 54, 45, 1, 1, 60, 0, Animation.LOOPING);
			}
		}
		
		/** Move player and shift map in the opposite direction */
		if(moveX(dx)) {
		}
	}

Thanks alot for the help, greatly appreciated!

Sorry I copied wrong, it was “drawing the same image multiple places?” They are closely overlapped so it seems like 1 image with “shadows” of the same image around it.

I don’t see the drawing code in there.

You can probably safely move the state out of the animation class. Once all the player specific code is out of the animation class it will be a lot more useful to you as you will be able to use it for all the game animations.

Is your game multi-threaded?

My game is single-threaded, the actual drawing code is in the GameCore class which I’ll post below, and yea you are right about seperating states with animation class, I’ll implement that next. ;D
My game loop is based on Kevin Glass’s awesome tutorial on his site(coke and code) and abit of killer programming in java.

GameCore.java

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferStrategy;
import java.util.ArrayList;


public class GameCore extends Canvas implements KeyListener {
	private static final long serialVersionUID = 1L;
	
	/** Screen width */
	public static final int WINDOWS_WIDTH = 1024;
	/** Screen Height */
	public static final int WINDOWS_HEIGHT = 768;
	/** The buffered strategy used for accelerated rendering */
	private BufferStrategy strategy;
	public boolean gameRunning = true;
	
	LoadImage img;
	Map map;
	Player player;
	Enemy1 enemy1;
	Enemy1 enemy2;
	Animation anim = new Animation();
	public static float mapPosX;
	public static float mapPosY;
	public static float fixX, fixY, fixX1, fixY1;

	
	
	static ArrayList<Enemy> enemyList = new ArrayList<Enemy>();
	
	GameCore() {
		Frame frame = new Frame("Side Scroller");
		frame.setLayout(null);
		setBounds(0,0,WINDOWS_WIDTH,WINDOWS_HEIGHT);
		frame.add(this);
		frame.setSize(WINDOWS_WIDTH,WINDOWS_HEIGHT);
		frame.setResizable(false);
		frame.setLocationRelativeTo(null);
		frame.setVisible(true);
		frame.addKeyListener(this);
		addKeyListener(this);
		//Exit windows
		frame.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				System.exit(0);
			}
		});
		
		/** create the strategy used for accelerated rendering. */ 
		createBufferStrategy(2);
		strategy = getBufferStrategy();
		
		/** Load images */
		img = new LoadImage();
		img.load();
		
		/** Create map */
		map = new Map(img);
		
		/** Start pos */
		enemyList.add(new Enemy1(map, 8, 20, img));
		enemyList.add(new Enemy1(map, 13, 20, img));
		player = new Player(map, 3, 2, img, anim);
		
		
		/** Center the map relative to player */
		mapPosX = Player.visibleX - Player.px;
		mapPosY = Player.visibleY - Player.py;
		
		gameLoop();
	}
	
	public static void main(String[] args) {
		new GameCore();
	}

	private void gameLoop() {
		long lastLoopTime = System.currentTimeMillis();
		while(gameRunning) {
			long elapsedTime = System.currentTimeMillis() - lastLoopTime;
			lastLoopTime = System.currentTimeMillis();

			gameUpdate(elapsedTime);
			gameRender();
			try {
				Thread.sleep(calcSleepTime());
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	private void gameUpdate(long elapsedTime) {
		/** X and Y offsets based on player's position*/
		mapPosX = Player.visibleX - Player.px;
		mapPosY = Player.visibleY - Player.py;
		cornerShift();
		/** Update enemies */
		for(int i = 0; i < enemyList.size(); i++) {
			enemyList.get(i).update(elapsedTime);
		}
		/** Update player */
		player.update(elapsedTime);
	}

	/** Checking if player is near the edge, if yes shift the map so things 
	 outside the wall is not shown */
	private void cornerShift() {
		if(Player.visibleX > Player.px - 1) {
			// Minus 1 to not show wall
			fixX = Player.px - Player.visibleX - 1;
		} else if(Player.px > Map.MAP_WIDTH - Player.visibleX - 1) {
			fixX = Player.px - Map.MAP_WIDTH + Player.visibleX + 1;
		}
		if(Player.visibleY > Player.py - 1) {
			fixY = Player.py - Player.visibleY - 1;
		} else if(Player.py > Map.MAP_HEIGHT- Player.visibleY - 1 ) {
			fixY = Player.py - Map.MAP_HEIGHT + Player.visibleY + 1;
		}
	}
	
	
	private void gameRender() {
		Graphics2D g = (Graphics2D) strategy.getDrawGraphics();
		
		// clear the screen
		g.setColor(Color.BLACK);
		g.fillRect(0,0,WINDOWS_WIDTH,WINDOWS_HEIGHT);
		g.translate(0, 30);
		
		map.paint(g);
		
		/** paint enemy from list */
		for(int i = 0; i < enemyList.size(); i++) {
			if(enemyList.get(i).isAlive()) {
				enemyList.get(i).paint(g);
			}
		}
		/** paint player */
		player.paint(g);
		// flip the buffer so we can see the rendering
		g.dispose();
		strategy.show();
	}

	/**Calculates sleep time*/
	private long calcSleepTime() {
		long beforeTime, timeDiff, sleepTime;
		beforeTime = System.currentTimeMillis();
		
		timeDiff = System.currentTimeMillis() - beforeTime;
		sleepTime = 10 - timeDiff;
		
		if(sleepTime <= 0) {
			sleepTime = 5;
		}
		return sleepTime;
	}
	
	@Override
	public void keyPressed(KeyEvent e) {
		player.keyPressed(e);
	}
	
	@Override
	public void keyReleased(KeyEvent e) {
		player.keyReleased(e);
	}

	@Override
	public void keyTyped(KeyEvent e) {}
	
	/** Return Enemy list */
	public static ArrayList<Enemy> getEnemyList() {
		return enemyList;
	}
}

There are a few problems I noticed when quickly looking over GameCore mostly in the calcSleepTime() method. The function is returning how long the System.currentTimeMillis() took to execute when it should be calculating how long the game took to update and render for the previous tick. The System.currentTimeMillis() is not recommended for a game System.nanoTime() should be used instead as it is much more accurate, and math corrected for the change of course.

You may wish to check out this post http://www.java-gaming.org/index.php/topic,21919.0.html.

Hope this helps.

Thanks for the reply! I’ll go fix that up as soon as I get off work =D

Yea, after implementing the loop from that thread my multiple image problem is gone, thanks! ;D I don’t get some of the maths tho, why is he dividing by 1000 * 1000? Isnt nano 100010001000?

update((int) ((currentUpdateTime - lastUpdateTime)/(1000*1000)));

Dividing nanoseconds by (1000*1000) give you milliseconds. The deltaTime in the update method is usually in milliseconds for game.

Ah I understand now, that makes alot of sense. Thanks for your loop!