Re: [程式] 關於SLG系統的寫法

看板GameDesign作者 (godfat 真常)時間16年前 (2008/04/09 17:00), 編輯推噓2(204)
留言6則, 2人參與, 最新討論串2/3 (看更多)
※ 引述《etrexetrex (moonet)》之銘言: : 我把這些對應到物件導向程式 : 1.選人 = 選物件 : 2.選行為 = 選方法 : 3.選對象 = 選參數 : 4.行動 = call 物件.方法(參數) [...] : 我希望做出來的系統是容易對人物,行為,對象或目標作異動的系統 : 所以我以為系統應該寫成一般式,步驟不一定只有4步 不是很清楚你想作成什麼樣子,根據不同的遊戲系統, 我想寫出來的東西可以大相逕庭。你可以從你希望怎麼操作你的東西開始思索, 像是我會希望能夠這樣做(in Ruby): > unit_pool = {} # a hash map > godfat = Unit.new "godfat" > godfat.hp = godfat.mp = 10 > # active ability > godfat.obtain_ability Heal.new(:level => 10, :cooldown => 29) > # passive ability > godfat.obtain_ability Flying.new(:height => 2) 像這樣就可以任意指定某個單位有哪些能力,事後也能修改單位的屬性和能力。 存起來以便事後 clone 出來: > unit_pool["godfat"] = godfat 治療自己: > action = godfat.abilities["heal"].targets(godfat) > action.apply 反治療: > action.unapply 這裡有一個重點,就是 unapply 不可能自己生出來, 必須針對每一個 ability 都寫一次。除非不用這種 undo 法, 而是把整個狀態記錄下來,然後重新寫回去,像是: > before_heal = godfat.dump_state > action.apply 反治療: > godfat.restore_state before_heal 不過我是不太建議這種作法,因為這樣你就必須知道被影響的單位有哪些, 像是在這裡只有影響到 godfat, 所以只要這樣做就好了。 要全面回復,可能就會有: > targets_to_restore = action.target_list > before_heals = targets_to_restore.map{ |t| t.dump_state } > action.apply 反治療: > targets_to_restore.zip( before_heals ).each{ |t, s| t.restore s } 唔,zip 和 map 的用法我就先不說了,總之這樣會有很多要處理的。 或是乾脆全場記下來,可能還簡單些: > last_step = game.dump_state > action.apply 反治療: > game.restore_state last_step 不過我不知道你是不是要做這麼複雜的 undo, 還是只是單純取消之前下達的命令,例如換人之類的,而不是說「悔棋」 如果只是要取消之前下達的命令,那根本不用 undo, 只要不要執行就好了... 等到「回合結算」的時候才真的去執行,那就很單純。 其他程式碼:(我隨手寫的,所以其實很粗糙) class Unit attr_reader :name, :abilities attr_accessor :cooldown, :hp, :mp def initialize name @name = name @abilities = {} @cooldown = @hp = @mp = 0 end def obtain_ability ability ability.owner = self @abilities[ability.name] = ability end end class Ability attr_accessor :owner attr_reader :name, :target_list def targets *list # 這表示引數可以無限多 @target_list = list self end def apply end def unapply end protected def initialize name @name = name end end class Heal < Ability def initialize opts super "heal" @opts = opts end def apply self.owner.mp -= (opts[:level] * 1.5).round self.target_list.each{ |t| t.hp += opts[:level] * 2 } self.owner.cooldown += opts[:cooldown] end def unapply self.owner.cooldown -= opts[:cooldown] self.target_list.each{ |t| t.hp -= opts[:level] * 2 } self.owner.mp += (opts[:level] * 1.5).round end private attr_reader :opts end class Flying < Ability # 略... end 上面那個 unapply, 其實可以寫得更抽象化一點,像是: > apply_steps << subtract 'self.owner.mp', '(opts[:level] * 1.5).round' > << lambda{ self.target_list.each{ |t| ....略 } } > << addition 'self.owner.cooldown', 'opts[:cooldown]' 然後 apply / unapply 就能這樣寫: > def apply > apply_steps.each &:apply > end > def unapply > apply_steps.reverse_each &:unapply > end 然後 subtract 的 unapply 當然就是 addition, addition 的 unapply 當然就是 subtract. 中間的 lambda 會複雜許多,所以我就不寫了... 當然這樣是有點走火入魔啦,只是要大量使用 undo 的話就得做徹底一點。 補充: 這邊每 apply 一次,就會洗掉上一個 target_list, 所以如果需要直接放入 stack 做 undo list 的話, Ability#targets 可能要這樣寫: class Ability def targets *list @target_list = list self.clone end end 這樣就可以直接放到 stack 去不怕影響到別人了 (當然,那個 clone 在 Heal/Flying 那些 class 裡要定義) -- #!/usr/bin/env ruby [露比] /Programming (Kn|N)ight/ 看板《Ruby》 # if a dog nailed extra legs that http://www.ptt.cc/bbs/Ruby/index.html # walks like an octopus, and Welcome ~Ruby@ptt~ # talks like an octopus, then ◢█◣ http://www.ruby-lang.org/ # we are happy to treat it as http://www.ruby-doc.org/ # if it were an octopus. http://www.rubyforge.org/ -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 220.135.28.18 ※ 編輯: godfat 來自: 220.135.28.18 (04/09 17:21)

04/09 19:34, , 1F
我想我說的undo在這篇文章是指你說的很簡單的部分
04/09 19:34, 1F

04/09 19:35, , 2F
"取消之前下達的命令"這個部分而已
04/09 19:35, 2F

04/09 19:35, , 3F
只是我在想一個命令要怎麼存才好分段undo
04/09 19:35, 3F

04/09 21:58, , 4F
你只要實作一個循環式的stack就好了...
04/09 21:58, 4F

04/09 21:58, , 5F
循環式的stack有出現在作業系統的分頁演算法計算
04/09 21:58, 5F

04/09 21:59, , 6F
或者是一種queue跟stack合併的一種具有最大指令數目的stack
04/09 21:59, 6F
文章代碼(AID): #17_8Mikn (GameDesign)
文章代碼(AID): #17_8Mikn (GameDesign)