我正在进行多人游戏,我遇到了同步玩家的问题。
当玩家按下其中一个移动键(W,A,S,D)时,客户端会发送一个关于按下的按钮的数据包,服务器根据按下的键设置速度,然后发回给所有附近的玩家新的速度。
当玩家释放密钥然后客户端发送数据包时,服务器将玩家速度设置为0,0并将位置和速度发送给所有附近的玩家。
所以问题是当我释放钥匙时,大多数时候玩家会跳回来。
我怎么能解决这个问题?
我正在使用socket.io。
客户方:
socket.on('positionEntity', function (data) {
console.log((data.x - entities[data.id].x)+" "+(data.y - entities[data.id].y));
entities[data.id].setPosition(data);
});
$(document).keyup(function(e) {
if (e.keyCode == 87) {
keys.W = false;
socket.emit("stopMove", {dir: 0, time: Date.now()});
}
if (e.keyCode == 65) {
keys.A = false;
socket.emit("stopMove", {dir: 1, time: Date.now()});
}
if (e.keyCode == 83) {
keys.S = false;
socket.emit("stopMove", {dir: 2, time: Date.now()});
}
if (e.keyCode == 68) {
keys.D = false;
socket.emit("stopMove", {dir: 3, time: Date.now()});
}
});
$(document).keydown(function(e) {
if (e.keyCode == 87 && !keys.W) {
keys.W = true;
socket.emit("startMove", {dir: 0, time: Date.now()});
}
if (e.keyCode == 65 && !keys.A) {
keys.A = true;
socket.emit("startMove", {dir: 1, time: Date.now()});
}
if (e.keyCode == 83 && !keys.S) {
keys.S = true;
socket.emit("startMove", {dir: 2, time: Date.now()});
}
if (e.keyCode == 68 && !keys.D) {
keys.D = true;
socket.emit("startMove", {dir: 3, time: Date.now()});
}
});
服务器端:
socket.on('startMove', function(data) {
if (data.dir == 0) socket.player.setMotionY(-5);
if (data.dir == 1) socket.player.setMotionX(-5);
if (data.dir == 2) socket.player.setMotionY(5);
if (data.dir == 3) socket.player.setMotionX(5);
io.sockets.emit("positionEntity", socket.player.serializePosition());
});
socket.on('stopMove', function(dir) {
socket.player.setMotionX(0);
socket.player.setMotionY(0);
io.sockets.emit("positionEntity", socket.player.serializePosition());
});
答案 0 :(得分:11)
这是你正在进行的一项非常复杂的任务,以及我作为宠物项目的一部分所做的事情;)
您正在开发客户端 - 服务器架构游戏,因此服务器是游戏逻辑和决策的最终权威。他们目前处理渲染的方式会因延迟而使速度和方向发生突然变化(正如您所注意到的那样!)
诀窍是缓冲远程玩家的移动信息,以便您总是稍微延迟渲染玩家。我在项目中保持原始状态,只使用位置数据,而不是加速度或速度。例如,当玩家A在他的机器上移动时,命令不会立即被发送以接收确认,他移动并在我的网络发送循环的下一个滴答中(每秒10个滴答),他的位置被激发到更新所有客户端的服务器。附近有这个新职位。这些客户端为每个“远程”播放器都有一个缓冲区,它在呈现更新之前将每个位置存储一段时间(100毫秒)。通过这种方式,客户端会稍微延迟渲染,但我可以在每个位置坐标之间进行插值(平滑精灵/模型的过渡),以实现平滑运动和速度与加速度的幻觉。
客户端代码的BASIC插值函数。该系统最多只为每个远程播放器排队两个更新,其中更新阵列中的索引0是两个中的较旧者。所以索引0可能是远程玩家位置0ms而索引1是远程玩家位置100ms。
interpolate: function() {
var timeDifference = new Date().getTime() - this.serverUpdates[1].time;
// Percentage of time passed since update was received (I use 100ms gaps)
var interPercent = (timeDifference) / 100;
// Latest updates (index 1 is the newest state)
var s1 = this.serverUpdates[0],
s2 = this.serverUpdates[1];
// Need to lerp between values provided in latest update and older one
var p = (new Vector3(s2.position)).subtract(new Vector3(s1.position));
p = p.timesScalar(interPercent);
// New position is the older lerped toward newer position where lerp
//percentage is the time passed
this.position = new Vector3(s1.position).add(p);
// Now update rotation in a smooth manner
var rotationDifference = (s2.rotation - s1.rotation);
if (rotationDifference && rotationDifference != 0) {
this.rotation = s1.rotation + (rotationDifference * interPercent);
}
},
在该代码中,我收到的间隔大约为100毫秒,因此在时间0位置为s1,时间为100ms s2为位置。因此,如果我们收到s2后已经过了50ms,则实体在两个位置之间的比例为50%。这对我的需求来说很好,但在其他类型的游戏中可能无法解决,或者可能需要调整。
这些资源是解释网络游戏和处理延迟的良好开端,你会惊讶于实现插值和推断对远程客户端游戏平滑性的影响。
祝你好运:)