我正在尝试实施客户端预测和服务器对账的一个示例,如果有更好的方法,请让我知道!我试图隐藏网络游戏的延迟。目前,我正在为客户端使用lidgren和XNA,并且仅使用服务器的控制台应用程序。我将服务器端的lidgren设置为模拟1.5秒的延迟。所以当我运行这个代码时,它的作用大部分是这样的,但似乎客户端正在缓冲移动,然后最终在屏幕上移动字符,但根据我在下面引用的演示,我没有看到缓冲类型的行为只是难住它可能是什么?如果您需要查看更多的代码,请告诉我,我不想让帖子泛滥太多,请提前感谢您提供的帮助。客户端预测问题
在SendMovement方法中,我将用户输入和序列化命令发送到服务器,它将继续并移动播放器并将移动命令存储在队列中。
private Queue<MovementCommand> _previousMovements = new Queue<MovementCommand>(5000);
public void SendMovement(float elapsed, byte direction)
{
MovementCommand movement = new MovementCommand(_id, _sequenceNumber++, elapsed, direction);
OutputCommand<MovementCommand> moveCommand = new OutputCommand<MovementCommand>(GamePacketTypes.Movement, movement);
byte[] msgData = moveCommand.Serialize();
Send(msgData);
Console.WriteLine(string.Format("Elapsed Time = {0}, Sequence # = {1}", elapsed, _sequenceNumber));
if (_clientSidePrediction == true)
{
_player.Move(movement);
_previousMovements.Enqueue(movement);
}
}
当我收到一条消息来自我更新播放位置,然后检查,看看有什么是从服务器相比,本地序列号的最后输入序列回服务器。
public void HandleMessages()
{
while (true)
{
if (mIncomingMessages.Count > 0)
{
for (int i = 0; i < mIncomingMessages.Count; i++)
{
byte command = mIncomingMessages[i].ReadByte();
switch ((GamePacketTypes)command)
{
case GamePacketTypes.UpdateEntity:
EntityStateType stateObj = EntityStateType.Deserialize(mIncomingMessages[i].ReadBytes(mIncomingMessages[i].LengthBytes - 1));
_player.Position(stateObj);
if (_serverReconciliation == true)
{
if (stateObj.ID == _id)
{
int j = 0;
while (j < _previousMovements.Count)
{
MovementCommand move = _previousMovements.Peek();
if (move.InputSequence <= stateObj.LastProcessedInput)
{
_previousMovements.Dequeue();
}
else
{
_player.Move(_previousMovements.Dequeue());
j++;
}
}
}
}
break;
}
}
mIncomingMessages.Clear();
}
Thread.Sleep(25);
}
}
在服务器端,我只是把来自客户端的命令,然后应用到自己的性格和对客户端设置的最后处理序列时,下一个状态更新熄灭。从加布里埃尔Gamebetta
private async Task<bool> HandleMovement(MovementCommand move)
{
switch((DirectionHeading)move.Direction)
{
case DirectionHeading.North:
_player.Y -= (move.PressedTime * _player.Velocity);
break;
case DirectionHeading.East:
_player.X += (move.PressedTime * _player.Velocity);
break;
case DirectionHeading.South:
_player.Y += (move.PressedTime * _player.Velocity);
break;
case DirectionHeading.West:
_player.X -= (move.PressedTime * _player.Velocity);
break;
}
_player.Direction = move.Direction;
LastProcessedInput = move.InputSequence;
Console.WriteLine("Last Processed Input = {0}", LastProcessedInput);
return true;
}
示例代码(希望他不记...)
// Get inputs and send them to the server.
// If enabled, do client-side prediction.
Client.prototype.processInputs = function() {
// Compute delta time since last update.
var now_ts = +new Date();
var last_ts = this.last_ts || now_ts;
var dt_sec = (now_ts - last_ts)/1000.0;
this.last_ts = now_ts;
// Package player's input.
var input;
if (this.key_right) {
input = { press_time: dt_sec };
} else if (this.key_left) {
input = { press_time: -dt_sec };
} else {
// Nothing interesting happened.
return;
}
// Send the input to the server.
input.input_sequence_number = this.input_sequence_number++;
input.entity_id = this.entity_id;
this.server.network.send(client_server_lag, input);
// Do client-side prediction.
if (client_side_prediction) {
this.entity.applyInput(input);
}
// Save this input for later reconciliation.
this.pending_inputs.push(input);
}
Server.prototype.processInputs = function() {
// Process all pending messages from clients.
while (true) {
var message = this.network.receive();
if (!message) {
break;
}
// Update the state of the entity, based on its input.
// We just ignore inputs that don't look valid; this is what prevents
// clients from cheating.
if (this.validateInput(message)) {
var id = message.entity_id;
this.entities[id].applyInput(message);
this.last_processed_input[id] = message.input_sequence_number;
}
}
}
Client.prototype.processServerMessages = function() {
while (true) {
var message = this.network.receive();
if (!message) {
break;
}
// World state is a list of entity states.
for (var i = 0; i < message.length; i++) {
var state = message[i];
if (state.entity_id == this.entity_id) {
// Got the position of this client's entity.
if (!this.entity) {
// If this is the first server update, create a local entity.
this.entity = new Entity();
}
// Set the position sent by the server.
this.entity.x = state.position;
if (server_reconciliation) {
// Server Reconciliation. Re-apply all the inputs not yet processed by
// the server.
var j = 0;
while (j < this.pending_inputs.length) {
var input = this.pending_inputs[j];
if (input.input_sequence_number <= state.last_processed_input) {
// Already processed. Its effect is already taken into account
// into the world update we just got, so we can drop it.
this.pending_inputs.splice(j, 1);
} else {
// Not processed by the server yet. Re-apply it.
this.entity.applyInput(input);
j++;
}
}
} else {
// Reconciliation is disabled, so drop all the saved inputs.
this.pending_inputs = [];
}
} else {
// TO DO: add support for rendering other entities.
}
}
}
我试图添加一个链接到您的网页,它不会允许我:(我希望我可以。 – lakedoo 2014-08-27 13:52:32