前往
大廳
主題

關於我從出版轉行資工那檔事 EP1:999個報錯遊戲卻能正常運作!?關於用插件踢到鐵板的那些事

夜川霖 | 2025-09-13 23:39:08 | 巴幣 1142 | 人氣 195

零、前言

之前在培訓班上課時,曾看過同學專案裡面有999+的Error但還能預覽遊戲,內心頓時升起一絲欽佩,覺得我的功力果然還太嫩了,這才是真正的用BUG來做遊戲,儘管Code一團糟但編譯還是過關的典範。
後來詢問了狀況才知道,原來是同學在專案裡用了大量插件,但因為不清楚插件的版本差異跟相容性,也沒有意識到自己用不到插件的所有功能,才會導致插件內部邏輯拼命報錯,但因為遊戲本體沒用到所以可以預覽的情況。
那當下我疑惑的點是有Error為什麼可以預覽?後來我才發現這些插件是在開始預覽後才開始連環報錯,但因為牽涉到的地方太多,又有太多插件要拆開來看,所以我比一個讚表示敬意後,默默地回去處理自己的專案內容。

後來聽說同學的解決方法是把報錯的插件全部砍掉,結果遊戲本體還是沒受影響。

一、凡事多用插件的培訓期

以前在培訓班製作專題時,我因為使用了「Magica Cloth 2」這個物理運算人物頭髮跟衣服的插件,陷入了如果想讓遊戲變得更好,就要找強力插件來輔助的偏執心態。
畢竟插件使用起來的效果太好,只要把程式碼掛上去,根據文件說明操作就能做出優秀的效果,不但大幅減少了開發時間,還能讓繁雜的運算邏輯變簡單,如曲線計算用DoTween、攝影機運鏡用CineMachine之類的。

確實在製作遊戲時,某些插件基乎是減少開發週期必不可少的存在,但我在培訓期並沒有培養好一個觀念,多數功能盡量是真的碰壁或需求過大時,才去考慮使用插件。
畢竟插件可能會一口氣包含許多feature,未必每個都符合我們專案的需求,而且沒先搞懂插件的使用方法跟特性,很可能就會搞出開頭說的狀況,明明就報錯上千條但遊戲還能運作的恐怖情況。

可後來看大多Error都是從Debug.LogWarring裡面Print出來的就是了。

所以我進入業界後的第一道難關,就是先把基本觀念給穩健下來。

二、用插件之前,先把基本觀念搞定

我個人會粗略把插件劃分為四類:

1. 程式輔助類(UniTask、DoTween):這類插件比較偏向輔助工程師寫程式用,它並非直接把遊戲功能製作出來,而是幫助我們更方便地撰寫邏輯。
2. 實用功能類(Magica Cloth 2、I2 Localization、CineMachine):這類插件會提供現成方案,像MC2就有提供衣服與頭髮的物理運算(其他功能不贅述),I2則是提供完整的多國語言切換系統跟資料庫。
3. 工作環境類(Odin Inspector):這類插件可以改變我們介面環境,例如說Odin可以讓Editor的功能與視覺編排更加豐富,但我跟Odin還很不熟,這邊有興趣的可以自己去網上翻閱文件了解。
4. 外部功能類(Spine、Live2D):這類插件他們本質並不是為了Unity而生,但有提供相關插件可以讓自己家軟體輸出的檔案在Unity上運行。

正式進入業界後,我開始學習如何根據專案需求製作功能,好幾次都因為看不懂學長的程式碼而感到痛苦,好幾次都因為AI講解了還是看不懂而崩潰,好幾次因為看不懂這成堆的程式碼資料怎麼流動而自卑。
所以有好幾次,我覺得應該可以透過插件做出更好的效果,甚至是在別人跟我說需要一套劇情系統時,我真的跑去物色了幾個插件(例如:Naninovel、Utage4)。
但業內專案,豈是我說想用插件就能用的?要用也得給個正當理由,否則插件幾乎都是要錢的。

因此,拆解需求、分析架構、閱讀程式碼、以及學長大量的作業,就此拉開了序幕。

學長是熱情且有責任感的人,有不懂的東西我都會問他,但我同時也不好意思告訴他自己很多時候問了還是一知半解,經常是在實作中瞎幹狂碰壁後才搞懂這些是幹嘛的,例如我最早開始學的UniTask。
我在培訓班學到的協程都是Coroutine,也用Coroutine做了一大堆東西,C#原生的協程也不太懂,所以閱讀程式碼的過程中,我頻繁看到UniTask出現時,滿腦子都是大量疑問。
當時我只從async/await看出他是用來跑協程的,但那個cancellationtoken是啥?為什麼初始化系統要跑UniTask而不是Awake跟Start?為什麼應該要Update更新邏輯的程式碼裡沒有Update,只有一個UniTask?

光一個插件的疑問,就直接把我這個轉科生給幹矇了,當時我只知道UniTask就是把Coroutine的功能轉成C#原生的模式運作,會讓效能變得更好,但為什麼可以讓效能變得更好?
為此學長還先從Coroutine的運作邏輯講解,在講回UniTask之所以可以讓效能變好的原因,最後才慢慢講起UniTask的基本使用方法。

那為了讓自己快速了解UniTask這個插件,我逼自己拋棄培訓班的所有習慣,把每個協程用UniTask來運作,直到我搞懂它的基本功能,才發現它的好用之處。

例如最直觀的一點:「UniTask可以跑完後回傳值,但Coroutine要另外用委派(或別的包裝)才能取得。」

光這點就大幅改變了我的程式設計思路,更重要的是,我開始看懂了前輩們在幹嘛,為什麼初始化要用UniTask?為什麼這邊沒有Update而是用UniTask跑更新邏輯?那跑完後資料流到了哪裡?
終於,在不知道幾百次的Error跟運行失敗後(真的崩潰到想把腦袋打爆放在交通最繁忙的十字路口),終於是學會使用這兩個插件了,雖然我還沒辦法在專案結構裡讓整體程式碼跑得更優雅,但至少是先學會怎麼敲釘子,接下來就是造輪子。

三、學會跟插件共生後,可以開始造輪子

經過大量閲讀程式碼以及學長的觀念薰陶後,我發現很多時候企劃需求其實並不複雜,用套件製作可能會出現功能過剩的情形,那多的功能也可能會因為套件邏輯而持續產生多餘運算。

舉個例子,之前有個專案需要做多國語言處理,但因為有「切換圖片」以及切換字體的需求,所以我原本想拿之前用過的「I2 Localization」來製作。
但後來我發現需要切圖片的地方不多,基本上只有Logo跟教學文件而已,再加上I2如果也要用在劇情系統,更新文字前要先進另一層主邏輯取得詞條的Key值,再透過I2本身開放的API對比才能取得正確的詞條,所以我決定自己寫邏輯。

先把切換語言的主要邏輯寫成languageManager,在針對劇情、UI介面文字、圖片三個邏輯分別寫對應的接收器,再把企劃想要的字體製成獨立物件後,統一由SO打包管理。
語言類別透過enum(列舉)控管狀態機,每當語言切換時就會透過Delegate發送通知,各個接收器收到消息會先透過Key找到「該語言」對應的詞條後認領字體做更新。
圖片的話則是直接在需要更新的Image區塊,在接收器的欄位裡直接保存對應語言的圖片,接收到切換語言的通知後直接在現場更新。

當然,因為需求簡單才這樣做,甚至整套系統也還有很多可以優化的地方(像是字體可以切材質更輕便之類的),但自己寫的好處就是相對可控,我可以根據自己寫的劇情系統去做API串接,而不需要為了配合套件去修改劇情系統的邏輯。
而且有什麼功能要擴充的話,腦袋裡也會比較有想法知道要怎麼做,像之後如果有語音切換的需求,只要基於主架構另外寫一個切換邏輯就好。

慢慢的,我一邊用著學來的知識,一邊透過諸如UniTask這類編程插件的幫助,打造出了一整套運作穩定的劇情系統、多國語言系統、UI介面管理系統、EventBus、資料暫存與調用邏輯......等。
每完成一個系統,我都會重新審視整套運作邏輯,整體架構是否合理?耦合度是否夠低?安全度是否夠高?該GC的部分有沒有清掉?擴充性是否夠高?
時間久了,處理經驗增加的同時,想像力也開始逐步提升,對於抽象的系統概念也開始有了比較多的解決想法,甚至能換位思考企劃對於資料填寫的難易度,適時地抽換邏輯讓資料填寫簡化。

好比說我劇情系統從入行到現在,至少寫了六、七種版本了,每種版本有些是小更新,有些則是整個邏輯重寫的那種大更新。
最初的前幾個版本存在許多意義不明的填寫欄位,還只能聯網抓雲端試算表序列化後暫存取用,因為潛藏問題很多,所以我決定做功能更新。
後來我新寫了一個序列化工具,可以一鍵下載雲端Data的試算表存放在本地,只要遊戲一啟動系統就會去Resource抓.txt檔讀取,可欄位凌亂的問題還沒完全解決。
所以我後來把填寫邏輯變成多樣化指令的形式,一行一指令的填寫方法,例如你這段劇情需要有個黑幕要做淡入淡出,就要先填寫FadeInOut指令並把參數填在參數欄位後,才可以填寫Say指令去把你要的文本做更新,但這樣被企劃說有點麻煩,所以我又做了一版。
最新版採用指令集成,把每個欄位都變成一個指令,根據欄位決定這段劇情會有什麼樣的演出,才暫時讓劇情系統告一段落。

但也因為有這段重構的經驗,現在我聽到有什麼企劃需求時,基本上都能想像出系統大概可以怎麼做,除非是我沒接觸過的領域,例如:惡夢的Live2D互動系統,下一集我會專門講述我跟Live2D相愛相殺的故事。

四、轉行到現在,我的心態出現哪些轉變?

正式從培訓班畢業找到工作後,其實我非常恐懼不安,因為我覺得自己的能力無法勝任開發產出,也還需要很長一段時間做準備,但職場不是等你慢慢學習的地方,可以的話我也不希望自己真的就像個廢物待在那邊,靜靜地等待下一個作業到來然後學習。

所以我積極爭取進入專案系統協助處理問題,然後因為看不懂在寫什麼而被擊沉。
又不死心地跑去請教學長反覆練習,然後被大量如同文字天書的文件跟資料給擊沉。
又覺得自己應該要規劃系統來練習,所以用下班時間自製AVG系統,然後大量的被Error跟無法運作給擊沉。
後來終於搓出一個劇情系統了,然後被企劃需求跟實用度給擊沉。
後來有了新的工作新的需求,花很久時間跟AI划拳討論,終於把功能給完成,然後因為耦合度過高被擊沉。

於是我學會了先思考系統結構、資料走向、需要用到的輔助插件跟功能,遇到Error時也不會感到慌張,而是冷靜判斷原因,針對問題點去做修正。

我在磨練期間撞得頭破血流,但也因為失敗的經驗遠比成功的經驗更多,我發現自己遇到問題已經開始不那麼慌張,因為說到底程式碼會有問題,多半都是實際運作狀況跟預期的邏輯不同,把資料流Print出來檢查,重新梳理一下多半都能解決。

從恐懼不安到現在可以坦然面對需求,從想去依賴插件到現在會先思考怎麼設計架構,從希望需求少一點到現在會主動提議新規格。

我知道自己不聰明,但真的非常地努力。

五、後記

這是新開的日誌系列,作為一個文科轉資工的跨領域者,我有很多學習日記想要紀錄,也希望這些學習日記可以帶給同樣在跨領域的夥伴們鼓勵。

下一集會講我跟Live2D的故事,這是關於我過度依賴AI做出一整套互動系統,結果耦合度過高變成MageCode,運作起來既不聰明也不好用,所以我決定重零開始自己構思跟處理邏輯的故事。

ToBeContinue......



今日的註解:

1. 插件:非Unity原生提供的功能,必須透過外部或Unity Asset Store購買(就是Unity的商店)匯入的額外工具。
2. Coroutine:想像一下你有一件事情要分段執行,比方說:遊戲角色要煮泡麵,你告訴他「倒水 → 放調味料 → 等3分鐘→加蛋跟其他食材」,Coroutine的作用就是會自動幫你記住進度,等3分鐘到了以後才繼續做後面的行為,而遊戲的其他部分會照常運作。
3. API:想像一下自動販賣機,你投錢後「按下可樂的按鈕」,這件事本身就算是呼叫API,那機器就會因為你的呼叫而掉出一瓶可樂,以程式來說就是回傳一個我要的結果。
4. EventBus:可以想像成是大型廣播站,提供許多電台讓人鄉村裡的居民們訂閱,那當A電台開始播放節目時,訂閱A電台的觀眾就要知道節目開始,然後開始接收節目資訊。
5. GC:你今天參加了一場派對,但要隨時通知清潔工(GC)來清理現場,否則會有一個名叫「記憶體」的保安大哥,拿著超強的電擊棒跑來暴打在場所有人一頓後,自己把那些垃圾帶走,那在你們身體恢復之前派對都會一直處於暫停的狀況,如果大夥兒被打到住院的話,那派對就直接不用玩了。
送禮物贊助創作者 !
0
留言
2025-09-14 22:12:11
是啊,我想大部分的人都不太清楚它們的意思,簡單說明一下會比較好,祝奶茶順利😄
2025-09-14 23:29:01
剛剛補上說明了~感謝山梗菜QQ~
2025-09-14 09:30:00
雖然很想說什麼,但我是連插件、Coroutine、API串接、EventBus這些專有名詞都要先Google一次確認才知道意思的門外漢,所以只能說加油了👍
程式碼的世界果然很難😭
2025-09-14 21:28:03
抱歉抱歉,寫得太順就忽略了易讀性......之後每一集結尾我都加個註解解釋一下專有名詞好了,感謝山梗菜的鼓勵[e38] ~
追蹤 創作集

作者相關創作

更多創作