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