Back to Arcade

The need for an interface

While developing games, it became more and more clear that when the library of games grew, we needed a core to keep our code organized. This first came in the form of 'move', a system which allowed simple code for checking if a certain type of key was pressed, and more.

Yet each time a game was finished, integration was taking a increasing chunk of time, as each time, it had to be imported into game hub, a highscore menu had to be made, intervals could not stop because the common interval variable was different, not to speak of pausing a game, which was a different system in and of itself.

To avoid doing this every game, we needed two base systems, one of which was the 'move' interface. The other was a way of generating an interval which could be paused. Then the 'Interfaces' emerged, which shrunk the amount of code to load, for example, the hub, to usually one line.

Too late

By the time all interfaces were done, the project was over, as the project deadline for school was reached. Most of these interfaces work seamlessly, but still need testing and implementation. They are, however still very interesting.

Lessons learnt

The biggest lesson however, has been learnt. Don't program in plan JavaScript. Nowadays I use TypeScript, which rules out a big problem when working with multiple people: they think differently. With a statically typed language, it is impossible to, say, call a function incorrectly. This site is also built using TypeScript.

Interfaces

Global access

Although global variables aren't very clean, for the purpose of the arcade, it is very usefull. Some functions need to access other interfaces and by using global variables, this is simplified a lot. The most used global variable is the let interfaces, and looks like:
let interfaces = {
    hub:{active: false, object:{}},
    menu:{active: false, object:{}},
    game:{interval: null, object:{}},
    highscores:{active: false, object:{}},
    howtoplay:{active: false, object:{}},
    esc:{active: false, object:{}}
}

Move

The biggest problem faced every game was the movement. This usually took up many lines of code, because our needs changed per game. We would often find ourselves copying code from other games, which included bugs which needed fixing. This meant that we would need to find every chuck of that code and fix it, which was tedious. This was because most often, keychecking systems would work agains each other, as we needed a way of checking:

  • A single press
  • A continuous press
  • A keypress cooldown (preventing spamming the key)

Implementation

The move interface was to prove very usefull and fast for development. Code which was previously at least 10 lines for a keycheck, would now only take up about 10 characters.

class {
    // ...
    this.ispressed = false
    // ...
    function update() {
        if ((map.Button1 || map[keycode["a"]]) && !ispressed) {
            this.ispressed = true
        } else if (!(map.Button1 || map[keycode["a"]])){
            this.ispressed = true
        }
    }
}

Lots of code, unmaintainable

if (move.up){
    // ...
}

Easy as pie

But what about the other things? how can I check for a continuous press? And what about that cooldown? Not a problem anymore. move contains ways to enable that, and more. Controller support was also added, together with multiplayer, if your game wanted it.

move.continuous = true;
move.cooldown = true;

if (move.up){
    // ...
}

move.multiplayer = true
if (move.p1.up || move.p2.up){
    // ...
}
Besides all that, one could also give a callback, which would execute code only if a key was pressed, removing constant double keychecking. This is because move has its own interval.

Readability

The code for move was constant in flux, as new things has to be added all the time. This had made the code extremely unreadable. A lot of redundant code meant a huge object of mostly repeating paterns, creating a hell if a single change was made, since every recurrance had to reflect the change. So the keychecking system had to be OOP. A key could be instanciated four times for four keys, and that was it.

The code went from something I will spare you, to:

class MovePlayer{
	constructor(keybinding, controllerID){
		this.keybinding = keybinding || "wasd";
		this.controllerID = controllerID || null;
		this._continuous = false;

		this._left = new MoveKey();
		this._right = new MoveKey();
		this._up = new MoveKey();
		this._down = new MoveKey();
	}
    // ...
}
After which the move system could just be something as simple as:
class Move{
	constructor(){
		this._multiplayer = false;
		this.player = [new MovePlayer()];
	}
}
And all the internal logic would handle the move.up calls. When I look at the code now, I can still see enhancements, but this is so much better than what it once was.

Interval

The interval system is absolutely trivial, but very essential. This is because the builtin function setInterval can only stop, not pause. Using this interval, we could create a pause menu, the EscInterface. In itself it is just a loop, which checks if it is are paused, and if not, executes the given task. It looks and types just like the normal function setInterval, but without the update speed parameter:

let interval = new Interval(() => {
    console.log("this is a test");
});

interval.pause();
interval.resume();
interval.stop();

HubInterface

The hubinterface was the first of all systems to be designed. The design today is the same as the first version, and initially only bundled the first two games. It bugged frequently, as the class Interval did not exist at that time, and often would not stop drawing, even when the game was loaded. Of all the interfaces available, it is one of the most buggiest still.

The way of loading the hub went through a lot of changes, which meant constantly updating the code in other games when exiting them. As of this moment, there are two ways of loading the hub:

// The following creates a fake loading bar before starting the hub.
loadingBar("hub", "new HubInterface")

// The way of loading the hub without loadingbar.
loadHub()

Two ways of loading the same thing is not only redundant, but also confusing, and most definately a learning moment for new projects.

HighScoresInterface

HighScoresInterface is a very complex system, using local storage as database (although at some point, a server database was planned). It only had to be fed the game name and the score the player achieved, and it would do the rest for you. You could enter your name, completely without a keyboard and view the other highscores achieved by either yourself, or others.

The following code, would result in the following images, where a score of 0 would take you to the second image immediately:

new HighScoresInterface("Asteroids", 220);