Question

I'm writing a simple game in JavaScript. I was using a single JS file which turned into a mess. So I'm refactoring it into a couple of classes. But I'm running into an issue with some vars becoming 'null'.

In the redraw function the "this.ctx" variable is null. I don't understand why it is null... in the launchGame method it initialises correctly.

var Game = {
    username : "",
    password : "",
    state : "login",

    interactables : [],
    interactablesOffset : 0,
    c : document.getElementById('gameCanvas'),
    ctx : null,

    usernameInput : "", passwordInput : "", loginBtn : "", loginBtnText : "Login",

    //Initialises handlers and launches the game cycle
    launchGame : function () {
        //Initialize
        this.c.addEventListener("click", this.handleClick, false);
        this.ctx = this.c.getContext('2d');

        setInterval(gameInstance.redraw, 300);
    },

    //Redraws the game every 300 ms
    redraw: function () {
        this.interactablesOffset = 0;
        var user = this.usernameInput != null ? this.usernameInput.value() : "";
        var pw = this.passwordInput != null ? this.passwordInput.value() : "";
        **this.ctx.clearRect(0, 0, 800, 800);//this.ctx is null**
        switch (state) {
            case "login":
                displayLoginMenu(user, pw);
                break;
        }
    },

    //Displays the login menu
    displayLoginMenu : function (_username, _password) {
        ctx.font = "bold 14px sans-serif";
        ctx.fillText("Username:", 5, 70);
        usernameInput = new CanvasInput({ canvas: document.getElementById('gameCanvas'), x: 100, y: 50, value: _username });

        ctx.font = "bold 14px sans-serif";
        ctx.fillText("Password:", 5, 120);
        passwordInput = new CanvasInput({ canvas: document.getElementById('gameCanvas'), x: 100, y: 100, value: _password });

        loginBtn = Object.create(BaseButton);
        loginBtn.x = 100;
        loginBtn.y = 150;
        loginBtn.width = 162;
        loginBtn.height = 25;
        this.addIteractable(obj);

        this.paintInteractables();
    },

    //Paints the interactables to the screen
    paintInteractables : function () {
        for(var i = 0; i < interactables.length; i++)    {
            var obj = interactables[i];
            if(obj !=null)
                obj.paint();
        }
    },
    //Adds an interactable component to the screen
    addIteractable : function addIteractable(obj) {
        interactables[interactablesOffset] = obj;
        interactablesOffset++;
    },

    //Handles clicks on game
    handleClick : function (e) {
        console.log('click: ' + e.offsetX + '/' + e.offsetY);
        for (var i = 0; i < interactables.length; i++) {
            var element = interactables[i];
            if (e.offsetX >= element.x && e.offsetX <= element.x + element.w
                && e.offsetY >= element.y && e.offsetY <= element.y + element.h) {
                console.log("clickBtn");
                interactables[i].pressed();
            }
        }
    }

}

var gameInstance = Object.create(Game);
gameInstance.launchGame();
Was it helpful?

Solution

You are loosing context in this code:

setInterval(gameInstance.redraw, 300);

so gameInstance.redraw is going to be called in global object context (window). It means that this inside of it is going to point not to a Game instance but to window object.

To fix it you can:

launchGame : function () {
    //Initialize
    this.c.addEventListener("click", this.handleClick, false);
    this.ctx = this.c.getContext('2d');

    var self = this;
    setInterval(function() {
        self.redraw();
    }, 300);
},

or

setInterval(this.redraw.bind(this), 300);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top