游戏开发论坛

 找回密码
 立即注册
搜索
查看: 1648|回复: 0

quake2服务端和客户端代码浅析

[复制链接]

5

主题

14

帖子

21

积分

注册会员

Rank: 2

积分
21
发表于 2010-6-17 16:28:00 | 显示全部楼层 |阅读模式
引用自 http://www.cnblogs.com/Perit/articles/1759577.html 作者:Perit 整理于2010.6
// 以下代码摘自quake2-v3.21版本.

//-----------------------------------------------
// server:服务端由服务端引擎和Game模块组成(quake.exe game.dll)
// 服务端的帧循环,这儿将关键调用摘出.
//-----------------------------------------------
SV_Frame
       // 处理客户端玩家命令
       SV_ReadPackets
              SV_ExecuteClientMessage
                     SV_ClientThink (cl, &newcmd);
                            // game模块针负责计算玩家在游戏世界的表现
                            ge->ClientThink (cl->edict, cmd);

       // game
       SV_RunGameFrame ();
              // 在game模块实现了游戏世界各对象与游戏世界的交互,包括物理交互
              G_RunEntity (ent);
              // 保存游戏对象帧相对信息
              ClientEndServerFrames
                     ClientEndServerFrame
                            G_SetClientFrame

       // 交服务器引擎负责将游戏对象信息以帧间差分形式发给客户端玩家
       SV_SendClientMessages
              SV_SendClientDatagram (c);
                     SV_BuildClientFrame (client);
                     SV_WriteFrameToClient (client, &msg);
                            SV_WritePlayerstateToClient (oldframe, frame, msg);
                            SV_EmitPacketEntities (oldframe, frame, msg);


// 游戏逻辑模块的帧结构,下面是关键调用
SV_RunGameFrame ()
       // 计算游戏逻辑帧,一般100ms一次, 首选完成游戏对象和游戏世界的交互,
       // 然后运行游戏对象的AI, 最后更新游戏对象的状态
       G_RunEntity (ent)
              SV_RunThink (ent)
                     monster_think (edict_t *self)
                            M_MoveFrame (self);

//--------------------------------------------------------------------------
// client: 客户端由客户端引擎和ref_gl.dll模块组成(quake.exe ref_gl.dll)
// 下面是Q2的客户端引擎,主要完成对服务器发过来的数据帧解析,游戏
// 世界的渲染,游戏客户端的输入处理,向服务器的命令发送,游戏对象的
// 动画绘制。
//--------------------------------------------------------------------------
// 客户端的帧结构如下
CL_Frame
       // 解析服务器发过来的数据
       CL_ReadPackets ();
              CL_ParseFrame
                     // 根据上帧间差值更新游戏对象状态
                     CL_ParsePlayerstate (old, &cl.frame);
                     CL_ParsePacketEntities (old, &cl.frame);

       //  响应客户端操作,向服务端发送玩家自身动作命令
       CL_SendCommand ();

       // 在这儿实现客户端平滑运动,采用预测方法不等服务器返回状态即更新自已状态,
       // 前提是在超时时间到达之前,否则帧会卡住..
       CL_PredictMovement ();

              //  玩家运动核心函数,调用引擎检测游戏对象和游戏世界间的碰撞和反应,
              //  是Q2的碰撞检测引擎的高级调用接口。
              Pmove
                     CL_PMTrace
                            // 最后分解为BOX和游戏世界的连续碰撞检测和碰撞反应.
                            CM_BoxTrace
                                   //  这儿是检测游戏对象间的碰撞。这儿的游戏对象主要指玩家,
                                   //  游戏对象的组织采用相当于KD树的组织方法,将游戏世界进
                                   //  行按平行坐标轴方向均匀划分,虽然在数据存储上利用了静态场景
                                   //  的BSP场景数据存储,但是确是一个动态的游戏对象组织方法,也
                                   //  是现今游戏普遍采用的方法(动态场景和静态场景分开管理)。在 每
                                   //  次可移动对象更新时先将其从场景树中取出(unlinkentity),更新完成
                                   //  后再linkentity(重新加入动态对象场景树)。
                                   CL_ClipMoveToEntities

       //--------------------------------------------------------------------------------
       //  通过这个调用进行各种客户端的显示图像的绘制,主要是关于场景绘制,游戏对象的
       //  绘制。M2模型动画是关键帧动画。早期卡马克就想到了在Q2中采用3D的显示方法,
       //  即在显   示图像时采用绘制 视角相差15度的两幅视图,通 过 3D眼 镜  实现3D效果。
       //--------------------------------------------------------------------------------
       SCR_UpdateScreen ();
              V_RenderView
                     CL_AddEntities
                            cl.lerpfrac = 1.0 - (cl.frame.servertime - cl.time) * 0.01;
                            CL_CalcViewValues ();
                            CL_AddPacketEntities (&cl.frame);

                     re.RenderFrame (&cl.refdef);
                            R_DrawWorld ();
                                   currentmodel = r_worldmodel;
                                   currententity = &ent;
                                   ent.frame = (int)(r_newrefdef.time*2);

                                   //------------------------------------------------------
                                   // 进行游戏世界的绘制
                                   // 这儿展示了BSP场景在场景渲染加速方面的使用方法,如果仔细
                                   // 分析会发现Q2的BSP场景在绘制时实现了back face culling(背
                                   // 面剔除), 而且几乎没有任何代价。而且可以做到从前往后渲染
                                   // 场景。当然,Q2场景渲染的速度快的方法功劳还是首推他的场
                                   // 景 PVS(场景可视的图形的集合)信息,此信息的预生成和优
                                   // 化存储和快速解码也是值的深入学习的。当然现在BSP的主要
                                   // 不是用在渲染方面,更多用在了碰撞检测方面,光线跟踪方面,
                                   // 这点也在Q2中展示出了具体的实现方法。当然下面只是渲染的
                                   // 部分内容。
                                   R_RecursiveWorldNode(r_worldmodel->nodes);

                            // M2模型绘制, 精灵等的图元,老的M2关键帧动画作为学习动画
                            // 原理,动作插值入门资料还可以。
                            R_DrawEntitiesOnList ();
                                   currententity = &r_newrefdef.entities;
                                   currentmodel = currententity->model;
                                   R_DrawSpriteModel (currententity);
                                   R_DrawBrushModel (currententity);
                                          R_DrawInlineBModel ();
                                   R_DrawAliasModel (currententity);
                                          GL_DrawAliasFrameLerp (paliashdr, currententity->backlerp);


//--------------------------------------------------------------------------------
// 模型动画控制: 由于Q2采用关键帧动画,在服务器上按照服务器设定的帧速进行动画
// 的控制,默认为每帧100ms, 动画信息由各个monster文件单独标明。其中monster AI
// 也由该该文件标明
//--------------------------------------------------------------------------------
// 下面指出在Q2中当同时出现各种动画要求时的动画优先级
// 动作转换优先级: ANIM_DEATH -> ANIM_ATTACK -> ANIM_PAIN -> ANIM_JUMP ->ANIM_WAVE -> ANIM_BASIC

// 下面是各个动作单独摘要

ANIM_BASIC(默认动作, 在没有其它动画播放要求时的动画)
// 先检测当前是否为其它几种动作,若没有做其它的动作,则目前只会是这stand 和run几
// 种动作中的一种,根据xyspeed(水平移动速度)区分 stand 和 run。
G_SetClientFrame (edict_t *ent)
              client->anim_priority = ANIM_BASIC  

ANIM_WAVE(应该不是游泳的动作,而是从空中落到地上的缓冲动作,振动效果)
// 紧跟着ANIM_JUMP动作后的下一个动作
       G_SetClientFrame (edict_t *ent)
              ent->client->anim_priority = ANIM_WAVE;  

ANIM_JUMP(跳跃动作)
//  条件为(!ent->groundentity), 即是不是在空中,如果在空中漂浮则施展该动作
       G_SetClientFrame (edict_t *ent)
              client->anim_priority = ANIM_JUMP;


ANIM_PAIN(受伤动作, 换武器时的动作和受伤动作采用同一模型动作)
// 以下三种情况均可以导致该动画的播放
       P_DamageFeedback (edict_t *player)
       ChangeWeapon (edict_t *ent)
       G_SetClientFrame (edict_t *ent) // 由此处完成ANIM_PAIN的下一帧。



// 以下几种情况可以引起该动作的播放
ANIM_ATTACK(攻击动作)
       Weapon_Generic(...)
       weapon_grenade_fire (edict_t *ent, qboolean held)
       Weapon_HyperBlaster_Fire (edict_t *ent)
       Machinegun_Fire
       Chaingun_Fire (edict_t *ent)
       G_SetClientFrame (edict_t *ent) // 由此处完成ANIM_PAIN的下一帧。


//  以下几种情况可以引起该动作的播放
ANIM_DEATH(死亡动作)
       ThrowClientHead (edict_t *self, int damage))
       player_die(...)
       G_SetClientFrame (edict_t *ent) // 由此处完成ANIM_PAIN的下一帧。


//  以下几种情况可以引起该动作的播放
ANIM_REVERSE(主要用于枪械的反复动作)
       weapon_grenade_fire (edict_t *ent, qboolean held)
       Weapon_Generic(...)
       G_SetClientFrame (edict_t *ent) // 由此处完成ANIM_PAIN的下一帧。


//--------------------------
// 物理系统分析
//--------------------------
Q2的物理系统放在了game模块,即游戏逻辑模块,实现了几种最基本的物理运动模型
       MOVETYPE_PUSH:
       MOVETYPE_STOP:
       MOVETYPE_NONE:
       MOVETYPE_NOCLIP:
       MOVETYPE_STEP:
       MOVETYPE_TOSS:
       MOVETYPE_BOUNCE:
       MOVETYPE_FLY:
       MOVETYPE_FLYMISSILE:
其中最重要的是MOVETYPE_STEP, 是玩家的物理运动模型,其实现为
SV_Physics_Step
  SV_FlyMove (ent, FRAMETIME, mask);

其中SV_FlyMove的实现是核心函数。是该函数实现了角色和世界间平滑的连续碰撞检测。其实现方法仍然对现在的游戏开发有很大的参考价值。是个很好的学习材料。

主要做了下面工作:
1)检查是否在地面上
2)增加重力加速度
3)增加摩擦力(角速度,直线移动速度)
4)设定没有站在地面上,根据现在的速度向前运动,遇到障碍则延障碍滑动


下面是monster(怪物)的物理运动模型M_walkmove 详细实现在SV_movestep中:

物体总共三种类型
1)陆地行走的物体
2)空中飞行的物体
3)水中游动的物体

可以成功移动的前提条件是满足下面之一:
1)物体在地面上
2)物体是可飞行的或可在水面游动的

对于陆地的物体:
检查如果满足下面的条件之一就禁止移动:
1)目的地在物体中
2)目的地是水中
3)前面是陡峭的地方且物体不允许离开地面

碰撞检测方法利用引擎提供的trace方法.值的注意的是怪物和人类玩家的运动方式是不一样的,也就是说怪物不会撞到墙上,再按墙面滑动,因为怪物发现前面距离墙面已经很近了,以至于再走一步就撞墙了这样它就会停止向前运动.就防止撞墙了.


总结
    以上分析了Q2的几个模块内的帧循环调用顺序,和功能的摘要。Q2的清晰的组织结构,优秀的C语言编码风格。展现了计算机图形学在3D游戏中的灵活运用,到目前为止仍是游戏开发人员的极好的学习资料。

                                                                                                            
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2025-6-8 14:41

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表