2014-06-04

Delegate先生

 今回は手抜き記事です。なんとかいつもどおり実例を用意しようと思ったものの、複雑になりすぎるので省略。
 とりあえずこちらのサイトをご覧ください。
 http://marupeke296.com/DP_Delegate.html
 おそらく、最初何が書いてあるのか分からないでしょう。私も実際に自分で作ろうとするまで意味不明でした。でも、作ってしまうととても便利。

 たとえばキャラクターがスキルを習得したい場合に、スキルポイントを消費するとします。スキル名か何かをクリックするとスキルを習得できるとして、誤クリックの可能性があります。そういう時はやはり一度、「スキルを習得しますか?」という確認画面がほしい。そこでYESをクリックしたら実際にスキルを習得するような形がいいでしょう。
 それはステータスでもアイテムの売買でもそうかもしれない。確認してから実行するというのはゲームではしょっちゅう出てきます。そういう確認ウィンドウをそれぞれ別個に用意して、ある処理専用の確認ウィンドウを作るというのは結構めんどくさいわけです。
 そこでDelegate先生の出番です。
 YESボタンがクリックされたらどうするかというのを、専用のYESボタンを作るのではなく、クリックされたら持っているDelegateを実行するボタンとして作っておき、確認ウィンドウを作るときにYESボタンにDelegateを持たせておくのです。
 スキル名をクリックしたら、「そのスキルを上昇させる処理を呼び出すDelegateを持つYESボタンを表示する」ことで、YESボタンをクリックするとスキルが上昇する事になります。もちろん、スキルを上昇させる処理自体はどこか別なところに作っておかないといけません。私はCharacterクラスあたりにスキル上昇関数を持たせてあります。

 Delegateはその他さまざまな場面で使えます。すでにどこかにある処理を間接的に呼び出したいときには、たいてい役立ってくれるはずです。Delegateを使う前は、すでにどこかにある処理を呼び出す専用のクラスを作ってました。別に、それでも動きますし、そのクラスを用意しているコードを見たときに何をしたいのかが分かりやすいという利点もあります。しかし、延々と増え続けるクラスにうんざりしてましたので、とても助かりました。

 上記サイトの説明の助けになるかもしれませんので、私が作ったDelegateを一部乗せておきます。引数によって使い分けないといけないので、何種類も用意する必要がありますが。

class DelegateBase{
public:
 DelegateBase(){}
 virtual ~DelegateBase(){}

 virtual void Apply() = 0;
};

//Delegate
template < class T >
class Delegate : public DelegateBase{
private:
 T* p_actor;
 void (T::*p_func)();

public:
 Delegate(){
 }
 virtual ~Delegate(){}

 virtual void Apply(){
  (p_actor->*p_func)();
 }

 void SetParameter(T* tp_actor, void (T::*tp_func)()){
  p_actor = tp_actor;
  p_func = tp_func;
 }

 // デリゲータ生成関数
 static DelegateBase* CreateDelegator(T* tp_actor, void (T::*tp_func)()){
  Delegate* dg = new Delegate;
  dg->SetParameter(tp_actor, tp_func);
  return dg;
 }
};

template < class T >
class DelegateWithInt : public DelegateBase{
private:
 T* p_actor;
 void (T::*p_func)(int);
 int n_number;

public:
 DelegateWithInt(){}
 virtual ~DelegateWithInt(){}

 virtual void Apply(){
  (p_actor->*p_func)(n_number);
 }

 void SetParameter(T* tp_actor, void (T::*tp_func)(int),int t_int){
  p_actor = tp_actor;
  p_func = tp_func;
  n_number = t_int;
 }

// デリゲータ生成関数
 static DelegateBase* CreateDelegator(T* tp_actor, void (T::*tp_func)(int),int t_int){
  DelegateWithInt* dg = new DelegateWithInt;
  dg->SetParameter(tp_actor, tp_func,t_int);
  return dg;
 }
};

 呼び出し元での記述はこんなかんじ。

Delegate< WindowList >::CreateDelegator(p_windowlist, &WindowList::RemoveCommandSelectWindow)

 これは、コマンド選択画面を消去する処理を持ったDelegateを生成しています。このDelegateを「画面を閉じる」ボタンにでも持たせておけば、クリックされたときに画面を閉じてくれたりします。私の場合は右クリックしたときに呼び出してたりするので、ちょっと今回の説明とは使い方が違いますが。

スポンサーサイト



2014-06-13

スケールの問題

 ゲームを作るとき、いつも躓くのがこれ。
 たとえばSummonPanthersであれば1HEXの広さはどのくらいなのかとか、1ターンはどのくらいの時間なのかとか。一般的なゲームではこの辺はあいまいにされてます。ファイアーエムブレムの1スクエアの広さがどのくらいなのかとか、1ターンはどのくらいの時間なのかとか、考えるだけ無駄でしょう。
 ゲームというのはそういうあいまいなお約束を積み重ねて作るものです。ところが私は性格的に、その辺のスケールをある程度決めたくて仕方のないタイプなのです。幸いSummonPanthersの場合にはモデルとなるゲームがありましたので、それをアレンジする形ですみました。しかし、全部一から仕様を考える場合には、どうしてもそのあたりで悩んでしまいます。
 The Marshalの場合、おおよその人間の国の広さはドイツ程度。北を除く国境線の向こう側に魔王の国があると想定しました。しかしこれ、ゲームの開始時には連隊が一つずつしかないのはおかしな話です。1500人程度でドイツフランス国境線を防衛するとか正気の沙汰とは思えませんw
 それでもあのゲームの戦力単位が連隊なのは、兵科単位で部隊を編成したかったからです。スケールとしては師団の方が適切ですが、装甲師団といえども戦車だけで編成されているわけではありません。ドイツ式であれば1-2個の戦車連隊と2個程度の機械化歩兵連隊、工兵大隊や対空砲大隊などで構成されます(理想上は)。が、ゲームシステムの都合上、単一兵科で編成するようにしたいという事情がありました。そのため、編成単位は連隊に押さえたわけです。
 では、最初から3連隊程度ずつを配備しておいたらどうなるかといえば、まちがいなく煩雑なプレイを求める事になるでしょう。敵もそれだけ数が多くなり、バランスを維持するためには比率も維持する事になり、単純に言って3倍の連隊を編成してもらうデザインになります。めんどくさい!
 そういうわけで、ここで私はスケール的なおかしさには目をつぶって、プレイアビリティを優先したわけです。

 そしてまた基本的に、スケール的な違和感とプレイアビリティを天秤にかけたならば、無条件でプレイアビリティを優先すべきだと考えています。ぶっちゃけ、スケールなんて少しくらいずれてても誰も気にしません。うん。でも、私が気にしてしまう。分かってはいるけど気になっちゃう系です。

 ゲームを作るにあたって、1日にできる事の量とか、1マスに存在できる物体の量とか、あるパラメーターが1違う事の差とかそこまでまじめに考えるものではありません。考えすぎると、私みたいに無駄な労力を使うことになりますから注意してください。
 現在作ってるゲームはけっこう気楽な雰囲気なので、その辺のリアリティにはこだわらずにすんでますので、いままでよりは作るの楽です。

2014-06-23

増援

 SLGでは、途中で敵の増援部隊が湧き出る事があります。特に、SRPGでは頻繁にそういう場面を見かけるでしょう。
 しかし、私をはじめとして、そういうのが嫌いな人というのがいます。SPWAWなんかでもそうですが、突然何の前触れもなく目の前のHEX、もしくはすでに自軍ユニットのいるHEXから敵がわらわらと湧き出したりすると、やっぱりゲームシステムの限界だなと感じてしまうわけです。
 こういうのが一度でも発生すると、奇襲を受けないためには、すべてのターン、すべてのHEXに対して警戒心を持たねばならなくなり、それではゲームになりません。ファイアーエムブレムなんかだと自軍ユニットが押さえていない砦は常に警戒対象で、それ以外のスクエアも警戒しないと危険ですが、いきなり大軍が出てくるイベントが発生してそのまますぐに攻撃を受けるのは、初見ノーリセットプレイ時にはどうにもなりません。やっぱりSRPGというのはやり直しが前提のバランスだと感じます。
 私自身がSPWAWで理不尽さを感じていたため、SummonPanthersで敵の増援が出現する際にはマップの端から出現させる事と、増援が出現しうる状況である事を明らかにするように心がけました。突発的な増援イベントもあるため、後者については例外もありますが。

 SRPGの場合は物語の都合というものが優先されたりするのである程度仕方ないんですが、純粋に作戦を楽しむゲームの場合は、原則としてゲームが開始した後は場に手を入れてはいけないと考えています。まして、プレイヤーが予測し得ないような操作をするのは、作戦を楽しむという点では邪魔でしかない。現実的に予想外の事は起きるものだという考え方もありますが、事前に探知し得ないような超常現象として伏兵が出現する時点で現実的ではありません。

 基本的に、考える事を楽しむゲームを作る場合には、考えても仕方のない要素は排除した方がよいと思います。がんばって調べれば条件が分かるようなものもあるかもしれませんが、初めから条件がわかっている場合の方が、考える事を楽しむことはしやすいはずです。たとえば、俺の屍を越えていけの敵大将が逃走してしまう条件は、説明書で明記されています。条件が分かっているからこそ、そうならないようにはどうすればいいかを考えながら戦えるわけです。
 そういう条件を調べる過程も楽しんでほしいというゲームもあるかもしれません。それはそれ。それは考える事を楽しんでもらう事を第一の目標においたゲームではないわけですから、扱いが違うのは仕方ない。

Copyright (C) 旅の雲. All rights reserved. Template by Underground