Javascript,Phaser3,Socket.io 玩家起始位置不正确,多人游戏
Javascript, Phaser3, Socket.io Player Starting Position Not As Set, Multiplayer
代码上下文:
Phaser3
Socket.io
类似于Agar.io
的多人游戏
在套接字连接上,服务器广播:玩家 ID、皮肤、x 位置、y 位置、“质量”
服务器还为玩家发送之前的房间数据
客户端根据数据“点 4”创建精灵,在本地声明、呈现和存储副本
当 Phaser3 的 update() 函数循环时发出位置
问题:
创建精灵时,将它们全部创建在一个位置,而不是在随机分配的 x,y 坐标处
(可能是单独的问题)它似乎渲染了两次精灵,其中一次不随控件移动
server.js:
function hypotenuse(x,y,x1,y1)
{
deltaX = x1-x;
deltaY = y1-y;
a = Math.sqrt((deltaX * deltaX) + (deltaY * deltaY));
return a;
}
function randomInt() {
min = Math.ceil(0.1);
max = Math.floor(8000);
return Math.floor(Math.random() * (max - min) + min);
}
io.on('connection',(socket)=>
{
console.log(`${socket.id} joined the universe!`);
universe['0'].players[socket.id] = {playerID:socket.id,x:randomInt(),y:randomInt(),mass:50}; //assign an arbitrary value for x and y, set it client side.
socket.emit('client',socket.id,universe['0'].players[socket.id].x,universe['0'].players[socket.id].y,null,universe['0'].players[socket.id].mass);
socket.broadcast.emit('join',socket.id,universe['0'].players[socket.id].x,universe['0'].players[socket.id].y, null,universe['0'].players[socket.id].mass);
socket.emit('loadUniverse', universe['0'].players); //sending to client only
socket.on('updatePos',({player,vx,vy,x,y})=> //make into object
{
if(universe['0'].gameRules.mass<200)
{
let id = 'mass:'+ randomInt() + randomInt() + randomInt();
console.log(`mass id: ${id}`);
universe['0'].players[id] = {playerID:id,x:randomInt(),y:randomInt(),mass:10};
//do not use this, broadcast to individual rooms with io.to(room).emit(channel,content)
io.emit('spawnMass',id,universe['0'].players[id].x,universe['0'].players[id].y,null,universe['0'].players[id].mass);
universe['0'].gameRules.mass+=1;
}
if(player in universe['0'].players)
{
universe['0'].players[player].x = x;
universe['0'].players[player].y = y;
for(const player1 in universe['0'].players)
{
if(player1!==player)
{
//distance between two coord pairs
let distance = hypotenuse(universe['0'].players[player1].x,universe['0'].players[player1].y,x,y);
//console.log(distance);
//console.log('debugging NaN1')
if(distance < 100)
{
console.log('overlapping')
if(universe['0'].players[player].mass>universe['0'].players[player1].mass)
{
universe['0'].players[player].mass+=universe['0'].players[player1].mass
socket.emit('attracted',universe['0'].players[player].playerID,universe['0'].players[player1].playerID)
socket.emit('eliminated',universe['0'].players[player1].playerID) //have all state change in same socket event
delete universe['0'].players[player1]
}
else
{
universe['0'].players[player1].mass+=universe['0'].players[player].mass
socket.emit('attracted',universe['0'].players[player1].playerID,universe['0'].players[player].playerID)
socket.emit('eliminated',universe['0'].players[player].playerID) //have all state change in same socket event
delete universe['0'].players[player]
}
}
//if in radius, remove player from room and add points to winner
}
}
}
socket.broadcast.emit("updatePos1", player,vx,vy);
})
socket.on('disconnect',()=>
{
delete universe['0'].players[socket.id]
//emit a delete event
console.log(`${socket.id}player left the universe!`)
})
})
相关客户代码:
scene.socket.on('connect',()=> //needs to chain from the connect
{
this.scene.socket.emit('join',this.socketID) //gives id to server, server chooses skin, server broadcasts
console.log(`connected`);
})
scene.socket.on('updatePos1',(socketID1,vx,vy)=>
{
if(this.socketID!==socketID1)
{
//console.log('socket was not yours!');
this.scene.gameHandler.players[socketID1].sprite.body.setVelocity(vx,vy);
}
})
//ITEM BREAKS UP INTO SMALLER CHUNKS IF GRAVITY IS "VIOLENT" ENOUGH
scene.socket.on('attracted',(player,player1)=>
{
this.scene.gameHandler.players[player].mass+=this.scene.gameHandler.players[player1].mass
})
scene.socket.on('eliminated',(player)=>
{
console.log('eliminated',player,this.scene.gameHandler.players[player])
this.scene.gameHandler.players[player].sprite.destroy(true);
delete this.scene.gameHandler.players[player];
})
scene.socket.on('join',(socketID0,x,y,skin,mass)=>
{
console.log(`Player ${socketID0} joined`);
this.scene.gameHandler.loadPlayer(socketID0,x,y,skin,mass);
})
scene.socket.on('client',(socketID0,x,y,skin,mass)=>
{
this.socketID = socketID0;
this.scene.gameHandler.loadPlayer(socketID0,x,y,skin,mass);
})
scene.socket.on('loadUniverse',(universe)=>
{
for(const property in universe)
{
console.log(`propery:${property}`);
console.log(universe[property].x,universe[property].y)
this.scene.gameHandler.loadPlayer(property,universe[property].x,universe[property].y,null,universe[property].mass);//make this dynamic
}
})
scene.socket.on('spawnMass',(id,x,y,skin,mass)=>
{
this.scene.gameHandler.loadPlayer(id,x,y,skin,mass);
this.scene.gameHandler.players[id].sprite.setScale(0.2,0.2)
})
}
updatePos()
{
console.log(`${this.socketID} for updatePos()`)
this.scene.socket.emit(
'updatePos',
{
player:this.socketID,
vx:this.scene.gameHandler.players[this.socketID].sprite.body.velocity.x,
vy:this.scene.gameHandler.players[this.socketID].sprite.body.velocity.y,
x:this.scene.gameHandler.players[this.socketID].sprite.body.x,
y:this.scene.gameHandler.players[this.socketID].sprite.body.y
}
)
}
gameHandler.js 中的 loadPlayer 函数:
loadPlayer(socketID,x,y,skin,mass) //get websocket to load player
{
if(socketID)
{ //id:{sprite:x}
this.players[socketID] =
{
sprite: this.scene.physics.add.sprite
(
x,//this.scene.game.config.width * 0.5,
y,//this.scene.game.config.height * 0.5,
"arc_pink"
),
mass: mass
}
}
console.log(`Load player returned: ${this.players[socketID].mass}`)
this.players[socketID].sprite.x = x;
this.players[socketID].sprite.y = y;
this.players[socketID].sprite.setOrigin(0.5,0.5);
this.players[socketID].sprite.body.setCollideWorldBounds(true);
socketID == this.scene.socketsHandler.socketID?this.scene.cameras.main.startFollow(this.players[socketID].sprite):null;
console.log(`Load player returned: ${this.players[socketID]}`)
}
在 index.js 我有这个移相器配置:
const config = {
parent: "phaser-example",
type: Phaser.AUTO,
scale:
{
mode: Phaser.Scale.FIT,
width: 1800,
height: 1600
},
autoCenter: Phaser.Scale.CENTER_BOTH,
physics: {
default: "arcade",
arcade: {
Gravity: { x: 0, y: 0 },
debug: false,
},
},
scene: MyGame,
};
世界范围:
-90000, -90000, 90000, 90000
问题:
我做错了什么,怎么错了?我致力于学习多人游戏是如何制作的,任何输入都会有所帮助。谢谢。
嗯,两次绘制是由于 socket.emit('loadUniverse',...);
的调用,因为玩家需要创建玩家精灵两次(第一次 socket.emit('client',...);
)。
其余的看起来不错,至少我用你的代码重新创建了演示。
btw. 1: You could check the default phaser libraries / functions. Phaser has many helper functions, like for example:
- your
hypotenuse
function can be done with Phaser.Math.Distance.Between
https://photonstorm.github.io/phaser3-docs/Phaser.Math.Distance.html
randomInt()
can be done with Phaser.Math.Between
https://photonstorm.github.io/phaser3-docs/Phaser.Math.html#.Between__anchor
btw. 2: a compact phaser multiplayer game tutorial can be found here https://gamedevacademy.org/create-a-basic-multiplayer-game-in-phaser-3-with-socket-io-part-1/ uses phaser headless on the server side. Just if you want to se an other approach
代码上下文:
Phaser3
Socket.io
类似于Agar.io
的多人游戏在套接字连接上,服务器广播:玩家 ID、皮肤、x 位置、y 位置、“质量”
服务器还为玩家发送之前的房间数据
客户端根据数据“点 4”创建精灵,在本地声明、呈现和存储副本
当 Phaser3 的 update() 函数循环时发出位置
问题:
创建精灵时,将它们全部创建在一个位置,而不是在随机分配的 x,y 坐标处
(可能是单独的问题)它似乎渲染了两次精灵,其中一次不随控件移动
server.js:
function hypotenuse(x,y,x1,y1)
{
deltaX = x1-x;
deltaY = y1-y;
a = Math.sqrt((deltaX * deltaX) + (deltaY * deltaY));
return a;
}
function randomInt() {
min = Math.ceil(0.1);
max = Math.floor(8000);
return Math.floor(Math.random() * (max - min) + min);
}
io.on('connection',(socket)=>
{
console.log(`${socket.id} joined the universe!`);
universe['0'].players[socket.id] = {playerID:socket.id,x:randomInt(),y:randomInt(),mass:50}; //assign an arbitrary value for x and y, set it client side.
socket.emit('client',socket.id,universe['0'].players[socket.id].x,universe['0'].players[socket.id].y,null,universe['0'].players[socket.id].mass);
socket.broadcast.emit('join',socket.id,universe['0'].players[socket.id].x,universe['0'].players[socket.id].y, null,universe['0'].players[socket.id].mass);
socket.emit('loadUniverse', universe['0'].players); //sending to client only
socket.on('updatePos',({player,vx,vy,x,y})=> //make into object
{
if(universe['0'].gameRules.mass<200)
{
let id = 'mass:'+ randomInt() + randomInt() + randomInt();
console.log(`mass id: ${id}`);
universe['0'].players[id] = {playerID:id,x:randomInt(),y:randomInt(),mass:10};
//do not use this, broadcast to individual rooms with io.to(room).emit(channel,content)
io.emit('spawnMass',id,universe['0'].players[id].x,universe['0'].players[id].y,null,universe['0'].players[id].mass);
universe['0'].gameRules.mass+=1;
}
if(player in universe['0'].players)
{
universe['0'].players[player].x = x;
universe['0'].players[player].y = y;
for(const player1 in universe['0'].players)
{
if(player1!==player)
{
//distance between two coord pairs
let distance = hypotenuse(universe['0'].players[player1].x,universe['0'].players[player1].y,x,y);
//console.log(distance);
//console.log('debugging NaN1')
if(distance < 100)
{
console.log('overlapping')
if(universe['0'].players[player].mass>universe['0'].players[player1].mass)
{
universe['0'].players[player].mass+=universe['0'].players[player1].mass
socket.emit('attracted',universe['0'].players[player].playerID,universe['0'].players[player1].playerID)
socket.emit('eliminated',universe['0'].players[player1].playerID) //have all state change in same socket event
delete universe['0'].players[player1]
}
else
{
universe['0'].players[player1].mass+=universe['0'].players[player].mass
socket.emit('attracted',universe['0'].players[player1].playerID,universe['0'].players[player].playerID)
socket.emit('eliminated',universe['0'].players[player].playerID) //have all state change in same socket event
delete universe['0'].players[player]
}
}
//if in radius, remove player from room and add points to winner
}
}
}
socket.broadcast.emit("updatePos1", player,vx,vy);
})
socket.on('disconnect',()=>
{
delete universe['0'].players[socket.id]
//emit a delete event
console.log(`${socket.id}player left the universe!`)
})
})
相关客户代码:
scene.socket.on('connect',()=> //needs to chain from the connect
{
this.scene.socket.emit('join',this.socketID) //gives id to server, server chooses skin, server broadcasts
console.log(`connected`);
})
scene.socket.on('updatePos1',(socketID1,vx,vy)=>
{
if(this.socketID!==socketID1)
{
//console.log('socket was not yours!');
this.scene.gameHandler.players[socketID1].sprite.body.setVelocity(vx,vy);
}
})
//ITEM BREAKS UP INTO SMALLER CHUNKS IF GRAVITY IS "VIOLENT" ENOUGH
scene.socket.on('attracted',(player,player1)=>
{
this.scene.gameHandler.players[player].mass+=this.scene.gameHandler.players[player1].mass
})
scene.socket.on('eliminated',(player)=>
{
console.log('eliminated',player,this.scene.gameHandler.players[player])
this.scene.gameHandler.players[player].sprite.destroy(true);
delete this.scene.gameHandler.players[player];
})
scene.socket.on('join',(socketID0,x,y,skin,mass)=>
{
console.log(`Player ${socketID0} joined`);
this.scene.gameHandler.loadPlayer(socketID0,x,y,skin,mass);
})
scene.socket.on('client',(socketID0,x,y,skin,mass)=>
{
this.socketID = socketID0;
this.scene.gameHandler.loadPlayer(socketID0,x,y,skin,mass);
})
scene.socket.on('loadUniverse',(universe)=>
{
for(const property in universe)
{
console.log(`propery:${property}`);
console.log(universe[property].x,universe[property].y)
this.scene.gameHandler.loadPlayer(property,universe[property].x,universe[property].y,null,universe[property].mass);//make this dynamic
}
})
scene.socket.on('spawnMass',(id,x,y,skin,mass)=>
{
this.scene.gameHandler.loadPlayer(id,x,y,skin,mass);
this.scene.gameHandler.players[id].sprite.setScale(0.2,0.2)
})
}
updatePos()
{
console.log(`${this.socketID} for updatePos()`)
this.scene.socket.emit(
'updatePos',
{
player:this.socketID,
vx:this.scene.gameHandler.players[this.socketID].sprite.body.velocity.x,
vy:this.scene.gameHandler.players[this.socketID].sprite.body.velocity.y,
x:this.scene.gameHandler.players[this.socketID].sprite.body.x,
y:this.scene.gameHandler.players[this.socketID].sprite.body.y
}
)
}
gameHandler.js 中的 loadPlayer 函数:
loadPlayer(socketID,x,y,skin,mass) //get websocket to load player
{
if(socketID)
{ //id:{sprite:x}
this.players[socketID] =
{
sprite: this.scene.physics.add.sprite
(
x,//this.scene.game.config.width * 0.5,
y,//this.scene.game.config.height * 0.5,
"arc_pink"
),
mass: mass
}
}
console.log(`Load player returned: ${this.players[socketID].mass}`)
this.players[socketID].sprite.x = x;
this.players[socketID].sprite.y = y;
this.players[socketID].sprite.setOrigin(0.5,0.5);
this.players[socketID].sprite.body.setCollideWorldBounds(true);
socketID == this.scene.socketsHandler.socketID?this.scene.cameras.main.startFollow(this.players[socketID].sprite):null;
console.log(`Load player returned: ${this.players[socketID]}`)
}
在 index.js 我有这个移相器配置:
const config = {
parent: "phaser-example",
type: Phaser.AUTO,
scale:
{
mode: Phaser.Scale.FIT,
width: 1800,
height: 1600
},
autoCenter: Phaser.Scale.CENTER_BOTH,
physics: {
default: "arcade",
arcade: {
Gravity: { x: 0, y: 0 },
debug: false,
},
},
scene: MyGame,
};
世界范围: -90000, -90000, 90000, 90000
问题:
我做错了什么,怎么错了?我致力于学习多人游戏是如何制作的,任何输入都会有所帮助。谢谢。
嗯,两次绘制是由于 socket.emit('loadUniverse',...);
的调用,因为玩家需要创建玩家精灵两次(第一次 socket.emit('client',...);
)。
其余的看起来不错,至少我用你的代码重新创建了演示。
btw. 1: You could check the default phaser libraries / functions. Phaser has many helper functions, like for example:
- your
hypotenuse
function can be done withPhaser.Math.Distance.Between
https://photonstorm.github.io/phaser3-docs/Phaser.Math.Distance.htmlrandomInt()
can be done withPhaser.Math.Between
https://photonstorm.github.io/phaser3-docs/Phaser.Math.html#.Between__anchor
btw. 2: a compact phaser multiplayer game tutorial can be found here https://gamedevacademy.org/create-a-basic-multiplayer-game-in-phaser-3-with-socket-io-part-1/ uses phaser headless on the server side. Just if you want to se an other approach