An engine that can be used to manage and play Visual Novels with limited interactivity. It supports a simple scripting language so you can control the story from within your XML file (which is the actual story).

Post tutorial Report RSS How the engine works.

In this tutorial I'll try to explain the inner workings of the engine and how it reads and displays the story. The engine itself is exported as a library which you can include in your personal projects. But for easy of use I have provided you with 2 'players', more on those in another tutorial.

Posted by on - Basic Client Side Coding


Here is an overview of how the engine actually works.

  • Load a given XML file
  • Parse the contents of that XML file
    • List all the assets needed
    • List all the dialogs and frames that make up the story
  • Load all the assets that are needed
  • Show the 'cover' image of the story
  • Proceed the story frame by frame with dialogs
  • Stop showing frames when the end is reached

Now that's all fine for people that aren't looking to expand or change the code itself. So how does it actually work?

For starters the engine itself has to be driven by something, an outer layer that will tell the engine to start working and fetch whatever information the engine has to give (frame images, dialogs, …).

So basically without the outer layer (which you create yourself or use one I provide) the engine is worthless.

Let's assume we've got an outer layer and it calls the proper functions of the engine.

It all starts with this function:

		public static function processXML(xmlPath:String):void {
			var xmlLoader:URLLoader = new URLLoader();
			xmlLoader.addEventListener(Event.COMPLETE, xmlLoaded);
			storyPath = getFolder(xmlPath);
			xmlLoader.load(new URLRequest(xmlPath));

This function get's the ball rolling. It's not that special but it's the first step in loading the XML file.
Here's the actual loading of the XML itself.

		private static function xmlLoaded(e:Event):void {
			storyXML = XML(;
			for each (var sound:XML in storyXML.assets.sounds.sound) {
				Sounds.addSound(sound.@id, storyPath + "sounds/" + sound.@src);
			for each (var image:XML in storyXML.assets.images.image) {
				Images.addImage(image.@id, storyPath + "images/" + image.@src);		

Here you can clearly see that it basically runs over every asset listed in the XML file and tells the asset manager to load the specified asset.

Asset managers?

You can have 2 asset types, sounds and images. I've created 2 managers for these assets. Both do (almost) the same thing but for a different asset type.

The manager is used to load assets but also to retrieve them afterwards by using ID's or actual name's.

The engine waits until this function is called (by the asset managers):

		private static function assetsLoaded(e:Event):void {
			if (Sounds.loaded) Sounds.removeEventListener(assetsLoaded);
			if (Images.loaded) Images.removeEventListener(assetsLoaded);
			if (Sounds.loaded &amp;&amp; Images.loaded) evDis.dispatchEvent(new Event(EVENT_ASSETS_LOADED));

You can now see that the engine will wait until everything is loaded before it goes on. This creates 2 things you should think about when making assets.

1) The engine will "wait" for the assets so you should optimize them.
2) Once the engine continues everything will be loaded and there will no longer be any pauses needed.

Now that everything is loaded the outer layer will have to tell the engine that it can start with the story.

Proceeding with the story the engine uses this function:

		public static function nextFrame():void {
			frameXML = storyXML.story.frame[curFrame];			
			backImage = frameXML.@image_backid;
			frontImage = frameXML.@image_frontid;
			curDialog = 0;

There are 2 big things that happen in this function, it calls for the next dialog and it runs a script (if there is one).

The engine works with frames and dialogs. You can mix them, for instance you can create an entire story by just using the dialogs (which are nested in 1 frame) or have the story be all frames with 1 dialog in each frame.

This is the function that loads a dialog:

		public static function nextDialog():void {
			var dialogXML:XML = frameXML.dialog[curDialog];
			if (dialogXML == null) return;
			for each (var answer:XML in dialogXML.answer) {
				answers.addItem({text: answer.@text, script: answer.@script});
			dlgChar = "";
			dlgChar = dialogXML.@character;
			dlgText = "";
			dlgText = dialogXML.text;

Again a script is run if there is one.


I'll cover this in more depth in another tutorial but for easier control of the story flow I've included a small scripting language.

		private static function commandParse(command:String, args:Array):void {
			var bln:Boolean = false;
			switch (command) {
				case "playSound":
					if (args[1] == "true") bln = true;
				case "stopSound":
				case "stopAllSounds":
				case "proceedStory":
				case "gotoFrame":
					curFrame = getFrameNumber(args[0]);
				case "proceedDialog":
				case "gotoDialog":
					curDialog = getDialogNumber(args[0]);
				case "changeBack":
					backImage = args[0];
				case "changeFront":
					frontImage = args[0];
				case "end":
					evDis.dispatchEvent(new Event(EVENT_END));

Basically by using script you call internal functions that either change the current frame, dialog, asset, ...

By using these scripts you can have multiple story paths and/or endings. There are multiple places where you can put a script a few places are

  • cover (it will run the script when the cover is loaded)
  • new frame (it will run the script when the frame is loaded)
  • new dialog (it will run the script when the dialog is loaded)
  • dialog answer (it will run the script when the answer is selected)

Once the engine hits the "end()" script it will close the story and notifies the outer layer that the story has ended. By sending out an event.

There are a few events that the engine fire's, and the outer layer should listen to these events so it can do the appropriate action when one is fired off.

  • EVENT_ASSETS_LOADED (is fired when all the assets are loaded)
  • EVENT_FRAME_UPDATED (is fired when the story frame is updated, new dialog or new frame)
  • EVENT_END (is fired when the story has reached the ending)

This concludes the tutorial covering the actual engine. If you do not want to change the engine or make a new player it might be a good thing to know these things but it shouldn't matter.

In the next tutorial I'll cover how the outer layers or shells work.


Post a comment
Sign in or join with:

Only registered members can share their thoughts. So come on! Join the community today (totally free - or sign in with your social account on the right) and join in the conversation.

Follow Report Profile
Windows, Mac
Send Message
Release date
Engine watch
Related Engines
Visual Novel Engine
Visual Novel Engine Creative Commons
Related Groups
IntoGames Developer & Publisher