Chronicles of a videogame in Java – 2. How to make the character move + simple animation

The code discussed below is already available on my GitHub page at theBoyWithNoName. Please feel free to download it to take a better look. Ask me anything about it.

Hello again guys! So today we’re going to see how to move the main character of a 2D platform around the screen in Java. We’re also going to add a little animation to make the guy properly run.

Oh, by the way, before we start: I’m calling the game The Boy With No Name. Not that the title means anything, I just needed a name for the project and couldn’t come up with a better name. Soooo, yup.

Screenshot n.1

Anyways, if you’re a Java programmer you should already be aware of the concepts of JPanel, JFrame and Thread. If you’re not you can easily look up this stuff on the internet, the first two of those are pretty easy to use if you have any programming experience.Threads are a little tricky, but you don’t really need that much knowledge to understand the code I’m about to let you see.

Let’s start with the KeyboardController class. The KeyboardController is a KeyListener, meaning it can listen to the keys you press on your keyboard (DUH!). Here’s what it looks like:

public class KeyboardController implements KeyListener{

	public KeyboardController(){
		activeKeys=new HashSet<Integer>();
	}
	
	@Override
	public void keyPressed(KeyEvent e) {
		activeKeys.add(e.getKeyCode());
	}

	@Override
	public void keyReleased(KeyEvent e) {
		activeKeys.remove(e.getKeyCode());
	}

	@Override
	public void keyTyped(KeyEvent e) {
	}
	
	public static HashSet<Integer> getActiveKeys(){
		return activeKeys;
	}
	
	private static HashSet<Integer> activeKeys;
}

Basically the keys you press are stored into the HashSet called “activeKeys“. The HashSet is really the best data structure to store this kind of data because it automatically handles duplicates. In this way you don’t need to worry about having multiple “LEFT_ARROW_KEY” characters inside activeKeys, because the add(element) function of HashSet will only add the element if not already contained in the set.

The GameManager is the main thread of the game. It basically operates a sequence of instructions again and again until the game is over (either you die or exit the game). Here’s how I wrote the main functions of the GameManager:

@Override
	public void run() {
		while(gameIsRunning){
			//manage the keys currently pressed
			manageKeys();
			
			gamePanel.repaintGame();
			
			try {
				Thread.sleep(MAIN_SLEEP_TIME);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	//the function manages the keys currently pressed associating concrete
	//actions to them
	private void manageKeys() {
		//get the currently pressed keys from the KeyboardController
		HashSet<Integer> currentKeys=KeyboardController.getActiveKeys();
		
		//manage the two possible run direction
		if(currentKeys.contains(KeyEvent.VK_RIGHT)){
			//move right
			boy.move(KeyEvent.VK_RIGHT);
		} else if (currentKeys.contains(KeyEvent.VK_LEFT)){
			//move left
			boy.move(KeyEvent.VK_LEFT);
		} else if(currentKeys.isEmpty()){
			//if the player is not pressing keys, the protagonist stands still
			boy.stop();
		}
		
		
	}

As you can see in the run method, the GameManager continuosly checks what is in the set of currently pressed keys by calling the manageKeys private function. This last function is pretty straightforward: if the user is pressing a right key (KeyEvent.VK_RIGHT) or a left key (KeyEvent.VK_LEFT), it calls the move function on the boy (the name I gave to the character) object, passing the direction as a parameter. If the player is not milking any key, manageKeys calls the stop() function on the boy, which basically only serves a graphic purpose turning the current moving frame of the boy into an “idle” frame.
After the managefunction(), the main cycle of the GameManager (that you can see in the run() method) thread simply repaints the game, making the position changes visible on screen, and then sleeps for 18ms.

And finally here comes the main character, the hero, or as I call him, the boy. Before I show you the code let’s just point out that the most important variable in the Boy is its boundingBox. A boundingBox is just a rectangle, built around the character’s sprite, that moves with the character following him anywhere like a shadow. The bounding box is really important because very soon we’ll be using it for collision detection, probably the hardest topic that we’ll see in this diary/tutorial/thing I’m doing. Now take a look at the important methods of the Boy class:

//function called by the GameManager's manageKeys() function
	public void move(int direction) {
		switch (direction) {
			//in case you have to move left..
			case KeyEvent.VK_LEFT:
				//update the character's position
				currentX=currentX-DISPLACEMENT;
				
				//update the character's bounding box position
				boundingBox.setLocation(currentX, currentY);
				
				//change the current frame in animation
				setFrameNumber();
				currentFrame=run_L[currentFrameNumber];
				
				//set the left direction as the last one 
				last_direction=KeyEvent.VK_LEFT;
				break;
			
			//in case you have to move right..
			case KeyEvent.VK_RIGHT:
				//update the character's position
				currentX=currentX+DISPLACEMENT;
				
				//update the character's bounding box position
				boundingBox.setLocation(currentX, currentY);
				
				//change the current frame in animation
				setFrameNumber();
				currentFrame=run_R[currentFrameNumber];
				
				//set the right direction as the last one 
				last_direction=KeyEvent.VK_RIGHT;
				break;
				
			default:
				break;
		}
		moveCounter++;
	}
	
	//sets the current frame when the boy is moving - we have a total of 5 frames for 
	//each run direction. The variable moveCounter is incremented each time the gameManager
	//calls the move function on the Boy. So according to moveCounter we can choose the current
	//frame. The frame changes every MOVE_COUNTER_THRESH increments of the moveCounter variable.
	//In this case MOVE_COUNTER_THRESH is set to 5. The use of "6" instead of a variable is temporary
	//because I still don't know how many frames will be used in the final animation
	private void setFrameNumber() {
		currentFrameNumber  = moveCounter/MOVE_COUNTER_THRESH;
		currentFrameNumber %= 6;
		
		if(moveCounter>MOVE_COUNTER_THRESH*6){
			moveCounter=0;
		}
	}

The move function takes a direction as a parameter. We are only dealing with left and right arrow keys now so it is as simple as it gets: if the direction is RIGHT, then the character’s position is incremented by 5 pixels (DISPLACEMENT=5). As a result, the location of the boundingBox is updated.
Now let’s talk about frame switching, better known as animation. The last function you see above is called setFrameNumber() and, even if it looks pretty bad, it works pretty nicely to animate our little friend. To explain it let’s first make a step behind. Every time the move() function is called, the character moves and a moveCounter is incremented by one. The moveCounter variable is what makes the frame switching possible because every 5 increments of the moveCounter, we change frame. Why every 5 increments? because making the frame switch at every increment would make the protagonist move like a wierdo, animating way way way too fast. So a MOVE_COUNTER_THRESH of 5 is a good trade off. Here’s the six frames I used in the game:

frameSheet

Aaaaaaand that would be all for today’s diary. Oh hey, remember that this is not a proper tutorial, but just a “how I make things” kind of thing. So if you have suggestions or you don’t understand what I say, please comment below or contact me on twitter. I’m also on tumblr, drawing stuff.

Advertisements
Chronicles of a videogame in Java – 2. How to make the character move + simple animation

6 thoughts on “Chronicles of a videogame in Java – 2. How to make the character move + simple animation

  1. Andy says:

    Hi. Is there any way you can update the code so that doesn’t appear as <Integer>

    I was confused at first, but once I looked at your github it all cleared up for me. I’m just looking out for those who come next.

    Liked by 1 person

  2. John Ma says:

    Hey dude, just wondering a few things. I took a look at your code and i tried doing something myself. A few questions :
    1) When adding the player to the thread does it affect the movement of the player? (uhm hard to explain i guess, but i guess dont worry about the sprites?? not sure .. kind of new to cding aswell)
    2) Why do you do playPanel.repaint in a method in the GamePanel class?

    Like

    1. Hey, I’m not sure I understand your first question but the player HAS to be in the main thread in order to be controlled in general. Since the Player class stores every information about its position you need to be able to access those parameters when you move the character. All of these operations happen inside the main thread in the GameManager.

      The answer to your second question is really a matter of encapsulation. The GamePanel is the graphic class that “talks” to the main logic thread in the code. The PlayPanel is just the panel that shows you the game (to make it simple), so it is stored inside the GamePanel. If you notice, in my code the main thread calls a method of the gamePanel (“repaintGame()”) because that’s its gateway to the graphic part of the code. Then the GamePanel tells the PlayPanel to repaint itself to show all the objects in their new positions etc. etc.

      I hope this made it a little more clear, if you have any other questions feel free to ask mate!

      Like

      1. John Ma says:

        Thanks so much! Your guide actually probably is the least complicated out of most of them. If i need anymore help, is it possible that if i can contact you through a private way?

        Liked by 1 person

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s