4.) MMO From Scratch - The Client

Results 1 to 1 of 1
  1. #1
    :-) s-p-n is offline
    DeveloperRank
    Jun 2007 Join Date
    Next DoorLocation
    2,146Posts

    4.) MMO From Scratch - The Client


    RaGEZONE Recommends

    RaGEZONE Recommends

    Previous (Plant)
    Table of Contents (Introduction)
    Next (Players)

    The project we have up until this point is on github:
    https://github.com/s-p-n/surfaceTension/tree/master/1

    The client for Surface Tension uses jQuery, Socket.io and Phaser to render a game world in HTML5.

    The client is responsible for storing cookies, accepting packets from the server, and sending commands to the server. We're using the authoritative server, and dumb client approach for this project. We're going to talk about latency correction, and we'll simulate lag to test that it works.

    Let us start with the cookies library. I used some code from QuirksMode.org for this project, and modified it for my needs.
    /public/js/cookies.js
    Code:
    var cookies = {
        create: function (name, value, days) {
            var date, expires;
            if (days) {
                date = new Date();
                date.setTime(date.getTime()+(days*24*60*60*1000));
                expires = "; expires="+date.toGMTString();
            } else {
                expires = "";
            }
            document.cookie = name+"="+value+expires+"; path=/";
        },
        read: function (name) {
            var nameEQ = name + "=";
            var ca = document.cookie.split(';');
            var i, c;
            for(i=0;i < ca.length;i++) {
                c = ca[i];
                while (c.charAt(0)==' ') {
                    c = c.substring(1,c.length);
                }
                if (c.indexOf(nameEQ) == 0) {
                    return c.substring(nameEQ.length,c.length);
                }
            }
            return null;
        },
        erase: function (name) {
            cookies.create(name, "", -1);
        }
    }
    I'll be using Socket.io to send/receive forms, and to send HTML content. I create a variable called 'comms' which is short for communications, that is the socket. When a form with [method="socket"] is submitted, the action of that form is used to determine the name of the socket channel to send to the server, and the form data is serialized and sent as well. Checkboxes are converted to true/false values. I'll update this function later to suit our needs. The server can send content to the client, which has a selector (the CSS selector for jQuery to use), and the HTML to put inside of the element(s) that is/are selected.
    /public/js/io.js
    Code:
    var comms = (function () {
        var socket = io();
    
        $('html').on('submit', 'form[method="socket"]', function (e) {
            var formData = [];
            $(this).find('input[name]').each(function () {
                var type = $(this).attr('type');
                var result = {name: $(this).attr('name'), value: void 0};
                switch (type) {
                    case 'checkbox':
                        result.value = this.checked.toString();
                        break;
                    default:
                        result.value = $(this).val();
                }
                formData.push(result);
            });
            console.log('action:', $(this).attr('action'), formData);
            socket.emit($(this).attr('action'), formData);
            e.stopPropagation();
            e.preventDefault();
            return false;
        });
    
        socket.on('content', function (data) {
            console.log("content:", data);
            $(data.selector).html(data.html);
        });
    
        socket.on('cookie', function (data) {
            console.log("setting cookie:", data);
            cookies.create(data.name, data.value, data.days);
        });
    
        return socket;
    }());
    The game client code will be in /public/js/main.js

    Here is the index.html:
    Code:
    <!doctype html>
    <html>
    <head>
        <meta charset="UTF-8" />
        <title>Surface Tension</title>
        <link rel="stylesheet" href="/css/main.css"></link>
    </head>
    <body>
    <div id="gameShell">
        <div id="canvas">
            
        </div>
        <div id="rightPanel"></div>
        <div style="clear:both;"></div>
        <div id="bottomPanel">
            <div id="chatPanel">
                <div id="chatBox">
    
                </div>
                <div id="chatBtn">
                </div>
            </div>
            <div id="infoBox">
            </div>
        </div>
    </div>
        <script src="/js/cookies.js"></script>
        <script src="/socket.io/socket.io.js"></script>
        <script src="/js/jquery.js"></script>
        <script src="/js/io.js"></script>
        <script src="/js/phaser.min.js"></script>
        <script src="/js/main.js"></script>
    </body>
    </html>
    So I went over the custom libraries, all of the files are on github, so now we'll work on /public/js/main.js.

    If you were to run the server and view this in a browser now, it wouldn't be that interesting. Let's change that in this tutorial.

    Now, in /public/js/main.js - we're going to start phaser.
    Code:
    var game = new Phaser.Game(1000, 450, Phaser.AUTO, 'canvas', {
        preload: preload, 
        create: create,
        update: update,
        render: render
    });
    
    function preload() {}
    function create() {}
    function update() {}
    function render() {}
    I'll summarize how Phaser works very quickly.

    • Preload is done before the canvas is created- that's where you start loading all your assets and stuff.
    • Create is done after the canvas is created- and is where you start creating your game objects.
    • Update is run on every tick of the game-loop. All of the code in update should execute in under 16ms.
    • Render is run on every tick, right after update. We should only use render for testing purposes.


    We're going to use a plugin style to get our game objects to Phaser. Let's start with the grass background.
    First, let's make a folder to put our game objects- /public/js/objects
    Second, let's create a file called /public/js/objects/map.js
    We'll use this skeleton for all of our objects:
    Code:
    function Map (main) {
        "use strict";
        var self = this;
        self.preload = function () {};
        self.create = function () {};
        self.update = function () {};
        self.render = function () {};
    }
    Everytime we create a new public JS file, we should include it in index.html before we include main.js
    Code:
    <script src="/js/objects/map.js"></script>
    Now, we don't want to load the game before the user logs in, so we should put everything in main.js inside of a function to be executed later. We're going to have several game plugins each with a preload, create, update, and render. So we're going to put all of these plugins into an array, and loop through each of them and execute the corresponding functions for Phaser.

    /public/js/main.js
    Code:
    function initializeGame (main) {
        main.game = new Phaser.Game(1000, 450, Phaser.AUTO, 'canvas', {
            preload: preload, 
            create: create,
            update: update,
            render: render
        });
    
        var plugins = [new Map(main)];
    
        function preload() {
            plugins.forEach(function (plugin) {
                plugin.preload();
            });
        }
        function create() {
            plugins.forEach(function (plugin) {
                plugin.create();
            });
        }
        function update() {
            plugins.forEach(function (plugin) {
                plugin.update();
            });
        }
        function render() {
            plugins.forEach(function (plugin) {
                plugin.render();
            });
        }
    }
    Now, all of the game plugins may share and manipulate game state.

    Let's load some grass. Our grass graphic is in /public/assets/game/grass.png. To load an image in phaser, we use game.load.image(<name:string>, <srcImage:urlString>). If you notice, we pass main to every plugin, and we assign the game variable to main. So in the plugin, we can simply use main.game to get the game object.
    /public/js/objects/map.js:[4-6]
    Code:
        self.preload = function () {
            main.game.load.image('grass', './assets/game/grass.png');
        };
    We should create a group for map tiles since there are going to be more than one of these, and they all have the same physics specifications. After that, we'll try to add a grass tile to the map. We do that in create.
    /public/js/objects/map.js[7-10]
    Code:
        self.create = function () {
            self.map = main.game.add.group();
            self.map.create(0, 0, 'grass');
        };
    Now (if we call the initializeGame(..) in main.js) we have a single grass tile (512x512). That's great, but let's get a login working, and a database set up so that we can start loading assets from the server.

    Previous (Plant)
    Table of Contents (Introduction)
    Next (Players)
    Last edited by s-p-n; 29-01-16 at 08:59 PM.





Advertisement