0

So i am trying to implement simple touch controls on a javascript game. I have the following answer from a search:

Snake Game with Controller Buttons for Mobile Use **UPDATED**

However I was trying to change this jquery into javascript so that it would work with my game

Jquery:

    $(document).on('click', '.button-pad > button', function(e) {
            if ($(this).hasClass('left-btn')) {
                e = 37;
            }

Javascript:

     var contoller = document.getElementById("button-pad").on('click', 
     '.button-pad > button', function(e) {
                if ('.button-pad > button'(this).hasClass('btn-left')) {
                     e = 37;
                 }

I thought I had it sorted but it is not working at all

Codepen here:

https://codepen.io/MrVincentRyan/pen/VqpMrJ?editors=1010

16
  • 3
    There is no native version of on() for Elements. Have you done any web searches for native javascript event delegation? Commented Dec 27, 2018 at 17:53
  • 3
    Also if ('.button-pad > button'(this).hasClass('btn-left')) { is completely syntatically broken Commented Dec 27, 2018 at 17:54
  • Ref. stackoverflow.com/questions/1687296/… Commented Dec 27, 2018 at 18:00
  • 1
    @Cryptopat While onXyz element properties do work, their use is widely discouraged as they don't provide a robust way to managed events. Instead. always use .addEventListener(). Commented Dec 27, 2018 at 18:18
  • 1
    Interesting. New things to think about (and continue to avoid all together), heh. @ScottMarcus Commented Dec 27, 2018 at 19:05

3 Answers 3

3

Your existing code has some problems with it, but it was close enough where I could translate it. However, your current code seems to want to reassign the event argument being passed to the click handler (e) to 37. This makes no sense. Most likely you just want another variable set to 37 and that's what I've done below:

spaceInvader(window, document.getElementById('space-invader'));
window.focus();

let game = null;
let ship = null;

function spaceInvader (window, canvas) {

    canvas.focus();
    var context = canvas.getContext('2d');

    /* GAME */

    function Game () {
        this.message = '';
        this.rebel = [];
        this.republic = [];
        this.other = [];
        this.size = {x: canvas.width, y: canvas.height};
        this.wave = 0;

        this.refresh = function () {
            this.update();
            this.draw();
            requestAnimationFrame(this.refresh);
        }.bind(this);

        this.init();
    }
    Game.MESSAGE_DURATION = 1500;
    Game.prototype.init = function () {
        this.ship = new Ship(this);
        this.addRebel(this.ship);
        this.refresh();
    };
    Game.prototype.update = function () {
        this.handleCollisions();
        this.computeElements();
        this.elements.forEach(Element.update);
        if (!this.rebel.length) {
            this.showText('Gatwick closed', true);
            return;
        }
        if (!this.republic.length) this.createWave();
    };
    Game.prototype.draw = function () {
        context.clearRect(0, 0, this.size.x, this.size.y);
        this.elements.forEach(Element.draw);
        Alien.drawLife(this.republic);
        if (this.message) {
            context.save();
            context.font = '30px Arial';
            context.textAlign='center';
            context.fillStyle = '#FFFFFF';
            context.fillText(this.message, canvas.width / 2, canvas.height / 2);
            context.restore();
        }
    };
    Game.prototype.computeElements = function () {
        this.elements = this.other.concat(this.republic, this.rebel);
    };
    Game.prototype.addRebel = function (element) {
        this.rebel.push(element);
    };
    Game.prototype.addRepublic = function (element) {
        this.republic.push(element);
    };
    Game.prototype.addOther = function (element) {
        this.other.push(element);
    };
    Game.prototype.handleCollisions = function () {
        this.rebel.forEach(function(elementA) {
            this.republic.forEach(function (elementB) {
                if (!Element.colliding(elementA, elementB)) return;
                elementA.life--;
                elementB.life--;
                var sizeA = elementA.size.x * elementA.size.y;
                var sizeB = elementB.size.x * elementB.size.y;
                this.addOther(new Explosion(this, sizeA > sizeB ? elementA.pos : elementB.pos));
            }, this);
        }, this);
        this.republic = this.republic.filter(Element.isAlive);
        this.rebel = this.rebel.filter(Element.isAlive);
        this.other = this.other.filter(Element.isAlive);
        this.republic = this.republic.filter(this.elementInGame, this);
        this.rebel = this.rebel.filter(this.elementInGame, this);
    };
    Game.prototype.elementInGame = function (element) {
        return !(element instanceof Bullet) || (
            element.pos.x + element.halfWidth > 0 &&
            element.pos.x - element.halfWidth < this.size.x &&
            element.pos.y + element.halfHeight > 0 &&
            element.pos.y - element.halfHeight < this.size.x
        );
    };
    Game.prototype.createWave = function () {
        this.ship.life = Ship.MAX_LIFE;
        this.ship.fireRate = Math.max(50, Ship.FIRE_RATE - 50 * this.wave);
        this.wave++;
        this.showText('Wave: ' + this.wave);
        var waveSpeed = Math.ceil(this.wave / 2);
        var waveProb = (999 - this.wave * 2) / 1000;
        var margin = {x: Alien.SIZE.x + 10, y: Alien.SIZE.y + 10};
        for (var i = 0; i < 2; i++) {
            var x = margin.x + (i % 8) * margin.x;
            var y = -200 + (i % 3) * margin.y;
            this.addRepublic(new Alien(this, {x: x, y: y}, waveSpeed, waveProb));
        }
    };
    Game.prototype.showText = function (message, final) {
        this.message = message;
        if (!final) setTimeout(this.showText.bind(this, '', true), Game.MESSAGE_DURATION);
    };

    /* GENERIC ELEMENT */

    function Element (game, pos, size) {
        this.game = game;
        this.pos = pos;
        this.size = size;
        this.halfWidth = Math.floor(this.size.x / 2);
        this.halfHeight = Math.floor(this.size.y / 2);
    }
    Element.update = function (element) {
        element.update();
    };
    Element.draw = function (element) {
        element.draw();
    };
    Element.isAlive = function (element) {
        return element.life > 0;
    };
    Element.colliding = function (elementA, elementB) {
        return !(
            elementA === elementB ||
            elementA.pos.x + elementA.halfWidth < elementB.pos.x - elementB.halfWidth ||
            elementA.pos.y + elementA.halfHeight < elementB.pos.y - elementB.halfHeight ||
            elementA.pos.x - elementA.halfWidth > elementB.pos.x + elementB.halfWidth ||
            elementA.pos.y - elementA.halfHeight > elementB.pos.y + elementB.halfHeight
        );
    };

    /* SHIP */

    function Ship(game) {
        var pos = {
            x: Math.floor(game.size.x / 2) - Math.floor(Ship.SIZE.x / 2),
            y: game.size.y - Math.floor(Ship.SIZE.y / 2)
        };
        Element.call(this, game, pos, Ship.SIZE);
        this.kb = new KeyBoard();
        this.speed = Ship.SPEED;
        this.allowShooting = true;
        this.life = Ship.MAX_LIFE;
        this.fireRate = Ship.FIRE_RATE;
    }
    Ship.SIZE = {x: 67, y: 100};
    Ship.SPEED = 8;
    Ship.MAX_LIFE = 5;
    Ship.FIRE_RATE = 200;
    Ship.prototype.update = function () {
      if (this.kb.isDown(KeyBoard.KEYS.LEFT) && this.pos.x - this.halfWidth > 0) {
        this.pos.x -= this.speed;
      } else if (this.kb.isDown(KeyBoard.KEYS.RIGHT) && this.pos.x + this.halfWidth < this.game.size.x) {
        this.pos.x += this.speed;
      }
    
      if (this.allowShooting && this.kb.isDown(KeyBoard.KEYS.SPACE)) {
        var bullet = new Bullet(
          this.game,
          {x: this.pos.x, y: this.pos.y - this.halfHeight },
          { x: 0, y: -Bullet.SPEED },
          true
        );
        this.game.addRebel(bullet);
        this.toogleShooting();
      }
    };
    
    Ship.prototype.draw = function () {
        var img = document.getElementById('ship');
        context.save();
        context.translate(this.pos.x - this.halfWidth, this.pos.y - this.halfHeight);
        context.drawImage(img, 0, 0);
        context.restore();
        this.drawLife();
    };
    
    Ship.prototype.drawLife = function () {
        context.save();
        context.fillStyle = 'white';
        context.fillRect(this.game.size.x -112, 10, 102, 12);
        context.fillStyle = 'red';
        context.fillRect(this.game.size.x -111, 11, this.life * 100 / Ship.MAX_LIFE, 10);
        context.restore();
    };
    
    Ship.prototype.toogleShooting = function (final) {
        this.allowShooting = !this.allowShooting;
        if (!final) setTimeout(this.toogleShooting.bind(this, true), this.fireRate);
    };

    /* ALIENS */

    function Alien(game, pos, speed, shootProb) {
        Element.call(this, game, pos, Alien.SIZE);
        this.speed = speed;
        this.shootProb = shootProb;
        this.life = 3;
        this.direction = {x: 1, y: 1};
    }
    Alien.SIZE = {x: 51, y: 60};
    Alien.MAX_RANGE = 350;
    Alien.CHDIR_PRO = 0.990;
    Alien.drawLife = function (array) {
        array = array.filter(function (element) {
            return element instanceof Alien;
        });
        context.save();
        context.fillStyle = 'white';
        context.fillRect(10, 10, 10 * array.length + 2, 12);
        array.forEach(function (alien, idx) {
            switch (alien.life) {
                case 3:
                    context.fillStyle = 'green';
                    break;
                case 2:
                    context.fillStyle = 'yellow';
                    break;
                case 1:
                    context.fillStyle = 'red';
                    break;
            }
            context.fillRect(10 * idx + 11, 11, 10, 10);
        });
        context.restore();
    };
    Alien.prototype.update = function () {
        if (this.pos.x - this.halfWidth <= 0) {
            this.direction.x = 1;
        } else if (this.pos.x + this.halfWidth >= this.game.size.x) {
            this.direction.x = -1;
        } else if (Math.random() > Alien.CHDIR_PRO) {
            this.direction.x = -this.direction.x;
        }
        if (this.pos.y - this.halfHeight <= 0) {
            this.direction.y = 1;
        } else if (this.pos.y + this.halfHeight >= Alien.MAX_RANGE) {
            this.direction.y = -1;
        } else if (Math.random() > Alien.CHDIR_PRO) {
            this.direction.y = -this.direction.y;
        }
        this.pos.x += this.speed * this.direction.x;
        this.pos.y += this.speed * this.direction.y;

        if (Math.random() > this.shootProb) {
            var bullet = new Bullet(
                this.game,
                {x: this.pos.x, y: this.pos.y + this.halfHeight },
                { x: Math.random() - 0.5, y: Bullet.SPEED },
                false
            );
            this.game.addRepublic(bullet);
      }
    };
    Alien.prototype.draw = function () {
        var img = document.getElementById('fighter');
        context.save();
        context.translate(this.pos.x + this.halfWidth, this.pos.y + this.halfHeight);
        context.rotate(Math.PI);
        context.drawImage(img, 0, 0);
        context.restore();
    };

    /* BULLET */

    function Bullet(game, pos, direction, isRebel) {
        Element.call(this, game, pos, Bullet.SIZE);
        this.direction = direction;
        this.isRebel = isRebel;
        this.life = 1;

        try {
            var sound = document.getElementById('sound-raygun');
            sound.load();
            sound.play().then(function () {}, function () {});
        }
        catch (e) {
            // only a sound issue
        }
    }
    Bullet.SIZE = {x: 6, y: 20};
    Bullet.SPEED = 3;
    Bullet.prototype.update = function () {
        this.pos.x += this.direction.x;
        this.pos.y += this.direction.y;
    };
    Bullet.prototype.draw = function () {
        context.save();
        var img;
        if (this.isRebel) {
            context.translate(this.pos.x - this.halfWidth, this.pos.y - this.halfHeight);
            img = document.getElementById('rebel-bullet');
        }
        else {
            context.translate(this.pos.x + this.halfWidth, this.pos.y + this.halfHeight);
            img = document.getElementById('republic-bullet');
            context.rotate(Math.PI);
        }
        context.drawImage(img, 0, 0);
        context.restore();
    };

    /* EXPLOSION */

    function Explosion(game, pos) {
        Element.call(this, game, pos, Explosion.SIZE);
        this.life = 1;
        this.date = new Date();

        try {
            var sound = document.getElementById('sound-explosion');
            sound.load();
            sound.play().then(function () {}, function () {});
        }
        catch (e) {
            // only a sound issue
        }
    }
    Explosion.SIZE = {x: 115, y: 100};
    Explosion.DURATION = 150;
    Explosion.prototype.update = function () {
        if (new Date() - this.date > Explosion.DURATION) this.life = 0;
    };
    Explosion.prototype.draw = function () {
        var img = document.getElementById('explosion');
        context.save();
        context.translate(this.pos.x - this.halfWidth, this.pos.y - this.halfHeight);
        context.drawImage(img, 0, 0);
        context.restore();
    };

    /* KEYBOARD HANDLING */

    function KeyBoard() {
        var state = {};
        window.addEventListener('keydown', function(e) {
            state[e.keyCode] = true;
        });
        window.addEventListener('keyup', function(e) {
            state[e.keyCode] = false;
        });
        this.isDown = function (key) {
            return state[key];
        };
    }
    KeyBoard.KEYS = {
        LEFT: 37,
        RIGHT: 39,
        SPACE: 32
    };
   
   
    window.addEventListener('load', function() {
        game = new Game();

    });
    
   
// Get all the button elements that are children of elements that have 
// the .button-pad class and convert the resulting node list into an Array
let elements = 
  Array.prototype.slice.call(document.querySelectorAll('.button-pad button'));

// Loop over the array    
elements.forEach(function(el){

  el.textContent = "XXXX";
  // Set up a click event handler for the current element being iterated:
  el.addEventListener('click', function(e) {

    // When the element is clicked, check to see if it uses the left-btn class
    if(this.classList.contains('left-btn')) {

      // Perform whatever actions you need to:
      ship.update();
    }
  });
});  
   
}
<h1>Gatwick invaders</h1>
<p>Press <b>left arrow</b> to go left, <b>right arrow</b> to go right, and <b>space</b> to shoot...</p>
<canvas id="space-invader" width="640" height="500" tabindex="0"></canvas>
<img id="fighter" src="https://raw.githubusercontent.com/MrVIncentRyan/assets/master/drone1.png" />
<img id="ship" src="https://raw.githubusercontent.com/MrVIncentRyan/assets/master/cop1.png" />
<img id="rebel-bullet" src="https://raw.githubusercontent.com/OlivierB-OB/starwars-invader/master/rebelBullet.png" />
<img id="republic-bullet" src="https://raw.githubusercontent.com/OlivierB-OB/starwars-invader/master/republicBullet.png" />
<img id="explosion" src="https://raw.githubusercontent.com/OlivierB-OB/starwars-invader/master/explosion.png" />
<audio id="sound-explosion" src="https://raw.githubusercontent.com/OlivierB-OB/starwars-invader/master/explosion.mp3"></audio>
<audio id="sound-raygun" src="https://raw.githubusercontent.com/OlivierB-OB/starwars-invader/master/raygun.mp3"></audio>

	</div>

	<div class="button-pad">

		<div class="btn-up">
			<button type="submit" class="up">
		    	<img src="http://aaronblomberg.com/sites/ez/images/btn-up.png" />
		    </button>
		</div>
		
		<div class="btn-right">
			<button type="submit" class="right">
		    	<img src="http://aaronblomberg.com/sites/ez/images/btn-right.png" />
		    </button>
		</div>
		
		<div class="btn-down">
			<button type="submit" class="down">
		    	<img src="http://aaronblomberg.com/sites/ez/images/btn-down.png" />
		    </button>
		</div>
		
		<div class="btn-left">
			<button type="submit" class="left">
		    	<img src="http://aaronblomberg.com/sites/ez/images/btn-left.png" />
		    </button>
		</div>

	</div>

Sign up to request clarification or add additional context in comments.

18 Comments

37 is the button code for the left arrow on the keyboard. I am trying to use an onscreen button to have the same reaction as hitting left arrow.
@VincentRyan What would you expect the on screen action to be in this case? Instead of trying to trigger keystroke 37, we just perform the action that the keystroke would have done.
This is the action that would have been triggered. I thought it would be simpler to try trigger the key stroke: Ship.prototype.update = function () { if (this.kb.isDown(KeyBoard.KEYS.LEFT) && this.pos.x - this.halfWidth > 0) { this.pos.x -= this.speed;
@VincentRyan We can't trigger keystrokes in JavaScript, so we just perform the action(s) that the keystroke would have performed. See my updated answer that includes your code.
if(this.classList.contains('left-btn') { Unexpected token error.
|
1

A custom solution for emulating keypresses on mobile in both vanilla Javascript as well as jQuery!

// jQuery (edge), for use with ES2015-19
/*

$(document).on("click", ".example-btn", e => { // Click event handler
    if($(this).hasClass("example-btn")) { // Verifying that element has class
    e = 37
    jQuery.event.trigger({type: "keypress", which: character.charCodeAt(e)}) // Simulating keystroke

    // The following is simply for debugging, remove if needed
    alert("Button validation confirmed!")
    console.log("E: ", e)
  }
})

*/



// Pure Javascript (ECMA Standard)

document.querySelector(".example-btn").addEventListener("click", function(e) { // Click event handler
    if(this.classList.contains("example-btn")) { // Verifying that element has class
    e = 37

    if(document.createEventObject) {
        var eventObj = document.createEventObject();
      eventObj.keyCode = e;
      document.querySelector(".example-btn").fireEvent("onkeydown", eventObj);
    } else if(document.createEvent) {
        var eventObj2 = document.createEvent("Events");
      eventObj2.initEvent("keydown", true, true);
      eventObj2.which = e;
      document.querySelector(".example-btn").dispatchEvent(eventObj2);
    }

    // The following is simply for debugging, remove if needed
    alert("Button validation confirmed!");
    console.log("E: ", e);
  }
});



// ---------------------------------------------------------------------------------------------------
/* 
You can not use the "this" statement when referring to an embedded element. In your previous code "this" would refer to ".button-container > .example-btn" which the compiler will interpret as only the parent element, being .button-container (.button-pad in your code) not the child element in which you want. Also there is no such thing as returning a character code and expecting it to automatically know what to do with it. I assume you are doing this to emulate a keystroke on a mobile device and I assure you that this design works although it might be flawed. Give it a try and I hope it does something to at least help if not solve your problem. 
*/
// ---------------------------------------------------------------------------------------------------

4 Comments

Hey man thanks for this. I am indeed trying to emulate the button. This looks very promising. It is definitely doing something. However the ship element is still not moving. I'll work through it a little more
I get the confirmation "button validation confirmed!" but even when I remove that code it still doesn't make the ship move!
I think the issue is with e. So now if I have it trigger the same button. It triggers all functions to do with e so I get the ship flying to the right and firing continually although I have triggered go left....!
Hmmm... I'm not sure why it wouldn't move. Have you perhaps tried replacing the keydown keyword with keypress or keyup?
0

When an event listener is attached to an element, that listener is not unique for the element, but it propagates to its children. This functionality is enabled in jQuery by adding a parameter on an event listener a parameter that targets the element that we want. This is not case in vanillaJS, but using e.target we can inspect in which elements the event is executed.

Probably your are looking something like this. However, I would prefer to add an id in the button so you can more easily work with it.

document.addEventListener('click', function(e){
                if(e.target.tagName === 'BUTTON' && e.target.classList.value.includes('btn-left')){
                    // execute your code
                }
        });

4 Comments

But you will need to change e.target.tagName === 'BUTTON' to e.target.id = 'left'
Hmm this still doesn't seem to be moving the element:
document.addEventListener('click', function(e){ if(e.e.target.id = 'left' && e.target.classList.value.includes('left')){ // execute your code e = 37; } });
There is a typo. Use e.target.id

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.