智慧 + 毅力 = 无所不能

正确性、健壮性、可靠性、效率、易用性、可读性、可复用性、兼容性、可移植性...
随笔 - 991, 文章 - 0, 评论 - 27, 阅读 - 341万

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

3D MMORPG客户端的一般架构

Posted on   Bill Yuan  阅读(1814)  评论(0编辑  收藏  举报
一、前言
    通常说的游戏指的是real-time interactive video game, 这里面交互性指的是玩家的操作会影响画面的显示(这是与电影、动画片等的区别),由于系统会对玩家的操作进行反馈,所以这类的游戏更真实,更吸引人,real-time 是指玩家的操作、环境的变化必须要实时的在画面上表现出来。普通PC上运行的客户端游戏是video game的一种,其利用通用的计算机实现了实时交互性的效果。
   本质上PC是数字计算机,即信号是离散的(与连续相对)、数字的,所有连续的效果通过足够快的更新画面来实现,实际上电影、电视都是用离散来模拟连续。因为一般人眼的反应时间大概是毫秒这个量级,所以使用离散来模拟连续是可行的。

二、几个基本术语
gameplay:大概是“玩法”的意思
gamepad (also called joypad or control pad):游戏手柄
MMORPG:Massively multiplayer online role-playing game
Skeletal animation(骨骼动画):模型的动画实际上是连续的多个离散的顶点位置集合,顶点动画是将这些位置的集合事先计算好,在使用时直接放入显卡进行渲染,
骨骼动画是在每一帧中计算各个抽象的骨头的变换矩阵,之后在渲染时由显卡负责将这些变换作用到起初设定的skin上面。
ragdoll physics:骨骼动画里面的每个bone(rigid bodies )之间建立了一种相互约束的关系(比如胳膊只能向里面弯曲,这样可以模拟人体的运动),这样使动画效果更真实

三、3D MMORPG客户端要解决的一些问题(需求
1、实时性(战场、玩家信息等数据要实时的更新)
2、保证帧平稳(画面播放要平稳、不能有快进或卡住的感觉),由于是实时进行计算所以这个还是有一定难度的
3、不能卡住整个线程
4、3d模型的效果要真实(主要是物理模拟等)

四、数据结构
  一般游戏的规则是由server端来计算的,client本质上是一个状态“播放器”,为了更有效的实时的显示出最新的状态,本地要保存一个player和环境的数据结构(如果所有数据实时的从server端取回,则render只能在消息回调中执行,这样会卡帧),为了体现最新的状态client要不停的显示需要的数据结构,这样只要刷新的足够快,用户就会觉得是实时同步更新的。MMO中核心的数据结构是player,这个结构server端和client端基本是一致的(client端的ID用来标记server端的一个对象)。

    对于3D游戏而言一个屏幕上显示的内容只是虚拟世界的一小部分,整个世界一般用场景图结构来表示

五、运行时基本逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
void System::Run()
  {
     Start(); /// 初始化定时器
 
     while (mRunning) // 帧循环
     {
        Step(); // 一步tick
     }
 
     LOG_DEBUG("System: Exiting...");
     exitCallback();  // 退出,清理资源
     LOG_DEBUG("System: Done Exiting.");
  }
 
   void System::Step()
   {
     static bool first = true;
 
     if (first)
     {
        InitVars();  // 初始化
        first = false;
     }
 
     SystemStep();
   }
 
  void SystemImpl::SystemStep()
  {
     const Timer_t lastClockTime  = mTickClockTime;
     mTickClockTime = mClock.Tick();
 
     const double realDT = mClock.DeltaSec(lastClockTime, mTickClockTime);
 
     // update real time variable(s)
     mRealClockTime += Timer_t(realDT * 1000000);
 
     if (mPaused)
     {
        mTotalFrameTime = 0.0;  // reset frame timer for stats
        mWasPaused = true;
        EventTraversal(0.0, realDT);
        PostEventTraversal(0.0, realDT);
        Pause(realDT);
        CameraSynch(0.0, realDT);
        FrameSynch(0.0, realDT);
        Frame(0.0, realDT);
     }
     else
     {
        if (!mUseFixedTimeStep)
        {
           mTotalFrameTime = 0.0;  // reset frame timer for stats
           mWasPaused = false;
 
           // update simulation time variable(s)
           const double simDT = realDT * mTimeScale;
           mSimulationTime      += simDT;
           mSimTimeSinceStartup += simDT;
           mSimulationClockTime += Timer_t(simDT * 1000000);
 
           EventTraversal(simDT, realDT);                      // 先处理事件
           PostEventTraversal(simDT, realDT);
           PreFrame(simDT, realDT);                          // 主要是物理模拟等
           CameraSynch(simDT, realDT);                        // 摄像机
           FrameSynch(simDT, realDT);
           Frame(simDT, realDT);                              // 画一帧
           PostFrame(simDT, realDT);
        }
        else
        {
           SystemStepFixed(realDT);
        }
     }
 
     FinishFrameStats();
  }

 


  1、游戏不仅有游戏中的状态,还有很多其他的状态,比如登陆服务器、设置操作界面、查看装备属性等,可以利用state来封装一种状态,state之间的切换构成了最上层的层次结构,state具体的切换细节可以用有限状态机来描述。
  2、具体一个state中的基本逻辑 
 总结:一帧里面的基本操作包括:
1、事件处理,即从上一帧到这一帧之间缓存的事件需要一一进行处理。这些事件包括本地的键盘鼠标操作,server传过来的message等。
   这些事件的处理逻辑主要包括:
       1》角色移动、转向等(内部是更改mesh的matrix4等属性、以及摄像机的操作等)
       2》跳跃、走动 ----切换动画数据
       3》切换人物  -----改变摄像机的target
       4》拉近视角、拉远视角 --------摄像机操作
       5》自动寻路           --------寻找关键点、播放走路动画
       6》攻击、发技能       --------播放特定粒子效果、动画等
       7》触发某个界面元素   --------进行相应逻辑处理、向server发相应请求等
       8》拾取物品           --------2d ----3d ----3d object(即顺着摄像机方向创建射线、之后找出相交的object,向server发拾取的请求)
2、物理模拟
   1》碰撞检测(求交点等),可以实现人物贴着地面行走、摄像机不会掉到地面以下、人物不会跑出地图边界,人物在坡上时不会悬空等效果
   2》反向动力学模拟,      可以实现ragdoll 效果,
   3》流体力学等复杂的物理模拟, 基本上牛顿经典力学所涉及的速度、加速度、质量,运动学等都可以做到(如人物走时会受摩擦力影响、人物跳下时会有重力加速度效果),牛X的游戏可以模拟出流体力学(这样就会体现出空气阻力、游泳时的浮力、阻力,水流动时的表面张力等)等更逼真的效果
3、preframe
   1》计算骨骼动画的当前transform矩阵(之前先是在物理引擎中计算)、顶点动画的当前数据,摄像机移动的当前位置等动画相关数据
   2》计算粒子系统当前的状态
   3》计算音效、背景音乐等当前的数据
4、update场景
   1》计算摄像机新的变换矩阵
   2》根据摄像机matrix4更新modelView matrix、 projection matrix等(之所以使用matrix4x4是因为显卡支持4阶矩阵并行计算,注意不是并发是真正的并行,这是GPU的特点)
   3》根据新的视锥体进行场景剔除(cull),一般室外场景是八叉树裁剪、室内场景是二叉树裁剪
   4》更新每个node的orientation,position、scale等属性(主要是要将父节点的数据传递给子节点)

5、渲染场景
   1》依次渲染renderQueue中的mesh,(这里面可以根据shader等来分组进行渲染、进而提高效率,GPU的特点是并行计算快,但是切换状态时很费时间)
     1.1》为GPU绑定顶点数据、
     1.2》设置vertixshader和pixelshader的参数
     1.3》执行shader程序(这里面可以作一些特效)
   2》post effect特效
6、播放一帧音效、音乐

六、静态结构
    client端的代码基本上可以包括以下组件:
    GUI(游戏内的悬浮button等)、渲染、场景管理、动画、粒子、物理模拟、AI、网络、窗口界面(qt等)、音效、脚本、IO事件、game框架、摄像机管理、player封装等
(评论功能已被禁用)
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· [AI/GPT/综述] AI Agent的设计模式综述
点击右上角即可分享
微信分享提示