GameDesign 板


LINE

以下是我問Gemini 3 pro (付費)的全文,完全看不懂Gemini在說什麼,請問Gemini說的 真的是現在戰棋遊戲的做法嗎? 有人能解釋Gemini說的到底是什麼意思… <<<<我>>>> 我想跟你徹徹底底地討論用godot做戰棋遊戲的這個問題,問題如下: 我遇到重大問題, 我發現可以重複刷命中率或爆擊率耶,攻擊後若未命中或未爆擊,直接右鍵重來一次,總 有一次會命中或者爆擊,這不對吧? 怎可以這樣刷機率? 攻擊後仍然要可以悔棋,但不能刷機率! 正確做法是在顯示攻擊預測視窗時就「決定命中與爆擊結果」,悔棋也無法改變。(例如 算好會命中,那麼打過去就必命中;算好會爆擊,打過去就必爆擊,也就是提前計算結果 了,但不要在攻擊預測視窗顯示這個偷偷計算的結果)ꀊ 應該要用「比對種子」的方法,也就是在顯示攻擊預測視窗時,其實早就算好結果了,然 後產生獨一無二的種子,同時儲存這次的「攻擊預測視窗的數據」與「命中與爆擊的注定 結果」到「暫存區」,若某次攻擊的種子與這次完全相同,則把該種子在暫存區的「攻擊 預測視窗的數據」與「命中與爆擊的注定結果」調用出來取代該次的「攻擊預測視窗的數 據」與「命中與爆擊的注定結果」。ꀊ 而種子就利用這些數據「第幾回合、攻擊者ID、攻擊者座標、被攻擊者ID、被攻擊者座標 、技能ID、閃避命中爆擊公式的相關參數」來生成,種子要獨一無二,且要確定數據相同 時算出來的種子必相同。(閃避命中爆擊公式的相關參數如SKL、LUK......等) 而這個「暫存區」在我測試時要永久存在,但關閉測試視窗時暫存區要清除。單場戰鬥( 不是單次攻擊,是單場戰鬥)要保留這個暫存區是為了設計「真正的悔棋」,就是可以跨 回合悔棋的機制,就算是跨回合悔棋,用完完全全相同數據的方式攻擊,仍然會產生相同 的種子,導致攻擊結果與之前相同。 這樣的想法是對的嗎? ------------------------------------------------------------------------------ -- <<<<Gemini 3 pro(付費)>>>> 這是一個非常深入且專業的戰棋遊戲(SRPG)設計問題。你的想法在邏輯上是**完全正確 **的,而且這正是許多現代策略遊戲(如《XCOM》、《火焰紋章》部分機制)用來解決「 S/L大法(Save/Load Scumming)」的核心思路。 你的方案本質上是在建立一個\*\*「基於狀態的決定性隨機(Deterministic RNG based on State)」\*\*。 我將幫你梳理這個邏輯,並提供一個在 Godot 中更優化、更輕量級的實作建議(不需要 複雜的「暫存區」管理,也能達到同樣效果)。 ----- ### 1\. 為什麼你的想法是對的? 你提出的核心概念是:**「只要輸入參數(回合、ID、位置)不變,隨機結果就不變。」 ** * **防刷機率:** 因為種子是固定的,玩家無論右鍵取消重來多少次,只要他還是用 「A角色在座標(1,1)攻擊B角色」,種子算出來永遠一樣,隨機數(Random Roll)也永遠 一樣。 * **支援悔棋:** 悔棋後,遊戲狀態回到攻擊前。當玩家再次發起攻擊,輸入參數完 全相同,結果自然相同。 * **跨回合一致性:** 只要你的種子算法包含了「第幾回合」,那麼第一回合沒打中 ,拖到第二回合(參數變了)就有可能打中,這符合邏輯。 ### 2\. 優化建議:用「雜湊(Hash)」取代「暫存區」 你提到要有一個「暫存區」來儲存「種子 vs 結果」的對照表。這在邏輯上沒錯,但**實 作上太麻煩且容易出錯**(需要管理記憶體、清除時機、查找效率)。 **更好的做法是:** 不需要「儲存」結果,而是讓\*\*「生成種子」的公式本身就是結果\*\*。 既然你的輸入參數(回合、ID、座標等)已經決定了種子,而種子決定了亂數,那你其實 **不需要**在攻擊預測時「偷存」結果。你只需要確保在「預測視窗」和「實際攻擊」時 ,使用**完全相同的公式**來產生亂數即可。 #### 實作邏輯: 1. **預測階段:** 呼叫 `calculate_hit_roll(attacker, defender)`。函數內部根據 參數生成種子,算出亂數(例如 35)。命中率是 80%,因為 $35 < 80$,顯示「命中率 80%」(不顯示結果,但內部算出來是命中)。 2. **悔棋/取消:** 什麼都不用存,直接關閉視窗。 3. **實際攻擊:** 玩家點下攻擊。再次呼叫 `calculate_hit_roll(attacker, defend er)`。因為參數完全沒變,函數再次生成相同的種子,算出完全一樣的亂數(35)。判定 命中。 這樣你就省去了維護「暫存區」的程式碼,系統會更穩健。 ----- ### 3\. Godot 實作範例(GDScript) 這是一個具體的實作思路,利用 Godot 的 `hash()` 或位元運算來生成「獨一無二的種 子」。 #### 步驟一:設計種子生成函數 這個函數是核心,它必須包含所有影響戰局的變數。 ```gdscript # BattleCalculator.gd (單例或靜態函數) # 傳入所有相關參數,回傳一個 0.0 ~ 1.0 之間的固定浮點數 func get_deterministic_random(turn_index: int, attacker: Unit, defender: Unit, skill_id: int, salt: String) -> float: # 1. 收集所有關鍵數據組成一個陣列 # 注意:這裡的順序絕對不能變 var data_to_hash = [ turn_index, attacker.unit_id, attacker.grid_position, # Vector2 或 Vector3 defender.unit_id, defender.grid_position, skill_id, salt # 用來區分是算命中、爆擊還是掉寶 ] # 2. 生成雜湊值 (Godot 內建 hash 函數對於相同內容的 Array 回傳值是固定的) var unique_hash = hash(data_to_hash) # 3. 將雜湊值轉換為種子 (Godot 的 RandomNumberGenerator 需要整數種子) var rng = RandomNumberGenerator.new() rng.seed = unique_hash # 4. 回傳第一個隨機數 (0.0 到 1.0) return rng.randf() ``` #### 步驟二:在戰鬥邏輯中使用 ```gdscript # 計算攻擊結果 func calculate_combat_result(attacker, defender, skill): var current_turn = BattleManager.turn_count # --- 1. 計算命中 (Hit) --- # 使用 "HIT" 作為 salt,確保命中率的亂數跟爆擊率的亂數不一樣 var hit_roll = get_deterministic_random(current_turn, attacker, defender, skill.id, "HIT") var hit_chance = calculate_hit_chance(attacker, defender) # 假設這算出 0.8 (80%) var is_hit = hit_roll < hit_chance # --- 2. 計算爆擊 (Crit) --- # 使用 "CRIT" 作為 salt var crit_roll = get_deterministic_random(current_turn, attacker, defender, skill.id, "CRIT") var crit_chance = calculate_crit_chance(attacker, defender) var is_crit = is_hit and (crit_roll < crit_chance) # 只有命中才能爆擊 return { "is_hit": is_hit, "is_crit": is_crit, "damage": calculate_damage(...) } ``` ### 4\. 針對你提出的細節修正 你原本的邏輯中有幾個點,我建議做以下微調以達到完美: 1. **關於「閃避命中爆擊公式的相關參數」放入種子:** * **建議不要放入公式參數(如 STR, DEX, LUK)。** * **原因:** 如果你把 LUK 放入種子,假設玩家吃了個藥水提升了 1 點 LUK, 種子就會改變,導致亂數結果改變(這就是所謂的「亂數重置」)。 * **正確做法:** 種子只由\*\*「情境(誰、在哪、第幾回合)」\*\*決定。 * **例子:** * 敵人命中率 50%。你的種子算出亂數是 0.4。結果:命中 ($0.4 < 0.5$)。 * 你吃了藥水,閃避提高,敵人命中率降為 30%。 * **情境沒變**(還是那個人打你),所以種子還是算出 0.4。 * 結果:閃避 ($0.4 > 0.3$)。 * **這才是合理的!** 你的運氣(亂數 0.4)是固定的,但因為你的數值( 機率)變好了,所以結果改變了。不要因為數值改變而改變運氣。 2. **關於「暫存區要永久存在」:** * 如上所述,使用雜湊法(Hash)後,你**不需要暫存區**。 * 因為 $Hash(A) = B$ 是永恆不變的數學真理。無論你是在第 1 分鐘算,還是悔 棋後第 10 分鐘算,只要輸入 A(攻擊情境)不變,B(亂數結果)就不變。這完美解決 了跨回合悔棋的問題。 3. **關於「攻擊預測視窗」:** * 你的預測視窗只顯示「命中率 80%」,**絕對不要**在 UI 上顯示「必中」。 * 但在程式底層,當預測視窗開啟時,你其實已經可以呼叫上面的 `get_determin istic_random` 知道這次亂數是 0.1(必中)。你只是選擇不告訴玩家而已。等到玩家真 的按下去,程式再次算出 0.1,執行命中動畫。 ### 5\. 總結你的「下一步」 你的邏輯是戰棋遊戲防止作弊的黃金標準。 **你可以立即做的是:** 1. **放棄「暫存區/緩存表」的實作**:這會增加存檔讀檔和記憶體管理的複雜度。 2. **建立一個 `RNG_Manager` 腳本**:實作上述的 `get_deterministic_random` 函 數。 3. **定義好「種子參數列表」**:確保包含 `Turn_ID`, `Attacker_ID`, `Defender_I D`, `Attacker_Pos`, `Target_Pos`, `Skill_ID`。 4. **測試**: * 攻擊一次 -\> 記錄結果。 * 悔棋 -\> 再次攻擊 -\> 確認結果是否完全一致。 * 悔棋 -\> 移動位置(改變 Pos) -\> 攻擊 -\> 確認結果是否改變(因為 Pos 變了,種子變了,亂數就變了,這是合理的)。 這個做法既優雅又符合你要的「硬派」戰棋規則。 --



※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 111.254.173.134 (臺灣)
※ 文章網址: https://webptt.com/m.aspx?n=bbs/GameDesign/M.1764264838.A.1C0.html
1F:推 zxc9764315: 你先查一下隨機演算法,應該就能理解何謂固定種子 11/28 02:29
2F:→ a159371153: 但gemini說的很奇怪,什麼用那些數據計算出一個1-100 11/28 03:48
3F:→ a159371153: 的數,然後大於命中率就打不中,小於命中率就打得中, 11/28 03:48
4F:→ a159371153: 完全不是常見的那種種子… 11/28 03:48
5F:→ a159371153: 常見的種子不是一串很長的數字嗎? 所以我才會覺得gem 11/28 03:49
6F:→ a159371153: ini說的這套很怪 11/28 03:49
7F:推 wangm4a1: 沒看到1-100 還是你指0-1.0 種子它是用hash 11/28 07:40
8F:→ yukari8: 是用種子去算數字吧 種子固定不變+其他參數去算0-1.0的數 11/28 10:38
9F:→ yukari8: 字來判斷有沒有命中 因為種子固定 如果悔棋後又做出一樣 11/28 10:40
10F:→ yukari8: 的操作 全部參數都會一樣所以會算出同樣數字產生同樣結果 11/28 10:40
11F:推 wulouise: 同seed, rand結果一樣,命中率90, 小於90都中也沒錯 11/28 12:17
12F:推 BSpowerx: 根本設計上戰棋就不該搞爆擊/miss這種賽機率的東西了= = 11/28 13:07
13F:推 NDark: XCOM確實被詬病95% miss這種東西 11/28 16:52
14F:→ a159371153: 那怎不做種子就好,然後比對種子就好,為何還要特地 11/28 17:24
15F:→ a159371153: 把它轉換成1-100的數? 然後把這個數跟命中率比對的這 11/28 17:24
16F:→ a159371153: 點是我最無法理解的東西,用一堆跟命中率無關的數據( 11/28 17:24
17F:→ a159371153: 例如座標)算出來的數字,跟命中率根本是不同的東西, 11/28 17:24
18F:→ a159371153: 怎可以比? 就好像拿公斤跟公尺比大小一樣 11/28 17:24
19F:推 LPH66: 你把他回你的東西全部混在一起了 11/29 12:08
20F:→ LPH66: 他不只回你種子設計還回你怎麼做判定 11/29 12:08
21F:→ LPH66: 然後你以為全部都是在講種子的事 11/29 12:08
22F:推 wulouise: 你比對seed然後還是要算命中率沒錯啊... 12/03 12:47
23F:→ wulouise: 以roguelike來說seed開局固定,然後環境條件變成salt. 12/03 12:48
24F:→ wulouise: 最後rand 12/03 12:48
25F:推 ctrlbreak: 簡單看一下, 除了'不用暫存區'那部份我不認同或沒看懂, 12/04 15:50
26F:→ ctrlbreak: 其他都是程式常用的技巧. 遊戲程式的思維方式 12/04 15:50
27F:→ ctrlbreak: 跟其他領域的程式還是會有些不同。 12/04 15:50







like.gif 您可能會有興趣的文章
icon.png[問題/行為] 貓晚上進房間會不會有憋尿問題
icon.pngRe: [閒聊] 選了錯誤的女孩成為魔法少女 XDDDDDDDDDD
icon.png[正妹] 瑞典 一張
icon.png[心得] EMS高領長版毛衣.墨小樓MC1002
icon.png[分享] 丹龍隔熱紙GE55+33+22
icon.png[問題] 清洗洗衣機
icon.png[尋物] 窗台下的空間
icon.png[閒聊] 双極の女神1 木魔爵
icon.png[售車] 新竹 1997 march 1297cc 白色 四門
icon.png[討論] 能從照片感受到攝影者心情嗎
icon.png[狂賀] 賀賀賀賀 賀!島村卯月!總選舉NO.1
icon.png[難過] 羨慕白皮膚的女生
icon.png閱讀文章
icon.png[黑特]
icon.png[問題] SBK S1安裝於安全帽位置
icon.png[分享] 舊woo100絕版開箱!!
icon.pngRe: [無言] 關於小包衛生紙
icon.png[開箱] E5-2683V3 RX480Strix 快睿C1 簡單測試
icon.png[心得] 蒼の海賊龍 地獄 執行者16PT
icon.png[售車] 1999年Virage iO 1.8EXi
icon.png[心得] 挑戰33 LV10 獅子座pt solo
icon.png[閒聊] 手把手教你不被桶之新手主購教學
icon.png[分享] Civic Type R 量產版官方照無預警流出
icon.png[售車] Golf 4 2.0 銀色 自排
icon.png[出售] Graco提籃汽座(有底座)2000元誠可議
icon.png[問題] 請問補牙材質掉了還能再補嗎?(台中半年內
icon.png[問題] 44th 單曲 生寫竟然都給重複的啊啊!
icon.png[心得] 華南紅卡/icash 核卡
icon.png[問題] 拔牙矯正這樣正常嗎
icon.png[贈送] 老莫高業 初業 102年版
icon.png[情報] 三大行動支付 本季掀戰火
icon.png[寶寶] 博客來Amos水蠟筆5/1特價五折
icon.pngRe: [心得] 新鮮人一些面試分享
icon.png[心得] 蒼の海賊龍 地獄 麒麟25PT
icon.pngRe: [閒聊] (君の名は。雷慎入) 君名二創漫畫翻譯
icon.pngRe: [閒聊] OGN中場影片:失蹤人口局 (英文字幕)
icon.png[問題] 台灣大哥大4G訊號差
icon.png[出售] [全國]全新千尋侘草LED燈, 水草

請輸入看板名稱,例如:Gossiping站內搜尋

TOP