程序的天空

我的学习心得

导航

Quake2源码分析(4)

Posted on 2008-08-16 02:02  帕托  阅读(1180)  评论(0)    收藏  举报

     在前面的分析中,我们知道在quake2运行的函数框架中,最终每桢的执行会涉及到game(游戏逻辑)模块和ref_XXX(图形系统)模块的交互,这一节我们来看这两个模块的输出函数。

game模块的输出函数:

     在文件game.h中,我们找到下面的代码:

 

//
// functions exported by the game subsystem,game系统的输出函数
//
typedef struct
{
 int   apiversion;        //api版本

 
 void  (*Init) (void);           //初始化game系统,分配全局数据


 void  (*Shutdown) (void);          //关闭game系统

 

 // each new level entered will cause a call to SpawnEntities, 加载新的地图后,展开地图中的实体
 void  (*SpawnEntities) (char *mapname, char *entstring, char *spawnpoint);


 void  (*WriteGame) (char *filename, qboolean autosave);            //写入游戏文件


 void  (*ReadGame) (char *filename);               //读取游戏文件


 void  (*WriteLevel) (char *filename);               //写入地图文件


 void  (*ReadLevel) (char *filename);               //读取地图文件

 

 qboolean (*ClientConnect) (edict_t *ent, char *userinfo);          //客户端连接


 void  (*ClientBegin) (edict_t *ent);               //客户端开始
 void  (*ClientUserinfoChanged) (edict_t *ent, char *userinfo);          //客户端用户信息改变
 void  (*ClientDisconnect) (edict_t *ent);          //客户端断开连接
 void  (*ClientCommand) (edict_t *ent);               //客户端命令
 void  (*ClientThink) (edict_t *ent, usercmd_t *cmd);          //客户端思考

 

 void  (*RunFrame) (void);               //运行一桢

 
 void  (*ServerCommand) (void);          //服务器端命令

 

//全局数据
 struct edict_s *edicts;                //game中的实体(游戏单元,具体看edict_s定义)
 int   edict_size;                         //实体数目
 int   num_edicts;            // current number, <= max_edicts,当前实体数
 int   max_edicts;               //最大实体数


} game_export_t;

 

game输出函数的实现:

     在g_main.c的GetGameAPI函数中,我们找到了game模块输出函数的对应实现函数:

 

globals.apiversion = GAME_API_VERSION;            //值为3


 globals.Init = InitGame;               //初始化了许多游戏参数和实体,客户端,预先分配了最大树木的实体和客户端


 globals.Shutdown = ShutdownGame;          //释放游戏和地图中的分配


 globals.SpawnEntities = SpawnEntities;          //一个地图包里包含实体(Entity),实体中可能需要展开物品(Item),展开某一类别的实体,调用实体的展开函数展开所有的物品,找到团队等。

 

 globals.WriteGame = WriteGame;          //将游戏状态和每个客户端写入文件   (game_locals_t和g_client_t)
 globals.ReadGame = ReadGame;          //读游戏状态和每个客户端
 globals.WriteLevel = WriteLevel;          //将地图数据和每个实体写入文件   (level_locals_t和edict_t)
 globals.ReadLevel = ReadLevel;           //读地图数据和每个实体

 

 globals.ClientThink = ClientThink;          //复杂的函数,客户端思考,根据状态进行移动,计算移动中和其他实体的碰撞,武器等


 globals.ClientConnect = ClientConnect;          //建立连接,从game.clients中分一个给edict.client


 globals.ClientUserinfoChanged = ClientUserinfoChanged;          //更新客户端信息


 globals.ClientDisconnect = ClientDisconnect;          //客户端断开连接,广播其他客户端它的断开,重置它的edict结构


 globals.ClientBegin = ClientBegin;               //客户端开始,移动?等待展开(spawn)?新的连接?调用ClientEndServerFrame(客户端:结束服务器端桢)


 globals.ClientCommand = ClientCommand;          //解析并执行客户端的命令

 

 globals.RunFrame = G_RunFrame;          //在客户的服务器端每桢中,游戏逻辑运行的主函数,我们将稍候讨论这个函数

 

 globals.ServerCommand = ServerCommand;          //解析并执行服务器端命令

 

G_RunFrame简述:

      服务器端每桢中都运行哪些游戏逻辑呢?让我们看一下:

          

          AI_SetSightClient ();          //在这一桢中,选择一个客户端作为怪兽攻击的目标

          对每个实体:调用G_RunEntity          //根据每个实体的状态,应用相应的物理系统移动每个实体

          ClientEndServerFrames ();          //将要结束服务器端每桢了,还有许多工作要做:

                    对每个连接的客户端:

                         更新移动的状态;

                         P_WorldEffects ();        //应用岩浆,水等效果

                         更新视角;

                         计算速度;
                          P_FallingDamage (ent);      //是否打到地面?损坏地面?

                          P_DamageFeedback (ent);        //应用这一桢所有的损坏效果


                          SV_CalcViewOffset (ent);            //计算视角偏移
                          SV_CalcGunOffset (ent);          //计算枪支偏移

                          SV_CalcBlend (ent);               //在视角偏移后重新计算颜色混合,确定看到的内容

 

                          G_SetClientEvent (ent);          //设置客户端事件

                          G_SetClientEffects (ent);          //设置客户端效果

                          G_SetClientSound (ent);          //设置客户端声音

                          G_SetClientFrame (ent);              //设置客户端桢数

                          更新统计数据。

 

     这些函数都会影响到客户端的实体(也就是ent,一个edict_s结构体),最终客户端每桢会用到,所以游戏每桢的运行可以总结为:

     

     服务器端每桢处理游戏的整个逻辑,调用上层game模块,

     客户端每桢处理游戏的图形渲染效果,调用下层的ref_gl或ref_soft模块。

 

      下一节,我们分析ref_XXX模块。