Null Reference after restarting a State - javascript

I created a little game with Phaser for presentation purposes. After you've won or lost you can restart the game. This is done with states. When I try to fire a bullet after the game has been restarted, a null reference error occurs and the game freezes. It seems the null reference occurs because the this.game property is not set correctly in the Weapon classes after the state is restarted.
var PhaserGame = function () {
this.background = null;
this.stars = null;
this.player = null;
this.enemies = null;
this.cursors = null;
this.speed = 300;
this.weapons = [];
this.currentWeapon = 0;
this.weaponName = null;
this.score = 0;
};
PhaserGame.prototype = {
init: function () {
this.game.renderer.renderSession.roundPixels = true;
this.physics.startSystem(Phaser.Physics.ARCADE);
},
preload: function () {
this.game.time.advancedTiming = true;
},
create: function () {
this.background = this.add.tileSprite(0, 0, this.game.width, this.game.height, 'background');
this.background.autoScroll(-40, 0);
this.stars = this.add.tileSprite(0, 0, this.game.width, this.game.height, 'stars');
this.stars.autoScroll(-60, 0);
this.weapons.push(new Weapon.SingleBullet(this.game));
//this.weapons.push(new Weapon.FrontAndBack(this.game));
this.weapons.push(new Weapon.ThreeWay(this.game));
//this.weapons.push(new Weapon.EightWay(this.game));
this.weapons.push(new Weapon.ScatterShot(this.game));
this.weapons.push(new Weapon.Beam(this.game));
this.weapons.push(new Weapon.SplitShot(this.game));
//this.weapons.push(new Weapon.Pattern(this.game));
this.weapons.push(new Weapon.Rockets(this.game));
this.weapons.push(new Weapon.ScaleBullet(this.game));
//this.weapons.push(new Weapon.Combo1(this.game));
//this.weapons.push(new Weapon.Combo2(this.game));
this.currentWeapon = 0;
for (var i = 1; i < this.weapons.length; i++)
{
this.weapons[i].visible = false;
}
this.player = this.add.existing(new Spaceship(this.game, 100, 200, 'player'));
this.player.events.onKilled.add(this.toGameOver, this);
this.physics.arcade.enable(this.player);
this.player.body.collideWorldBounds = true;
this.player.animations.add('flame', [0, 1, 2, 3], 10, true);
this.player.animations.play('flame');
//Enemies
this.enemies = this.add.group();
//Enable Physics for Enemies
//this.physics.arcade.enable(this.enemies);
this.enemies.enableBody = true;
for (var i = 0; i < 24; i++) {
//create a star inside the group
var enemy = this.enemies.add(new Enemy(this.game, 1000 + (i * 50), 10 + Math.random() * 300, 'enemy'));
enemy.events.onKilled.add(this.raiseCounter, this);
}
//this.weaponName = this.add.bitmapText(8, 364, 'shmupfont', "ENTER = Next Weapon", 24);
// Cursor keys to fly + space to fire
this.cursors = this.input.keyboard.createCursorKeys();
this.input.keyboard.addKeyCapture([ Phaser.Keyboard.SPACEBAR ]);
var changeKey = this.input.keyboard.addKey(Phaser.Keyboard.ENTER);
changeKey.onDown.add(this.nextWeapon, this);
},
nextWeapon: function () {
// Tidy-up the current weapon
this.weapons[this.currentWeapon].visible = false;
this.weapons[this.currentWeapon].callAll('reset', null, 0, 0);
this.weapons[this.currentWeapon].setAll('exists', false);
// Activate the new one
this.currentWeapon++;
if (this.currentWeapon === this.weapons.length)
{
this.currentWeapon = 0;
}
this.weapons[this.currentWeapon].visible = true;
//this.weaponName.text = this.weapons[this.currentWeapon].name;
},
enemyHit: function (bullet, enemy) {
bullet.kill();
enemy.dealDamage(2);
},
playerHit: function (player, enemy) {
player.dealDamage(10);
enemy.dealDamage(1);
},
raiseCounter: function () {
this.score++;
},
toGameOver: function () {
this.game.state.start('GameOver', true, false, this.score);
},
update: function () {
//Framerate
this.game.debug.text(this.time.fps || '--', 2, 14, "#00ff00");
this.game.debug.text('Health: ' + this.player.health || 'Health: ---', 2, 30, "#00ff00");
this.game.debug.text('Counter: ' + this.score || 'Counter: ---', 2, 44, "#00ff00");
this.game.physics.arcade.overlap(this.weapons[this.currentWeapon], this.enemies, this.enemyHit, null, this);
this.game.physics.arcade.overlap(this.player, this.enemies, this.playerHit, null, this);
this.player.body.velocity.set(0);
this.enemies.setAll('body.velocity.x', -50);
if (this.cursors.left.isDown)
{
this.player.body.velocity.x = -this.speed;
}
else if (this.cursors.right.isDown)
{
this.player.body.velocity.x = this.speed;
}
if (this.cursors.up.isDown)
{
this.player.body.velocity.y = -this.speed;
}
else if (this.cursors.down.isDown)
{
this.player.body.velocity.y = this.speed;
}
if (this.input.keyboard.isDown(Phaser.Keyboard.SPACEBAR))
{
this.weapons[this.currentWeapon].fire(this.player);
}
}
};
The weapon-classes were taken from Phaser-Coding-Tips 7:
Weapon.SingleBullet = function (game) {
console.log(game);
Phaser.Group.call(this, game, game.world, 'Single Bullet', false, true, Phaser.Physics.ARCADE);
this.nextFire = 0;
this.bulletSpeed = 600;
this.fireRate = 200;
for (var i = 0; i < 64; i++)
{
this.add(new Bullet(game, 'bullet5'), true);
}
return this;
};
Weapon.SingleBullet.prototype = Object.create(Phaser.Group.prototype);
Weapon.SingleBullet.prototype.constructor = Weapon.SingleBullet;
Weapon.SingleBullet.prototype.fire = function (source) {
//Here occurs the problem, because this.game is null after restarting the state
if (this.game.time.time < this.nextFire) { return; }
var x = source.x + 50;
var y = source.y + 15;
this.getFirstExists(false).fire(x, y, 0, this.bulletSpeed, 0, 0);
this.nextFire = this.game.time.time + this.fireRate;
};
The problem occurs consistent in all Weapon classes after restarting the state.

In the beginning of the create-method before weapons is filled, i forgot to empty the array. The funny thing is: I tried emptying the array before and it didn't work. When i logged the length of weapons it suddenly started to work as expected. Maybe the cause was a strange optimization made by the javascript-engine.

Related

Custom webgl engine with Cannon.js , How to resolve jumping, blocking and walk

I look at stackoverflow other people similar problems a they have very similar solution but all examples are done in three.js (Not in my case).
Jump is relative good working at the moment, i powered gravity (double it).
Stairs also works fine even without jumping it is ok for me.
I put one big cube to make blocking volume but i dont now how to make player to simple stops because i use WASD for body set position. My player in big cube just have big jump , i wanna prevent WASD body set position in moment of first touch.
Some solutions [not yet tested]:
1] To make movement only with force [not set position] => [possible not nature movements]
2] Make collide detection then prevent set position => [possible angle problems]
3] Some other solution....
All related code is visible in example.
Matrix-engine example for FPS:
import App from '../program/manifest';
import * as CANNON from 'cannon';
import {ENUMERATORS, ORBIT_FROM_ARRAY, OSCILLATOR, randomFloatFromTo} from '../lib/utility';
export var runThis = (world) => {
// Camera
canvas.style.cursor = 'none';
App.camera.FirstPersonController = true;
matrixEngine.Events.camera.fly = false;
App.camera.speedAmp = 0.01;
matrixEngine.Events.camera.yPos = 2;
// Audio effects
App.sounds.createAudio('shoot', 'res/music/single-gunshot.mp3', 5);
// Prevent right click context menu
window.addEventListener("contextmenu", (e) => {
e.preventDefault();
});
// Override mouse up
App.events.CALCULATE_TOUCH_UP_OR_MOUSE_UP = () => {
App.scene.FPSTarget.glBlend.blendParamSrc = matrixEngine.utility.ENUMERATORS.glBlend.param[4];
App.scene.FPSTarget.glBlend.blendParamDest = matrixEngine.utility.ENUMERATORS.glBlend.param[4];
App.scene.FPSTarget.geometry.setScale(0.1);
App.scene.xrayTarget.visible = false;
};
// Override right mouse down
matrixEngine.Events.SYS.MOUSE.ON_RIGHT_BTN_PRESSED = (e) => {
App.scene.FPSTarget.geometry.setScale(0.6);
App.scene.FPSTarget.glBlend.blendParamSrc = matrixEngine.utility.ENUMERATORS.glBlend.param[5];
App.scene.FPSTarget.glBlend.blendParamDest = matrixEngine.utility.ENUMERATORS.glBlend.param[5];
App.scene.xrayTarget.visible = true;
};
// Override mouse down
App.events.CALCULATE_TOUCH_DOWN_OR_MOUSE_DOWN = (ev, mouse) => {
// `checkingProcedure` gets secound optimal argument
// for custom ray origin target.
if(mouse.BUTTON_PRESSED == 'RIGHT') {
// Zoom
} else {
// This call represent `SHOOT` Action.
// And it is center of screen
matrixEngine.raycaster.checkingProcedure(ev, {
clientX: ev.target.width / 2,
clientY: ev.target.height / 2
});
App.sounds.play('shoot');
}
};
window.addEventListener('ray.hit.event', (ev) => {
console.log("You shoot the object! Nice!", ev);
// Physics force apply also change ambienty light.
if(ev.detail.hitObject.physics.enabled == true) {
// Shoot the object - apply force
ev.detail.hitObject.physics.currentBody.force.set(0, 0, 1000);
// Apply random diff color
if(ev.detail.hitObject.LightsData) ev.detail.hitObject.LightsData.ambientLight.set(
randomFloatFromTo(0, 2), randomFloatFromTo(0, 2), randomFloatFromTo(0, 2));
}
});
// Load obj seq animation
const createObjSequence = (objName) => {
function onLoadObj(meshes) {
for(let key in meshes) {
matrixEngine.objLoader.initMeshBuffers(world.GL.gl, meshes[key]);
}
var textuteImageSamplers2 = {
source: [
"res/bvh-skeletal-base/swat-guy/textures/Ch15_1001_Diffuse.png" // ,
// "res/bvh-skeletal-base/swat-guy/textures/Ch15_1001_Diffuse.png"
],
mix_operation: "multiply", // ENUM : multiply , divide
};
var animArg = {
id: objName,
meshList: meshes,
currentAni: 0,
animations: {
active: 'walk',
walk: {
from: 0,
to: 20,
speed: 3
}
}
};
world.Add("obj", 1, objName, textuteImageSamplers2, meshes[objName], animArg);
// Fix object orientation - this can be fixed also in blender.
matrixEngine.Events.camera.yaw = 0;
// Add collision cube to the local player.
world.Add("cube", 0.5, "playerCollisonBox");
var collisionBox = new CANNON.Body({
mass: 500,
linearDamping: 0.001,
position: new CANNON.Vec3(0, 0, 0),
shape: new CANNON.Box(new CANNON.Vec3(3, 3, 3))// new CANNON.Sphere(2)
});
physics.world.addBody(collisionBox);
App.scene.playerCollisonBox.physics.currentBody = collisionBox;
App.scene.playerCollisonBox.physics.enabled = true;
App.scene.playerCollisonBox.physics.currentBody.fixedRotation = true;
App.scene.playerCollisonBox.geometry.setScale(0.02);
App.scene.playerCollisonBox.glBlend.blendEnabled = true;
App.scene.playerCollisonBox.glBlend.blendParamSrc = ENUMERATORS.glBlend.param[5];
App.scene.playerCollisonBox.glBlend.blendParamDest = ENUMERATORS.glBlend.param[6];
// test
addEventListener('hit.keyDown', (e) => {
// console.log('Bring to the top level', e.detail.keyCode);
// dont mess in events
if(e.detail.keyCode == 32) {
setTimeout(() => {
App.scene.playerCollisonBox.physics.currentBody.force.set(0, 0, 111)
}, 250)
} else if(e.detail.keyCode == 87) {
// Good place for blocking volume
// App.scene.playerCollisonBox.physics.currentBody.force.set(0,10,0)
setTimeout(() => {
// App.scene.playerCollisonBox.physics.currentBody.force.set(0,100,0)
}, 100);
}
});
var playerUpdater = {
UPDATE: () => {
App.scene[objName].rotation.rotateY(
matrixEngine.Events.camera.yaw + 180)
var detPitch;
var limit = 2;
if(matrixEngine.Events.camera.pitch < limit &&
matrixEngine.Events.camera.pitch > -limit) {
detPitch = matrixEngine.Events.camera.pitch * 2;
} else if(matrixEngine.Events.camera.pitch > limit) {
detPitch = limit * 2;
} else if(matrixEngine.Events.camera.pitch < -(limit + 2)) {
detPitch = -(limit + 2) * 2;
}
if(matrixEngine.Events.camera.virtualJumpActive == true) {
// invert logic
// Scene object set
App.scene[objName].rotation.rotateX(-detPitch);
var detPitchPos = matrixEngine.Events.camera.pitch;
if(detPitchPos > 4) detPitchPos = 4;
App.scene.playerCollisonBox.physics.currentBody.mass = 10;
App.scene.playerCollisonBox.physics.currentBody.force.set(0, 0, 700);
App.scene[objName].position.setPosition(
App.scene.playerCollisonBox.physics.currentBody.position.x,
App.scene.playerCollisonBox.physics.currentBody.position.z,
App.scene.playerCollisonBox.physics.currentBody.position.y
)
// Cannonjs object set / Switched Z - Y
matrixEngine.Events.camera.xPos = App.scene.playerCollisonBox.physics.currentBody.position.x;
matrixEngine.Events.camera.zPos = App.scene.playerCollisonBox.physics.currentBody.position.y;
matrixEngine.Events.camera.yPos = App.scene.playerCollisonBox.physics.currentBody.position.z;
App.scene.playerCollisonBox.
physics.currentBody.angularVelocity.set(0, 0, 0);
setTimeout(() => {
matrixEngine.Events.camera.virtualJumpActive = false;
App.scene.playerCollisonBox.physics.currentBody.mass = 550;
}, 1350);
} else {
// Make more stable situation
App.scene.playerCollisonBox.physics.currentBody.mass = 500;
App.scene.playerCollisonBox.physics.currentBody.quaternion.setFromEuler(0,0,0)
// Tamo tu iznad duge nebo zri...
// Cannonjs object set
// Switched Z - Y
matrixEngine.Events.camera.yPos = App.scene.playerCollisonBox.physics.currentBody.position.z;
// Scene object set
App.scene[objName].rotation.rotateX(-detPitch);
var detPitchPos = matrixEngine.Events.camera.pitch;
if(detPitchPos > 4) detPitchPos = 4;
App.scene[objName].position.setPosition(
matrixEngine.Events.camera.xPos,
matrixEngine.Events.camera.yPos, // - 0.3 + detPitchPos / 50,
// App.scene.playerCollisonBox.physics.currentBody.position.y,
matrixEngine.Events.camera.zPos,
)
// Cannonjs object set
// Switched Z - Y
App.scene.playerCollisonBox.
physics.currentBody.position.set(
matrixEngine.Events.camera.xPos,
matrixEngine.Events.camera.zPos,
matrixEngine.Events.camera.yPos)
// App.scene.playerCollisonBox.physics.currentBody.position.y)
}
}
};
App.updateBeforeDraw.push(playerUpdater);
// Player Energy status
App.scene.player.energy = {};
for(let key in App.scene.player.meshList) {
App.scene.player.meshList[key].setScale(1.85);
}
// Target scene object
var texTarget = {
source: [
"res/bvh-skeletal-base/swat-guy/target.png",
"res/bvh-skeletal-base/swat-guy/target.png"
],
mix_operation: "multiply",
};
world.Add("squareTex", 0.25, 'FPSTarget', texTarget);
App.scene.FPSTarget.position.setPosition(0, 0, -4);
App.scene.FPSTarget.glBlend.blendEnabled = true;
App.scene.FPSTarget.glBlend.blendParamSrc = matrixEngine.utility.ENUMERATORS.glBlend.param[4];
App.scene.FPSTarget.glBlend.blendParamDest = matrixEngine.utility.ENUMERATORS.glBlend.param[4];
App.scene.FPSTarget.isHUD = true;
App.scene.FPSTarget.geometry.setScale(0.1);
// Energy active bar
// Custom generic textures. Micro Drawing.
// Example for arg shema square for now only.
var options = {
squareShema: [8, 8],
pixels: new Uint8Array(8 * 8 * 4)
};
// options.pixels.fill(0);
App.scene.player.energy.value = 8;
App.scene.player.updateEnergy = function(v) {
this.energy.value = v;
var t = App.scene.energyBar.preparePixelsTex(App.scene.energyBar.specialValue);
App.scene.energyBar.textures.pop()
App.scene.energyBar.textures.push(App.scene.energyBar.createPixelsTex(t));
};
function preparePixelsTex(options) {
var I = 0, R = 0, G = 0, B = 0, localCounter = 0;
for(var funny = 0;funny < 8 * 8 * 4;funny += 4) {
if(localCounter > 7) {
localCounter = 0;
}
if(localCounter < App.scene.player.energy.value) {
I = 128;
if(App.scene.player.energy.value < 3) {
R = 255;
G = 0;
B = 0;
I = 0;
} else if(App.scene.player.energy.value > 2 && App.scene.player.energy.value < 5) {
R = 255;
G = 255;
B = 0;
} else {
R = 0;
G = 255;
B = 0;
}
} else {
I = 0;
R = 0;
G = 0;
B = 0;
}
options.pixels[funny] = R;
options.pixels[funny + 1] = G;
options.pixels[funny + 2] = B;
options.pixels[funny + 3] = 0;
localCounter++;
}
return options;
}
var tex2 = {
source: [
"res/images/hud/energy-bar.png",
"res/images/hud/energy-bar.png"
],
mix_operation: "multiply",
};
world.Add("squareTex", 1, 'energyBar', tex2);
App.scene.energyBar.glBlend.blendEnabled = true;
App.scene.energyBar.glBlend.blendParamSrc = matrixEngine.utility.ENUMERATORS.glBlend.param[5];
App.scene.energyBar.glBlend.blendParamDest = matrixEngine.utility.ENUMERATORS.glBlend.param[5];
App.scene.energyBar.isHUD = true;
// App.scene.energy.visible = false;
App.scene.energyBar.position.setPosition(0, 1.1, -3);
App.scene.energyBar.geometry.setScaleByX(1)
App.scene.energyBar.geometry.setScaleByY(0.05)
App.scene.energyBar.preparePixelsTex = preparePixelsTex;
options = preparePixelsTex(options);
// App.scene.energyBar.textures[0] = App.scene.energyBar.createPixelsTex(options);
App.scene.energyBar.textures.push(App.scene.energyBar.createPixelsTex(options));
App.scene.energyBar.specialValue = options;
}
matrixEngine.objLoader.downloadMeshes(
matrixEngine.objLoader.makeObjSeqArg(
{
id: objName,
// path: "res/bvh-skeletal-base/swat-guy/anims/swat-multi",
path: "res/bvh-skeletal-base/swat-guy/FPShooter-hands/FPShooter-hands",
from: 1,
to: 20
}),
onLoadObj
);
};
let promiseAllGenerated = [];
const objGenerator = (n) => {
for(var j = 0;j < n;j++) {
promiseAllGenerated.push(new Promise((resolve) => {
setTimeout(() => {
world.Add("cubeLightTex", 1, "CUBE" + j, tex);
var b2 = new CANNON.Body({
mass: 1,
linearDamping: 0.01,
position: new CANNON.Vec3(1, -14.5, 15),
shape: new CANNON.Box(new CANNON.Vec3(1, 1, 1))
});
physics.world.addBody(b2);
App.scene['CUBE' + j].physics.currentBody = b2;
App.scene['CUBE' + j].physics.enabled = true;
resolve();
}, 1000 * j);
}));
}
}
// objGenerator(15);
createObjSequence('player');
Promise.all(promiseAllGenerated).then((what) => {
console.info(`Runtime wait for some generetion of scene objects,
then swap scene array index for target ->
must be manual setup for now!`, what);
// swap(5, 19, matrixEngine.matrixWorld.world.contentList);
});
// Add ground for physics bodies.
var tex = {
source: ["res/images/complex_texture_1/diffuse.png"],
mix_operation: "multiply",
};
// Load Physics world.
// let gravityVector = [0, 0, -9.82];
let gravityVector = [0, 0, -29.82];
let physics = world.loadPhysics(gravityVector);
// Add ground
var groundBody = new CANNON.Body({
mass: 0, // mass == 0 makes the body static
position: new CANNON.Vec3(0, -15, -2)
});
var groundShape = new CANNON.Plane();
groundBody.addShape(groundShape);
physics.world.addBody(groundBody);
// Matrix engine visual
world.Add("squareTex", 1, "FLOOR_STATIC", tex);
App.scene.FLOOR_STATIC.geometry.setScaleByX(20);
App.scene.FLOOR_STATIC.geometry.setScaleByY(20);
App.scene.FLOOR_STATIC.position.SetY(-2);
App.scene.FLOOR_STATIC.position.SetZ(-15);
App.scene.FLOOR_STATIC.rotation.rotx = 90;
// Target x-ray
// See through the objects.
// In webGL context it is object how was drawn before others.
var texTarget = {
source: [
"res/bvh-skeletal-base/swat-guy/target-night.png"
],
mix_operation: "multiply",
};
world.Add("squareTex", 0.18, 'xrayTarget', texTarget);
App.scene.xrayTarget.glBlend.blendEnabled = true;
App.scene.xrayTarget.glBlend.blendParamSrc = matrixEngine.utility.ENUMERATORS.glBlend.param[5];
App.scene.xrayTarget.glBlend.blendParamDest = matrixEngine.utility.ENUMERATORS.glBlend.param[5];
App.scene.xrayTarget.isHUD = true;
App.scene.xrayTarget.visible = false;
App.scene.xrayTarget.position.setPosition(-0.3, 0.27, -4);
// Energy
var tex1 = {
source: [
"res/images/hud/energy.png"
],
mix_operation: "multiply",
};
world.Add("squareTex", 0.5, 'energy', tex1);
App.scene.energy.glBlend.blendEnabled = true;
App.scene.energy.glBlend.blendParamSrc = matrixEngine.utility.ENUMERATORS.glBlend.param[5];
App.scene.energy.glBlend.blendParamDest = matrixEngine.utility.ENUMERATORS.glBlend.param[5];
App.scene.energy.isHUD = true;
// App.scene.energy.visible = false;
App.scene.energy.position.setPosition(-1, 1.15, -3);
App.scene.energy.geometry.setScaleByX(0.35)
App.scene.energy.geometry.setScaleByY(0.1)
// good for fix rotation in future
world.Add("cubeLightTex", 1, "FLOOR2", tex);
var b2 = new CANNON.Body({
mass: 0,
linearDamping: 0.01,
position: new CANNON.Vec3(1, -14.5, -1),
shape: new CANNON.Box(new CANNON.Vec3(3, 1, 1))
});
physics.world.addBody(b2);
App.scene['FLOOR2'].position.setPosition(1, -1, -14.5)
App.scene['FLOOR2'].geometry.setScaleByX(3);
App.scene['FLOOR2'].physics.currentBody = b2;
App.scene['FLOOR2'].physics.enabled = true;
world.Add("cubeLightTex", 2, "FLOOR3", tex);
var b3 = new CANNON.Body({
mass: 0,
linearDamping: 0.01,
position: new CANNON.Vec3(0, -19, 0),
shape: new CANNON.Box(new CANNON.Vec3(3, 3, 3))
});
physics.world.addBody(b3);
App.scene['FLOOR3'].position.setPosition(0, 0, -19)
// App.scene['FLOOR3'].geometry.setScaleByX(3);
App.scene['FLOOR3'].physics.currentBody = b3;
App.scene['FLOOR3'].physics.enabled = true;
world.Add("cubeLightTex", 5, "WALL_BLOCK", tex);
var b5 = new CANNON.Body({
mass: 0,
linearDamping: 0.01,
position: new CANNON.Vec3(10, -19, 0),
shape: new CANNON.Box(new CANNON.Vec3(5, 5, 5))
});
physics.world.addBody(b5);
App.scene['WALL_BLOCK'].position.setPosition(10, 0, -19)
// App.scene['WALL_BLOCK'].geometry.setScaleByX(3);
App.scene['WALL_BLOCK'].physics.currentBody = b5;
App.scene['WALL_BLOCK'].physics.enabled = true;
};
Any suggestion ?

How would I add a level system for the score increasing?

I am making a slight Tetris remake and I wanted to add a leveling system for when my score reaches, for example, 100, the speed that the blocks go down will also increase. How would I go about doing this? I have the entirety of the javascript code right here so please let me know what I can do to fix it:
//-------------------------------------------------------------------------
// base helper methods
//-------------------------------------------------------------------------
function get(id) { return document.getElementById(id); }
function hide(id) { get(id).style.visibility = 'hidden'; }
function show(id) { get(id).style.visibility = null; }
function html(id, html) { get(id).innerHTML = html; }
function timestamp() { return new Date().getTime(); }
function random(min, max) { return (min + (Math.random() * (max - min))); }
function randomChoice(choices) { return choices[Math.round(random(0, choices.length-1))]; }
if (!window.requestAnimationFrame) { // http://paulirish.com/2011/requestanimationframe-for-smart-animating/
window.requestAnimationFrame = window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback, element) {
window.setTimeout(callback, 1000 / 60);
}
}
//-------------------------------------------------------------------------
// game constants
//-------------------------------------------------------------------------
var KEY = { ESC: 27, SPACE: 32, LEFT: 37, UP: 38, RIGHT: 39, DOWN: 40 },
DIR = { UP: 0, RIGHT: 1, DOWN: 2, LEFT: 3, MIN: 0, MAX: 3 },
stats = new Stats(),
canvas = get('canvas'),
ctx = canvas.getContext('2d'),
ucanvas = get('upcoming'),
uctx = ucanvas.getContext('2d'),
speed = { start: 0.6, decrement: 0.005, min: 0.1 }, // how long before piece drops by 1 row (seconds)
nx = 10, // width of tetris court (in blocks)
ny = 20, // height of tetris court (in blocks)
nu = 5; // width/height of upcoming preview (in blocks)
//-------------------------------------------------------------------------
// game variables (initialized during reset)
//-------------------------------------------------------------------------
var dx, dy, // pixel size of a single tetris block
blocks, // 2 dimensional array (nx*ny) representing tetris court - either empty block or occupied by a 'piece'
actions, // queue of user actions (inputs)
playing, // true|false - game is in progress
dt, // time since starting this game
current, // the current piece
next, // the next piece
score, // the current score
vscore, // the currently displayed score (it catches up to score in small chunks - like a spinning slot machine)
rows, // number of completed rows in the current game
step; // how long before current piece drops by 1 row
//-------------------------------------------------------------------------
// tetris pieces
//
// blocks: each element represents a rotation of the piece (0, 90, 180, 270)
// each element is a 16 bit integer where the 16 bits represent
// a 4x4 set of blocks, e.g. j.blocks[0] = 0x44C0
//
// 0100 = 0x4 << 3 = 0x4000
// 0100 = 0x4 << 2 = 0x0400
// 1100 = 0xC << 1 = 0x00C0
// 0000 = 0x0 << 0 = 0x0000
// ------
// 0x44C0
//
//-------------------------------------------------------------------------
var i = { size: 4, blocks: [0x0F00, 0x2222, 0x00F0, 0x4444], color: 'cyan' };
var j = { size: 3, blocks: [0x44C0, 0x8E00, 0x6440, 0x0E20], color: 'blue' };
var l = { size: 3, blocks: [0x4460, 0x0E80, 0xC440, 0x2E00], color: 'orange' };
var o = { size: 2, blocks: [0xCC00, 0xCC00, 0xCC00, 0xCC00], color: 'yellow' };
var s = { size: 3, blocks: [0x06C0, 0x8C40, 0x6C00, 0x4620], color: 'lime' };
var t = { size: 3, blocks: [0x0E40, 0x4C40, 0x4E00, 0x4640], color: 'purple' };
var z = { size: 3, blocks: [0x0C60, 0x4C80, 0xC600, 0x2640], color: 'red' };
var p = { size: 3, blocks: [0x0F00, 0x2222, 0x00F0,], color: 'maroon' };
//------------------------------------------------
// do the bit manipulation and iterate through each
// occupied block (x,y) for a given piece
//------------------------------------------------
function eachblock(type, x, y, dir, fn) {
var bit, result, row = 0, col = 0, blocks = type.blocks[dir];
for(bit = 0x8000 ; bit > 0 ; bit = bit >> 1) {
if (blocks & bit) {
fn(x + col, y + row);
}
if (++col === 4) {
col = 0;
++row;
}
}
}
//-----------------------------------------------------
// check if a piece can fit into a position in the grid
//-----------------------------------------------------
function occupied(type, x, y, dir) {
var result = false
eachblock(type, x, y, dir, function(x, y) {
if ((x < 0) || (x >= nx) || (y < 0) || (y >= ny) || getBlock(x,y))
result = true;
});
return result;
}
function unoccupied(type, x, y, dir) {
return !occupied(type, x, y, dir);
}
//-----------------------------------------
// start with 4 instances of each piece and
// pick randomly until the 'bag is empty'
//-----------------------------------------
var pieces = [];
function randomPiece() {
if (pieces.length == 0)
pieces = [i,i,i,i,j,j,j,j,l,l,l,l,o,o,o,o,s,s,s,s,t,t,t,t,z,z,z,z,p,p,p,p];
var type = pieces.splice(random(0, pieces.length-1), 1)[0];
return { type: type, dir: DIR.UP, x: Math.round(random(0, nx - type.size)), y: 0 };
}
//-------------------------------------------------------------------------
// GAME LOOP
//-------------------------------------------------------------------------
function run() {
showStats(); // initialize FPS counter
addEvents(); // attach keydown and resize events
var last = now = timestamp();
function frame() {
now = timestamp();
update(Math.min(1, (now - last) / 1000.0)); // using requestAnimationFrame have to be able to handle large delta's caused when it 'hibernates' in a background or non-visible tab
draw();
stats.update();
last = now;
requestAnimationFrame(frame, canvas);
}
resize(); // setup all our sizing information
reset(); // reset the per-game variables
frame(); // start the first frame
}
function showStats() {
stats.domElement.id = 'stats';
get('menu').appendChild(stats.domElement);
}
function addEvents() {
document.addEventListener('keydown', keydown, false);
window.addEventListener('resize', resize, false);
}
function resize(event) {
canvas.width = canvas.clientWidth; // set canvas logical size equal to its physical size
canvas.height = canvas.clientHeight; // (ditto)
ucanvas.width = ucanvas.clientWidth;
ucanvas.height = ucanvas.clientHeight;
dx = canvas.width / nx; // pixel size of a single tetris block
dy = canvas.height / ny; // (ditto)
invalidate();
invalidateNext();
}
function keydown(ev) {
var handled = false;
if (playing) {
switch(ev.keyCode) {
case KEY.LEFT: actions.push(DIR.LEFT); handled = true; break;
case KEY.RIGHT: actions.push(DIR.RIGHT); handled = true; break;
case KEY.UP: actions.push(DIR.UP); handled = true; break;
case KEY.DOWN: actions.push(DIR.DOWN); handled = true; break;
case KEY.ESC: lose(); handled = true; break;
}
}
else if (ev.keyCode == KEY.SPACE) {
play();
handled = true;
}
if (handled)
ev.preventDefault(); // prevent arrow keys from scrolling the page (supported in IE9+ and all other browsers)
}
//-------------------------------------------------------------------------
// GAME LOGIC
//-------------------------------------------------------------------------
function play() { hide('start'); reset(); playing = true; }
function lose() { show('start'); setVisualScore(); playing = false; }
function setVisualScore(n) { vscore = n || score; invalidateScore(); }
function setScore(n) { score = n; setVisualScore(n); }
function addScore(n) { score = score + n; }
function clearScore() { setScore(0); }
function clearRows() { setRows(0); }
function setRows(n) { rows = n; step = Math.max(speed.min, speed.start - (speed.decrement*rows)); invalidateRows(); }
function addRows(n) { setRows(rows + n); }
function getBlock(x,y) { return (blocks && blocks[x] ? blocks[x][y] : null); }
function setBlock(x,y,type) { blocks[x] = blocks[x] || []; blocks[x][y] = type; invalidate(); }
function clearBlocks() { blocks = []; invalidate(); }
function clearActions() { actions = []; }
function setCurrentPiece(piece) { current = piece || randomPiece(); invalidate(); }
function setNextPiece(piece) { next = piece || randomPiece(); invalidateNext(); }
function reset() {
dt = 0;
clearActions();
clearBlocks();
clearRows();
clearScore();
setCurrentPiece(next);
setNextPiece();
}
function update(idt) {
if (playing) {
if (vscore < score)
setVisualScore(vscore + 1);
handle(actions.shift());
dt = dt + idt;
if (dt > step) {
dt = dt - step;
drop();
}
}
}
function handle(action) {
switch(action) {
case DIR.LEFT: move(DIR.LEFT); break;
case DIR.RIGHT: move(DIR.RIGHT); break;
case DIR.UP: rotate(); break;
case DIR.DOWN: drop(); break;
}
}
function move(dir) {
var x = current.x, y = current.y;
switch(dir) {
case DIR.RIGHT: x = x + 1; break;
case DIR.LEFT: x = x - 1; break;
case DIR.DOWN: y = y + 1; break;
}
if (unoccupied(current.type, x, y, current.dir)) {
current.x = x;
current.y = y;
invalidate();
return true;
}
else {
return false;
}
}
function rotate() {
var newdir = (current.dir == DIR.MAX ? DIR.MIN : current.dir + 1);
if (unoccupied(current.type, current.x, current.y, newdir)) {
current.dir = newdir;
invalidate();
}
}
//This is how we make the piece drop down and place:
function drop() {
if (!move(DIR.DOWN)) {
addScore(10);
dropPiece();
removeLines();
setCurrentPiece(next);
setNextPiece(randomPiece());
clearActions();
if (occupied(current.type, current.x, current.y, current.dir)) {
lose();
}
}
}
function dropPiece() {
eachblock(current.type, current.x, current.y, current.dir, function(x, y) {
setBlock(x, y, current.type);
});
}
function removeLines() {
var x, y, complete, n = 0;
for(y = ny ; y > 0 ; --y) {
complete = true;
for(x = 0 ; x < nx ; ++x) {
if (!getBlock(x, y))
complete = false;
}
if (complete) {
removeLine(y);
y = y + 1; // recheck same line
n++;
}
}
if (n > 0) {
addRows(n);
addScore(100*Math.pow(2,n-1)); // 1: 100, 2: 200, 3: 400, 4: 800
}
}
function removeLine(n) {
var x, y;
for(y = n ; y >= 0 ; --y) {
for(x = 0 ; x < nx ; ++x)
setBlock(x, y, (y == 0) ? null : getBlock(x, y-1));
}
}
//-------------------------------------------------------------------------
// RENDERING
//-------------------------------------------------------------------------
var invalid = {};
function invalidate() { invalid.court = true; }
function invalidateNext() { invalid.next = true; }
function invalidateScore() { invalid.score = true; }
function invalidateRows() { invalid.rows = true; }
function draw() {
ctx.save();
ctx.lineWidth = 1;
ctx.translate(0.5, 0.5); // for crisp 1px black lines
drawCourt();
drawNext();
drawScore();
drawRows();
ctx.restore();
}
function drawCourt() {
if (invalid.court) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (playing)
drawPiece(ctx, current.type, current.x, current.y, current.dir);
var x, y, block;
for(y = 0 ; y < ny ; y++) {
for (x = 0 ; x < nx ; x++) {
if (block = getBlock(x,y))
drawBlock(ctx, x, y, block.color);
}
}
ctx.strokeRect(0, 0, nx*dx - 1, ny*dy - 1); // court boundary
invalid.court = false;
}
}
function drawNext() {
if (invalid.next) {
var padding = (nu - next.type.size) / 2; // half-complete attempt at centering next piece display
uctx.save();
uctx.translate(0.5, 0.5);
uctx.clearRect(0, 0, nu*dx, nu*dy);
drawPiece(uctx, next.type, padding, padding, next.dir);
uctx.strokeStyle = 'black';
uctx.strokeRect(0, 0, nu*dx - 1, nu*dy - 1);
uctx.restore();
invalid.next = false;
}
}
function drawScore() {
if (invalid.score) {
html('score', ("00000" + Math.floor(vscore)).slice(-5));
invalid.score = false;
}
}
function drawRows() {
if (invalid.rows) {
html('rows', rows);
invalid.rows = false;
}
}
function drawPiece(ctx, type, x, y, dir) {
eachblock(type, x, y, dir, function(x, y) {
drawBlock(ctx, x, y, type.color);
});
}
function drawBlock(ctx, x, y, color) {
ctx.fillStyle = color;
ctx.fillRect(x*dx, y*dy, dx, dy);
ctx.strokeRect(x*dx, y*dy, dx, dy)
}
//-------------------------------------------------------------------------
// FINALLY, lets run the game
//-------------------------------------------------------------------------
run();
You have a function called addScore that seems to be used to update the user's score. You could add a conditional block inside this function that will update the game speed when the new score is above the threshold you choose. Alternatively, if you would prefer to keep addScore purely focused on updating the value of the score, you could add this conditional block wherever you're calling addScore.

Converting jquery controls to javascript

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
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>
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.
*/
// ---------------------------------------------------------------------------------------------------
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
}
});

TweenJS animation not working

I am working on a tool to perform sequence of animations by updating an object array. This array would have all the parameters to customize animation as per varied requirements. This tool is being developed with JavaScript and makes use of createJS library and TweenJS to animate objects. Objects are created dynamically and positioned, then the tween is applied. Tween doesn't seem to work in my code.
I have the whole code below
var AnimationFlow = (function () {
'use strict';
var AnimationFlow = function (canvasID) {
this.canvas = document.getElementById(canvasID);
this.stage = new createjs.Stage(this.canvas);
this.timeline = new createjs.Timeline();
this.tween = [];
this.preload;
this.animData;
this.manifest = [];
this.animObject = [];
this.stageObject = [];
this.loadProgressLabel;
this.loadingBarContainer;
this.loadingBar;
};
AnimationFlow.prototype.setData = function (data) {
this.animData = data;
this.manifest = [];
this.renderProgressBar();
for (var i = 0; i < this.animData.length; i++) {
this.manifest.push({'src': this.animData[i].targeturl, 'id': this.animData[i].targetID});
}
};
AnimationFlow.prototype.init = function () {
createjs.Ticker.addEventListener("tick", this.tick.bind(this));
createjs.Ticker.setFPS(30);
createjs.Ticker.useRAF = true;
this.preload = new createjs.LoadQueue(false);
this.preload.addEventListener("complete", this.handleComplete.bind(this));
this.preload.addEventListener("progress", this.handleProgress.bind(this));
this.preload.loadManifest(this.manifest);
this.stage.update();
};
AnimationFlow.prototype.handleProgress = function () {
this.loadingBar.scaleX = this.preload.progress * this.loadingBarWidth;
this.progresPrecentage = Math.round(this.preload.progress * 100);
this.loadProgressLabel.text = this.progresPrecentage + "% Loaded";
this.stage.update();
};
AnimationFlow.prototype.handleComplete = function () {
//Load images logic to be added
for (var i = 0; i < this.manifest.length; i++) {
this.animObject.push(this.preload.getResult(this.manifest[i].id));
}
this.loadProgressLabel.text = "Loading complete click to start";
this.stage.update();
this.canvas.addEventListener("click", this.handleClick.bind(this));
};
AnimationFlow.prototype.handleClick = function () {
this.start();
this.stage.removeChild(this.loadProgressLabel, this.loadingBarContainer);
this.canvas.removeEventListener("click", this.handleClick);
};
AnimationFlow.prototype.start = function () {
for (var i = 0; i < this.animObject.length; i++) {
this.obj = new createjs.Bitmap(this.animObject[i]);
this.obj.x = this.animData[i].initialXPos;
this.obj.y = this.animData[i].initialYPos;
this.obj.visible = this.animData[i].initialVisibility;
this.stage.addChild(this.obj);
this.stageObject.push(this.obj);
if(this.animData[i].isAnimatable){
var c = createjs.Tween.get(this.obj);
c.to({x:this.animData[i].params.xpos}, this.animData[i].duration);
c.call(this.tweenComplete);
this.timeline.addTween(c);
}
}
this.stage.update();
};
AnimationFlow.prototype.tick = function () {
console.log("heart beat on....");
this.stage.update();
};
AnimationFlow.prototype.tweenComplete = function () {
console.log("tweenComplete.......");
};
AnimationFlow.prototype.renderProgressBar = function () {
this.loadProgressLabel = new createjs.Text("", "18px Verdana", "black");
this.loadProgressLabel.lineWidth = 200;
this.loadProgressLabel.textAlign = "center";
this.loadProgressLabel.x = this.canvas.width / 2;
this.loadProgressLabel.y = 50;
this.stage.addChild(this.loadProgressLabel);
this.loadingBarContainer = new createjs.Container();
this.loadingBarHeight = 20;
this.loadingBarWidth = 300;
this.LoadingBarColor = createjs.Graphics.getRGB(0, 0, 0);
this.loadingBar = new createjs.Shape();
this.loadingBar.graphics.beginFill(this.LoadingBarColor).drawRect(0, 0, 1, this.loadingBarHeight).endFill();
this.frame = new createjs.Shape();
this.padding = 3;
this.frame.graphics.setStrokeStyle(1).beginStroke(this.LoadingBarColor).drawRect(-this.padding / 2, -this.padding / 2, this.loadingBarWidth + this.padding, this.loadingBarHeight + this.padding);
this.loadingBarContainer.addChild(this.loadingBar, this.frame);
this.loadingBarContainer.x = Math.round(this.canvas.width / 2 - this.loadingBarWidth / 2);
this.loadingBarContainer.y = 100;
this.stage.addChild(this.loadingBarContainer);
};
return AnimationFlow;
})();
var data = [{targetID: 'background', targeturl: 'assets/images/heliGame/sky.png',isAnimatable:true, duration: 2000, params: {xpos: '600'}, isVisibleAfterAnimation: true, isVisibleAtStartAnimation: true, initialVisibility: true, initialXPos: '0', initialYPos: '0'},
{targetID: 'mousePointer', targeturl: 'assets/images/heliGame/helicopter.png',isAnimatable:true, duration: 1000, params: {xpos: '500'}, isVisibleAfterAnimation: false, isVisibleAtStartAnimation: true, initialVisibility: true, initialXPos: '0', initialYPos: '0'}];
var buttons = ["playPauseBtn", "startFirstBtn", "reverseBtn"];
var animTool = new AnimationFlow('testCanvas');
animTool.setData(data);
animTool.init();
I have the code in JSFiddle
https://jsfiddle.net/jamesshaji/t4pxzsoo/
There were a couple of issues. Firstly, you need to define all of your positions as integers and not as strings (ie: take the quotes off of your position data in your data object). Secondly, you need to call: this.timeline.gotoAndPlay(0); to start the timeline execution. Otherwise the animations won't play. Here is a fixed version:
var AnimationFlow = (function() {
'use strict';
var AnimationFlow = function(canvasID) {
this.canvas = document.getElementById(canvasID);
this.stage = new createjs.Stage(this.canvas);
this.timeline = new createjs.Timeline();
this.tween = [];
this.preload;
this.animData;
this.manifest = [];
this.animObject = [];
this.stageObject = [];
this.loadProgressLabel;
this.loadingBarContainer;
this.loadingBar;
};
AnimationFlow.prototype.setData = function(data) {
this.animData = data;
this.manifest = [];
this.renderProgressBar();
for (var i = 0; i < this.animData.length; i++) {
this.manifest.push({
'src': this.animData[i].targeturl,
'id': this.animData[i].targetID
});
}
};
AnimationFlow.prototype.init = function() {
createjs.Ticker.addEventListener("tick", this.tick.bind(this));
createjs.Ticker.setFPS(30);
createjs.Ticker.useRAF = true;
this.preload = new createjs.LoadQueue(false);
this.preload.addEventListener("complete", this.handleComplete.bind(this));
this.preload.addEventListener("progress", this.handleProgress.bind(this));
this.preload.loadManifest(this.manifest);
this.stage.update();
};
AnimationFlow.prototype.handleProgress = function() {
this.loadingBar.scaleX = this.preload.progress * this.loadingBarWidth;
this.progresPrecentage = Math.round(this.preload.progress * 100);
this.loadProgressLabel.text = this.progresPrecentage + "% Loaded";
this.stage.update();
};
AnimationFlow.prototype.handleComplete = function() {
//Load images logic to be added
for (var i = 0; i < this.manifest.length; i++) {
this.animObject.push(this.preload.getResult(this.manifest[i].id));
}
this.loadProgressLabel.text = "Loading complete click to start";
this.stage.update();
this.canvas.addEventListener("click", this.handleClick.bind(this));
};
AnimationFlow.prototype.handleClick = function() {
this.start();
this.stage.removeChild(this.loadProgressLabel, this.loadingBarContainer);
this.canvas.removeEventListener("click", this.handleClick);
};
AnimationFlow.prototype.start = function() {
for (var i = 0; i < this.animObject.length; i++) {
this.obj = new createjs.Bitmap(this.animObject[i]);
this.obj.x = this.animData[i].initialXPos;
this.obj.y = this.animData[i].initialYPos;
this.obj.visible = this.animData[i].initialVisibility;
this.stage.addChild(this.obj);
this.stageObject.push(this.obj);
if (this.animData[i].isAnimatable) {
console.log("animatable:" + this.animData[i].params.xpos + " " + this.animData[i].duration);
var c = createjs.Tween.get(this.obj);
c.to({
x: this.animData[i].params.xpos
}, this.animData[i].duration);
c.call(this.tweenComplete);
this.timeline.addTween(c);
}
}
this.timeline.gotoAndPlay(0);
this.stage.update();
};
AnimationFlow.prototype.tick = function() {
this.stage.update();
};
AnimationFlow.prototype.tweenComplete = function() {
console.log("tweenComplete.......");
};
AnimationFlow.prototype.renderProgressBar = function() {
this.loadProgressLabel = new createjs.Text("", "18px Verdana", "black");
this.loadProgressLabel.lineWidth = 200;
this.loadProgressLabel.textAlign = "center";
this.loadProgressLabel.x = this.canvas.width / 2;
this.loadProgressLabel.y = 50;
this.stage.addChild(this.loadProgressLabel);
this.loadingBarContainer = new createjs.Container();
this.loadingBarHeight = 20;
this.loadingBarWidth = 300;
this.LoadingBarColor = createjs.Graphics.getRGB(0, 0, 0);
this.loadingBar = new createjs.Shape();
this.loadingBar.graphics.beginFill(this.LoadingBarColor).drawRect(0, 0, 1, this.loadingBarHeight).endFill();
this.frame = new createjs.Shape();
this.padding = 3;
this.frame.graphics.setStrokeStyle(1).beginStroke(this.LoadingBarColor).drawRect(-this.padding / 2, -this.padding / 2, this.loadingBarWidth + this.padding, this.loadingBarHeight + this.padding);
this.loadingBarContainer.addChild(this.loadingBar, this.frame);
this.loadingBarContainer.x = Math.round(this.canvas.width / 2 - this.loadingBarWidth / 2);
this.loadingBarContainer.y = 100;
this.stage.addChild(this.loadingBarContainer);
};
return AnimationFlow;
})();
var data = [{
targetID: 'background',
targeturl: 'https://s13.postimg.org/tyj4iop93/Sky_Pic.jpg',
isAnimatable: true,
duration: 2000,
params: {
xpos: -100
},
isVisibleAfterAnimation: true,
isVisibleAtStartAnimation: true,
initialVisibility: true,
initialXPos: 0,
initialYPos: 0
}, {
targetID: 'mousePointer',
targeturl: 'http://jamesshaji.com/angular/assets/images/heliGame/helicopter.png',
isAnimatable: true,
duration: 2000,
params: {
xpos: 100
},
isVisibleAfterAnimation: false,
isVisibleAtStartAnimation: true,
initialVisibility: true,
initialXPos: 450,
initialYPos: 50
}];
var buttons = ["playPauseBtn", "startFirstBtn", "reverseBtn"];
var animTool = new AnimationFlow('testCanvas');
animTool.setData(data);
animTool.init();
<script src="https://code.createjs.com/createjs-2015.11.26.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tweenjs/0.6.2/tweenjs.min.js"></script>
<div>Animation</div>
<canvas height="500" width="500" id="testCanvas">
</canvas>

Multiplayer coins

Having a slight issue getting some coins to generate multiplayer using eureca.io websockets. I've got the players working multiplayer and from remote connections but I can't seem to get the coins to generate across the connection so they appear in the same place on all the players connections. I'm using phaser to generate the game and eureca and engine for my web connection. I've got the coins to spawn on the page with no problems, but whenever a new player joins, the coin always displays in a different place, I wondering how I can make them generate across the connection. My game code is below:
Game Code
var myId = 0;
var background;
var blueSquare;
var player;
var coins;
var goldCoin;
var squareList;
var coinList;
var cursors;
var score;
var highScore;
var ready = false;
var eurecaServer;
var eurecaClientSetup = function () {
var eurecaClient = new Eureca.Client();
eurecaClient.ready(function (proxy) {
eurecaServer = proxy;
});
eurecaClient.exports.setId = function (id)
{
myId = id;
create();
eurecaServer.handshake();
ready = true;
}
eurecaClient.exports.kill = function (id)
{
if (squareList[id]) {
squareList[id].kill();
console.log('Player has left the game ', id, squareList[id]);
}
}
eurecaClient.exports.spawnBlueSquare = function (i, x, y)
{
if (i == myId) return;
console.log('A new player has joined the game');
var blsq = new BlueSquare(i, game, blueSquare);
squareList[i] = blsq;
}
eurecaClient.exports.spawnCoins = function (c, x, y)
{
console.log('A coin has been generated');
var cn = new GenerateCoin(c, game, coin);
coinList[c] = cn;
}
eurecaClient.exports.updateState = function (id, state)
{
if (squareList[id]) {
squareList[id].cursor = state;
squareList[id].blueSquare.x = state.x;
squareList[id].blueSquare.y = state.y;
squareList[id].blueSquare.angle = state.angle;
squareList[id].update();
coinList.cursor = state;
coinList.blueSquare.x = state.x;
coinList.blueSquare.y = state.y;
coinList.update();
}
}
}
BlueSquare = function (index, game, player) {
this.cursor = {
left:false,
right:false,
up:false,
down:false,
}
this.input = {
left:false,
right:false,
up:false,
down:false,
}
var x = 0;
var y = 0;
this.game = game;
this.player = player;
this.currentSpeed = 0;
this.isBlue = true;
this.blueSquare = game.add.sprite(x, y, 'blueSquare');
this.blueSquare.anchor.set(0.5);
this.blueSquare.id = index;
game.physics.enable(this.blueSquare, Phaser.Physics.ARCADE);
this.blueSquare.body.immovable = false;
this.blueSquare.body.collideWorldBounds = true;
this.blueSquare.body.setSize(34, 34, 0, 0);
this.blueSquare.angle = 0;
game.physics.arcade.velocityFromRotation(this.blueSquare.rotation, 0, this.blueSquare.body.velocity);
}
BlueSquare.prototype.update = function () {
var inputChanged = (
this.cursor.left != this.input.left ||
this.cursor.right != this.input.right ||
this.cursor.up != this.input.up ||
this.cursor.down != this.input.down
);
if (inputChanged)
{
if (this.blueSquare.id == myId)
{
this.input.x = this.blueSquare.x;
this.input.y = this.blueSquare.y;
this.input.angle = this.blueSquare.angle;
eurecaServer.handleKeys(this.input);
}
}
if (this.cursor.left)
{
this.blueSquare.body.velocity.x = -250;
this.blueSquare.body.velocity.y = 0;
}
else if (this.cursor.right)
{
this.blueSquare.body.velocity.x = 250;
this.blueSquare.body.velocity.y = 0;
}
else if (this.cursor.down)
{
this.blueSquare.body.velocity.x = 0;
this.blueSquare.body.velocity.y = 250;
}
else if (this.cursor.up)
{
this.blueSquare.body.velocity.x = 0;
this.blueSquare.body.velocity.y = -250;
}
else
{
this.blueSquare.body.velocity.x = 0;
this.blueSquare.body.velocity.y = 0;
}
}
BlueSquare.prototype.kill = function () {
this.alive = false;
this.blueSquare.kill();
}
GenerateCoin = function (game, goldCoin) {
this.game = game;
this.goldCoin = goldCoin;
x = 0;
y = 0;
this.coins = game.add.sprite(x, y, 'coin');
game.physics.enable(this.coins, Phaser.Physics.ARCADE);
this.coins.body.immovable = true;
this.coins.body.collideWorldBounds = true;
this.coins.body.setSize(16, 16, 0, 0);
}
window.onload = function() {
function countdown( elementName, minutes, seconds )
{
var element, endTime, hours, mins, msLeft, time;
function twoDigits( n )
{
return (n <= 9 ? "0" + n : n);
}
function updateTimer()
{
msLeft = endTime - (+new Date);
if ( msLeft < 1000 ) {
element.innerHTML = "Game Over!";
} else {
time = new Date( msLeft );
hours = time.getUTCHours();
mins = time.getUTCMinutes();
element.innerHTML = (hours ? hours + ':' + twoDigits( mins ) : mins) + ':' + twoDigits( time.getUTCSeconds() );
setTimeout( updateTimer, time.getUTCMilliseconds() + 500 );
}
}
element = document.getElementById( elementName );
endTime = (+new Date) + 1000 * (60*minutes + seconds) + 500;
updateTimer();
}countdown( "countdown", 5, 0 );
}
var game = new Phaser.Game(700, 600, Phaser.AUTO, 'Square Hunt', { preload: preload, create: eurecaClientSetup, update: update, render: render });
function preload () {
game.load.spritesheet('coin', 'assets/coin.png', 18, 18);
game.load.image('blueSquare', 'assets/blue-square.png');
game.load.image('redSquare', 'assets/red-square.png');
game.load.image('earth', 'assets/sand.png');
}
function create () {
game.world.setBounds(0, 0, 1500, 1500);
game.stage.disableVisibilityChange = true;
background = game.add.tileSprite(0, 0, 800, 600, 'earth');
background.fixedToCamera = true;
squareList = {};
player = new BlueSquare(myId, game, blueSquare);
squareList[myId] = player;
blueSquare = player.blueSquare;
blueSquare.x = Math.floor(Math.random() * 650);
blueSquare.y = Math.floor(Math.random() * 650);
blueSquare.bringToTop();
game.camera.follow(blueSquare);
game.camera.deadzone = new Phaser.Rectangle(150, 150, 500, 300);
game.camera.focusOnXY(0, 0);
cursors = game.input.keyboard.createCursorKeys();
coinList = {};
goldCoin = new GenerateCoin(game, coins);
coinList = goldCoin;
coins = goldCoin.coins;
coins.x = Math.floor(Math.random() * 650);
coins.y = Math.floor(Math.random() * 650);
coins.bringToTop();
}
function update () {
if (!ready) return;
game.physics.arcade.collide(blueSquare, coins);
player.input.left = cursors.left.isDown;
player.input.right = cursors.right.isDown;
player.input.up = cursors.up.isDown;
player.input.down = cursors.down.isDown;
player.input.fire = game.input.activePointer.isDown;
player.input.tx = game.input.x+ game.camera.x;
player.input.ty = game.input.y+ game.camera.y;
background.tilePosition.x = -game.camera.x;
background.tilePosition.y = -game.camera.y;
for (var i in squareList)
{
if (!squareList[i]) continue;
var curBlue = squareList[i].blueSquare;
for (var j in squareList)
{
if (!squareList[j]) continue;
if (j!=i)
{
var targetBlue = squareList[j].blueSquare;
}
if (squareList[j].isBlue)
{
squareList[j].update();
}
}
}
}
function test(){
console.log('collsion');
}
function render () {}
Server.js Files
var express = require('express')
, app = express(app)
, server = require('http').createServer(app);
app.use(express.static(__dirname));
var clients = {};
var EurecaServer = require('eureca.io').EurecaServer;
var eurecaServer = new EurecaServer({allow:['setId', 'spawnBlueSquare', 'spawnCoins', 'kill', 'updateState']});
eurecaServer.attach(server);
eurecaServer.onConnect(function (conn) {
console.log('New Client id=%s ', conn.id, conn.remoteAddress);
var remote = eurecaServer.getClient(conn.id);
clients[conn.id] = {id:conn.id, remote:remote}
remote.setId(conn.id);
});
eurecaServer.onConnectionLost(function (conn) {
console.log('connection lost ... will try to reconnect');
});
eurecaServer.onDisconnect(function (conn) {
console.log('Client disconnected ', conn.id);
var removeId = clients[conn.id].id;
delete clients[conn.id];
for (var c in clients)
{
var remote = clients[c].remote;
remote.kill(conn.id);
}
});
eurecaServer.exports.handshake = function()
{
for (var c in clients)
{
var remote = clients[c].remote;
for (var cc in clients)
{
var x = clients[cc].laststate ? clients[cc].laststate.x: 0;
var y = clients[cc].laststate ? clients[cc].laststate.y: 0;
remote.spawnBlueSquare(clients[cc].id, x, y);
}
}
}
eurecaServer.exports.handleKeys = function (keys) {
var conn = this.connection;
var updatedClient = clients[conn.id];
for (var c in clients)
{
var remote = clients[c].remote;
remote.updateState(updatedClient.id, keys);
clients[c].laststate = keys;
}
}
server.listen(8000);
Instead of generating coins randomly positioned on each client, you could generate this random position(x and y) in server side(on conexion event) and then send this position to all clients so they can generate the coin in the same position.

Categories

Resources