RPGにおけるイベントスクリプトの構造
昨日に続いて、RPGネタです。
マップに配置したイベントIDをキーに、イベントスクリプトを呼び出す、
という話だったのですが、
今回はその「イベントスクリプト」の構造に触れてみたいと思います。
イベントスクリプトは、
イベントID
├フラグ操作
└イベント内容
というのが、最低限必要な構造になります。
そして、より汎用的なスクリプトにする場合は、
- if〜else
- while
- 変数
などの制御、データを追加します。
…まあ、あんまり汎用的なものを目指すと、
RPG作っているのだか、スクリプト作っているのだか、
良く分からなくなるので(´∀`;
最低限必要な機能に絞ったほうがいいと思います。
それで、肝心のスクリプトなのですが、
1から作るよりも、XMLを使うと楽ができます。
例えば、こんな感じです。
<root> <!-- GreetingScript ID=1 --> <event id="1"> <matter no="0"> <flag if="1" on="2" off="1"/> <!-- When first flag stands, --> <!-- scond flag turn on --> <!-- third flag tunn off --> <msg>Good Morning!</msg> </matter> <matter no="1"> <flag if="2" on="3" off="2"/> <msg>Good Day!</msg> </matter> <matter no="2"> <flag if="3" on="1" off="3"/> <msg>Good Evening!</msg> </matter> <matter no="-1"> <!-- There is no corresponding flag. --> <msg>Hello!</msg> </matter> </event> <!-- TreasureBoxScript ID=100 --> <event id="100"> <matter no="-1"> <flag nif="10" on="10"/> <money>500</money> <!-- 500yen Get --> </matter> </event> <!-- ChangeSceneScript ID=1000 --> <event id="1000"> <matter no="-1"> <msg>Goto Field?</msg> <scene id="1" posx="0" posy="1" /> <!-- It changes to coordinates(0,1) of SceneID1. --> </matter> </event> </root>
XMLは素人なので、これが正しい書き方なのか微妙です(´Д`;
と、とりあえず解説すると、トップに「root」があり、
その下にユニークなイベントIDを持ったイベントスクリプト「event」があります。
各イベントスクリプトは、発生イベントを1つ以上持っています。
それが「matter」になります。
「matter」はその下にある、「flag」のif属性に一致した場合に、
イベントを実行します。
そして、イベント終了後は「flag」のon属性に一致するフラグを立て、
off属性に一致するフラグを下げます。
また、「matter」のid属性に「-1」を指定すると、
発生するイベントが存在しなかった場合に発生する
デフォルトのイベントとなります。
サンプルとして、PythonでこのXMLを解析するスクリプトをのせておきます。
(そんな需要があるんかいな(´Д`;
from xml.dom.minidom import parse, parseString class XMLLoader: def __init__(self): self.map = {} self.rootTag = "" def initialize(self): self.map = {} self.rootTag = "" def load(self, filepath, rootTag): self.rootTag = rootTag dom = parse(filepath) # parse an XML file by name self.parse(dom, None, None, None) dom.unlink() def parse(self, dom, map, key, childMap): for node in dom.childNodes: if(node.nodeType == node.ELEMENT_NODE): # ELEMENT if(key != node.tagName): childMap = None key = node.tagName # ATTRIBUTE if(key == self.rootTag): tmpMap = {} self.map[node.getAttribute("id")] = self.parse(node, tmpMap, key, None) elif(key == "matter"): tmpMap = {} map[node.getAttribute("no")] = self.parse(node, tmpMap, key, None) elif(key == "flag"): map["flag"] = { "if": node.getAttribute("if"), "nif": node.getAttribute("nif"), "on": node.getAttribute("on"), "off": node.getAttribute("off") } elif(key == "scene"): map["scene"] = { "id": node.getAttribute("id"), "posx": node.getAttribute("posx"), "posy": node.getAttribute("posy") } else: self.parse(node, map, key, None) elif(node.nodeType == node.TEXT_NODE): # TEXT txt = node.data.strip() if(txt != ""): map[key] = txt return map def main(): xl = XMLLoader() xl.load("event.xml", "event") print xl.map if __name__ == "__main__": main()
これを実行するとこんな感じです。
{ u'1': { u'1': { u'msg': u'Good Day!', 'flag': { 'on': u'3', 'nif': '', 'off': u'2', 'if': u'2' } }, u'0': { u'msg': u'Good Morning!', 'flag': { 'on': u'2', 'nif': '', 'off': u'1', 'if': u'1' } }, u'2': { u'msg': u'Good Evening!', 'flag': { 'on': u'1', 'nif': '', 'off': u'3', 'if': u'3' } }, u'-1': { u'msg': u'Hello!' } }, u'100': { u'-1': { u'money': u'500', 'flag': { 'on': u'10', 'nif': u'10', 'off': '', 'if': '' } } }, u'1000': { u'-1': { u'msg': u'Goto Field?', 'scene': { 'id': u'1', 'posx': u'0', 'posy': u'1' } } } }
こうして読み出したデータは、
使う側から、なるべくXMLの内部構造を意識しないように、
クラス・関数でラッピングして、
getXXX()メソッドのように利用すると、
XMLの仕様を変えた場合に、デグレが発生しにくくなります。