http://topic.csdn.net/t/20041210/13/3633447.html
这个帖子也讨论过魔兽的游戏过程记录问题,其中很多人都说是记录操作流,重现一次,但是在游戏这样的环境中,真的会完全精确到最后一秒吗?因为过程中,只要出现一次“意外”,就会产生蝴蝶效应,影响大局。大家可能觉得这样一个问题无聊,我也不是随便提这样一个问题,因为我在做一个游戏,就是利用操作流来记录游戏,这样重现不到几秒钟,就会出现误差。原因是在游戏这样的环境下,一般不使用多线程,一般用一个主线程来轮流处理所有事情,包括绘制画面,巡视键盘,播放声音和处理AI。如果单单记录操作流,播放的时候也是同样的过程,录制和重现的“巡视键盘”的时间节奏可能不能完全吻合。导致某个录制两个up(当然这两个up都是有实质性的)产生重现时候的一个实质性的up。这个可以这样理解,重现的时候不用考虑AI,会快一点,这样操作流的第一个up产生一个向上的动作,但是动作需要时间,还没赶得及动,第二个up就又来了,结果重叠成一个up,最后被AI击中。结果就完全不同了。请问各位大牛,有什么方法可以解决上面的问题呢?因为我还是想顺着游戏,把操作流精确导入到“巡视键盘”中去,这样游戏播放才逼真,而且播放器跟游戏能同用一个引擎。

解决方案 »

  1.   

    线程有线程的好处,比如说渲染跟逻辑就可以分开,渲染慢,但不会影响操作。这个可以这样理解,如果只使用一个主线程,渲染就耗掉一大半时间,逻辑上来不及处理,比如你按了几次up,结果来不及update逻辑,最终只处理了一次up,你就会怀疑你的键盘是不是有问题。玩家按了up之后,游戏是不会直接更改角色的坐标的,只是记录了你是up方向,而且moving=true,等执行到update逻辑的时候,统一根据这些记录来更新角色状态的改变,更新角色的图片,更新角色的坐标..最后设moving=false。在较低级cpu和显卡的机器上跑多线程的游戏,即使渲染处理不过来,操作还是很流畅的,这样的结果就是帧速放慢(其实是跳帧的结果),这样你就会怀疑你的显卡是不是有问题。现在是多核时代,多线程能同时运行在多个核中,这个也是单线程无法匹敌的。如果你的游戏对渲染要求不是特别高,或者就是普通2d游戏,一条线程就够了。因为线程之间切换也要开销,而且难度很大有些地方还要加锁,处理不当容易导致难以分析的错误。以多线程为框架的游戏,其复杂性,跟单线程的比,要高出几个级别。我写的是2d游戏,起码效率不是瓶颈问题,所以就采用简单的单线程,代码严格依赖上下文关系,打破这种关系,与重写无异。整个游戏就差录制播放这部分,但是这个是很高深的技术啊。
      

  2.   

    这个操作流看你怎么理解:比如,你认为是键盘鼠标输入流,那么电脑当时的运行环境导致处理速度不一致,偏差就是100%会出现的。但是,当你把它定义为键盘鼠标输入流所产生的游戏的事件集,并且和游戏本身所产生的事件集合并起来,就不会有问题了。比如说:
    00100: 角色A MoveLeft(101,99.98); //来源于玩家'A' DOWN.
    00103:  BOSS出现;            // 来源于游戏世界逻辑,或者角色动作触发器。
    00111:  角色A 开火(参数); // 来源于玩家'LeftButton' DOWN.
      

  3.   

    gyk120有失偏颇,记录游戏跟多线程没有必然的联系,多线程的好处上面已经说了,不赘述。
    whoo说对了一半,记录的每个动作,是不用记录时间点的,只要外部给的时间偏差,是录制游戏的机器给的,游戏就能重现。
    这个问题解决了。
    一般的游戏肯定或多或少会使用计时器,不然,速度慢的机器跟速度快的机器,处理的时间不一致,快的机器的状态变化就会快很多。打个比方,飞机游戏,敌人的飞机被你击中,爆炸的片段如果是由静态的图片切换表示出来,快的机器就会用几百毫秒播放出来,慢的机器可能要用几秒钟。如果使用了计时器,这个时间就确定了,慢的机器可以跳帧,快的机器帧间可以等。所以游戏while(1)里面,会获取一个与上一帧的时间差,来判断是否执行游戏逻辑,以及判断需要跳多少个逻辑帧,看下面一段代码,虽然没有跳帧,但是也说明了上面的情况:void CRoleStateStart::Update(float fDelta)
    {
    static const float DELTA_START = 0.03f;
    static const int MAX_SEQ = 78; m_fTime += fDelta;
    if (m_fTime > DELTA_START)
    {
    m_fTime -= DELTA_START;
    m_nAniSeq++;
    if (m_nAniSeq == MAX_SEQ)
    {
    ChangeState(new CRoleStateNormal(m_pRole));
    }
    }
    }角色从开始状态,一般会一闪一闪,转入正常状态。
    所以记录的时候,计时器的时间差很重要。这个时间差,代表了你的机器的当前资源环境,如果不记录,那你播放的时候默认采用自己机器的资源所表示的时间差。再打个比方,A机器比B机器快,看下面一段代码,表示一个炸弹:void CBomb::Update(float fDelta)
    {
    m_fRemainTime -= fDelta;
    if (m_fRemainTime <= 0.0f) // 等着爆
    {
    Explode();
    return;
    }
    // 炸弹冒烟,变红
    }
    fDelta是游戏时钟传入的时间,一般游戏时间只设定一个,不然每颗子弹都有时钟,将造成很大的资源浪费。
    B机器慢,游戏时钟传入的时间比较粗,原先设定4秒之后暴的,一次传入3秒,变成了6秒才暴(这个例子有点夸张),A机器快,传入的时间比较细,一次传入2秒,这样4秒刚好就暴了。如果你的飞机要逃跑,可能在B机器可以,在A机器就不行。但是如果在A机器传入的是B机器当时传入的3秒,A机器播放的片段,跟B机器录制,就重合上了。
    谢谢大家的支持。