作者godfat (godfat 真常)
看板GameDesign
标题Re: [程式] 关於SLG系统的写法
时间Wed Apr 9 17:00:20 2008
※ 引述《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://webptt.com/cn.aspx?n=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)
1F:推 etrexetrex:我想我说的undo在这篇文章是指你说的很简单的部分 04/09 19:34
2F:→ etrexetrex:"取消之前下达的命令"这个部分而已 04/09 19:35
3F:→ etrexetrex:只是我在想一个命令要怎麽存才好分段undo 04/09 19:35
4F:推 sunneo:你只要实作一个循环式的stack就好了... 04/09 21:58
5F:→ sunneo:循环式的stack有出现在作业系统的分页演算法计算 04/09 21:58
6F:→ sunneo:或者是一种queue跟stack合并的一种具有最大指令数目的stack 04/09 21:59