CHRONICLES OF A VIDEOGAME IN JAVA – 6. NPCs

Previously on the Chronicles of a Videogame in Java: Collision Detection for Dummies.

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

dsads
Welcome back to the journey of “The Boy With No Name”, a 2D platform game that I’m coding from scratch in Java. Today I’m going to talk a bit about NPCs, acronym that stands for Non-Playable Character. For those of you who may not know, NPCs in videogames are basically characters that you meet but can’t directly control. In my experience, there can be three types of NPC in a videogame:

The unavoidable one is a part of the story. The main character is forced to interact with him in order to move forward in the game. (example: Professor Oak in the Pokémon series)

The helpful one helps the main character through his journey giving him wise words or even objects to overcome adversities. (example: the merchant from Resident Evil 4)

The useless one is really just a piece of furniture. He may even speak two or three words in a row but there’s no purpose in those words. They don’t help you finish the game. (example: the citizens in Zanarkand at the beginning of Final Fantasy X)

The kind of NPCs that I’m adopting for “The Boy With No Name” is the second one, the helpful kinda guy. Here’s how it works: all the secondary characters in the game will wait idle in a specific place of a specific level and, only if talked to, they will suggest something or maybe give something to the main character. They will not be unavoidable though: the player will be perfectly able to just walk past them and go on with his adventure. Pretty simple.

npcs

At this point we have delined the standard behaviour of the NPCs in the game. Now it’s time to put this thoughts to code.

From an architectural point of view, I found inconvenient to use some sort of map or bidimensional array for the positioning of the secondary characters in the stage. Do you remember the tiled map we used to position blocks of terrain? Well we could have used the same concept to store current NPCs, but to me that’s a crazy thing to do if you don’t plan to put hundreds of secondary little characters in the game.

So yeah, the NPCManger class stores all of the stage’s Non-Playable Characters in a simple array of objects. Informations about where to put the NPC and what the NPC has to say is stored in text files that the NPCManager consults accordingly to the level currently played by the user. So here’s what the class looks like:

public class NPCManager {
	public NPCManager(int currentLevel){
		this.currentLevel=currentLevel;
		currentNPCs=new ArrayList<NPC>();
		loadInformations();
	}
	
	public void initializeStage(int currentLevel) {
		currentNPCs.clear();
		this.currentLevel=currentLevel;
		loadInformations();
	}
	
	private void loadInformations() {
		InputStream is=this.getClass().getResourceAsStream("/npc_info/level"+String.valueOf(currentLevel)+".txt");
		if(is==null){
			return;
		}
		BufferedReader reader=new BufferedReader(new InputStreamReader(is));
		String line=null;
		String[] singleNPCInfo;
		try {
			while((line=reader.readLine())!=null){
				singleNPCInfo=line.split(" ");
				currentNPCs.add(new NPC(singleNPCInfo[0],Integer.valueOf(singleNPCInfo[1]),
						Integer.valueOf(singleNPCInfo[2]),Integer.valueOf(singleNPCInfo[3]),currentLevel));
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public ArrayList<NPC> getNPCs() {
		return currentNPCs;
	}
	
	//returns the closest enemy within two tiles of distance (which is 
	//the maximum distance allowed for an interaction with a NPC)
	public NPC closestNPC(int boyRow, int boyCol) {
		NPC closestNPC=null;
		int currentDistance;
		for(int i=0; i<currentNPCs.size(); i++){
			if(boyRow!=currentNPCs.get(i).getRow()){
				continue;
			}
			if((Math.abs(currentNPCs.get(i).getCol()-boyCol))<=MAXIMUM_TALKING_DISTANCE){
				currentDistance=Math.abs(currentNPCs.get(i).getCurrentX()-(boyCol*Tile.TILE_SIZE));
				if(closestNPC==null){
					closestNPC=currentNPCs.get(i);
				} else {
					if(currentDistance<Math.abs(closestNPC.getCurrentX()-boyCol+Tile.TILE_SIZE)){
						closestNPC=currentNPCs.get(i);
					}
				}
			}
		}
		
		return closestNPC;
	}	
	
	private static final int MAXIMUM_TALKING_DISTANCE=2;
	private ArrayList<NPC> currentNPCs;
	private int currentLevel;
}

As you may notice there’s a closestNPC() function that, as you can imagine, is called every time you try to interact with a non playable character. This function finds the closest Non-Playable-Character given the protagonist’s position. But the character can’t interact with people further than 2 tiles, and that’s the reason of the control at line 46 based on the constant MAXIMUM_TALKING_DISTANCE.

An instance of the NPCManager class is stored in the GameManager, the main thread of the game which also controls a set of currently pressed keys and reacts to them in the proper way via the manageKeys() function. Here’s what the manageKeys() function will look like if we bound the <enter> key to the interaction with an NPC:

private void manageKeys() {
		[...]
		if(currentKeys.contains(KeyEvent.VK_ENTER)){
			NPC tempNpc;
			//find the closest npc according to the character's position
			if((tempNpc=npcManager.closestNPC(boy.getRow(),boy.getCol()))!=null){
				
				//if the npc is already talking, keep talking...
				if(tempNpc.isTalking()){
					if(!(tempNpc.continueTalking())){
						listening=false;
					}
				
				//otherwise interact with the npc
				} else {
					tempNpc.interact();
					
					//put the character in &amp;amp;amp;lt;idle&amp;amp;amp;gt; status when he's talking
					boy.stop();
					
					//prevent the character from moving when talking
					listening=true;
				}
			}
			currentKeys.remove(KeyEvent.VK_ENTER);
		}
		
	}

Now what remains to be seen is the actual NPC class, but to be honest there’s not much to explain here. The NPC is similar to the Protagonist (class Boy) in the sense that it has a couple of pixel coordinates and a couple of “tile-coordinates” that identify the position of the secondary character in the tiled map. The pixel coordinates are really just the tile-coordinates multiplied by the size of a tile (which in my case is 64) but you know, it’s handy and a lot more polished to have them stored separately.

What really makes the difference in the NPC class are these guys:

	private boolean talking=false;
	private String[] sentences;
	private int currentSentence=0;
	private int numberOfSentences;	

If the talking boolean is false the game will not display any of the speech balloons associated with the NPC. If true it will display a specific speech depending on how many times the player has talked to this NPC. The times you talk to an NPC are stored in the currentSentence variable, that increments each time you press in front of the guy you want to talk to. The currentSentence integer is also used to iterate over the sentences array, which contains all the lines the NPC is supposed to say in that specific spot of that specific level. The PlayPanel (the JPanel that shows the actual game) will draw the speech balloons accordingly to the NPC position and will fill the speech balloon with the words defined by sentences[currentSentence].

So yep, this is probably the easiest approach you can have to build a working NPC system. But it works:

Aaaaaaand that would be all for today’s diary. Once again, 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 get in touch with me on twitter. I’m also on tumblr, drawing stuff. See you soon guys!

Advertisements
CHRONICLES OF A VIDEOGAME IN JAVA – 6. NPCs

4 thoughts on “CHRONICLES OF A VIDEOGAME IN JAVA – 6. NPCs

      1. Half_NO_oB says:

        definitely great Infos… Did you find time to develop it further? like covering hud or inventory stuff? 😀

        Like

      2. Still haven’t found the time since I’m working on some other projects but I’m surely going to do some new tutorials as soon as I find the time! Thanks a lot for the kind words.

        Like

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 )

Google+ photo

You are commenting using your Google+ 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 )

w

Connecting to %s