ここ数日FlowChart.Netという製品を使った開発をやってる。こいつはフローチャート・ダイアグラムの類を簡単に実装できる製品で、ちょっと使ってみた限りとても便利に感じたのだが、一点だけちょっとアレだなあと思ったことがあった。
俺がやりたかったのは「ダブルクリック時に角丸と菱形など図形ごとに異なった動作をさせる」だったのだが、そのためには以下のようなイベントハンドラを書いて、自前のShapeNodeのサブクラスであるMyShapeNodeを実装する必要があった。
private void diagram1_NodeDoubleClicked(object sender, NodeEventArgs e)
{
if (!(e.Node is MyShapeNode)) return;
MyShapeNode node = (MyShapeNode) e.Node;
node.DoSomething();
}
private void diagram1_NodeCreated(object sender, NodeEventArgs e)
{
MindFusion.Diagramming.ShapeNode node = (ShapeNode) e.Node;
MyShapeNode newNode = new MyShapeNode(node);
diagram1.Nodes.Remove(node);
diagram1.Nodes.Add(newNode);
}
public class MyShapeNode : ShapeNode {
public MyShapeNode(ShapeNode node) : base(node) { ... }
public void DoSomething() {
//後はフィールドに持っている Shape オブジェクトで処理を振り分け
}
}
これの何がムカつくってそりゃキャストしてるところに決まってる。そもそもキャストなんてのはシステム設計と言語設計の決定的敗北を意味すると俺は考えるので、できればこんなものに頼らずに済ませたいところだ。それに生成されたノードの付け替えをしてるところも実にダサい。もっともファクトリオブジェクトが自由に付け替えられないために、イベントハンドラに渡されるのは常にコンポーネント側で定義されたオブジェクトなのでしかたがないのだが。
ところでいわゆるOOPLにおいて、継承というのは単一継承か多重継承かに関わらず、基本的にツリーの末端に要素を追加していくようなイメージだと思う。
-------------- | BaseClass | -------------- |--------------- | | v v ------------- ------------- | SubClass1 | | SubClass2 | ------------- ------------- | v ------------------ | UserDefinedClass | ------------------
ところがこれだと、先に出したFlowChart.Netのイベントハンドラのように、あくまでもメソッドが必要なのは既存のクラスという場合にちょっと不便だ。なので、次のような継承構造を考えてみる。
-------------- | BaseClass | -------------- | ------------------ |<-----------| UserDefinedClass | | ------------------ |--------------- | | v v ------------- ------------- | SubClass1 | | SubClass2 | ------------- -------------
さあちょっと斜め上感が漂ってきた。つまり既存の継承ツリーの途中に全然関係ないクラスを混ぜ込んでしまおうというアイディアなんだが、果たしてそんな事をできる言語があるのか?
結論としてはそういう事のできる言語はあって、それは3.0及び2.6以降のPythonだ。動的型付け言語故に元からメソッドを後付けしたりできたんだが、新しく標準モジュールに搭載されたabcモジュールで、継承構造まで書き換え可能になっている。とりあえずざっくりしたサンプルを。
from abc import ABCMeta, abstractmethod
class Foo:
def foo(self):
print('foo')
class Bar(metaclass=ABCMeta):
def bar(self):
print('bar')
__additionalslot__ = [bar]
@classmethod
def __addfunction__(cls. tgt):
for slot in cls.__additionalslot__:
setattr(tgt, slot.__name__, slot)
@classmethod
def register(cls, tgt):
ABCMeta.register(cls, tgt)
cls.__addfunction__(tgt)
class Buz(Foo): pass
Bar.register(Buz)
print(issubclass(Bar, Foo)) # False
print(issubclass(Buz, Foo)) # True
print(issubclass(Buz, Bar)) # True
buz = Buz()
buz.foo() # 'foo'
buz.bar() # 'bar'
流石にデカいプロジェクトでこういう機能を野放しにするのはアウトだけど、制御可能な範囲内なら有効に働くんじゃないのかな、これは。特に先に出したイベントハンドラまわりでファクトリ部分が決め打ちされていると、どうしても似たような泥臭いコード(キャストとか)があちこちに散らばってしまう。実行時型情報とキャストに頼るのとこういう技巧に走るのとでどっちが好みかといったら、プロジェクトのメンバーにもよるけど俺は技巧に走るかもしれない(=現場じゃまず使えない)。
セブンスドラゴンが終わったので、購入予定リストのゲームをちょこちょこと遊んでる。まず手を付けたのはデスティニーリンクスなんだが、まあ普通の2DのアクションRPGだ。とりあえず三番目の島までクリアしたので、ファーストインプレッションでも。
まず目についたのはダンジョンに入ると武器の変更が出来ないということ。このゲームは敵によって武器の有効度が大きく違う上、弱点武器で攻撃するとドロップアイテム2倍という仕様で、装備品はおろか回復アイテムもドロップアイテムを元に作る必要があるので、効率的にアイテムを集めるには以下のような手順を取る必要がある。
ちなみにアイテムはそのままでは換金不可能で、ただ戦っていてもお金は稼げない。じゃあどうやってお金を稼ぐかというと、NPCのクエストをこなす必要がある。もっともそのクエストは「○を□個取ってきて」が殆どなので、ひたすら武器やアクセサリを作ってダンジョン内で試し斬りをしてるといつの間にか達成してる。クエストの状況はバックログ込みで確認可能なので、うっかりボタン連打で読み飛ばしても大丈夫だ。
それで俺はどうもNPCの台詞を読み飛ばしがちなのだが、テキスト自体の魅力はゼロではない。というかむしろ、ほのぼのとした絵本か何かのような雰囲気はなかなかいいと思う。もっとも直前までやはりテキスト量のそれなりに多いRPGをやっていたせいか、何かもうそういうのはお腹一杯という感じだ。
戦闘バランスは凄まじくピーキーで、「氷系の攻撃で凍結→死亡」とか「大枚はたいて傭兵召喚→ボス瞬殺」とか、一体いつの時代のゲームだかわからん愉快バランスだ。敵の攻撃が結構痛いので雑に遊んでるとすぐに死ぬが、死んだときのペナルティなんぞ無きに等しいので問題ない。何しろ救助費用として所持金をほんのちょびっと引かれるだけで、アイテムのロストもなしに帰還できるからな。命が尽きるまでひたすらダンジョンで敵を狩りつづけ、死んだらクエストの報告と新しい武器の作成というのもアリ。何回か武器を持ち替えて探索してたらクエストの大半を達成できたので、基本はシナリオガン無視でひたすらハンティングか?
他に重要な点としてレベルの概念がないので、育成マニアには多分あまり向かないゲームだ。一方で死ぬほど使い勝手の悪い両手武器でボスの撲殺を目論むマソプレイからひたすら逃げ回りながら飛び道具連発のチキンプレイまで、遊び方の幅自体は割と広めなので案外バトルマニア向けかもしれん。
ちなみにゲームのメイン画面はマジでドット絵のみに近い簡素な作りで、時々思い出したようにイメージムービーがちょろと流れる程度のビジュアル表現なので、美麗なCGとかイラストを拝みたいという人にもまったく向かない。オープニングムービーはそれなりに力が入ってるが、主題歌の終わるタイミングがバイオリズムに合致しないのが残念だった。
というわけで俺は殆ど密猟者のようなプレイスタイルで進めているが、今のところ結構楽しめてる。問題は敵の攻撃パターンがイマイチ少ないのと攻撃方法が武器ごとに二種類のみで回避行動等がないせいで、敵の密集地帯に飛び込みでもしないと展開がやや一本調子なところか。完全にダレる前に終わるか新しい攻撃パターンの投入でテンションを保つかすれば、良作といっても良さそうな気がする。
この怪文書はクリエイティブ・コモンズ・ライセンスの元でライセンスされています。引用した文章など Kuwata Chikara に著作権のないものについては、それらの著作権保持者に帰属します。