Fork me on GitHub

Adding Events

Most game engines have a way to abstract out the mundane part of managing events. There is no reason our game should sit idle waiting for something to happen and because of this we want some kind of event system to use that will give us some flexibility to add and listen for events. In this section of the tutorial series we will write a new event class that will merge the JavaScript event system with our own mixings.

What should an event manager do? The biggest feature a good event system will do is listen for events that we are interested in monitoring. It may be we want to know when data has been received from the server, or a collision happened between two objects like a laser hitting the player, or just about anything we can think of where we need to do something when something happens. Yes I know very ambiguous definition.

Let’s take some time and layout the feature we will want to add into the event system so we know what will be needed.

  • A way to add new kinds of events
  • Remove non-core specific events
    • What are core events?
  • Adding new event handlers
    • Pass Event specific data
    • Stop bubbling of event
  • Removing event handlers (stop listening to an event)
  • Handle document and window events

This should be a good start for our event class. This is by far not an exhaustive list and this event class is just for conceptual ideas and will need to be hardened during the development of a game.

function GameEvents(cfg) {
	var me = this;

	me.init = function (cfg) {
		Object.assign(me, cfg);
	};// End init

	if (cfg !== undefined) {
		me.init(cfg);
	}
}

If you have been following a long with this tutorial series you should recognize the code. It is in just about every class we define and it help us manage the polymorphic process of our classes by allowing our developers to change how a particular object can be used, extended and managed.

With that code out of the way we will need to add some of the basic data structures for our event system. We will use some tricky JavaScript to make this all happen which I will explain. These are just some really fun ways JavaScript make their developers life easier. Here is the data structures we need to add. The bolded lines are the ones that have been added and we will discuss them further.

	me._events = {};
	me._nextId = 0;
	me.MAXID = 2000000;

	me.init = function (cfg) {
		Object.assign(me, cfg);
	};// End init

The first new line is a JavaScript definition of a generic object. This is where we will store all the different types of events the game engine will handle. One of the limitation of the generic events in JavaScript is they can only attach to one function. This is where we will add event triggers and make our system accept multiple events handlers.

The next two lines aren’t really new, their almost a boiler plate code for tacking object in the system. When a new event handler is added we will use these variables to uniquely identify them in the system so that we have an accurate way of finding event objects.

Now that the data structures are defined we can now start creating function that will manage our event system. The first one we will want to make is a way to define a new type of event group. Here is the code for adding new event types:

	me.addEvent = function (type) {
		// Check if the event type is not defined
		if (me._events[type] === undefined) {
			me._events[type] = [];
		}
	};// End addEvent

Fairly straight forward function. In it we just check to make sure the provided event in not defined. The tricky part is how we access the events object variable. Typically we would see object variables being access through the dot notation. There are two problems with that, first what if we have an event type that has a space in the name? For example “Player Hit”. We couldn’t use this because it doesn’t fit the proper variable naming convention or does it? The only invalid character is the space but with this being passed in as a variable “type” it now conforms to the variable naming conventions. The trick here is instead of using dot notation we use the array accessor notation “[]” which allows us to use a object like it is an array but with our own index structure.

I know this is a lot to take in but trust me it is worth learning even if you’re not going to make a game engine or even a game. When writing a library of tools like this game engine it is nice to provide a semi bullet proof system that won’t crash because some other developer tried something you didn’t think about. There still is no guarantee that some new programmer won’t break it still.

Once we know the event type is not define we just create a blank array in the event object. Fairly simple really and that is the premise for this event class, to try and keep things as simple as possible so we don’t have a large overhead.

As with everything we also will want a way to remove an event type. So here is the function we will use to deal with trashing an event listener:

	me.rmEvent = function (type) {
		var evts = null;
		// Check if the event type is defined
		if (me._events[type] !== undefined) {
			// Save the event handler array
			evts = me._events[type];
			// Kaboom to the event type
			delete me._events[type];
		}

		return evts;
	};// End rmEvent

If you haven’t noticed there are a lot of time my code may look like I’m paranoid. The truth is when writing that could be improperly used you tend to write validations before you try something on undefined data structures. Now then the first thing here is we define an events list variable that is empty. Then we check to see if the event type is defined, store the event array in our blank variable, delete the event variable from our event object and finally return the event array.

Time to start adding events right? Not yet sorry we can define event types like ‘click’, ‘mousemove’, ‘etc’… but there still is no way to bind these events to functions we would like run when these events happen. The next step is to now add two more function that will allow us to bind handlers to an event. For now we will call then “on” and “off” because I hate to type a lot.

me.on = function (type, handler) {
		// Check if the event type is defined
		if (me._events[type] === undefined) {
			// Nope it isn't so we will be nice and define it for them
			me.addEvent(type);
		}
		// Create a new event handler object
		var evtHandler = {
			id: me.nextId(),
			handler: handler
		};
		// Push event object onto the stack
		me._events[type].push(evtHandler);
		return evtHandler;
	};// End on

The first couple of lines are to check if the event type is already defined. If it isn’t then we will graciously add it to the type of events we will handle. Another way to handle the missing event type would have been to throw a nasty error message and stop the game engine. But I find it nicer to try and make things work without crashing.

After we know there is a type defined for the event handler we create a generic object to store the handler with our unique id. We will look at the nextId function in a second. Next we now apply the new event handler object to our data structure by placing it at the end of the array. That wasn’t so bad now comes the next part removing an event handler.

	me.off = function (type, handler) {
		var retVal = false;
		if (me._events[type] !== undefined) {
			for (var i = 0; i < me._events[type].length; ++i) {
				if (me._events[type][i].handler === handler) {
					// Found event handler so removing it from array
					me._events[type].splice(i, 1);
					retVal = true;
					break;
				}
			}
		}

		return retVal;
	};// End off

This is the off function which is basically a remove function. For now we will just look at the function signature to see if it matches. Once the handler is found we just splice it out of the array and return that to the caller.

Now let’s take a look at the magic nextId function:

	me.nextId = function () {
		// Get next id
		var idx = me._nextId;
		// Calculate the next ID and make sure it doesn't exceed our limits!
		me._nextId = (me._nextId + 1) % me.MAXID;
		return idx;
	};// End nextId

This is just a simple boiler plate function that returns the current next ID value and then properly increments the next id value and make it so it can wrap around. Our other objects that use this kind of code should probably inherit this function but we will leave that for another tutorial.

Now this class can add event types, bind handlers to an event type, what else does it need? How about a way to trigger an event? We need to add a function called handle to the event class which we can then use to send events to the event system. Here is the code for the handle function:

	me.handle = function (type, evt) {
		// Check if the event type has been registered first
		if (me._events[type] !== undefined) {
			// Process each handler in the list
			for (var i = 0; i < me._events[type].length; ++i) {
				if (me._events[type][i].handler(evt) === false) {
					// Stop the bubble process because the event handler said so
					break;
				}
			}
		}
	};// End handle

There is a lot of thing happening in this little section of code. First we check to see if the event type is defined which keeps the handler from crashing the system. Once we know it is defined we loop through each item in the event type array. Call the magic function that will handle the called event. Check if it returns false. You may be wondering why we would stop the event loop if it returns false. This is done so that if something in the game engine has completed the needed functionality of the event they can stop all other event objects from being processed.

That is all there is to the events class. Now if we want to bind a system event to our event system how do we do that? We can’t pass the handle function directly to the event for example we can’t do ‘document.onclick = events.handle;’ because when the handle event is called there is no type defined. This is where we will need to add helper function in the event system to deal with these special events.

	me.handleClick = function (evt) {
		me.handle('click', evt);
	};// End handleClick

	me.handleMove = function (evt) {
		me.handle('mousemove', evt);
	};// End handleMove

These are just an example of two common events we will want to capture. Now we can bind these function to the document events and they will convert them to the event system.

Setting up events

In the init function we will now want to add a few pre-defined events so add the following code in bold:

me.init = function (cfg) {
		Object.assign(me, cfg);
		// Initialize the event system
		me.addEvent('ready');
		me.addEvent('click');
		me.addEvent('mousemove');
		document.onclick = me.handleClick;
		document.onmousemove = me.handleMove;
	};// End init

With all that work done what have we achieved? Well lots really we now have a working model of an event class that we now need to add it to the game engine. To do this we will need to add a variable to the game engine class that will hold the events object.

	///// Define all ening values that are not settable 
	me._isRunning = false;
	// Current game scene
	me._scene = [];
	me._events = new GameEvents({ _parent: me });

Since this is “a private” variable we will need an accessor function in the game engine. Add this simple accessor function to the game engine and we will be ready to handle events!

	me.events = function () {
		return me._events;
	};

Simple yet functional (pun intended).

Using Events

Time to take the events system for a little spin. Create a new HTML file in /src/view and call it tutorial-07.html. Copy the code from the previous tutorial and remove the lines that I have marked out in the listing below.

	>script<
		window.onload = start;
		var backScene;
		var game;
		function start() {
			game = new GEngine({
				world:{
					canvasSize: { x: 400, y: 300 },
					fullWindow: true
				},
				canvasId: 'theEngine',
				clearColor: ‘#d2d2d2’,
				fps: 60
			});
			var scene = new Scene();

			var box = new EntityRect({
				color: '#ff0000',
				translate: {
					position: {x: 300, y: 150}
				},
				drawPivotPt: true
			});
			box.pivot.x = 0;
			box.update = function (dt) {
				var newRot = (this.translate().rotation() + 0.5) % 360;
				this.translate().rotation(newRot);
			};
			scene.add(box);

			// Add current scene to game engine
			game.addScene(scene);

			scene = new Scene();
			box = new EntityRect({
				color: '#0000ff',
				translate: {
					position: { x: 300, y: 150 }
				},
				drawPivotPt: true
			});
			box.update = function (dt) {
				var newRot = (this.translate().rotation() + 0.5) % 360;
				this.translate().rotation(newRot);
			};
			scene.add(box);
			backScene = scene;

			game.run();
		}

		function swapScene() {
			var scene = backScene;
			backScene = game.popScene();
			game.addScene(scene);
		}
		window.onclick = function(e){
			e.preventDefault();
			swapScene();
		};
	>/script<

This should now have one scene with a rotating box. Simple but it should do to show the game engine is running and help us with the next section of code. Add these line just after the game.run(); line of code:

			function handleClick(evt) {
				var pos = { x: evt.clientX, y: evt.clientY };
				var cir = new EntityCircle({
					translate: {
						position: pos
					},
					radius: 5,
					age: 120,
					tage: 0
				});// End circle entity definition
				cir.update = function (dt) {
					if (this.tage++ > this.age) {
						var g = this.gengine();
						console.log('Goodbye! ' + this.id);
						g.rm(this.id);
					}
				};
				game.add(cir);
			}
			game.events().on('click', handleClick);

We create a function that will be used whenever a click event has been detected. In this function we want are going to add a new circle to the game scene where the mouse position was when the click event was triggered.

Note in this definition of the circle we are using the tools we added to the entity class to extend our circle entity so that we can track how long to keep the dot on the screen. I wanted to demonstrate how to remove an entity form the game engine after its life had expired. To complete this we needed two values. One for how old the circle should be in frames and the next was the current circle age. The last thing to do was create a custom update function that would track the age of the circle and when it was to old remove it from the game engine or technically the current scene.

The last thing to do was add our handleClick function to the event object and tell it we want to listen for event on the click event. Now run this code in a browser and every time you click on the game field it will add a dot to the screen and in about 2 seconds remove it.

Things to try on your own

Try adding simulate gravity to the scene for the circles we add.

Change removing the circle from age to when it goes off canvas.