How can I send real data in Node.js? - javascript

I have game Server-side, update logic with 60 FPS and Send data with 10 FPS
var PLAYER_LIST = {};
class Player {
constructor(socket) {
this.id = Math.random() * 9999;
this.pressKeyAttack = 0;
this.socket = socket;
PLAYER_LIST[this.id] = this;
}
setupSocket() {
this.socket.on("keypress", (data) => {
if (data.key == 1) this.pressKeyAttack = data.value;
});
}
send() {
//Simple data
//Send to this player all data from PLAYER_LIST
var data = {};
for (var id in PLAYER_LIST) {
data[id] = PLAYER_LIST[id].pressKeyAttack;
}
this.socket.emit("data", data);
}
update() {
if (this.pressKeyAttack == 1) {
//do something
}
}
}
io.sockets.on("connection", (socket) => {
new Player(socket);
});
setInterval(() => {
//Logic update
for (var id in PLAYER_LIST) {
PLAYER_LIST[id].update();
}
}, 1000 / 60);
setInterval(() => {
//Send data
for (var id in PLAYER_LIST) {
PLAYER_LIST[id].send();
}
}, 1000 / 10);
Example Client-side, when any player press [Space] then all other player will see animation from that Player.
var PLAYER_LIST = {}; //all player
for (var id in PLAYER_LIST) {
if (PLAYER_LIST[id].pressKeyAttack == 1) {
//run animation
}
}
socket.on("data", (data) => {
for (var id in data) {
PLAYER_LIST[id].pressKeyAttack = data[id];
}
})
document.onkeydown = function(e) {
// Player.pressKeyAttack = 1
if (e.keyCode == 32) socket.emit("keypress", { key: 1, value: 1 });
}
document.onkeyup = function(e) {
// Player.pressKeyAttack = 0
if (e.keyCode == 32) socket.emit("keypress", { key: 1, value: 0 });
}
My problem:
Ex: I'm a player in game (called Player_A) and some other players. When Player_A press [Space] down and up so fast, other players can't receive data (pressKeyAttack = 1) from Player_A, because Loop Send Data run 10 FPS (send every 100ms), Player_A.pressKeyAttack equal 1 and back to 0 in 50ms.
Look like:
at 0ms: Player_A.pressKeyAttack = 0;
at 20ms: Player_A.pressKeyAttack = 1; //Player press down
at 50ms: Player_A.pressKeyAttack = 0; //Player press up
at 100ms: Loop Data Run - Player_A.pressKeyAttack = 0.
In fact it used to be 1. How can I fix this?

Related

Reset Valuable and repeated action

I am building a game, where the computer makes a patter of four colors and you have to remember the pattern and repeat it.
And it works almost fine, but there is one error which i cant figure out.
When i played a round and lost and the game restarts the level value goes to 2 instead of 1.
And when the new game starts the first two buttons get pressed at the same time.
At the first round everything works fine but the next round not.
var gamePattern = [];
var playerPattern = [];
var buttonColors = ["red", "blue", "green", "yellow"]
var level = 0;
$(".btn").click(function () {
if (patternDone) {
pressButton(this.id);
playerPattern.push(this.id);
checkAnswer();
}
});
function resetGame() {
gamePattern = [];
playerPattern = [];
gamePattern.length = 0;
playerPattern.length = 0;
patternDone = false;
level = 0;
$("h1").html("Press A Key to Start")
$(document).keypress(startGame);
}
//start
$(document).keypress(startGame);
//start Game
function startGame() {
level = level + 1;
console.log("level " + level);
console.log(level);
gamePattern = [];
playerPattern = [];
createLevel();
console.log(gamePattern)
playPattern();
patternDone = true;
$("h1").html("Level " + level)
}
//play the patter
async function playPattern() {
for (k = -1; k < level; k++) {
d = level - k;
// console.log(gamePattern);
abcColor = gamePattern[gamePattern.length - d];
console.log(abcColor);
await delay(1000);
pressButton(abcColor);
d = 0;
}
}
//create the level
function createLevel() {
for (y = 0; y <= level; y++) {
var randomColor = buttonColors[Math.floor(Math.random() * buttonColors.length)];
gamePattern.push(randomColor);
}
}
//update h1
function h1Level() {
levelCopy = level + 1;
$("h1").html("level " + levelCopy);
}
//pressButton
function pressButton(colord) {
// console.log(colord);
animatePress(colord);
playSound(nameSound = colord);
}
// Sound
function playSound(nameSound) {
var audio = new Audio("sounds/" + nameSound + ".mp3");
audio.play();
}
// animateClick
function animatePress(currentColor) {
$("#" + currentColor).addClass("pressed");
// console.log(currentColor);
setTimeout(function () {
$("#" + currentColor).removeClass("pressed");
}, 100);
}
//delay
const delay = millis => new Promise((resolve, reject) => {
setTimeout(_ => resolve(), millis)
});
//Button click and deciding if its right
async function checkAnswer() {
if (playerPattern.length === gamePattern.length) {
if (playerPattern.join(',') === gamePattern.join(',')) {
$("h1").html("Correct!");
console.log("correct");
await delay(1000);
startGame();
} else if (playerPattern.join(',') !== gamePattern.join(',')) {
$("h1").html("Wrong!");
level = 0;
console.log("wrong");
await delay(3000);
resetGame();
}
}
}

Repeat previous pattern (gamePatten array) one button at a time, before adding Next Sequence. Simon Game

I would like to setTimeout to every step/item from the GamePattern array. Suppose I am at LEVEL 4:
gamePattern [ 'red', 'blue', 'blue ] then Next Sequence.
But I need to run the sequence one-second gap between them, like 1sec to 'red', then 1sec to 'blue', 1sec to 'blue'...
Here is my entire code.
let userPattern = [];
const gameColors = ["green", "red", "yellow", "blue"];
let level = 0;
const sound = new Audio(`sounds/green.mp3`);
const wrong = new Audio("sounds/wrong.mp3");
//PLAYGAME
function playGame() {
document.querySelector("#overlay").style.display = "none";
level = 0;
gamePattern = [];
nextSequence();
}
//NEXT SEQUENCE
const nextSequence = function () {
document.querySelector(
"h2"
).innerHTML = `<h2 class="detach">LEVEL ${level}</span></h2>`;
userPattern = [];
let index = Math.round(Math.random() * 3);
let randomChosenColour = gameColors[index];
gamePattern.push(randomChosenColour);
animatePress(randomChosenColour);
};
function animatePress(buttoncolor) {
let button = document.querySelector(`#${buttoncolor}`);
button.classList.remove("hidden");
sound.play();
setTimeout(() => button.classList.add("hidden"), 200);
}
function userChoice(e) {
let colorClicked = e.target.classList[1];
animatePress(colorClicked);
userPattern.push(colorClicked);
checkAnswer(userPattern.length - 1);
}
for (let button of gameColors) {
document.querySelector(`.${button}`).addEventListener("click", userChoice);
}
//CHECK
function checkAnswer(currentlevel) {
if (userPattern[currentlevel] === gamePattern[currentlevel]) {
if (userPattern.length === gamePattern.length) {
previousPattern();
level++;
}
} else {
return gameOver();
}
}
//GAMEOVER
function gameOver() {
wrong.play();
//add eventlistener to restart the game
document.body.addEventListener("keypress", playGame);
//display an overlay GAME OVER SETUP.
document.querySelector("#overlay").style.display = "block";
document.querySelector("h2").innerHTML =
'<h2><span class="detach"> Lost at level: ' + level + "</span></h2>";
}
//START
document.body.addEventListener("keypress", playGame);
function previousPattern() {
//loop over the gamePatter in order to show the iteration 0.5 second gap.
for (let i = 1; i <= gamePattern.length; i++) {
setTimeout(() => {
animatePress(gamePattern[i - 1]);
}, 500 * i);
console.log(gamePattern.length, gamePattern);
if (i === gamePattern.length) {
return setTimeout(nextSequence, 1000 * i);
}
}
}
The thing is, The code doesn't show any improvement, and button animations overlap, and I don't know If I've messed up with so much setTimeout added.
Any hint? Thanks in advance

Run function only once until onmouseout

I have an anchor tag that plays a sound everytime the link is hovered:
<a onmouseenter="playAudio();">LINK</a>
However, when hovered, the link creates some characters that, if I keep moving the mouse inside the element, it keeps playing the sound over and over again.
Is there a way to play the onmouseenter function only once until it has detected the event onmouseout?
I've tried adding:
<a onmouseenter="playAudio();this.onmouseenter = null;">LINK</a>
but then it only plays the sound once.
Here goes the TextScramble animation function:
// ——————————————————————————————————————————————————
// TextScramble
// ——————————————————————————————————————————————————
class TextScramble {
constructor(el) {
this.el = el
this.chars = '!<>-_\\/[]{}—=+*^?#________'
this.update = this.update.bind(this)
}
setText(newText) {
const oldText = this.el.innerText
const length = Math.max(oldText.length, newText.length)
const promise = new Promise((resolve) => this.resolve = resolve)
this.queue = []
for (let i = 0; i < length; i++) {
const from = oldText[i] || ''
const to = newText[i] || ''
const start = Math.floor(Math.random() * 40)
const end = start + Math.floor(Math.random() * 40)
this.queue.push({
from,
to,
start,
end
})
}
cancelAnimationFrame(this.frameRequest)
this.frame = 0
this.update()
return promise
}
update() {
let output = ''
let complete = 0
for (let i = 0, n = this.queue.length; i < n; i++) {
let {
from,
to,
start,
end,
char
} = this.queue[i]
if (this.frame >= end) {
complete++
output += to
} else if (this.frame >= start) {
if (!char || Math.random() < 0.28) {
char = this.randomChar()
this.queue[i].char = char
}
output += `<span class="dud">${char}</span>`
} else {
output += from
}
}
this.el.innerHTML = output
if (complete === this.queue.length) {
this.resolve()
} else {
this.frameRequest = requestAnimationFrame(this.update)
this.frame++
}
}
randomChar() {
return this.chars[Math.floor(Math.random() * this.chars.length)]
}
}
// ——————————————————————————————————————————————————
// Configuration
// ——————————————————————————————————————————————————
const phrases = [
'MAIN_HUB'
]
const el = document.querySelector('.link_mainhub')
const fx = new TextScramble(el)
let counter = 0
const next = () => {
fx.setText(phrases[counter]).then(() => {});
}
el.addEventListener('mouseenter', next);
<a class="link_mainhub" onmouseenter="playAudio();">LINK</a>
Thanks.
Set a variable when you start playing the audio, and check for this before playing it again. Have the animation unset the variable when it's done.
function audioPlaying = false;
function playAudio() {
if (!audioPlaying) {
audioPlaying = true;
audioEl.play();
}
}
const next = () => {
fx.setText(phrases[counter]).then(() => {
audioPlaying = false;
});
}

Totally destroy an instance

I am making a game with rounds that last either 100 minutes OR the time required for totalItemAmount to reach 500.
If "addPlayer" gets triggered when the game has ended, I want to push that player to next round.
I want to be able to destroy it/create new ones flawlessly.
First game always runs well.
Following games go wrong. To sum it up, I feel like the first game ran isn't being properly destroyed after it ends. Obviously, my code is much much larger than what you see down here, but let me ask you, is there any problem with my structure?
function Game(duration, playersForThisRound) {
console.log('NEW GAME')
this.playersForNextRound = [];
this.id = Math.random();
this.finished = 0;
this.players = {};
this.duration = 100;
this.totalItemAmount = 0;
this.endTimeout = setTimeout(() => {
this.end(this);
return
}, this.duration * 60 * 1000);
if (playersForThisRound && playersForThisRound.length > 0) {
Object.keys(this.playersForNextRound).forEach(function(key) {
this.addPlayer(this, key.player, key.items)
});
}
return
}
Game.prototype.addPlayer = function(game, player) {
if (this.finished || this.totalItemAmount >= 500) {
var playerIsInNextRound = 0;
Object.keys(this.playersForNextRound).forEach(function(key) {
if (key.id == player.id) {
playerIsInNextRound = 1
key.items.push(items)
}
})
if (!playerIsInNextRound)
this.playersForNextRound.push({
"id": player.id,
"player": player,
"items": items
});
if (game.totalItemAmount >= 500) {
clearTimeout(game.endTimeout)
this.end(this);
}
return
}
if (!game.players[player.id]) {
game.players[player.id] = new Player(game, player, items);
game.players[player.id].addItems(game, items, function(added) {
game.jackpot += added;
Object.keys(game.players).forEach(function(player) {
game.players[player].chance = game.players[player].getChance(game.jackpot)
});
})
}
Game.prototype.end = function(game) {
game.finished = 1;
clearTimeout(game.endTimeout)
setTimeout(() => {
game = new Game(10, this.playersForNextRound);
}, 1000 * 60 * 10);
}
//To start first game
var game = new Game();
module.exports = game;
Now in another file:
let game = require("./lib/games/roundbased.js");
let test = setInterval(() => {
for (var i = 0; i < 100; i++) {
game.addPlayer(game, {
id: '12345' + i,
nick: "player" + i
},

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