、啟動流程與腳本調(diào)試)
簡介《Torque3D規(guī)范及說明書》是一份面向Torque 3D游戲引擎初學者與進階開發(fā)者的中文技術(shù)文檔重點解決引擎框架理解、腳本入門與項目調(diào)試缺少系統(tǒng)參考的問題。文檔從游戲服務(wù)器端、客戶端及運行調(diào)用過程講起涉及網(wǎng)絡(luò)通信、狀態(tài)同步、圖形渲染和界面交互并延伸到UI編輯、世界地圖編輯與游戲腳本編輯同時整理常用命令、語法規(guī)則、變量聲明和物體定義等腳本基礎(chǔ)適合原型開發(fā)、引擎學習與團隊內(nèi)部查閱。資源包共1個doc文件約442KB體量輕便便于本地保存和關(guān)鍵詞檢索。已有246人學習說明其在Torque3D中文資料中具備一定參考價值。讀者可據(jù)此梳理引擎運行脈絡(luò)理解服務(wù)端與客戶端的分工并對照腳本語法、對象定義和模塊說明建立較完整的學習框架減少初期摸索成本。1. 搜 Torque 3D 入門的人先別急著關(guān)掉網(wǎng)頁搜 Torque 3D 入門的人十有八九點進官網(wǎng)之后會先愣三分鐘下載頁、文檔頁、論壇頁全是英文不清楚該從哪看起甚至不確定自己下載的到底是不是一個完整的游戲引擎。其實可以先把它想成一套宜家柜子——板子切好了孔打好了螺絲配齊了圖紙也附上了你要做的不是種樹煉鐵而是照圖紙把柜子裝起來。Torque 3D 就是一個把渲染、網(wǎng)絡(luò)連接、碰撞判斷、界面按鈕這些底層活兒提前打好包的 3D 游戲引擎你上手寫的是腳本描述“誰站在哪、按下某個鍵之后發(fā)生什么”。這份《Torque3D 規(guī)范及說明書》相當于那張圖紙它把引擎拆成 UI 編輯、世界地圖編輯、游戲腳本三塊還專門寫了一件事引擎啟動時到底先讀哪個文件。對零基礎(chǔ)的人來說這一點很重要——很多教程上來就貼一堆代碼卻不說清楚這些代碼該放進哪個目錄、由誰調(diào)用照著抄也只能停在照抄。這份說明書沒繞這個彎它先把框架攤開給你看再講語法、變量、物體定義順序正適合新手。2. 拆分引擎的兩半服務(wù)器端六個模塊和客戶端七個模塊新手看引擎架構(gòu)圖最容易犯的錯是把它當成一份“背誦材料”想著一口氣全記下來。真沒必要。你只需要先建立一個大印象這個引擎跑起來有兩半一半負責“算”一半負責“顯示”。負責算的叫服務(wù)器端負責顯示的叫客戶端。哪怕你只打算做單機小 demo這兩半也照樣都在只是跑在同一臺機器上而已。把這兩半的分工弄明白后面寫腳本時你才知道該把代碼往哪個文件里放。2.1 服務(wù)器端那六個模塊各管哪一攤Network Server 網(wǎng)絡(luò)服務(wù)模塊。它負責服務(wù)器與所有客戶機之間的連接和數(shù)據(jù)傳輸也負責版本升級這類事務(wù)。把它想成小區(qū)門口的傳達室所有進出的人、送來的包裹都得從這兒過一遍。你以后遇到“連不上”“延遲高”“版本對不上”這類問題第一個該懷疑的就是它。World Map 世界地圖模塊。服務(wù)器端也會載入一份地圖鏡像在服務(wù)器上模擬出一個跟客戶端看起來差不多的場景。它的用途不是給玩家看而是用來算碰撞、判斷誰撞到了誰。相當于裁判手里那套場地模型——球員看到的是球場裁判看的是同一塊場地的尺寸和邊界誰踩線了裁判說了算。這樣做的好處是所有客戶機看到的位置和時間能盡量保持一致。Database Connection 數(shù)據(jù)庫連接模塊。它負責讓游戲內(nèi)的數(shù)據(jù)和數(shù)據(jù)庫保持同步與及時更新。你可以把它當賬本玩家攢了多少金幣、買了什么裝備光存在內(nèi)存里一關(guān)服就沒了所以要寫進數(shù)據(jù)庫。ServerCommand 服務(wù)器執(zhí)行命令??蛻魴C發(fā)來的可執(zhí)行命令比如移動、攻擊最終都在這里被處理。玩家在客戶端按了一下鍵服務(wù)器接收后執(zhí)行對應(yīng)動作這個模塊就是執(zhí)行動作的那個環(huán)節(jié)。Message 客戶消息處理。它負責轉(zhuǎn)發(fā)和記錄客戶機之間、客戶機與服務(wù)器之間的消息聊天功能主要靠它。要是你想過“游戲里的聊天框是怎么實現(xiàn)的”答案就在這兒。Object Data 對象數(shù)據(jù)處理。場景里所有的 Player、AIPlayer、物品它們的動作、碰撞、特效全都歸這個模塊控制和處理。這是六個模塊里你日后打交道最頻繁的模塊——改角色屬性、加一個新道具最后基本都落到這里定義的數(shù)據(jù)上。2.2 客戶端那七個模塊畫面是怎么來的Canvas 畫面顯示處理模塊。它管的是整個游戲窗口的顯示設(shè)置顯示驅(qū)動類型、分辨率、色深這些。相當于投影儀的設(shè)置菜單先得把投影儀調(diào)對畫面才談得上好不好看。Gui 用戶交互界面模塊。從游戲開始到結(jié)束所有可見的畫面、按鈕都歸它。主菜單、血條、背包格子玩家眼睛能點能看的東西基本都在這個范疇里。Action Map 行為動作模塊。它是一張映射表哪個功能鍵對應(yīng)哪個操作。你按下 W 角色往前走按 Esc 彈出菜單背后就是這張表在把“按鍵”翻譯成“動作”。ServerConnection 服務(wù)器連接模塊。它是客戶端與服務(wù)器之間的橋所有游戲信息都要經(jīng)它傳輸交換。橋斷了客戶端就成了一個只會放畫面的空殼。ClientCommand 客戶端命令執(zhí)行模塊。方向跟 ServerCommand 相反服務(wù)器讓客戶端做的事比如讓某個角色坐下從這里下來執(zhí)行。一個管“往上發(fā)”一個管“往下發(fā)”別記混。Message 服務(wù)器消息處理模塊。收到服務(wù)器消息之后負責顯示和提示。你在屏幕中央看到的系統(tǒng)公告多半是從這兒出來的。World Map 世界地圖模塊。載入服務(wù)器指定的地圖并負責地圖的顯示和相應(yīng)處理。注意它跟服務(wù)器端那個 World Map 名字一樣、活兒不一樣那邊算碰撞這邊管顯示。2.3 為什么新手也要懂這個一次“按鍵開槍”的旅行光看模塊名沒什么感覺我們跟著一次按鍵走一遍。玩家在客戶端按下開火鍵Action Map 先查表確認這個鍵對應(yīng)“開火”客戶端把這個意圖通過 ServerConnection 發(fā)出去服務(wù)器端的 ServerCommand 接到命令交給 Object Data 去算這一槍打沒打中、撞到了什么算完之后Message 把結(jié)果廣播給相關(guān)客戶機客戶端收到消息由 Canvas 和 Gui 把開火特效、血量變化畫到屏幕上。一條鏈走下來可能只花了幾十毫秒。這條鏈的意義在于以后你想改任何一個環(huán)節(jié)都得先知道它掛在哪。想改按鍵去 Action Map想改傷害算法去 Object Data 相關(guān)的數(shù)據(jù)定義想改命中提示的顯示去客戶端 Message。不知道這條鏈改代碼就是碰運氣改完不知道為什么不生效也不知道為什么突然好了。注意單機跑 demo 的時候服務(wù)器端和客戶端在同一臺機器上你可能感覺不到它們的存在。但只要涉及網(wǎng)絡(luò)這兩半的分工就變得非常實在改動之前先想清楚這段邏輯該在哪一邊。3. 跟著啟動流程走一遍從 exe 到 main.cs 的那幾行代碼環(huán)境搭好之后最勸退的一步是不知道該改哪個文件。這一章就解決這件事。雙擊游戲可執(zhí)行文件之后引擎不是滿硬盤找腳本它有一條固定的路線。路線走通了你后面所有的改動才有落點。這段代碼用的是 TorqueScript語法接近 C 和 JavaScript下面代碼塊按 javascript 著色只是為了看著順眼。3.1 雙擊 exe 之后引擎第一眼找的是 main.cs主目錄下有一個 main.cs它是整場游戲的起點。里面干的事不復(fù)雜把要用的游戲目錄登記進去然后逐個執(zhí)行這些目錄下的 main.cs。$defaultGame starter.fps; // 默認游戲目錄名 // 把某個目錄登記進引擎的模塊搜索路徑再執(zhí)行該目錄下的 main.cs function loadDir(%dir) { setModPaths(pushback($userMods, %dir, ;)); // 往分號分隔的路徑列表尾部追加 exec(%dir /main.cs); // 是字符串拼接 } // 遞歸拆開分號分隔的路徑列表逐個執(zhí)行其中的 main.cs function loadMods(%modPath) { %modPath nextToken(%modPath, token, ;); // 切出第一段路徑和剩余部分 if (%modPath !$ ) loadMods(%modPath); // 還有剩余就繼續(xù)遞歸 if (exec(%token /main.cs) ! true) $modcount--; // 執(zhí)行失敗就把計數(shù)減一 } loadMods($userMods); // 正式啟動這段代碼里幾個關(guān)鍵點值得記住。$userMods是全局變量裝著以分號分隔的目錄列表pushback往列表尾部追加一項nextToken按分隔符把字符串切成第一段和剩余部分注意第二個參數(shù)寫的是token不帶 % 前綴這是這個函數(shù)要求的特殊寫法exec執(zhí)行一個腳本文件返回值可以拿來判斷成功與否是字符串拼接跟加號不是一回事后面講變量時還會專門說。%dir、%modPath這些以 % 開頭的都是局部變量只在函數(shù)內(nèi)部有效。$defaultGame、$userMods、$modcount以 $ 開頭是全局變量。這套前綴規(guī)則是 TorqueScript 最基礎(chǔ)的一條記不住它后面看代碼會很吃力。3.2 進到游戲目錄看 package StarterKit 里那兩個函數(shù)輪到游戲目錄下的 main.cs 干活時第一件事通常是加載 common 目錄那里放的是基本命令、函數(shù)和 GUI 文件。接著你會看到一個 package 塊里面包著onStart和onExit兩個函數(shù)。package StarterKit { function onStart() { Parent::onStart(); // 必須調(diào)用引擎內(nèi)部初始化 // 依次載入客戶端、服務(wù)器、數(shù)據(jù)三套腳本 exec(./client/init.cs); exec(./server/init.cs); exec(./data/init.cs); // 服務(wù)器對所有會話都要啟動因為客戶端也能在游戲內(nèi)開服 initServer(); // 二選一專用服務(wù)器模式還是客戶端模式 if ($Server::Dedicated) initDedicated(); else initClient(); } function onExit() { export($pref::*, ./client/prefs.cs, False); // 保存客戶端設(shè)置 if (isObject(moveMap)) moveMap.save(./client/config.cs, false); // 保存改過的按鍵映射 export($Pref::Server::*, ./server/prefs.cs, False); // 保存服務(wù)器設(shè)置 BanList::Export(./server/banlist.cs); // 導(dǎo)出封禁名單 Parent::onExit(); // 必須調(diào)用釋放資源 } }幾個容易被忽略的細節(jié)。package的作用是給一組函數(shù)套個殼殼里的同名函數(shù)會優(yōu)先于引擎原有版本執(zhí)行這是一種不修改原文件就能擴展行為的方式。Parent::onStart()和Parent::onExit()是在調(diào)用被覆蓋掉的那個原版函數(shù)漏寫會直接導(dǎo)致初始化或釋放流程中斷游戲可能出現(xiàn)莫名其妙的問題這是新手最常犯的錯誤之一。onStart和onExit這兩個名字是引擎自己會去調(diào)的你不需要在別處主動調(diào)用它們。$Server::Dedicated是引擎內(nèi)置的全局變量為真就走專用服務(wù)器分支否則走客戶端分支。./表示當前腳本文件所在的目錄這個相對路徑寫法后面會反復(fù)出現(xiàn)。3.3 三個 init.cs 各管什么先記這張表onStart里那三行 exec 是分水嶺腳本從此分成三攤。剛開始不用把每個文件都讀一遍記住各自的職責就夠了文件大致職責你什么時候會去改它./client/init.cs載入界面、鼠標、輸入映射等客戶端側(cè)腳本和 GUI 文件想改啟動界面、往主菜單加個按鈕./server/init.cs載入玩家、武器、AI、游戲規(guī)則等服務(wù)器側(cè)腳本想加一把新武器、調(diào) AI 行為./data/init.cs載入共享的數(shù)據(jù)定義各類 datablock 多半在這一層想改角色血量、跳躍高度需要提醒一句這三個文件是入口不是終點。它們內(nèi)部還會 exec 更多文件一層套一層。比如服務(wù)器側(cè)的 game.cs 里就會繼續(xù) exec 玩家腳本、武器腳本、AI 腳本。所以找代碼的正確姿勢是順藤摸瓜從這三個入口往下翻而不是在目錄里瞎翻。提示第一次讀的時候可以只讀 onStart 那三十來行把 exec 的路徑在紙上抄一遍畫成一棵樹。樹畫出來你就知道這個游戲是怎么一塊一塊拼起來的了。4. 第一個能跑的例子按下 ~ 鍵敲一行 echo理論看到這兒該動手了。這一章的所有操作都在游戲自帶的控制臺里完成不用新建文件不用改目錄出錯也不會把項目搞壞??刂婆_是新手最好的練習場也是老手最常用的排查工具。4.1 第一步喚出控制臺敲響第一聲運行游戲的可執(zhí)行文件進到主界面或者進游戲都行然后按鍵盤左上角那個 鍵——就是跟 ~ 同一個位置、不用按 Shift 的那個。控制臺窗口會彈出來底下有一行輸入框。輸入下面這行回車echo(hello!); // 控制臺立刻回顯 hello!echo就是把括號里的內(nèi)容打印到控制臺??粗黄鹧鄣悄愫竺嬲{(diào)試腳本的主要手段懷疑某段代碼沒執(zhí)行就在里面加一行 echo懷疑某個變量值不對就把它 echo 出來看。分號必須加跟 C 一樣它是語句結(jié)束的標記。多行語句可以一口氣粘進去引擎會按分號分行執(zhí)行$a 1; echo($a); // 輸出 1這里$a是全局變量是賦值echo($a)打印它的值。你可能會問為什么不能直接寫echo($a)而不先賦值因為沒賦值的變量是空的打印出來什么都沒有。養(yǎng)成“先賦值、再打印”的習慣能省掉很多困惑。4.2 第二步變量、字符串、數(shù)組一樣樣試先試一個能讓大部分人愣一下的例子echo(1 2); // 輸出 3 echo(1 2); // 輸出 12同樣是兩個帶引號的字符加號把它當數(shù)字算結(jié)果是 3才是字符串拼接結(jié)果是 12。這就是 TorqueScript 靈活的地方也是新手最容易栽的地方——你以為在做字符串拼接引擎卻在做加法。想做拼接認準。還有幾個拼接操作符注意必須大寫echo(abcd NL CDEF); // 輸出 abcd換行再輸出 CDEFNL是插入換行TAB是插入制表符SPC是插入一個空格。寫成小寫nl是不認的這個坑不少人踩過。接著看變量和數(shù)組的關(guān)系$a 2; $a[0] 3; echo($a); // 輸出 2 echo($a[0]); // 輸出 3 echo($a0); // 輸出 3是 $a[0] 的另一種寫法變量和數(shù)組同名互不影響$a和$a[0]是兩個獨立的值。更有意思的是$a0這種寫法它等價于$a[0]多維數(shù)組$a[0,0]還可以寫成$a0_0。這種靈活性看著方便讀別人代碼時卻容易認不出來心里有個數(shù)就行。最后試一下顏色控制這個在寫提示信息時挺實用echo(\c2hello!\c0hello!); // 第一個 hello 紅色第二個恢復(fù)默認色\c0到\c9是切換字體顏色\c0是默認色\n換行\(zhòng)t制表\\表示一個反斜杠本身。顏色只影響當前行后面的字符不會改掉整個控制臺的默認色。4.3 第三步寫第一個函數(shù)和一個 datablock函數(shù)定義跟 C 很像參數(shù)用 % 開頭// 定義一個打招呼的函數(shù)參數(shù)是局部變量 function sayHello(%name) { echo(hello, %name); // 拼接字符串 return 1; // 返回值可選不寫也行 } sayHello(newbie); // 輸出 hello, newbiefunction是關(guān)鍵字函數(shù)名后面跟括號里的參數(shù)列表參數(shù)用 % 前綴表示局部變量意味著它只在函數(shù)內(nèi)有效。return是可選的不寫就默認返回空。函數(shù)名和變量名都不區(qū)分大小寫sayHello和sayhello是同一個函數(shù)這一點跟 C 不一樣得適應(yīng)一下。然后是 TorqueScript 特有的東西——數(shù)據(jù)塊datablock PlayerData(PlayerBody) { renderFirstPerson false; // 第一人稱視角下是否渲染自己的身體 shapeFile ~/data/shapes/player/player.dts; // 模型文件~/ 指主游戲目錄 cameraMaxDist 3; // 第三人稱最大鏡頭距離 cameraDefaultFov 90.0; // 默認視野角度 cameraMinFov 5.0; // 最小視野角度 };數(shù)據(jù)塊的作用是給某一類物體預(yù)先定義一堆屬性。定義完之后它就在內(nèi)存里存了一份創(chuàng)建物體時直接引用這個定義就行不用每次都把屬性重寫一遍。注意結(jié)尾那個分號不能少少了會引發(fā)一串讓人摸不著頭腦的報錯。定義好了創(chuàng)建物體長這樣%player new Player() { dataBlock PlayerBody; // 必須項指定用哪套屬性 client %this; // 歸屬哪個客戶端連接 }; MissionCleanup.add(%player); // 加進場景退出時自動清理new返回一個句柄賦給%player。這個句柄本質(zhì)上是個整數(shù)在當前客戶端里唯一相當于物體的本地 ID。MissionCleanup.add那行也別省它的作用是讓這個物體在場景切換或退出時被自動清理掉不加的話容易留下殘余對象。4.4 第四步用 F10 和 F11 打開那兩個編輯器看一眼腳本之外還有兩個可視化編輯器值得花十分鐘摸一下。它們不寫代碼但能讓你直觀看到“界面”和“地圖”這兩樣?xùn)|西是怎么組織的。進游戲后按 F10打開 GUI 編輯器從控件面板拖一個按鈕到畫面上改個名字保存。按 F11打開世界編輯器在場景里選中一個物體挪個位置保存?;氐接螒蚶锿顺鲈龠M看看剛才的改動還在不在。需要有個心理準備編輯器只負責“擺樣子”按鈕點下去要發(fā)生什么、物體被碰到會怎樣這些功能都得回到腳本里補。文檔里把 UI 編輯和世界地圖編輯單獨列成兩節(jié)講的就是這個分工。提示第一次拖控件不用追求好看能保存、能重新加載出來就算成功。這一步的目的是讓你相信“改動是生效的”后面寫腳本才有底氣。5. 新手最容易踩的五個坑語法、路徑和 ID 上的翻車現(xiàn)場這一章的五條都是我見過新手最容易反復(fù)踩的。每一條按“現(xiàn)象、原因、解決辦法”三段說照著對號入座就行。5.1 語法上的三個翻車現(xiàn)場坑一語句末尾漏了分號。現(xiàn)象是整段腳本像沒生效或者控制臺報錯的行號跟你寫的位置對不上報在下一行甚至下一段。原因是 TorqueScript 靠分號斷句漏一個后面的語句會被拼到上一句里一起解析錯誤自然就漂移了。解決辦法是看報錯時別只盯著報的那一行往上翻一兩行找漏掉的分號另外函數(shù)調(diào)用、賦值、數(shù)據(jù)塊結(jié)尾這三處是重災(zāi)區(qū)??佣央p引號和單引號混著用?,F(xiàn)象是echo出來看著一模一樣但比較的時候死活不相等條件判斷走進奇怪的分支。原因是abcd是普通字符串a(chǎn)bcd是標記字符串兩者是不同的東西不能互換。解決辦法是統(tǒng)一用雙引號除非你明確知道自己需要標記字符串接手別人代碼時先掃一眼引號用得統(tǒng)不統(tǒng)一??尤约浩鸬娜肿兞扛嬖械淖擦恕,F(xiàn)象是某個數(shù)值怎么改都不對或者界面突然錯亂、鼠標失靈而且找不到直接原因。原因是$開頭的全局變量在整個客戶端里只有一份你起了個跟引擎內(nèi)置變量同名的等于把人家的值覆蓋掉了。解決辦法是起名前加自己的前綴比如$myGame_score這種能用局部變量 % 解決的就別用全局。5.2 路徑和 ID 上的兩個坑坑四用了絕對路徑目錄一挪就找不到資源?,F(xiàn)象是模型不顯示、貼圖變白或者啟動時提示找不到某個文件但文件明明就在硬盤上。原因是絕對路徑把整條硬盤路徑寫死了主目錄一改名、一移動路徑立刻失效。解決辦法是用相對路徑關(guān)鍵字./表示當前腳本文件所在目錄~/表示主游戲文件 main.cs 所在目錄。養(yǎng)成習慣之后整個項目文件夾可以隨便搬。坑五把本地 ID 當成網(wǎng)絡(luò) ID 用?,F(xiàn)象是單機測得好好的一聯(lián)網(wǎng)別人的角色在你屏幕上亂套或者你操作的角色不是你想操作的那個。原因是每個物體有兩個 ID一個是客戶端本地唯一的句柄一個是整個網(wǎng)絡(luò)唯一的網(wǎng)絡(luò) ID。單機邏輯用本地句柄完全沒問題但跨機器傳數(shù)據(jù)時傳本地句柄對方根本對不上號。解決辦法是記住這個區(qū)分涉及聯(lián)網(wǎng)的邏輯要拿網(wǎng)絡(luò) ID別順手把%player直接發(fā)出去。注意第五個坑在單機階段幾乎不會暴露所以特別陰。做單機 demo 時就把“本地”和“網(wǎng)絡(luò)”這兩個概念分開想等真聯(lián)機了能少熬幾個晚上。6. 接下來學什么從看懂腳本到自己寫一個能動的角色入門階段最容易犯的第二個錯是學完語法就一頭扎進一個完整游戲項目里。結(jié)果被幾千行腳本淹沒三天后放棄。比較穩(wěn)的路徑是四周每周只盯著一個目標。階段主要做什么怎么算過關(guān)第一周在控制臺里把 echo、變量、字符串拼接、if/while、函數(shù)都敲一遍不看資料能寫出一個帶參數(shù)、有返回值的函數(shù)第二周讀 server 側(cè)入口那幾個 init.cs順著 exec 往下翻兩三層能說出某個功能大概在哪個文件里第三周只改數(shù)值血量、鏡頭距離、視野角度、移動速度改完進游戲能明顯感覺到變化第四周加一個小東西一個能被撿起來的物品或者給現(xiàn)有武器換個傷害值加的東西能正常出現(xiàn)、正常生效、退出不報錯四周之后再回頭碰schedule()和eval()這兩個東西。schedule用來做延時和循環(huán)eval能把一段字符串當命令執(zhí)行兩個都很方便也都很容易把代碼寫得別人看不懂。剛?cè)腴T時優(yōu)先用直白的寫法等真的遇到非它不可的場景再學。還有個小習慣強烈建議早點養(yǎng)成每次動腳本之前先在要改的那段前后各加一行 echo確認這段代碼真的被執(zhí)行到了。聽起來笨但排查問題時它能幫你把范圍從“整個項目”縮到“某一行”。我最早學的時候跳過語法直接去改玩家腳本里的數(shù)值結(jié)果一個漏掉的分號讓我找了整整一晚控制臺報的行號指向一段我根本沒動過的代碼。從那以后我每次改腳本都強制走一遍加 echo、確認執(zhí)行、再改邏輯三步走完才敢繼續(xù)往下寫。希望幫到你。本文還有配套的精品資源點擊獲取