Welcome!

Join our community of MMO enthusiasts and game developers! By registering, you'll gain access to discussions on the latest developments in MMO server files and collaborate with like-minded individuals. Join us today and unlock the potential of MMO server development!

Join Today!

9.) MMO From Scratch - Spring Cleaning

Joined
Jun 8, 2007
Messages
1,985
Reaction score
490
Previous (Slire Herb)
Table of Contents (Introduction)
Next (Inventory)

Welp, this HTML5 thing still isn't a game- but I'll say we've reached a check-point. Good job! In this tutorial we'll sort the sprites, move some files around, and refactor a little.

Spring Cleaning, is that time we sit back and look at the mess we've gotten ourselves into, and clean up that mess a little bit. Little optimizations might happen, little bug fixes may happen, architectural problems may be solved here, etc. Every now and then, programmers should just sit back and look at their code from a different perspective. It's part of a healthy iterative software development life-cycle.

Before we start cleaning, let's add that sort feature right quick.

To sort players and herbs in Phaser, we have to put them all into the same group. This is annoying... My first go, long before I started these tutorials was chaotic. I had already put herbs in one group, players in another group, and sheep in another group. After that I tried to sort, and researched online only to be disappointed by Phaser's inability to sort sprites from several different groups. Lucky for you, I've already lived through that heartache, and learned from it.

Don't worry- I didn't get that far into this project before posting these tutorials, and I'm 100% positive I already made some mistakes, and you'll see me catch my failures soon enough.

For now, though, we must visit all of our sprites and add them all to the same group. So in /public/js/main.js around line 10 or so, put this line of code:
Code:
main.objects;

And in the create function, around line 20-23 or so, put this line of code:
Code:
main.objects = main.game.add.group();

Now we need to find all the times we made things sprites, and change that so we're adding the sprites to the main.objects group instead. The line in player.js code: /public/js/objects/player.js:45
Code:
self.player [COLOR=#666666]=[/COLOR] main.objects.create([COLOR=#666666]250[/COLOR], [COLOR=#666666]250[/COLOR], [COLOR=#BA2121]'player'[/COLOR]);
The line for other players: /public/js/objects/others.js:84-88
Code:
[COLOR=#000000]others[data.username].sprite [/COLOR][COLOR=#666666]=[/COLOR][COLOR=#000000] main.objects.create(
    data.game.x, 
    data.game.y, 
    [/COLOR][COLOR=#BA2121]'player'[/COLOR][COLOR=#000000]
);[/COLOR]
And the two lines for herbs: /public/js/objects/herbs.js:14,20
Code:
main.objects.create(herb.place[[COLOR=#666666]0[/COLOR]], herb.place[[COLOR=#666666]1[/COLOR]], herb.name);

Then we can try to sort at the very bottom of the update function in /public/js/objects/main.js
Code:
    [COLOR=#008000][B]function[/B][/COLOR] update() {
        plugins.forEach([COLOR=#008000][B]function[/B][/COLOR] (plugin) {
            plugin.update();
        });
        main.objects.sort([COLOR=#BA2121]'bottom'[/COLOR], Phaser.Group.SORT_ASCENDING);
    }

And test... Hm, strange. Everything is behind the map tiles now! Why is that? Oh, I see. We created the main.objects group before we ran any create code from the plugins- even before we ran the create code from the map tiles plugin... So, naturally, phaser displays objects on the stage in the order they are created. We created the main.objects group before the map tiles, so map tiles are drawn later- or on top of main.objects.

In the last tutorial, I said I considered something a game object if it is drawn on the game stage, and it is not a map tile. So, why did I put map.js inside of the objects folder? Let's move map.js somewhere else, and we shouldn't make it a plug-in, either. I'll make a folder, /public/js/maps, and move maps.js there. Then update the index.html file's include script for map.js... and take it out of the plug-in code in /public/js/main.js....
Code:
[COLOR=#008000][B]function[/B][/COLOR] initializeGame (main) {
[B]    [COLOR=#008000][B]var[/B][/COLOR] sortInterval [COLOR=#666666]=[/COLOR] [COLOR=#666666]100[/COLOR];
    [COLOR=#008000][B]var[/B][/COLOR] lastSort [COLOR=#666666]=[/COLOR] [COLOR=#666666]0[/COLOR];[/B]
    main.game [COLOR=#666666]=[/COLOR] [COLOR=#008000][B]new[/B][/COLOR] Phaser.Game([COLOR=#666666]1000[/COLOR], [COLOR=#666666]450[/COLOR], Phaser.CANVAS, [COLOR=#BA2121]'canvas'[/COLOR], {
        preload[COLOR=#666666]:[/COLOR] preload, 
        create[COLOR=#666666]:[/COLOR] create,
        update[COLOR=#666666]:[/COLOR] update,
        render[COLOR=#666666]:[/COLOR] render
    });
[B]    main.objects;
    main.map [COLOR=#666666]=[/COLOR] [COLOR=#008000][B]new[/B][/COLOR] Map(main);
    [COLOR=#008000][B]var[/B][/COLOR] plugins [COLOR=#666666]=[/COLOR] [[COLOR=#008000][B]new[/B][/COLOR] Player(main), [COLOR=#008000][B]new[/B][/COLOR] Others(main), [COLOR=#008000][B]new[/B][/COLOR] Herbs(main)];[/B]

    [COLOR=#008000][B]function[/B][/COLOR] preload() {
        main.game.time.advancedTiming [COLOR=#666666]=[/COLOR] [COLOR=#008000][B]true[/B][/COLOR];
        main.game.stage.disableVisibilityChange [COLOR=#666666]=[/COLOR] [COLOR=#008000][B]true[/B][/COLOR];
        [B]main.map.preload();[/B]
        plugins.forEach([COLOR=#008000][B]function[/B][/COLOR] (plugin) {
            plugin.preload();
        });
    }
    [COLOR=#008000][B]function[/B][/COLOR] create() {
[B]        main.map.create();
        main.objects [COLOR=#666666]=[/COLOR] main.game.add.group();[/B]
        plugins.forEach([COLOR=#008000][B]function[/B][/COLOR] (plugin) {
            plugin.create();
        });
        comms.emit([COLOR=#BA2121]'game-ready'[/COLOR], [COLOR=#008000][B]true[/B][/COLOR]);
    }
    [COLOR=#008000][B]function[/B][/COLOR] update() {
[B]        [COLOR=#008000][B]var[/B][/COLOR] now [COLOR=#666666]=[/COLOR] [COLOR=#008000]Date[/COLOR].now();
        main.map.update();[/B]
        plugins.forEach([COLOR=#008000][B]function[/B][/COLOR] (plugin) {
            plugin.update();
        });
[B]        [COLOR=#008000][B]if[/B][/COLOR] (lastSort [COLOR=#666666]+[/COLOR] sortInterval [COLOR=#666666]<[/COLOR] now) {
            lastSort [COLOR=#666666]=[/COLOR] now;
            main.objects.sort([COLOR=#BA2121]'bottom'[/COLOR], Phaser.Group.SORT_ASCENDING);
        }[/B]
    }
    [COLOR=#008000][B]function[/B][/COLOR] render () {
        main.game.debug.text(main.game.time.fps [COLOR=#666666]+[/COLOR] [COLOR=#BA2121]'fps'[/COLOR], [COLOR=#666666]2[/COLOR], [COLOR=#666666]15[/COLOR], [COLOR=#BA2121]"#00ff00"[/COLOR]);
[B]        main.map.render();[/B]
        plugins.forEach([COLOR=#008000][B]function[/B][/COLOR] (plugin) {
            plugin.render();
        });
    }
    
}

It turns out that sorting becomes expensive as there are more sprites.. Who knew? xD So I throttled the sort to 100ms, which is how long a player's animation takes, so that should be acceptable. It keeps my browser at 60fps with 2,191 sprites, and my computer isn't beefy. I'd much rather have a 'sort as you go' approach, where when a move-able sprite walks it just swaps that sprite in respect to other sprites when they move up or down.. And that is entirely possible- but we'll put that off until later. A wise man once said, premature optimization is the root of all evil.

Well, I suppose the sorting feature works as-is for now.

Wait a minute, 2,191 sprites!? These herbs are getting out of hand! >.< There's no doubt an herb's reproductive organs are functioning properly. Anyway, I won't fuss with the herbs right now.

Something else is bugging me- the plugin system. I have 3 plugins, and half the functions the plugin interface requires are just blank functions there for the sake of being required. I want to solve this problem now before I get too far into this project.

So, I suppose I'll create a file /public/js/plugins.js. We can make 4 arrays, one for each of the Phaser functions. We'll check if a plugin has any of the 4 phaser functions, and fill each array accordingly. Then we'll have a method for each of the phaser functions in the Plugins class thing, cycle through the appropriate array, and run each of the plugin's corresponding function in order. Order is important in phaser.. Hence, why we're using 4 arrays for this instead of one object for each plugin. We want to be able to pass n number of args to this class, and add each of those args. We'll make a public add method, so plugins might be added later. I should make a remove method, too, but that would be hard, and I don't have a need to remove plugins right now, so I won't do that until a need arises.
Code:
[COLOR=#008000][B]function[/B][/COLOR] Plugins() {
    [COLOR=#BA2121]"use strict"[/COLOR];
    [COLOR=#008000][B]var[/B][/COLOR] self [COLOR=#666666]=[/COLOR] [COLOR=#008000][B]this[/B][/COLOR];
    [COLOR=#008000][B]var[/B][/COLOR] args [COLOR=#666666]=[/COLOR] [COLOR=#008000]Array[/COLOR].apply([COLOR=#008000][B]null[/B][/COLOR], arguments);
    [COLOR=#008000][B]var[/B][/COLOR] preloads [COLOR=#666666]=[/COLOR] [];
    [COLOR=#008000][B]var[/B][/COLOR] creates [COLOR=#666666]=[/COLOR] [];
    [COLOR=#008000][B]var[/B][/COLOR] updates [COLOR=#666666]=[/COLOR] [];
    [COLOR=#008000][B]var[/B][/COLOR] renders [COLOR=#666666]=[/COLOR] [];
    [COLOR=#008000][B]function[/B][/COLOR] run (fn) {
        fn();
    }
    self.add [COLOR=#666666]=[/COLOR] [COLOR=#008000][B]function[/B][/COLOR] (plugin) {
        [COLOR=#008000][B]if[/B][/COLOR] ([COLOR=#008000][B]typeof[/B][/COLOR] plugin.preload [COLOR=#666666]===[/COLOR] [COLOR=#BA2121]'function'[/COLOR]) {
            preloads.push(plugin.preload);
        }
        [COLOR=#008000][B]if[/B][/COLOR] ([COLOR=#008000][B]typeof[/B][/COLOR] plugin.create [COLOR=#666666]===[/COLOR] [COLOR=#BA2121]'function'[/COLOR]) {
            creates.push(plugin.create);
        }
        [COLOR=#008000][B]if[/B][/COLOR] ([COLOR=#008000][B]typeof[/B][/COLOR] plugin.update [COLOR=#666666]===[/COLOR] [COLOR=#BA2121]'function'[/COLOR]) {
            updates.push(plugin.update);
        }
        [COLOR=#008000][B]if[/B][/COLOR] ([COLOR=#008000][B]typeof[/B][/COLOR] plugin.render [COLOR=#666666]===[/COLOR] [COLOR=#BA2121]'function'[/COLOR]) {
            renders.push(plugin.render);
        }
    };
    self.preload [COLOR=#666666]=[/COLOR] [COLOR=#008000][B]function[/B][/COLOR] () {
        preloads.forEach(run);
    };
    self.create [COLOR=#666666]=[/COLOR] [COLOR=#008000][B]function[/B][/COLOR] () {
        creates.forEach(run);
    };
    self.update [COLOR=#666666]=[/COLOR] [COLOR=#008000][B]function[/B][/COLOR] () {
        updates.forEach(run);
    };
    self.render [COLOR=#666666]=[/COLOR] [COLOR=#008000][B]function[/B][/COLOR] () {
        renders.forEach(run);
    };
    args.forEach(self.add);
}

This is elegant enough right now. So now I can go through all the JS files in the /public/js/objects folder and remove the frivolous empty functions. I need to update /public/js/main.js to reflect the new plugins system.

Code:
[COLOR=#008000][B]function[/B][/COLOR] initializeGame (main) {
    [COLOR=#008000][B]var[/B][/COLOR] sortInterval [COLOR=#666666]=[/COLOR] [COLOR=#666666]100[/COLOR];
    [COLOR=#008000][B]var[/B][/COLOR] lastSort [COLOR=#666666]=[/COLOR] [COLOR=#666666]0[/COLOR];
    main.game [COLOR=#666666]=[/COLOR] [COLOR=#008000][B]new[/B][/COLOR] Phaser.Game([COLOR=#666666]1000[/COLOR], [COLOR=#666666]450[/COLOR], Phaser.CANVAS, [COLOR=#BA2121]'canvas'[/COLOR], {
        preload[COLOR=#666666]:[/COLOR] preload, 
        create[COLOR=#666666]:[/COLOR] create,
        update[COLOR=#666666]:[/COLOR] update,
        render[COLOR=#666666]:[/COLOR] render
    });
    main.objects;
    main.map [COLOR=#666666]=[/COLOR] [COLOR=#008000][B]new[/B][/COLOR] Map (main);
    [COLOR=#008000][B]var[/B][/COLOR] plugins [COLOR=#666666]=[/COLOR] [COLOR=#008000][B]new[/B][/COLOR] Plugins(
        [COLOR=#008000][B]new[/B][/COLOR] Player(main),
        [COLOR=#008000][B]new[/B][/COLOR] Others(main), 
        [COLOR=#008000][B]new[/B][/COLOR] Herbs(main)
    );
    [COLOR=#008000][B]function[/B][/COLOR] preload() {
        main.game.time.advancedTiming [COLOR=#666666]=[/COLOR] [COLOR=#008000][B]true[/B][/COLOR];
        main.game.stage.disableVisibilityChange [COLOR=#666666]=[/COLOR] [COLOR=#008000][B]true[/B][/COLOR];
        main.map.preload();
        plugins.preload();
    }
    [COLOR=#008000][B]function[/B][/COLOR] create() {
        main.map.create();
        main.objects [COLOR=#666666]=[/COLOR] main.game.add.group();
        plugins.create();
        comms.emit([COLOR=#BA2121]'game-ready'[/COLOR], [COLOR=#008000][B]true[/B][/COLOR]);
    }
    [COLOR=#008000][B]function[/B][/COLOR] update() {
        [COLOR=#008000][B]var[/B][/COLOR] now [COLOR=#666666]=[/COLOR] [COLOR=#008000]Date[/COLOR].now();
        main.map.update();
        plugins.update();
        [COLOR=#008000][B]if[/B][/COLOR] (lastSort [COLOR=#666666]+[/COLOR] sortInterval [COLOR=#666666]<[/COLOR] now) {
            lastSort [COLOR=#666666]=[/COLOR] now;
            main.objects.sort([COLOR=#BA2121]'bottom'[/COLOR], Phaser.Group.SORT_ASCENDING);
        }
    }
    [COLOR=#008000][B]function[/B][/COLOR] render () {
        main.game.debug.text(main.game.time.fps [COLOR=#666666]+[/COLOR] [COLOR=#BA2121]'fps'[/COLOR], [COLOR=#666666]2[/COLOR], [COLOR=#666666]15[/COLOR], [COLOR=#BA2121]"#00ff00"[/COLOR]);
        main.map.render();
        plugins.render();
    }
}

I feel better now. I'll call it a day for Spring Cleaning. We'll continue developing like this in the future. We'll write code, test, write code, test, write code, test, then do spring cleaning every once in a while. We won't have predefined checkpoints or deadlines, we'll just keep going until we get a certain level of annoyed with the code we wrote in the past, and once we reach a certain level of boiling point, do spring cleaning.

Previous (Slire Herb)
Table of Contents (Introduction)
Next (Inventory)
 
Last edited:
Back
Top