quake2源碼分析(一)
我希望通過quake2的源碼分析來理解這個(gè)優(yōu)秀引擎的動(dòng)作方式。
由于我比較熟悉delphi的代碼組織方式,故從http://www.sourceforge.net/quake2delphi/下載了quake2的delphi代碼來進(jìn)行分析。
說明:為了描述的簡單化,我沒有對(duì)細(xì)節(jié)方面進(jìn)行描述。
下面讓我們進(jìn)入正題:
起始點(diǎn):
sys_win: WinMain 這是我們非常熟悉的windows入點(diǎn)
  Qcommon_Init  //qcommon是通用模塊,進(jìn)行通用模塊的初始化,這個(gè)步驟處理的東西相當(dāng)多,下一步會(huì)詳細(xì)講解
  while(true)
   Qcommon_Frame(time) //繪制每一幀
簡單地說WinMain要處理的主要就是這兩步操作
下面讓我們分析一下初始化操作:
 Qcommon_Init的內(nèi)容
//一 初始化z_chain
    z_chain.PRev := @z_chain;
    z_chain.next := z_chain.prev;
//二 初始化參數(shù) 
    COM_InitArgv(argc, argv);
//三 初始化交換算法及命令行緩沖區(qū)
    Swap_Init;
    Cbuf_Init;
//四 命令解釋器的初始化
    Cmd_Init;
//五 變量操作的初始化
    Cvar_Init;
//六 鍵盤映射表初始化
    Key_Init;
//七 初始化命令行,然后初始化文件系統(tǒng)
    Cbuf_AddEarlyCommands(False);
    Cbuf_Execute;
    FS_InitFilesystem;
//八 運(yùn)行腳本
    Cbuf_AddText('exec default.cfg'#10);
    Cbuf_AddText('exec config.cfg'#10);
    Cbuf_AddEarlyCommands(True);//添加命令行并將參數(shù)清空
    Cbuf_Execute;
//九 初始化變量
    Cmd_AddCommand('z_stats', Z_Stats_f);
    Cmd_AddCommand('error', Com_Error_f);
    host_speeds := Cvar_Get('host_speeds', '0', 0);
    log_stats := Cvar_Get('log_stats', '0', 0);
    developer := Cvar_Get('developer', '0', 0);
    timescale := Cvar_Get('timescale', '1', 0);
    fixedtime := Cvar_Get('fixedtime', '0', 0);
    logfile_active := Cvar_Get('logfile', '0', 0);
    showtrace := Cvar_Get('showtrace', '0', 0);
{$IFDEF DEDICATED_ONLY}
    dedicated := Cvar_Get('dedicated', '1', CVAR_NOSET);
{$ELSE}
    dedicated := Cvar_Get('dedicated', '0', CVAR_NOSET);
{$ENDIF}
//十 設(shè)置版本
    s := va('%4.2f %s %s %s', [VERSION, CPUSTRING, __DATE__, BUILDSTRING]);
    Cvar_Get('version', s, CVAR_SERVERINFO or CVAR_NOSET);
    if (dedicated.value <> 0) then
      Cmd_AddCommand('quit', Com_Quit);
//十一:初始化系統(tǒng)(控制臺(tái))
    Sys_Init;
//十二:初始化網(wǎng)絡(luò) 及 端口
    NET_Init;
    Netchan_Init;
//十三 初始化服務(wù)端
    SV_Init;
//十四 初始化客戶端
    CL_Init;
//十五:如果用戶沒有輸入+命令,而且沒有要顯示“獻(xiàn)給”標(biāo)志,則顯示演示程序
  // add + commands from command line
    if not Cbuf_AddLateCommands then
    begin                               // if the user didn't give any commands, run default action
      if (dedicated.value = 0) then
        Cbuf_AddText('d1'#10)
      else
        Cbuf_AddText('dedicated_start'#10);
      Cbuf_Execute;
    end
    else
    begin                               // the user asked for something explicit
      // so drop the loading plaque
      SCR_EndLoadingPlaque;
    end;
十六  結(jié)束
    Com_Printf('====== Quake2 Initialized ======'#10#10, []);
接下來對(duì)qcommon_init的十六步操作進(jìn)一步分析
一:初始化z_chain
這就引出了對(duì)z_chain的理解,z_chain是quake獨(dú)創(chuàng)的一種數(shù)據(jù)鏈表,用以管理分配的內(nèi)存空間,以保證臨時(shí)申請(qǐng)的內(nèi)存能正確地釋放。
每一次內(nèi)存分配請(qǐng)求會(huì)調(diào)用common的z_malloc來分配,z_malloc調(diào)用Z_TagMalloc來分配帶標(biāo)志的內(nèi)存空間。
我們來看一下z_chain的類型zhead_t的定義
  zhead_s = record
    prev, next: zhead_p;//典型的鏈表
    magic: SmallInt; //一個(gè)標(biāo)志位,保留const Z_MAGIC = $1D1D;
    tag: SmallInt;      // 用以成批釋放,看來類似于gc的概念
    size: Integer; //所分配的內(nèi)存塊的大小(包括zhead_s的大小)
  end;
  zhead_t = zhead_s;
二 初始化參數(shù) 
 將命令行數(shù)據(jù)存入 com_argc_ 及com_argv_[]
三 初始化交換算法及命令行緩沖區(qū)
 為了適應(yīng)不同的cpu結(jié)構(gòu),在程序中判斷是否大印第安格式還是小印第安格式,我的p4 cpu上是小印第安格式。
 給命令行緩沖區(qū)分配內(nèi)存
四 命令解釋器的初始化
初始化命令解釋器。quake有一個(gè)很獨(dú)特的模式,就是采用的是命令行配合全局變量驅(qū)動(dòng)方式。這讓我想起了古老的dos操作系統(tǒng)。
這種方式的優(yōu)點(diǎn)是模塊之間的耦合度可以很低。也很容易配置。
這一步中添加了以下幾條默認(rèn)命令
cmdlist:列出所有可用的命令
exec:運(yùn)行腳本
echo: 顯示文本
alias 顯示別名
wait  等待
五 變量操作的初始化
添加了以下用于操作變量的命令
set  設(shè)置變量
cvarlist 列出變量
六 鍵盤映射表初始化
定義了默認(rèn)的鍵盤映射表
添加了用于操作鍵盤映射表的命令
bind 將一個(gè)鍵綁定到一個(gè)字符上
unbind 取消一個(gè)鍵的綁定
unbindall 取消所有鍵的綁定
bindlist  顯示綁定列表
七 初始化命令行,然后初始化文件系統(tǒng)
將命令行參數(shù)傳入,設(shè)置好初始化文件系統(tǒng)所需的變量,然后初始化文件系統(tǒng)
八 運(yùn)行腳本
   運(yùn)行default.cfg 
   運(yùn)行config.cfg  
   用命令行傳入的參數(shù)覆蓋現(xiàn)有變量,也就是說命令行傳入的參數(shù)有較高的優(yōu)先級(jí)
九 初始化變量
   添加命令:
     z_stats: 顯示分配的內(nèi)存大小及塊數(shù)
     error:拋出一個(gè)異常,估計(jì)用于測試異常
    添加以下變量: 
    host_speeds 主機(jī)程度
    log_stats   日志狀態(tài)
    developer   開發(fā)?
    timescale   時(shí)間刻度
    fixedtime   固定時(shí)間?
    logfile_active 日志是否活動(dòng)
    showtrace  是否顯示跟蹤信息
{$IFDEF DEDICATED_ONLY}  是否是獻(xiàn)詞版本
    dedicated := Cvar_Get('dedicated', '1', CVAR_NOSET);
{$ELSE}
    dedicated := Cvar_Get('dedicated', '0', CVAR_NOSET);
{$ENDIF}
十 設(shè)置版本
   設(shè)置軟件版本變量
十一:初始化系統(tǒng)(控制臺(tái))
   這步操作是為了能像dos窗口一下輸入命令
十二:初始化網(wǎng)絡(luò) 及 端口
 這里初始化端口有一個(gè)小技巧,采用了毫秒的最后四位作為端口,不過我沒有看到端口沖突的解決方法。大概因?yàn)檫@個(gè)概率很小的原因吧。
十三 初始化服務(wù)端
添加了服務(wù)端要用到的命令及變量
十四 初始化客戶端
初始化控制臺(tái)
初始化渲染模塊vid_dll
初始化聲音模塊
初始化視圖用的命令
初始化菜單
初始化屏幕
初始化cd聲音
初始化本地操作命令
初始化 input
運(yùn)行autoexec.cfg腳本
十五:如果用戶沒有輸入+命令,而且沒有要顯示“獻(xiàn)給”標(biāo)志,則顯示演示程序
至此,第一階段分析告一段落。
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注