文章目錄
- 一、觸發器簡介
- 1.1 觸發器界面
- 1.2 ECA語句編輯及快捷鍵
- 1.3 參數設置
- 1.4 變量設置
- 1.5 實體觸發器
- 1.6 函數庫與觸發器復用
- 二、觸發器的多層結構
- 2.1 子觸發器(在游戲內對新的事件進行注冊)
- 2.2 觸發器變量作用域
- 2.3 復合條件
- 2.4 循環
- 2.5 計時器
- 2.6 單位組
- 2.7 玩家組
- 2.8 運動器
- 三、案例
- 3.1 NPC對話
- 3.2 裝備限制
- 3.2.1 裝備拾取
- 3.2.2 物品丟棄
- 3.3 特效
- 3.3.1 特效生成
- 3.3.2 特效銷毀
- 3.4 行為樹
- 3.5 使用排行榜
- 3.6 特效可見性
- 3.7 不同步問題
- 3.7.1 本地配置不同步日志環境
- 3.7.2 線上游戲不同步定位
- 3.7.2 定位不同步問題
- 3.7.3 游戲不同步的原因
- Y3編輯器文檔1:編輯器簡介及菜單欄詳解(文件、編輯、窗口、細節、調試)
- Y3編輯器文檔2:場景編輯(地形編輯、物件放置)
- Y3編輯器文檔3:物體編輯器
- Y3編輯器文檔4:觸發器1(對話、裝備、特效、行為樹、排行榜、不同步問題)
一、觸發器簡介
參考官方文檔《觸發器》
??觸發器是Y3編輯器中實現游戲邏輯的核心組件,它通過“事件-條件-動作”(Event-Condition-Action,簡稱ECA)的模式來實現各種效果。
- 事件(Event):觸發器的導火索,當設定的事件發生時觸發器才會執行。
- 條件(Condition):觸發執行必須滿足的條件。
- 動作(Action):觸發執行的結果。
1.1 觸發器界面
??通過主界面頂部功能欄或使用快捷鍵F4
可進入觸發器。進入觸發器窗口后,你可以添加或刪除文件夾和觸發器,并使用搜索功能快速找到你想找的觸發器;還可以對選定的觸發器進行以下操作: 復制, 剪切, 粘貼, 刪除, 重命名, 禁用, 和轉化成Lua代碼。
右鍵單擊觸發器并選擇禁用使其無效。單擊啟用以重新激活無效的觸發器
??你可以在變量管理與事件管理中提前寫入常用的變量和事件,以便在游戲中隨時調用,從而減少開發項目的記憶成本和后期維護成本。比如可以提前設置事件并添加事件中需要的參數,然后在觸發器-自定義事件中進行調用。
??在主界面-編輯-通用設置-編輯設置中勾選“縮略圖滾動條”,可以顯示ECA縮略圖滾動條。左鍵點擊可以定位到想要的行數,長按左鍵可以拖動滾動條。錯誤、點選與搜索結果也會以色塊形式顯示到普通滾動條上。縮略圖可以收起,收起后需要重新在“主界面-編輯-通用設置-編輯設置”打開。
1.2 ECA語句編輯及快捷鍵
??點擊事件/條件/動作后面的"+"為當前部分新建語句,也可以在空白處點擊鼠標右鍵,新建事件,條件,動作,子觸發器與注釋。如果想要更改已有的事件,條件或者動作,雙擊對應語句即可彈出選項窗口,選擇新的內容后即可進行覆蓋。
功能 | 快捷鍵 | 說明 |
---|---|---|
新建觸發器 | Ctrl + T | |
新生子觸發器 | Ctrl + Q | |
新建事件 | Ctrl + E | |
新建條件 | Ctrl + D | |
新建動作 | Ctrl + R | |
新建注釋 | Ctrl + N | |
ECA注釋開關 | Ctrl + M | 將當前語句轉為注釋 |
設為無效 | Ctrl + W | 使觸發器文件或eca條目無效(被跳過) |
條目下移 | Ctrl + ↓ | 快速將當前選中的eca下移一行 |
條目上移 | Ctrl + ↑ | 快速將當前選中的eca上移一行 |
新建收納盒 | Ctrl + P | |
添加至收納盒 | Ctrl + O | 添加當前選中eca到收納盒 |
變量引用查看 | A | |
全局元素引用查看 | S | |
跳轉到函數 | D |
功能 | 快捷鍵 | 說明 |
---|---|---|
查看上一個觸發器 | Ctrl + ← | |
查看下一個觸發器 | Ctrl + → | |
搜索框 | Ctrl + F | |
跳轉到物編器 | Ctrl + J | |
切換到觸發器 | Ctrl + 1 | |
切換到函數庫 | Ctrl + 2 | |
切換到觸發總覽 | Ctrl + 3 | |
跳轉 | F3 | |
復制參數 | Shift + C | 右鍵單擊參數使用。復制時,整個參數的值和類型都將被復制到剪貼板上 |
粘貼參數 | Shift + V | 右鍵單擊參數使用,復制的參數類型與目標參數類型必須相同 |
1.3 參數設置
??未賦值的參數會以紅色顯示,已賦值的參數會以填入的數據類型分配顏色,可選的參數則在次級菜單中選用,額外的選項會以藍色顯示,文本信息說明了當前觸發器語句的含義。需要指定所有的必選參數,本條動作以及所在的觸發器才能正常運行。
參數有以下幾種類型:
- 預設對象:當參數類型為可以放置在場景中的對象時( 單位,裝飾,物體,特效等),可以直接選擇該預設對象作為參數。
- 數值:你可以直接將一個固定的數字、文本或布爾值賦給一個參數
- 變量:包括全局變量和局部變量
- 函數:一組官方打包的獲取數據的動作,可以直接提供對應的返回值。
- 通用:用于設置 自定義值 和調用表格編輯器。
??當您單擊轉換為變量時,會在當前語句之前創建一個變量賦值語句,將當前語句結果賦值給一個變量。轉換為變量后,您可以單擊取消變量轉換以恢復到之前的狀態。
1.4 變量設置
你可以通過觸發器界面設置變量,或通過ECA選擇界面設置變量。
變量在創建時,需要聲明其類型,部分變量類型必須設置默認值。
- 非對象型變量:字符串、浮點數、整數、布爾值
- 對象型變量:單位、單位組、玩家組、單位命令
變量根據作用域類型分為全局變量和局部變量。
- 全局變量:可以在本項目內的任何觸發器中讀取和寫入。
- 局部變量:只能在當前觸發器中生效。
??數組是相同數據類型的集合,數組中的每個元素都在其基本值上添加了一個索引,使我們在數組中更容易找到和訪問它們。整數、單位、實體、單位名字、物品實體等變量都可以設置為數組。比如下面代碼設為按下H鍵之后,在Boss周圍召喚6個黑暗守衛(使用數組guards
實現)。
??變量只能用于儲存數據的媒介,并不會實時更新,任何對變量的操作都需要使用賦值動作進行實現。請不要忘記在觸發器中編寫更新變量的語句。例如,在游戲運行的時候設置變量“玩家人數”為6,5分鐘后有一位玩家退出游戲,此時游戲中玩家人數為5,如果沒有編寫變量更新語句,那么變量“玩家人數”依然為6。你需要編寫能表達“在玩家退出游戲的時候玩家人數減1”的邏輯。
1.5 實體觸發器
??在物體編輯器->觸發器中可進行實體的邏輯編寫,即為實體觸發器,其優勢在于它可以更簡單地將邏輯和目標物體綁定,而與其他擺件無關,所以可以減少性能的消耗。實體觸發器的編輯與上述觸發器操作一致,一個擺件也可以設置多條觸發。
1.6 函數庫與觸發器復用
??你可以將自己常用的觸發器語句/功能模塊添加到函數庫,接著就可以在觸發器中找到該函數語句進行重用。與觸發器的操作邏輯相同,在左側創建函數后,在右側編輯功能庫函數。
函數描述定義了函數在操作列表中的顯示方式。 函數描述包括以下內容:
- 名稱:要生成的觸發器語句的名稱。
- 說明:要生成的觸發器語句的內容和格式。
- 注意事項:當前函數的說明。
函數體包括以下內容:
- 參數:定義了該函數的輸入數據,可設置參數類型,如定時器、單位和整數。
- 返回值:定義了函數執行后返回的結果(輸出數據)。你可以點擊 "+"來設置返回值的名稱和類型,以方便觸發器的后續調用。
- 動作:顯示了函數的具體行動邏輯
??在觸發器中選擇想要復制的觸發進行復制,這條觸發器的源代碼內容會寫入到粘貼板中,此時可以在記事本等文本文件中進行本地粘貼保存,或者在新項目的觸發器界面選擇右鍵粘貼(Ctrl+V)。
二、觸發器的多層結構
2.1 子觸發器(在游戲內對新的事件進行注冊)
??觸發器具有一個事件,這個事件是通過一個注冊來實現的,即對一個事件進行注冊,從而讓系統在對應的事件觸發時能繼續按照條件與動作順序執行。全局觸發器是在游戲初始化時就注冊完成的,如果要在游戲內對新的事件進行注冊,實現更復雜的游戲邏輯,就需要通過子觸發器來實現。
??子觸發器是在觸發器的“動作”中創建的,用于在游戲進程中對新的事件進行注冊,注冊后會獨立運行(按ECA的邏輯執行)。子觸發器是一種變量類型,可以返回這個觸發器實例,以方便在使用完畢后銷毀它(如果需要的話)。因為是游戲進程中注冊,所以我們可以把變量中的數據傳遞給子觸發器進行注冊。
下面是一個簡單的示例,使用子觸發器將單位(關羽)移動到相應的點。
2.2 觸發器變量作用域
??作用域是指變量在程序中被定義后可以被訪問的范圍,它決定了變量的可見性和生命周期。全局變量可以在任何觸發器及其子觸發器的范圍內有效,局部變量可以在當前觸發器及其子觸發器的范圍內有效,子觸發器局部變量只能在當前子觸發器的范圍內有效。作用域的存在可以防止變量命名沖突,提供封裝和隔離性,提高程序的可靠性和可維護性。
??局部作用域中的變量優先級更高。比如在子觸發器中定義了一個局部變量,這個變量與全局變量或者外部作用域中的變量(另外一個觸發器中定義的變量)同名時,子觸發器內部只能訪問和修改這個局部變量,其余兩個同名變量被隱藏,無法直接訪問或修改。所以說,作用域機制提供了一定程度的隔離,可以防止子觸發器意外修改外部變量,從而避免潛在的錯誤和不可預測的行為,保持代碼的清晰和可維護性。
??根據作用域劃分,觸發器中的變量可以分為全局變量、函數局部變量、觸發局部變量、子觸發器局部變量和組變量。根據類型劃分,可以分為對象型變量(如單位、單位組、玩家組、單位命令)和非對象型變量(如字符串、實數、整數、布爾值)。
??子觸發器內部對于賦值運算(非單位類型局部變量的修改)只會在子觸發器內部生效,不會影響外部;但是對對象型變量(如單位組、玩家組等)的操作以及對單位屬性的修改會同步影響到外部。這種設計有助于在保持局部變量隔離性的同時,允許對共享資源進行必要的修改(單位對象是一個全局可訪問的對象)。
??下面使用全局字符串、全局單位類型、局部字符串、局部單位類型;全局單位組、局部單位組這6個變量進行測試。
-
先打印每個類型的初始值
[Info]: 全局字符串AAA [Info]: 全局單位類型UnitName:134219749張飛) [Info]: 局部字符串BBB [Info]: 局部單位類型UnitName:134274912(關羽)[Info]: 全局單位組中單位數量1 [Info]: 局部單位組中單位數量1
-
通過子觸發器修改這4個變量的值并打印
[Info]: 全局字符串CCC [Info]: 全局單位類型UnitName:134220068(大喬) [Info]: 局部字符串DDD [Info]: 局部單位類型UnitName:134257382(小喬)[Info]: 全局單位組中單位數量2 [Info]: 局部單位組中單位數量2
-
通過其他子觸發器再次打印變量的值:
[Info): 全局字符串CCC [Info): 全局單位類型UnitName:134220068(大喬) [Info]: 局部字符串BBB [Info): 局部單位類型UnitName:134274912(關羽)[Info]: 全局單位組中單位數量2 [Info]: 局部單位組中單位數量2
2.3 復合條件
??if
流程語句的結構如下,條件可以是取反(not語句)、所有條件成立(and語句)、任意條件語句(or語句)或者所有條件不成立。
所有條件成立:A和B都等于1 ,則單位會移動到指定的點
任意條件成立:A和B有一個等于1,則單位會移動到指定的點。
所有條件不成立:整數A 和整數B 都不等于 1,則單位會移動到指定的點。
如果A等于1,則單位會移動到指定的點A;否則,會移動到點B。
2.4 循環
循環是指重復執行某項動作。在Y3編輯器中,循環有三種類型:
-
指定次數或指定整數變量重復執行:這是最常用的循環觸發方式,例如,設定NPC敲門三次,動作會重復三次后自動停止。
-
條件成立,重復執行:在條件滿足的情況下,進行無限次的循環,直到條件不再滿足。例如,如果條件是門關著就敲門,那么敲門動作會一直重復,直到門開了才會停止。
-
遍歷數組變量循環:根據數組的索引數字,對數組中的每個單位重復執行動作。例如,有一個不同門的列表,NPC會按照順序敲門,直到所有門都被敲過。
下面是《觸發器2:循環》中的例子,通過第一種循環實現在BOSS周圍出現6個次元種子的效果。
2.5 計時器
??計時器就好比一個沙漏,在計時結束后開始執行某項動作。我們可以用計時器來處理和時間有關的游戲邏輯。在編輯器中,計時器一共有三種:
- 運行單次計時器:計時器運行一次后開始執行動作。例如,倒數三秒后約翰的門就會打開。
- 運行循環計時器:計時器循環運行,每次運行后都會執行動作。例如,每隔一秒約翰的門就會打開一次。
- 運行固定次數的計時器:計時器中間停留固定時間,固定次數的計時器。例如,每隔一秒約翰的門就會打開一次,一共打開三次。
??下面是《觸發器:計時器》中,使用計時器實現一只只次元入侵者每隔2s在“裂縫”處爬出的效果。
注意:你可以選擇
True
并立即執行動作,或者選擇False
在2秒后執行第一個動作。
2.6 單位組
??單位組是由一個或多個單位組成的集合,在單位組中,我們能同時對所有的單位或選擇某些單位發布命令。合理利用單位組,我們可以在游戲制作中節省大量的時間。下面是在《觸發器:單位組》中,使用單位組實現主人公發送特技“次元入侵”,入侵者瞬間死傷殆盡的效果。
2.7 玩家組
??玩家組是一個包含一個或多個玩家的集合。您可以直接對玩家組中的所有玩家執行操作。例如,下面的語句表示在點A創建一個關羽單位,并將該單位分配給玩家組BB中的每個玩家,單位面向180°角度。
2.8 運動器
??運動器可以為單位或者特效等添加運動效果,比如沿著直線運動或追蹤某一個單位等,是制作技能、特效等場景中十分常用的功能。
??在Y3編輯器中,運動器分為追蹤運動器、曲線運動器、直線運動器、環繞運動器(對單位)、環繞運動器(對點)。您可以設置運動器的 方向、距離、初始速度和加速度,以及一些可選參數。
下面是《觸發器:運動器》中,使用環繞運動器(對單位),實現電球的環繞和爆炸效果。
- 在物編器中創建一個自定義投射物
"Electric ball"
(球形藍色電流攻擊)作為環繞的球體,設置縮放0.4倍大小,勾選循環播放,讓特效一直播放。 - 設置自定義投射物
"Explosion"
(藍色電流沖擊波,0.3倍縮放)作為結束時的爆炸效果。 - 新建電球投射物:創建一個投射物變量
"Electric ball"
,投射物類型選擇預設的投射物"Electric ball"
,選擇在點創建,創建的位置點,選擇探險者所在的點。 - 新建運動器:選擇運動器->環繞運動器對單位,兩個參數分別設為變量
"Electric ball"
和探險者,環繞半徑為200
,角速度為100
。在可選參數中,添加環繞時間5s
和環繞高度100
。 - 新建投射物爆破動作:在運動完成動作列表下添加動作,在探險者所在的點創建投射物
"Explosion"
,持續時間1s
。 - 優化游戲內存:銷毀投射物
"Electric ball"
并移除運動器(為單位移除運動器,單位選擇探險者)。
三、案例
3.1 NPC對話
-
添加“Talk”屬性:通過物體編輯器給NPC添加自定義屬性“Talk”來存儲對話內容。
-
UI設計:通過界面編輯器設計聊天UI,這包括背景、NPC頭像、文本和退出按鈕。
3. 顯示聊天內容:創建一個顯示函數,參數為“NPC”單位。在函數里創建兩個本地變量存儲NPC頭像的ID和自定義屬性“Talk”。然后再創建界面組件“Icon”和“Talk text”并讀取這兩個變量。這樣當你具體調用與某個NPC的對話函數時,玩家UI就會出現其頭像和文字內容。
- 統一NPC對話入口:
- 當玩家靠近并點擊NPC時,程序通過自定義函數統一處理觸發事件,傳遞玩家角色和NPC作為參數。
- 函數獲取對應NPC的一系列信息參數,并顯示在聊天界面。
- 設計退出按鈕,當玩家點擊時,設置一個布爾值標記為‘true’以關閉界面。另外使用循環計時器檢測玩家與NPC的距離,如果超過500也會關閉界面。和前面的統一入口的實現邏輯是類似的,我們需要把不同的退出方式進行單獨處理,并最后通知在同一個出口上。
-
功能優化:為了保持界面關閉和顯示功能在同一模塊下,選擇一種相對消耗性能的方式,即使用一個布爾值類型的變量
Switch
來控制界面的開啟和關閉狀態。界面在啟動時會循環檢測Swich的狀態,默認狀態下,Swich為false
,當玩家點擊退出按鈕時,我們在事件的動作中設置Swich為True
。
-
整體實現:當玩家在地圖上選擇一個單位時,如果檢測到是NPC,則調用函數Z02.一次NPC對話事件,并讓玩家重新選中玩家角色。只有當玩家與NPC的距離小于400時,才啟動對話功能,否則提示距離過遠。
3.2 裝備限制
??對裝備的品類或者數量做限制是RPG類游戲中一個常見的機制,例如一個玩家覺得只能攜帶一把武器和一件裝甲,比如在FPS游戲中,玩家同時只能持有一把槍,想要切換到另一把槍就會把當前持有的武器移除掉,這些實際上的游戲效果都是裝備限制。
3.2.1 裝備拾取
??當玩家拾取裝備時,如果玩家單位上沒有該類型的裝備,則會直接進行裝備。否則拾取的裝備將被直接丟棄(系統丟棄,不會通知玩家,也不會觸發任何與玩家丟棄裝備相關的游戲事件或流程)。
-
裝備數據存儲:使用自定義值功能為每個裝備添加一個
type
屬性,用于標識裝備的類型。設置一個SystemDiscard
標記,用于指示某個裝備的丟棄是由系統自動處理的,而不是玩家的操作。 -
拾取物品:當玩家拾取一個物品時,系統會創建兩個局部變量,分別存儲玩家單位和拾取的物品。
-
邏輯判斷:
- 如果玩家單位已經裝備了相同
type
的裝備,那么新拾取的裝備將被系統丟棄。 - 如果玩家單位沒有裝備相同
type
的裝備,玩家將成功獲得新裝備,同時,系統會更新玩家持有物品的狀態,以便后續可以顯示相應的特效或其他游戲效果。
- 如果玩家單位已經裝備了相同
以上代碼邏輯為:
-
初始化變量:設置變量
unit
為獲得物品的單位,item
為單位獲得的物品。 -
檢查物品類型:如果
item
存在自定義鍵值"type"
,則繼續執行。 -
檢查玩家是否已裝備相同類型的物品:
- 如果玩家單位
unit
已經存在自定義鍵值type
,并且與item
的type
相同,則執行系統丟棄邏輯。 - 如果
unit
不存在自定義鍵值type
,或者type
與item
的type
不同,則執行裝備邏輯。
- 如果玩家單位
-
系統丟棄邏輯:
- 設置
item
的自定義鍵值"SystemDiscard"
為True
,表示這是一個系統自動處理的丟棄。 - 將
item
從unit
所在位置移除。
- 設置
-
裝備邏輯:
- 向玩家發送消息,告知玩家已經獲得了新的物品。
- 更新玩家單位的自定義鍵值
type
為item
的type
。 - 發送自定義事件
ProjectCreation
,參數為unit
和item
,用于可能的特效或其他邏輯的觸發。
3.2.2 物品丟棄
與拾取物品類似,當一個單位丟棄物品時,先使用局部變量對參數進行緩存,然后執行判斷:
- 玩家丟棄:如果物品被判斷是玩家手動拋棄,獲取物品type,清空單位對應自定義值項,這個清空的動作會導致特效結構檢測到數據清空,從而觸發特效的銷毀。
- 系統丟棄:如果該物品被檢測為系統丟棄,則跳過整個流程,當做無事發生(系統丟棄是靜默的,不會通知玩家,不更新玩家狀態,因此不會有新的特效或其他游戲效果被激活)。
以上代碼邏輯為:
-
初始化變量:設置變量
unit
為失去物品的單位,item
為單位失去的物品。 -
檢查物品類型:如果
item
存在自定義鍵值"type"
,則繼續執行。否則發送調試信息“丟棄的物品無類型” -
檢查是否為系統丟棄:
- 如果
item
的自定義鍵值"SystemDiscard"
為False
,則執行玩家丟棄邏輯。 - 如果為
True
,則表示這是一個系統自動處理的丟棄,不執行任何操作。
- 如果
-
玩家丟棄邏輯:
- 設置
type
為item
的自定義鍵值type
(在玩家丟棄裝備的過程中,臨時存儲裝備的類型信息,以便在后續的邏輯中使用這個信息來更新玩家狀態、觸發相關邏輯或發送消息等) - 刪除
unit
的自定義鍵值type
,表示玩家已經失去了這個類型的裝備。 - 向玩家發送消息,告知玩家已經丟棄了物品。
- 設置
-
系統丟棄邏輯:
- 刪除
item
的自定義鍵值"SystemDiscard"
,重置物品的狀態,這樣,如果物品再次被拾取或處理,系統可以重新判斷其狀態,而不是基于之前的標記。 - 向玩家發送調試消息“丟棄的物品為系統丟棄”
- 刪除
3.3 特效
??想要為一個已有的事件附加另一個效果,可以使用自定義事件功能,比如在獲得新裝備后,可以發送一個自定義事件,來激活對應的特效功能。
3.3.1 特效生成
通過發送和接收自定義事件來傳輸信息,實現特效的激活:
-
武器特效:武器特效是直接綁定在角色手部的拖尾,所以我們只需要直接將對應的特效路徑以自定義值的形式保存在物品上,就可以用同一個函數啟用所有的武器特效。
-
護甲特效:由多個特效組合而成,需要一個函數來不斷生成和銷毀這些特效。
-
特效分斂:根據自定義事件傳遞的物品和type參數,分斂到不同類型的特效創建函數,執行對應的效果。
如果自定義事件ProjectCreation
被觸發,則根據物品類型通知不同的特效生成機制:
- 如果自定義事件參數
item
的物品類型等于“寒冰劍”或“閃電發生器”,則設置變量projectType
為物品的projectType
,并為事件單位unit
的物品item
生成特效projectType
。 - 如果
item
的物品類型等于“冰霜斗篷”,則為事件單位unit
的物品item
生成盔甲01特效 - 如果
item
的物品類型等于“地精的護服”,則為事件單位unit
的物品item
生成盔甲02特效
3.3.2 特效銷毀
??在函數庫中,我們新建三個函數,分別是武器,盔甲1和盔甲2,來分別執行武器和盔甲的特效創建和銷毀過程。
??對于武器特效功能,是在單位的節點添加一個拖尾特效projectType
,同時啟動一個循環計時器,定期檢查玩家是否仍然持有該武器。如果玩家更換或丟棄了武器,特效將被移除,并對所有結構進行銷毀。
- 定義了一個名為“X01: 生成武器特效”的函數,該函數接收三個參數:
unit
(單位),item
(物品),projectType
(特效類型)。 - 為單位添加魔法效果
projectType
,并將此創建魔法效果賦值給變量project
,方便后續刪除 - 設置變量
type
為item
的type
- 啟動循環計時器,每隔一秒檢測一次,檢測
unit
的type
是否等于item
。是則繼續,否則刪除計時器和project
。
??盔甲1的特效顯示邏輯和武器特效類似,也是每秒檢測一次。不同的是,盔甲的特效將在每秒計時器到時后立即生成,并在兩秒后將其銷毀。
為了顯示盔甲2的特殊效果,我們為單位創建了兩個投射物,并使用局部變量進行儲存。
- 角度:為了實現球體的環繞效果,我們需要定義一個角度,這個角度將會每幀進行遞增,以實現幀疊加以后的球體圓周運動。
- 檢測:使用一個幀計時器(0.03秒)來檢測裝備是否還在被裝備,如果它被丟棄,對所有結構進行銷毀;否則繼續執行
- 循環:當程序一直持續循環運行,球體的角度會在每一幀減去6,并根據一個固定的距離,以角色位置為圓心,通過極坐標系確定本幀投射物該在的位置。
-
函數定義:定義了一個名為
X03: 生成護甲2特效
的函數,接收兩個參數:unit
(單位)和item
(物品)。 -
變量初始化:
type
變量被設置為item
的type
屬性,用于區分不同類型的裝備。int
變量被初始化為0,可能用于控制特效的生成或更新。angle01
和angle02
變量被初始化為90.0和270.0,用于確定特效的初始位置。- 在單位的位置創建兩個護甲特效,分別命名為
project[1]
和project[2]
。
-
循環計時器:創建一個循環計時器,每0.03秒執行一次,用于更新特效的位置。 如果單位裝備了“地精的護服”,則繼續執行循環,否則銷毀所有特效。
-
更新特效位置:
angle01
和angle02
變量用于控制特效的旋轉角度,每幀都會變化。根據旋轉角度,更新兩個特效project[1]
和project[2]
的位置point1
和point2
.。- 設置變量int每幀加1,然后進行條件判斷:
- 如果
int = 1
,在point2
創建特效project[3]
- 如果
2 ≤ int ≤ 13
,持續更新特效project[3]
的位置(點point2
)。 - 如果
int = 60
,重置int = 0
- 如果
??總的邏輯是每幀檢測一次,如果依舊裝備“地精的護服”,創建兩個特效project[1]
和project[2]
環繞其持有角色,然后每一秒的前13幀,在project[2]
的位置同時創建和更新特效project[3]
。
3.4 行為樹
??行為樹即是用樹狀結構分支使AI能根據條件執行不同行為(如走、跑、跳、攻擊),讓單位通過模擬真實玩家的行動而擁有“智能”行為,增強玩家的參與感,例如PVE的敵人,BOSS的戰斗模式,玩家召喚物等。
-
初始化行為樹:初始化前,需要創建一個全局變量(比如unitPlayer)來存儲玩家角色,創建一個全局變量單位組來存儲敵方單位。通過動作:“玩家-選擇單位”,讓玩家選中自己的主控單位。為了更沉浸的游玩體驗,使用“鏡頭 - 跟隨單位”功能讓玩家的視角能始終跟隨在自己的英雄上。
-
行為樹分斂:
- 游戲初始化結束(游戲開始0.1秒后),每隔1秒遍歷一次地方單位組,每次遍歷所有單位,根據地方單位類型(近衛和治療者),對其應用對應的行為樹。
- 判斷完單位類型后,通過自定義事件傳遞單位參數來實現行為樹的分發。參數必須是變量,不支持數組。比如行為樹是針對某單位執行,那么就需要將單位作為參數傳遞出去。
-
實現守衛AI:
- 新建一個單位類型局部變量
unit
,存儲通過“自定義事件:AI_Guard”發送過來的單位。 - 新建一個實數類型局部變量
distance
,用于儲存敵人和治療者之間的距離,通過判斷distance
是否小于300來判斷治療者是否安全。- 如果治療者不安全(距離小于300),則給單位unit向玩家角色unitPlayer發出一個攻擊指令。
- 如果治療者安全,判斷守衛自身HP,低于50%則移動至治療者(給單位unit向unitHealer發出一個移動指令)。
- 如果守衛安全,檢查警戒區域內是否有敵人,有則攻擊,無則巡邏。
- 新建一個單位類型局部變量
- 實現治療者AI:新建一個單位類型局部變量
unit
,存儲通過“自定義事件:AI_Healer”發送過來的單位。優先保障自身安全,滿HP(自己的HP是否等于其MaxHP)則治療友軍中HP最低的單位(如果有的話),沒有需要治療的單位則進行攻擊。- 使用一個函數獲取當前最需要治療的友軍。
- 新建局部變量
Temp
(百分比格式,初始化為1)和unit
(單位類型)分別存儲單位組中最低的HP百分比及遍歷到的單位。 - 逐一將
unit
血量和Temp
對比,如果unit
血量更低則將Temp
更新為這個值。當遍歷完全以后,我們就獲取到了單位組中HP百分比最低的那一個單位。 - 返回HP百分比最低的那一個單位
- 新建局部變量
- 使用一個函數獲取當前最需要治療的友軍。
- 如果HP百分比最低單位為空(友軍都是滿血),判斷自己身邊是否有敵人,如果有,就使用飛彈攻擊它;沒有則在警戒區域的中心不動,以方便巡邏的守衛保護自己。
- 如果有HP百分比最低的單位,則停止當前的動作(這里并非使用‘技能’來實現治療,所以為了防止治療者邊攻擊邊治療,需要先停止攻擊動作)并對其進行治療。為了讓這種動作更具真實感,我們可以播放對應的動畫動作,并為治療效果添加一個特效。
3.5 使用排行榜
-
點擊主界面【細節】-【存檔設置】打開存檔槽設置
-
點擊加號創建一個新的存檔槽并修改存檔槽數據類型為整數
-
選擇確認計入排行榜,這個存檔槽位就變成了排行榜存檔。可以選擇排行榜排序規則(升序降序)與排行榜最大人數。
-
使用以下eca可以獲取排行榜上所有玩家的存檔值,配合界面eca讓排行榜在界面顯示。玩家->整數型增量存檔eca可以讓整數類型存檔保持只增效果。
排行榜數據在游戲啟動后不會再刷新,使用雙槽位可以實現周排行榜。
-
創建兩個整數類型存檔槽位:A與B,A存儲第一周所有玩家的排行榜數據
-
當一周結束時(通過時間戳判斷),使用B榜存儲玩家在第二周的排行數據
-
在第二周開始時通過ECA清除玩家A槽位數據,以備第三周存儲玩家排行數據。需通過在作者之家清除A榜數據(權限需找運營申請)。
-
當第二周結束時,將玩家排行榜數據存儲在A榜并清除B槽位數據,循環往復
3.6 特效可見性
特效默認為全玩家可見,可通過ECA控制特效可見性。
- 通過“玩家”列表操作,實現對某個玩家的特效顯示/屏蔽
- 通過“玩家組”列表操作,實現對某個玩家組的特效顯示/屏蔽,比如某玩家的所有同盟玩家,某玩家的所有敵對玩家等等
3.7 不同步問題
參考《不同步相關》
3.7.1 本地配置不同步日志環境
??用戶由于使用ECA不當,頻繁出現游戲邏輯不同步的問題,會影響游戲正常運行。此時可通過在本地多開測試來進行調試。在【通用設置-調試】打開本地多開同步檢測。
??使用Lua文件配置更詳細的不同步日志:打開地圖路徑下Script文件夾下的main.lua
文件,在lua文件中配置不同步日志相關API
GameAPI.api_set_enable_detail_snapshot(true)
GameAPI.api_set_detail_snapshot_enable_tag(0xfffffff)
GameAPI.api_set_enable_eca_snapshot(true)
GameAPI.api_set_snapshot_traceback_level(2)
??配置完畢后,本地多開運行游戲。如遇游戲邏輯不同步,會有彈窗提示,并在本地生成不同步日志以供用戶定位不同步問題。
不同步日志Lua配置API說明:
API | 描述 | 參數 | 返回值 |
---|---|---|---|
api_set_enable_detail_snapshot | 控制不同步日志記錄的總開關。關閉后,其他設置接口將不生效,但可以提升性能。 | enable(bool) :是否開啟,默認為false | 無 |
api_set_snapshot_traceback_level | 設置日志堆棧記錄詳細等級,默認0 不記錄1 :僅記錄最近一層堆棧2 :記錄完整堆棧(數據壓縮)3 :記錄完整日志(不壓縮,數據量稍大)記錄越完整越有利于定位問題,但開銷增大 | level(int32) :堆棧記錄等級,默認值為0 | 無 |
api_set_enable_timer_snapshot | 開啟或關閉計時器不同步檢測日志(檢測額外創建的ECA計時器),但計時器不一致并不一定意味著游戲內容不同步。 | enable(bool) :默認值為false | 無 |
api_set_enable_eca_snapshot | 開啟或關閉ECA不同步檢測日志,開銷較高。可通過參數過濾掉一些安全的API以防止誤報,例如創建特效、UI操作等 | enable(bool) :默認值為false | 無 |
filter_mode(int32) :過濾模式,默認為1 1 :剔除模式,不記錄filter_set中指定的api;0 :包含模式,僅記錄filter_set中指定的api; | |||
filter_set(table) :過濾集合,默認為"client_only","client_possible" 可傳入想要剔除/包含的API(取決于上個參數) 如"GameAPI:print_to_dialog",“GameAPI:get_function_return_value。” | |||
api_set_detail_snapshot_enable_tag | 設置不同步詳細日志級別。越詳細越利于定位不同步產生點,但性能消耗會增高 | tag(UInt64) :控制開啟哪些日志的mask 1 運動器tick;2 運動器碰撞檢測4 尋路回調;8 尋路坐標更新16 血量變化;32 坐標瞬變0xFFFFFFFF 全部開啟;默認開啟16+32 | 無 |
add_detail_log | 記錄自定義日志 | log(string) :日志內容 | bool,值恒定為true |
3.7.2 線上游戲不同步定位
- 在本地配置不同步日志環境
打開地圖路徑下Script文件夾下的main.lua文件,在lua文件中配置不同步日志相關API,這樣線上游戲發生不同步時,根據配置API玩家客戶端會自動上傳日志。例如:
GameAPI.api_set_enable_detail_snapshot(true)
GameAPI.api_set_detail_snapshot_enable_tag(0xfffffff)
GameAPI.api_set_enable_eca_snapshot(true)
GameAPI.api_set_snapshot_traceback_level(2)
- 下載不同步日志
使用KK賬號登錄編輯器,打開任意地圖,點擊【菜單欄】【調試】【查看不同步日志】查看線上游戲的不同步日志。
查找需要定位問題的對局,下載不同步日志,不同步日志包含對局中所有玩家的對戰信息。
3.7.2 定位不同步問題
??不同步日志文件中包含的是出現不同步情況的幀信息,通過對比玩家日志差異,可以大致定位到問題所在。打開不同步日志文件夾,使用第三方文本對比工具進行對比(推薦使用BeyondCompare
)。比如在main.lua
文件中進行了如下配置:
GameAPI.api_set_enable_detail_snapshot(true);
GameAPI.api_set_enable_timer_snapshot(true);
打開不同步日志,查看逐幀信息:
將多名玩家日志成對拖放到beyondcompare中進行對比,發現玩家2比玩家1多了一個timer:
??通過對不同步信息附近的幀信息進行理解,可大致定位問題為某個客戶端上多了一個循環計時器,在項目中進行查找可能的問題所在。
3.7.3 游戲不同步的原因
- 本地值:
- 在單個客戶端(即單個玩家的設備)上有效的數據,比如界面顯示、特效、鏡頭位置、聲音等,這些數據在不同的客戶端之間不需要同步,因為它們只影響單個玩家的體驗。
- 任意值與【本地值】進行邏輯運算后得到的值等價于【本地值】
- 動作類ECA中,修改【本地值】,不會影響游戲的全局邏輯,因此不會影響游戲的同步性。
- 本地操作:針對本地值的操作,如【獲取本地玩家】、【獲取本地控件坐標】、【獲取鏡頭焦點】、【獲取滑動條當前值】等,這些獲取本地值的ECA函數,在不同客戶端得到的結果也是不同的。
- 全局值:在所有客戶端之間需要保持一致的值,比如單位、技能、物品等。這些值的一致性對于游戲的公平性和邏輯一致性至關重要。
- 全局操作:針對全局值的操作,比如修改單位的狀態、技能的效果、物品的數量等。這些操作會影響游戲的全局邏輯,因此需要確保在所有客戶端上都能正確同步。
??導致游戲邏輯不同步的一個重要原因就是將【本地值】作為參數,傳遞到【全局操作】中,或者是使用【本地值】進行邏輯判斷后,進行【全局操作】。這會導致不同客戶端執行不同的邏輯,最終導致不同客戶端上【全局值】不同,即游戲邏輯不同步。更多內容詳見文檔多人聯機同步機制。