我正在嘗試實施客戶端預測和服務器對賬的一個示例,如果有更好的方法,請讓我知道!我試圖隱藏網絡遊戲的延遲。目前,我正在爲客戶端使用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