网络游戏的移动同步(一)网络同步演示

引言

网络游戏的同步处理是在制作网络游戏中独有的处理方案,现在网络游戏几乎都是C/S架构,也就是说需要同步其他主机发送给服务器,并转发回本机的包,这里就涉及到如何发包给服务器,发哪些包,另外就是接收到这些包之后,客户端如何处理这些包的问题。

在不进行示例之前,如果简单的思考一下这些问题,一般人都会想到的是,其实只需要发布初始状态,然后每次变更发送更新状态即可。为了使这个问题简单化,本文的处理主要是移动同步,这个也是最常见,并且包量最大的一个协议,下面我们先创建这个更新包。

简单的更新协议

public class PlayerStatePack
{
	public var velocity:Vector2;
	public var position:Vector2;
	public var acceleration:Vector2;
	public var time:uint;
}

3个2维向量分别是位置,速度,加速度,此示例以2维为主,一般游戏可能用加速度比较少一些,但是为了更好的模拟更多的游戏,比如一些赛车或者炮弹之类的,这里的更新包中还有加速度。时间是发送包的时间,加这个主要是为了接收方能预知此时的真实位置。因为接收到的时候,发送方实际已经不在那个位置。

虚拟服务器

为了在客户端中直接模拟服务器延迟,可以直接做个简单的类模拟延迟。

public class Client
{
	// 模拟收到的网络包
	public var packets:Vector.;

	public function Client()
	{
		packets = new Vector.();
	}

	public function send(p:PlayerStatePack):void {
		// 延迟加入队列
		var delayTime:int = MathUtil.rand(Global.delay - Global.range, Global.delay + Global.range);

		setTimeout(sendNow(p), delayTime);
	}

	private function sendNow(p:PlayerStatePack):Function
	{
		return function():void {
			packets.push(p);
		}
	}
}

类的功能很简单,就是发送的时候增加setTimeout,另外这里有两个配置量,延迟量以及延迟波动量,这两个即可比较好的模拟出真实的网络环境,接收方处理的时候,直接从packets数组中获取包处理即可。

第一次尝试

假设这是一个简单的键盘移动游戏,因为鼠标移动同步相对比较简单,甚至于包都可以不需要那么复杂,只需要目标点就可以。
我们开始写发送及接收处理的代码。主循环代码如下

// 一般的线性移动
localPlayer.velocity.x = 0;
localPlayer.velocity.y = 0;
if (InputManager.instance.keyDown(Keyboard.LEFT)) {
	localPlayer.velocity.x = -localPlayer.speed;
}
if (InputManager.instance.keyDown(Keyboard.RIGHT)) {
	localPlayer.velocity.x = localPlayer.speed;
}
if (InputManager.instance.keyDown(Keyboard.UP)) {
	localPlayer.velocity.y = -localPlayer.speed;
}
if (InputManager.instance.keyDown(Keyboard.DOWN)) {
	localPlayer.velocity.y = localPlayer.speed;
}
// 发送协议
var p:PlayerStatePack = new PlayerStatePack();
p.velocity = localPlayer.velocity.clone();
p.position = localPlayer.position.clone();
p.acceleration = localPlayer.acceleration.clone();
p.time = getTimer();
client.send(p);

while (client.packets.length > 0) {
	var rp:PlayerStatePack = client.packets.shift();
	// 直接拉扯
	if (smoothMethodCb.selectedIndex == 0) {
		netPlayer.position.x = rp.position.x;
		netPlayer.position.y = rp.position.y;
		netPlayer.velocity = rp.velocity;
	}
}

// 更新场景
localScene.update();
netScene.update();

看这个简单的游戏主循环,主要的流程是获取输入->本地玩家更新->发送更新包->接收更新包->处理游戏逻辑。为了使整个游戏代码清晰,我清理了不太需要的细节代码。

这个简单的网络同步演示如下

Get Adobe Flash player

这个演示明显有几个问题,比如发送包太频繁(停止也在发送,因为是每帧发送),另外接受包的处理也很生硬,当网络延迟波动过大,拉扯现象非常明显。

网络游戏的移动同步(一)网络同步演示》上有5条评论

  1. wolfand11

    感觉网络延迟模拟有点问题 按照你现在的设定 后发的包如果延迟小于先发的包的话,可能会先被添加到vector中

    回复

发表评论

电子邮件地址不会被公开。 必填项已用*标注