【 实时运动 】
1)基于帧的运功。
基于帧的运功,思路是这样的:如果一个画面里有A、B、C、D 4个角色,其中A由你自己控制,而B、C、D是由其他玩家控制的。A的移动完全实时监听我们在客户端的操作,也许是键盘也许是鼠标,总之,不会听服务器端的返回再去移动——这么做是为了不产生用户操作的延迟,当然,如果server端的返回信息说当前操作是非法的,用户的角色会抖动,返回到正确的位置上。而B、C、D的移动是如何实现的呢?在A的玩家电脑上,B、C、D三个角色会保存一个变量——目标坐标,然后会在每一帧中向目标坐标移动一定距离。如果B的玩家进行了移动,会向server端发出请求,然后server端会广播给A、B、C、D四个玩家B的新目标坐标。目标坐标是一直在变的,通过server端进行广播,然后在每帧中,除玩家本人的角色外,其它角色都会朝自己的目标坐标移动一步。
简单地说,每个游戏对象都在不停地说“我在这里,我在这里,我在这里”,游戏对象并没有说将要往哪里去,只是说它在什么地方。这种方法的好处是易于编程。在时间同步不很重要的情况下它非常好用。而这种方法也有个主要的缺点,那就是:在某一时刻,运动中的游戏对象永远不会出现在它此刻应该在的位置。游戏对象的运动总是会有些延时,这是因为同步消息到达客户端之前需要一些时间。
private function enterFrame(e:Event) : void {
if (_myGuY != null){
checkKeys();
moveGuys();
if(getTimer() - _lastTimeSent > 500){
sendUpdate();
}
}
}
当客户端收到sendUpdate()更新之后的server端通知时,会更新用户目标位置
if(!guy.isMe){
guy.walkTo(x,y);
}
注意:每500ms发送一次更新信息到server端是比较合适的,每稍2~3次发送更新信息就比较合适——如果超过这个频率,改送再多的信息也并不能让结果看起来更好,而且毫无裨益。
2)网络延迟和服务器端时间。
基于时间的运动,首先要说明的是,这个时间不是本地时间,而是server端时间,这是所有客户端应该达到的一个共识。获取server端时间必须先获得一个网络延迟时间。
网络延迟的算法是从客户端发送一个请求至server端,server端广播回来,客户端接到到广播返回值的时间,从发送到返回的时间值除以2就是延迟时间。为了让延迟时间尽量精准,最好发送10次请求,然后去除10次请求中差导比较大的测量结果(如果某个测量值大于中间值的1.5倍,我们就可以断定该数据有问题然后舍弃它。),剩下的有价值的数据求一个平均值,就是比较精确的网络延时值了。
具体来说,服务器端时间可以这么来求:客户端发送一个ping请求,接着服务器返回一个含有毫秒级时间标签的响应,随后客户端用下面这个简单的公式就能测算出服务器端的时间:
server time = client time + offset
而offset应该为
offset = server time2 - client time2 + latency
server time2为ping返回的服务器时间标签,client time2为响应到达时的客户端时刻,而latency为ping值的一半(即网络延迟)。
3)基于时间的运动。
基于时间的运动和帧的运功原理不同:当知道一个物体的当前位置、速度、加速度,就可以算出未来某个时刻物体的位置:
x = x0 + vx * t + (1/2) * ax * t ^ 2
y = y0 + vy * t + (1/2) * ay * t ^ 2
一旦你接收到描述对象位置及其运动方向的信息后,你就可以继续让这个对象不断地运动下去。(不同于here I am的思路,运动路径是根据速度、方向、加速度自动计算的)。
如果A、B两个玩家,A玩家向B玩家发射一颗子弹,子弹速度为100像素/秒,而“发射”子弹的消息在到达B玩家的客户端时,已经用去了150ms的时间,那么子弹在从被A发射出到B接收到信息的这段时间里共运动了15像素。所以当子弹被添加到B的屏幕上时,它应该按照上面的公式,直接出现在15像素处,而不是在枪口处。(在玩家A自己的电脑里,子弹是从枪口慢慢移动至15像素处的,而在玩家B的电脑里,一开始就是出现在15像素处)基于时间的运动,可以很好地处理“同步即时性要求高”的需求,但它他产生位置跳跃,也称“抖动”,游戏对象的加速度越大,它在客户端屏幕上的抖动也就越大。如果网络延时为0,当然也就不可能有抖动的现象发生。
“网络延时隐藏”技术可以用来解决抖动的问题。这项技术通过尽量减小网络延时所带来的景程,为我们提供一种更为顺畅的游戏体验。在工业上也被称为“航位推测法”:在预测运动中加以平滑地修正航向,以使对象航向逐步收敛到正确航向。它需要先给定一个“收敛时间”,在这段时间里是用来纠正航向的,它会预测收敛时间后船的位置、航向和速度,用在“收敛时间”段里将船的当前位置、速度和方向调整为“收敛时间”后船的正确位置。“收敛时间”越短,船就越快地接近自己的真实位置,但抖动越大,而“收敛时间”越长,抖动越小,过程越平滑,但船离自己的真实位置越远。
“加速度”是另一个用于处理抖动的技巧。当物体启动或停止时,不要让其马上变化状态,而是给一个加速时间,比如500ms,物体在急速运动忽然停住时,如果不给一个缓冲时间,基于时间同步的运动很可能会让其它客户端里出现“往回收”的现象。而如果给一个缓冲时间,而且这个缓冲时间要大于网络延迟,这个缓冲时间就可以解决“往回收”的现象。在玩家自己的电脑里,从满速到停止有一个整个500ms可用,物体有一个较慢的减速过程,而在其它玩家电脑里,从满速到停止有一个“500ms - 网络延迟”的时间用于减速,它是一个相对快得多的减速过程,利用不同的减速加速度,可以让不同玩家在同一时间点停住,避免“往回收”的现象。
【 游戏流程 】
玩家从大厅系统进入游戏时,有下面的流程:
<1> 选择游戏列表中你想玩的游戏
<2> 看到该游戏加载的画面
<3> 游戏开始
<4> 游戏中
<5> 游戏结束
<6> 返回游戏大厅
而玩家将会存在并维持以下状态:
<1> 等待状态
<2> 初始化状态
<3> 游戏进行状态
<4> 游戏结束
【 等距视图 】
1)等距视图又被称为“2.5D”或者“3/4视图”。它是一种特殊的3D视图。
2)等距视图中,区块形状为菱形。这种菱形按以下3步形成:
<1> 绘制一个任意大小的正方形;
<2> 把这个正方形旋转45度;
<3> 把旋转后的正方形的高度缩放为原来的50%。
菱形区块的宽度是高度的两倍,常见的区块尺寸是64 X 32或者 128 X 64。(2的倍数,计算起来速度快,对性能有帮助)。但有时过于精确也会有问题。如果所绘制的区块像素尺寸与64 X 32分毫不差,那么在所有区块之间就会产生一条很细的线。解决办法就是在所绘制的区块的宽度和高度上各加一个像素,但在排列区块时依然按照64 X 32来进行排列。将区块尺寸变成65 X 33就能消除那条小细线。
3)等距视图的坐标问题。
等距视图的坐标和真实的坐标系需要进行一个转换—— 先围绕其x轴旋转30度,再围绕其y轴旋转45度。
public function Isometric(){
var theta : Number = 30;
var alpha : Number = 45;
theta *= Math.PI / 180;
alpha *= Math.PI / 180;
_sinTheta = Math.sin(theta);
_cosTheta = Math.cos(theta);
_sinAlpha = Math.sin(alpha);
_cosAlpha = Math.cos(alpha);
}