- ベストアンサー
状態とアクションの関係についての設計方法
- PHPの環境で状態とアクションの関係を設計する方法について詳しく説明します。
- 状態を基準にクラスを設計する方法と、アクションを基準にクラスを設計する方法の違いについて考えてみましょう。
- RPGの戦闘のシステムを設計する際に難しい要素や状態の絡み方についても解説します。
- みんなの回答 (40)
- 専門家の回答
質問者が選んだベストアンサー
>>この戦闘処理の状態を状態遷移図みたいなので表すとすると 複合状態(毒+石)も一つの新たな状態として存在することになるのでしょうか? それは、その世界内でのルールしだいだと思います。現実の世界では、「そういうケースは有り得ない」という前提条件で設計していても、何千回、何万回に1回だけど、ありえないとされる事象が発生するとか、パーツである基盤の仕様が間違っていて、有り得ないパターンが発生したりで「システムが停止したぞ!」というような苦情となったりします。 ゲームでは、設計者は「神様」ですから、その複合状態を有り得ないことにするのも、それを認めてそこからの回復条件をちょっと難しくすることも自由だと思います。 ですが状態遷移表を作る場合は、その複合状態も考慮して作成します。 >>つまり毒状態とも石状態とも違う毒+石という新たな状態。 もちろん複合状態もありだとすると、凄い数が増えることになると思うのですが そのとおりで、状態は掛け算で増えてゆき、凄い数になります。ちなみに○と矢印で記述する状態遷移図は、比較的簡単に書けるのですが、これはイベントを全て考慮しない図も書けるので、その場合バグの要因になります。 イベント(状態が変化する原因)とステータス(状態)をマトリクスにした状態遷移表の場合は、そういう抜けは発生しませんが、かなりでかい表になってしまいます。 それを防ぐには、状態遷移表を分割するわけですが、その表分割が不適切だと当然ながらバグ要因になりますね。 >>自分としては「毒+足怪我状態なので攻撃力が大幅に低下した」みたいな付加処理を 多種多様なパターンとして設計してみたいという欲望^^がありまして。。 ステートマシンで作っていれば、その付加処理の状態を「ありえない」としておいて、あとからそういう付加処理を追加するのはとても簡単ですしバグもたぶん発生しないでしょう。そのかわりに、初期段階の設計は複雑になりますね。
その他の回答 (39)
- lv4u
- ベストアンサー率27% (1862/6715)
No.27補足より >>class State_Poison extends State {// 毒の状態クラス という「毒の状態クラス」という考え方は、やはりおかしいと思います。 もちろん、目的のプログラムをどのようなクラスの集合体にするかということは、個人の考え方やプログラムの利用目的などにより、「こういうクラスにしないとダメである」ってルールはもちろん無いと思います。 が、今回の例で扇風機を兵士に置き換えて考えてみれば、兵士の状態の全てを把握するためにステートマシンを使うわけです。兵士クラスを作る過程で、「毒ってのもイロイロある。ただ麻痺するだけの催涙ガス程度のものから、目や口の機能を失わせるもの、すぐに死んでしまうものなどもある」と考えて毒の「状態」ではなく「毒」というオブジェクトのために「毒クラス」にしたほうがいいかな?と考えるのならわかりますけどね。 もちろん「すべての存在には、元になるクラスが絶対に必要である!」というクラス至上主義(クラス教条主義?)で作られるなら、「毒の状態のクラス」もありかもしれません。が、今回作成しようとしているプログラムでは必要以上にプログラムを複雑化させるだけというか、「クラスのためのクラス」のように思えます。 No.29のhogehoge78さんの回答より >>PHPでオブジェクト指向でやるとしたら、このfanfsmfsm.cの中身全部を保有するStatusクラスをつくってやって、ステータスの変動の度にそこに登録してあるファンクション(プロシジャ?サブルーチン?メソッド?)を呼び出してやるってことですよね。 そうです。ただ、クラス化するならネーミングとしては、Statusクラスよりも、Soldierなど、もっとかっこいいというか、オブジェクト指向プログラムっぽい名前にしたいところではありますが。
お礼
ご回答ありがとうございます。 う~ん、そうですか。なんで自分はクラスにしたいというか拘っているかというと 例えば毒の状態になった時に Battle::addMessage($this->owner.'は毒の状態になった'); とか 解除された時に Battle::addMessage($this->owner.'の毒は消え去った'); とかまあこれはメッセージだけですけど、なにか処理を加えたいと思ったわけです。 そうする場合にクラスにしてたほうが、まとまりがあるというか自分としては分かりやすい気がしたので。 public function enter()でその状態になった場合の処理を記述し public function exit()でその状態が解除なった場合の処理を記述できますし ほかにもいろんな場所(メソッド)を設けてもいいかもしれません。 それが本当に戦闘に必要かというのはありますが、もしこのような処理をクラスを用いないでやろうとした場合に lv4uさんなら具体的にどこにこのような処理を記述するのでしょうか? それとすいません、状態遷移表におけるhogehoge78さんが示した例の遷移ID(ACTION_ID)の確認なのですが ちょうど状態と事象(イベント)が交差する場所が遷移IDという認識で良いのでしょうか? ┌──────┬──────────┬──────────┐ │事象(イベント)→│ 通常攻撃 │ ギラ │ │状態↓ │ │ │ ├──────┼──────────┼──────────┤ │ │ここがhogehoge78さんの│例えばここなら │ │ 眠り │例のonXX() やonYY()に│onSleepGira()とか │ │ │相当する場所? │ │ ├──────┼──────────┼──────────┤ │ │ここで遷移先(状態)の│ │ │ 毒 │変更があればそれも行う│ │ │ │ │ │ ├──────┼──────────┼──────────┤
- hogehoge78
- ベストアンサー率80% (433/539)
■戦う場所というか戦う前の準備は先ほどの構造でいうとどのクラスで行うの これは、先の回答で書いたソースを見ていただきつつで。 最終的に物を取得してその物の振り分けを行うのがControllerなので、 BattleControllerで全部もち回す感じでしょうね。 BattleController::battleAction()としたところです。 ■戦闘メッセージについても聞きたい これも当然、基本的な考え方はMVCなので、なんかしらのViewクラスを作り、 その中にメッセージをセットしていき、各アクションの最後にレンダリングを行う、 という流れが自然かなと思いますが、いかがでしょう。 ■lv4uさんの提示されたステートマシン 私も実際に生成してみました。 これを見て、よくよく考えたら、Webアプリで言うところの「ステートフル」って奴ですね。 Picace_Flowとか、Xhwlayなんかのやっている事に良く似てました。 ステートレスであるHTTP通信にCookieとSessionでステータスをもちまわして、画面遷移をコントロールするって奴です。 PHPでオブジェクト指向でやるとしたら、このfanfsmfsm.cの中身全部を保有するStatusクラスをつくってやって、ステータスの変動の度にそこに登録してあるファンクション(プロシジャ?サブルーチン?メソッド?)を呼び出してやるってことですよね。 遷移図としては、 ・全体のゲームに関する遷移 ・何かの小さな挙動に関する遷移(兵士クラスの動作とか) とかといった複数の遷移があって、 兵士クラスないにステートマシンのようなものを組み込んでやってね、ってことですよね多分。 class SoldierState extends StateMachine{ public function __construct(Actor $soldier){ $this->stateobject = $soldier; } public function onXX(){ if($this->stateobject->isHoge()){ return $this->stateobject->hogeAction(); } } public function onYY(){ if($this->stateobject->isMoge()){ return $this->stateobject->mogeAction(); } } } で、これをテキストファイルかなんかに、 ACTION_ID,METHOD,Hoge,Moge XX,hogeAction,1,0 YY,mogeAction,0,1 とかといった、遷移IDと、遷移時のメソッド名と、各フラグのONOFFを書いてやって 自動生成スクリプトに渡してやると、それらの画面遷移に従ったクラスが出来上がる・・・みたいな。 ※もし間違ってたらコメントください。猛省します。 ■アスペクト指向について RubyのMixinは確かに便利そうです。 Javaだったら、コンパイル時点で、アノテーション(コメント)表記に従って横断処理を自動的にぶっこんであげるって感じになるんでしょうね。 PHPの場合は、実行の度に(恐らく最初の一回でキャッシュされるでしょうが)自前のアノテーションパーサで横断処理を入れるところを確認して、そのクラスのコンテナクラスで処理してやるって感じなのでしょうか。 面白い実装ではありますが、今使いたいって欲望はあまりないですね~。 ■マイグレーションについて 今は、Doctrineがそこら辺の機能が満載で、単純にデータベース操作を行うなら、Doctrineを使うのが良いかなと思います。 ZendFrameworkに組み込んで触ってみてますが、非常に感触は良いです。
お礼
ご回答ありがとうございます。 >最終的に物を取得してその物の振り分けを行うのがControllerなので、 BattleControllerで全部もち回す感じでしょうね。 なるほど、たしかに「戦闘をする(戦闘処理)」というのはモデルではないですよね? ちょっと(というかかなり)MVCに対する理解も曖昧なので やっぱりこれも何かで勉強した方がいいですね・・・ >なんかしらのViewクラスを作り、 これはモデルが Application_Model_Player とかなので Application_View~ という形になるのでしょか?(なんかそういうどうでもいい(?)ことばかり気になってすいません・・・) >兵士クラスないにステートマシンのようなものを組み込んでやってね、ってことですよね すいません、確認なのですがそもそも兵士クラスってhogehoge78さんのところの Actorとかplayerとかとイコールですよね? だとしたら兵士クラス(プレーヤー)にステートマシンを所持させる形では駄目なんですか? class Actor{ protected $stateMachine = new StateMachine(); } >Doctrineを使うのが良いかなと思います。 >ZendFrameworkに組み込んで触ってみてますが、非常に感触は良いです。 Doctrineですか、今後なにか機会があれば使ってみたいと思います。
- hogehoge78
- ベストアンサー率80% (433/539)
とりあえず、私のイメージした全体的な流れを書いてみました。 多分プログラムのエントリポイントのようなところで、ゲームが終了するまでループさせ続けると思います。 このループで、1秒間に何回描画するかとかを決める感じなので、一つのアクションが終わるたびに ここにもどってきて、もち回したゲーム全体を管理するステータス値を保存しているクラスをもち回しながら 行ったり来たりさせる感じかなと。 <?php class Main{ private $actions = array( 1 => 'BattleController', 2 => 'QuitController', ); private $instances = array(); private $id = null; //多分エントリポイント的なもの public function execute(){ //ゲームの開始 $this->setNext(1); $status = new GameStatus(); while(true){ try{ $status = $this->next($status); if($status->isEOG()){ //ゲーム終了ステータスが吐かれたので終了 exit(); } $this->setNext($status->getId());//次の画面のID }catch(Exception $e){ //例外によるゲームの強制終了 exit(); } usleep(33); } } private function setNext($id){ if(!isset($this->actions[$id])){ throw new Exception("そのIDは登録されてない"); } $this->id = $id; } private function next(Status $status){ if(!isset($this->instances[$this->id])){ $className = $this->actions[$this->id]; $this->instances[$this->id] = new $className(); } $instance = $this->instances[$this->id]; if($status->needInitialize()){ $instance->initialize(); } $instance->setStatus($status); return $instance->execute(); } } class BattleController extends Controller{ private $status = null; private $action = firstAction(); public function execute(){ call_user_func(array($this, $this->action)); return $this->getStatus(); } private function firstAction(){ //コマンド選択的なアクション //紆余曲折あった後次のアクションを決める $this->action = 'selectAction'; $this->view->render(); } private function battleAction(){ foreach($this->actors as $actor){ $actor->doAction($this->view); } $this->action = 'afterAction'; $this->view->render(); } } ?>
お礼
ご回答ありがとうございます。 なるほど(とは言ってますがまだちゃんと理解できてるわけではないです^^) これも凄いですね。。こうもきちっと設計できるもんですね。 >1秒間に何回描画するか なぜ秒数という概念を取り入れる必要があるのでしょうか? 1アクションに対して一回の描画ではないのでしょうか? もしかしたらweb(PHP)以外でのことを想定しているのでしょうか? それとすいません、No.29に対する質問をここでするのはあれかもしれませんが 後で疑問が出てきたものでここで質問させてください。 >遷移IDと、遷移時のメソッド名と、各フラグのONOFFを書いてやって onXXやHogeMogeを具体的(毒状態やそれに対するアクション時の付加処理など)な記述で 書いてもらえないでしょうか?onXXやonYYが何をさすものなのかがいまいち分からないですが
- lv4u
- ベストアンサー率27% (1862/6715)
>>なのでfanfsm_on1at0関数などの各ステートが自分としてはそれぞれ個別にクラスで作った方がいいんじゃないかと思いまして。 うーん、そのような考え方をされるってことは、takagoo100さんはオブジェクト指向プログラミングというものが分かっていないのではないでしょうか? 生成された関数をまとめて、例えば「兵士」というクラスにとりまとめるならわかりますが、fanfsm_on1at0という1つの関数を取り出してクラスにするのは全く意味がないことだと思います。 また、別の観点からいえば、このステートマシンはfanfsmstateが重要な役割を持っている変数ですが、グローバル変数になっています。もしfanfsm_on1at0のような関数単位でクラスを作るならば、それぞれのクラスが、このグローバル変数によって強く結び付けられてしまいます。それは、嫌われものであるgoto文を使ったプログラムよりもたちの悪いものになりますし、クラスを元に独立した複数のインスタンスを作ることができなくなり、オブジェクト指向プログラムとはいえないしろものになってしまいます。 >>もしそうではないとすると、lv4uさんの仰る各ステートというのは 具体的にどこに(どのファイル、どのクラスの中)用意すれば良いのでしょうか? サンプルでは、ステートを表現(保持)しているのは、「fanfsmstate」の持つ値になります。各ステートから何らかの攻撃(原因)等で別のステートに移動するときに、ひとつながりのアクションというかなんらかの処理をさせたりするわけですが、それらを「fanfsm_on1at0」などの関数の中に記述することになります。 なお、このサンプルプログラムでは、敵味方の複数兵士を表現しようとすると、とりあえずはfanfsmstateを配列にして対応するコーディングになると思います。(各種フラグなどがあれば、それらも配列にする必要があります) でも、スマートに作成するなら、兵士クラスのような形にまとめるほうがいいと思います。
お礼
ご回答ありがとうございます。 すいませんちょっと勘違いしてたかもしれません・・・(たぶん) fanfsm_on1at0などのひとつひとつのアクションをクラスにしたいと思っているのではなく ┌──────┬──────────┬──────────┐ │事象(イベント)→│ 通常攻撃 │ ギラ │ │状態↓ │ │ │ ├──────┼──────────┼──────────┤ │ │眠っていて攻撃できない│眠っていて呪文と唱える│ │ 眠り │というメッセージを追加 │ことができない(以下略│ │ │するアクション │ │ ├──────┼──────────┼──────────┤ │ │例えば攻撃力を増す │何も付加処理を │ │ 毒 │処理を施すアクション │行わない(スルー) │ │ │ │ │ ├──────┼──────────┼──────────┤ 自分がクラスにしたいと思ってるのは上の表の行(状態)で、 例えば
補足
補足を借ります。。 class State_Poison extends State {// 毒の状態クラス protected $count = 0; public function __construct() { $this->count = rand(1, 4); } public function enter() { Battle::addMessage($this->owner.'は毒の状態になった'); } public function execute() { switch($this->action) { case 通常攻撃: $this->owner->ATK += 10; break; case ギラ: break; } } public function exit() { Battle::addMessage($this->owner.'の毒は消え去った'); } } これらをStateMachineで管理してやればいいと思うのですが こういうクラスの使い方もおかしなやり方なのでしょうか?
- lv4u
- ベストアンサー率27% (1862/6715)
>>例を何か一つ具体的なプログラムで記述していただけないでしょうか? そうすればイメージできそうなのですが 以下のURLには雑誌インターフェース2001年5月号で紹介された状態遷移表コンパイラの記事を一部修正して著者の方がアップされています。 http://homepage3.nifty.com/~masumoto/embedded/sttc/sttc.html 以下のURLからは、状態遷移表コンパイラと簡単なサンプルがダウンロードできます。 http://homepage3.nifty.com/~masumoto/embedded/sttc/sttc140.lzh ダウンロードしたsttc140.lzhを展開するとsttc140というディレクトリが作成されます。 その中にあるSttc.exeとTsvSplit.dllがWindowsで使えるコンパイラです。 sample.LZHをさらに展開すると、記事中で紹介されたスイッチにより風の強さを制御する簡単な扇風機の制御プログラム例が入っています。 ただ、このサンプルで生成されているプログラムはアセンブラです。なので、コンパイラの2つのファイルを同じディレクトリにコピーして、以下のようにコマンドプロンプトで入力してC言語のサンプルを生成します。 C:\>sttc -nfanfsm -aC Table.txt これで2つのファイルが生成されます。 fanfsmfsm.h fanfsmfsm.c このファイルをコンパイラでコンパイルすれば実際に動作するプログラムができるわけですが、イメージを得ることが目的ならここで生成したソースをながめるだけでいいと思います。 ※蛇足 時々目にする「アスペクト指向」って何?と思っていましたが、構造化プログラムで普通に使われる「共通サブルーチン」に相当するものですよね? オブジェクト指向&クラス中心ではプログラムが作りにくい事例もわりと多いと思います。 RubyではMixinで対応したり、オープンクラス(モンキーパッチング)が強力な拡張性を提供しているようです。
お礼
ご回答ありがとうございます。 実際にfanfsmfsm.cを生成して見てみたのですが、 要は static void fanfsm_on0at0( void ) { return; } static void fanfsm_on1at0( void ) { fanfsmstate = 1; start_low(); return; } の一つ一つの関数がlv4uさんが仰っている各ステートということじゃないんですか? fanfsm_on1at0関数なら static void fanfsm_on1at0( void ) { //この中でアクションの共通的(横断的)な処理を呼び出す } その内側でアクションを呼び出すわけですよね? なのでfanfsm_on1at0関数などの各ステートが 自分としてはそれぞれ個別にクラスで作った方がいいんじゃないかと思いまして。 もしそうではないとすると、lv4uさんの仰る各ステートというのは 具体的にどこに(どのファイル、どのクラスの中)用意すれば良いのでしょうか? >「アスペクト指向」って何?と思っていましたが、構造化プログラムで普通に使われる「共通サブルーチン」に相当するものですよね? どうなんですかね、hogehoge78さんは分かりますでしょうか? 自分も便利だなぁと思ってはいるのですが、あまり理解して使ってるわけではないので・・^^ sabelのアスペクト指向についてですが、ここに自分が質問した例がありますね。 http://oshiete.goo.ne.jp/qa/5404265.html ここでダウンロードできるかもしれません Sabel ドキュメントとか(非公式) http://ebine.org/sabel/doc/index.html http://ebine.org/sabel/doc/container.html
- hogehoge78
- ベストアンサー率80% (433/539)
■Object等の命名規則に関して あるデータベースのテーブルのレコードを扱う具象クラスがテーブルの名前に成っているというのはわりかし一般的で、 ただ、そのクラスの抽象的な実装があったので、その上により抽象的な名前で命名をしただけです。 今回は、汎用ライブラリ(とかフレームワーク)を作成するという実装ではないので、そのようにしました。 ライブラリ側の命名規則と、ベンダーが実際に実装するものは、命名も別立てになることが多いので。 ZendFrameworkでいえば、Zend_Controller_Actionの継承クラスも、MyControllerクラスであって、Zend_Controller_Action_Myではなく、 Doctrine(ORマッパ)でいえば、Doctrine_Recordの継承クラスは、Tablenameクラスであって、Doctrine_Record_Tablenameではない。 どちらも、その部分の実装は、ライブラリから切り離されたベンダー側の実装なので、命名規則が変わるのかなと。 (ただこの実装も各ライブラリから指定されてのその名前ではあるんですが・・・) なので、ライブラリ的なインターフェイスのApplication_Model_Objectがあっても、 それ以下の継承クラスは、ライブラリ使用者側の実装になるわけですので、 PlayerやSpellやSkillとテーブルの名前で、今回たまたまソレより抽象度が高い中間的な、ActorとElementに別れたので、そのように書きました。 ここらへんは設計の方針とかである程度調整が必要なんですかね。 どちらにしても、PHP(<=5.2xx)以外の言語だと、Namespaceとかがあったりするので、命名の方針も若干違いそうですが。 ■Sabelについて んー、これは私はよくわかりませんでした。古いドキュメントも今は閲覧出来ないようで、最初のチュートリアルの内容だけではなんとも言えなかったんですが、Javascriptがフレームワークの中に統合されているのは良いですね。 (PHPのフレームワークはあまりJavascriptとかCSSとかが統合されたようなものはないので) takagoo100さんはどこら辺が良いと感じたのでしょうか。
お礼
ご回答ありがとうございます。 >PlayerやSpellやSkillとテーブルの名前で、今回たまたまソレより抽象度が高い中間的な、ActorとElementに別れたので、そのように書きました。 なるほど、理解できました。 これも聞いておきたいのですが、 戦う場所というか戦う前の準備は先ほどの構造でいうとどのクラスで行うのでしょうか? Application_Model_Battle とかでしょうかね? $players = $this->PlayersTable->findByActive(); //現在のパーティー面子を取得 for ($turn = 1; $turn <= 30; $turn++) { foreach ($players as $player) { $player->doAction(); } } みたいな処理を記述する場所です。 それと戦闘メッセージについても聞きたいのですが、自分としてはBattleクラスを作って class Battle { protected static $messages = array(); public function addMessage($message) { } } その都度、Battle::addMessage('~は~に攻撃した');みたいに呼び出して最後にその内容を表示する感じなのですが、 拡張性とか考慮した場合にメッセージクラスを別に作った方がいいのでしょうか? そもそもBattle(戦闘全般に関わる処理)とメッセージの関係性を知りたいのですが >Javascriptがフレームワークの中に統合されているのは良いですね。 そうですか。自分はそれについてはあまり意識してなかったです(というより理解できてなかったです^^) 自分的にはまずアスペクト指向による横断処理が便利だなぁと思いました。 データベースのトランザクション関係で同じような処理をあちこちに記述することになるので。 それとフロー制御や値のバリデーションなどをアノテーションで簡潔に記述できるのも 便利だと思いまいした(まあそこを利用するのはどうなのかなぁってのはありますが、、) その他にも、データベースのマイグレーションとか とにかく1つのアプリケーションを作成するにあたって「そういう機能があったら楽だよなぁ」って感じで 自分に合う感じでした。使用するライブラリを選択することによって自由度も調節できるようだし(この機能については自分の理解力じゃよく分からないですけど^^) 全然有名じゃないですけどこれ結構凄いんじゃないのかなぁと思いまして
- lv4u
- ベストアンサー率27% (1862/6715)
>>つまりその(眠り)カウンタがフラグの代わりってことですか? カウントが0ならフラグでいうところのfalseで0より大きい値のならtrue そうです。ただ眠りから覚めるのが時間ではなく、なにかのイベント(味方からの解毒剤投与など)と設定するなら、0か1の単なるフラグになるでしょうね。 >>もし「状態C」から眠りフラグが解除された場合、毒フラグは残ったままなので例えば「状態D」とかになるのでしょうか? それは、「どういう設定に自分がしたいか?ストーリの整合性がそれで納得できるか?」などで決まると思います。つまりシンプルに「状態B」に戻るだけにしたいか、それとも「状態D]にするかは、ゲームデザイナとしてどちらが良いと思うかで決めればいいと思います。 もちろん、「状態D」があるほうが遷移表は大きくなり、複雑度は上がりますね。 >>「各ステートの中」が具体的にどこのことなのかが分からないのですが、 やっぱりこれはクラスになるんじゃないんですか? それは、状態Aとか状態Bの値を保存する「状態変数」の値で表現することになります。状態が変わるごとに、「状態変数」に「状態A」とか「状態B]の値を代入していくわけですね。で、状態変数とイベント値(原因)の組み合わせによって、次の移動先の状態が、状態遷移コンパイラが生成したテーブルの値に従って決まってゆくことになります。 まあ、このあたりは、実際にステートマシンを動かしてみないと理解が難しいかもしれません。
お礼
ご回答ありがとうございます。 >つまりシンプルに「状態B」に戻るだけにしたいか、それとも「状態D]にするかは、 ここでいう「状態B」というのは眠りフラグが設定されていた(つまりtrue)状態をさすと思うので 眠りフラグが解除(false)されているのに「状態B」に戻るというのはおかしくないですか? この場合の、「状態B」って 状態の前後の推移関係なく眠りフラグが設定されたら「状態B」になるのか それとも 健康な状態Aから、眠り攻撃を受け「眠りフラグ」が設定されたら「状態B」になるってことですか? >共通的(横断的)な処理は関数化して、各ステートの中で呼び出せばいいように思うのですが・・・。 「~の中で呼び出す」というのは例えば関数の中とかで function func1() { // この中で呼び出す } というイメージなのですが、 >状態Aとか状態Bの値を保存する「状態変数」 それが変数だとしたら中で呼び出すというのがいまいちよく分からないのですが、 関数ならさきほどのようにその中でなにか別の処理を呼び出せますよね? lv4uさんが考えいてる >実際のアクションで、共通的(横断的)な処理は関数化して、各ステートの中で呼び出せばいいように思うのですが・・・。 の例を何か一つ具体的なプログラムで記述していただけないでしょうか? そうすればイメージできそうなのですが
- lv4u
- ベストアンサー率27% (1862/6715)
>>class State_Sleep {// 眠りの状態クラス 「眠りの状態のクラス」ってのは、私からみると変なクラス分けだと思います。 普通に作成すれば、例えば兵士クラスに「眠りフラグ」を作るのではないでしょうか? >>複合状態の場合はクラスとして用意するとしても、単独状態のようなカウントはないわけですよね。 複合状態は、クラスとして表現されるのではなく、ステータスとして表現します。状態遷移図で兵士オブジェクトが健康な状態Aから、眠り攻撃を受けたら「眠りフラグ」が設定された「状態B」に移行し、さらに毒攻撃で「毒フラグ」がセットされて「状態C」に移行するって感じですね。 攻撃を受ける順序により状態が別になる場合は、遷移図でそれぞれ別のステータス移動になるように設定します。 またカウンタも兵士オブジェクトに持たせておけばいいでしょうね。大きなループを作成して、その世界の「時の流れ」をカウントし、個々の兵士オブジェクトは、それに同期して、それぞれの持つカウンタを増やせばいいと思います。 例えば、眠りをカウンタで表現し、眠り攻撃を受けたら100をセットし、時間の流れに合わせてカウントダウンして、0になったら回復とかすればいいと思います。 >>そもそも複合状態ってなんなんだ?っていうのがあって^^ 上にも書きましたが、それは状態変数によって表現されます。兵士の例でいえば「状態A=0」「状態B=1」「状態C=2」みたいになりますね。この0~2の値を人間が割り当てるのは面倒ですので、状態遷移表をエクセル等で作成して、それをCSVファイルにして状態遷移コンパイラーのようなツールに入力して作ったりします。 この過程で、状態遷移が決定できないような矛盾があれば、エラー表示が出てきたりします。
お礼
ご回答ありがとうございます。 >、眠りをカウンタで表現し、眠り攻撃を受けたら100をセットし、時間の流れに合わせてカウントダウンして、0になったら回復とかすればいいと思います。 つまりその(眠り)カウンタがフラグの代わりってことですか? カウントが0ならフラグでいうところのfalseで0より大きい値のならtrue >兵士オブジェクトが健康な状態Aから、眠り攻撃を受けたら「眠りフラグ」が設定された「状態B」に移行し、さらに毒攻撃で「毒フラグ」がセットされて「状態C」に移行 もし「状態C」から眠りフラグが解除された場合、毒フラグは残ったままなので例えば「状態D」とかになるのでしょうか? つまり全てのケースの状態遷移図を作成するということでしょうか?(そもそも状態遷移図とはそういうものかもしれませんが) >「眠りの状態のクラス」ってのは、私からみると変なクラス分けだと思います。 >普通に作成すれば、例えば兵士クラスに「眠りフラグ」を作るのではないでしょうか だとすると、No.21で回答して頂いた >大きな流れというか制御はステートマシンで構成し、実際のアクションで、共通的(横断的)な処理は関数化して、各ステートの中で呼び出せばいいように思うのですが・・・。 の「各ステートの中」が具体的にどこのことなのかが分からないのですが、 やっぱりこれはクラスになるんじゃないんですか? >攻撃を受ける順序により状態が別になる場合は、遷移図でそれぞれ別のステータス移動になるように設定します。 なるほど、そこまでの仕様にするなら大変なことかもしれませんが たしかにそうする必要がありますね。自分はとりあえずそこまでは考えてないですけど
- hogehoge78
- ベストアンサー率80% (433/539)
■Application_Model_Objectの意義について とりあえず長いので、「Object」としておきますね。 Object > Element >Spell Object > Actor > Player という継承関係を作っておいたのは、型が同じものと出来るように、ですね。 if($player instanceof Object){ //これはtrue } if($player instanceof Actor){ //これもtrue } といった感じ。 一応Objectは、データベースのレコードだよ、という意味が注入されていて Actorは、登場人物だよ、という意味が注入されている といった感じです。 そのElement(じゅもん、どうぐなど)ともActor(主人公、モンスターなど)とも別のデータをデータベースから引っ張る場合にも Objectを継承した、 Object > Weapon みたいな感じのものにしたほうが、便利かなと思いました。 Objectクラスに、__getや__setのようなマジックメソッドとか基本的な実装をして、 ActorやElementにその固有情報、Spellでさらに呪文の固有情報を入れたり、Monsterクラスにモンスター共通の情報(autoTargetメソッドとか。)を入れ、さらに、 Monster > Boss という継承クラスで、ボスの固有情報(ザキが効かないとか)を入れてやるって階層構造ですかね。 Object、と書いたから分かりづらかったですかね。 Application_Model_Record とかですかね。 ORマッパーとして、Doctrineなんかを触ってみると、ちょっとわかるかもしれません。 ■自分としてはただフレームワークを眺めてても分からないことだらけ そうですね。ZendFrameworkは、フレームワークとしては、ソース追うのがちょっときびしかったですね。 CodeIgniterやCakePHPのほうが、追いやすいかもしれません。 でも、ZendFrameworkの良いところは、必要な部分だけライブラリとして使える、というところで、 一つ一つのライブラリが疎結合なため、ライブラリ単位だったら、比較的ソースも追いやすいので自分がこれからやろうとしている事に近いライブラリでも眺めてみると、ひらめくこともあるかもしれません。 また、wordpressのようなCMSとか、何かの完成品のソースを眺めてみるのもまた何かを得るチャンスです。 ■基本的なことがある程度理解できてれば、毎回同じようなことでの質問の回数も減らせそうな感じがするんですけど 結局はそういった理解も、自分で体験していかないとなかなか覚えられないですし、何度も体験していれば、そこから派生で応用も出来るようになるのかな、と思います。聞いたり見たりしたことはとにかく実際に入力して実行してみないと、結構すぐ忘れちゃったりしますね。
お礼
ご回答ありがとうございます。 なるほど、いろいろなフレームワークを見てみるとたしかになんらかしらObjectから 派生してるパターンが多いですよね。継承しておいて損はないというかそれも自然な形かもしれません。 すいません、これも先ほどの時に質問しておくべきでしたが、 ActorはApplication_Model_Object_Actor なのになぜ PlayerはApplication_Model_Player なのでしょうか? PlayerもApplication_Model_Object_Player あるいはActorがApplication_Model_Actorとか。 もしかしたら細かいことかもしれませんが、 作るにあたってここをしっかり把握してないと先に進むことができなそうなので是非知りたいのですが。 ところでhogehoge78さんはsabelフレームワークというのをご存知でしょうか? http://sabel.php-framework.org/ 少し使ったことがあるのですが、これが結構良くて、 自分的にアプリケーションを作るにあたってたしかにその機能はあった方がいいよなぁというのがあるんですよね。 hogehoge78さん的にはこのフレームワークについてなにか感想はありますか?(急に言われてもあれだと思いますが^^)
- lv4u
- ベストアンサー率27% (1862/6715)
>>付加処理が違うので、前の2つの状態の付加処理も行ってもいいと思うんです。 なんかこういう感じでやっていけばいけそうな気がするんですけど、まあでも大変ですよね・・・^^ 大きな流れというか制御はステートマシンで構成し、実際のアクションで、共通的(横断的)な処理は関数化して、各ステートの中で呼び出せばいいように思うのですが・・・。 >>ところでlv4uさんは何かお勧めの書籍などはありますでしょうか? できればプログラム関係でお願いしたいのですが そうですね。書籍はいろいろ買っていますが、「プログラムがうまく作れない」と悩んでいるころに役にたったといえば、以下の本です。 ・ソフトウエア作法 木村泉訳 ・ソフトウェアの複合/構造化設計 G.J.マイヤーズ 前者の言語は時代性を反映して、フォートランですが、今でも十分に勉強になる内容だと思います。後者は、データ構造とロジックをどう組み立てればいいかを学ぶことができました。 それと有名なトム・デマルコ氏やワインバーグ氏の著作はどれもお勧めです。 そして、私が一番長く仕事で使ってきたのはC言語ですが、それを学ぶのに役立ったのは ・エキスパートCプログラミング ・Cコンパニオン アレン・ホラブ などです。 それから、Minixが話題になったちょっと後の頃のLinuxは、日本語が通らないソースが多かったので、ソースコードを読んで日本語が通るようにCコンパイラやシェルのソースを修正したりしていました。 プログラムの書籍を読むのも勉強になりますが、GNUライブラリーなど、凄腕プログラマさんの作られたソースコードはとても良い教科書になると思います。
お礼
ご回答ありがとうございます。 >実際のアクションで、共通的(横断的)な処理は関数化して、各ステートの中で呼び出せばいいように思うのですが・・・。 自分もたぶんそのように考えているのですが、 やっぱり複合状態(状態というより付加処理を与えるクラス)の単独状態との差別化が難しいというか厄介で 単独状態(つまり普通の状態)というのは、その状態がいつまで続くかというオブジェクト自身の存在カウントがあって class State_Sleep {// 眠りの状態クラス protected $count = 0; public function __construct() { $this->count = rand(1, 4); } // 後はそれぞれのアクションに対する付加処理をcase文などで分けて行う } それをステートマシンで管理する形を考えているのですが、 複合状態の場合はクラスとして用意するとしても、単独状態のようなカウントはないわけですよね。 「眠り」と「毒」の2つの状態を所有していた時に呼び出される class State_SleepPoison {// 眠り+毒の複合状態(というか付加処理)クラス // それぞれのアクションに対する付加処理をcase文などで分けて行う } というクラスを用意してあるアクションを実行する前に呼び出されたとしても、 それは「眠り」と「毒」を所有していれば呼び出されるわけで 「眠り」と「毒」どちらかが解除されれば自然とState_SleepPoisonの処理は実行されないわけです(つまり オブジェクトも作成されないし単独状態とは違ってプレーヤーが所有するものでもない) そもそも複合状態ってなんなんだ?っていうのがあって^^ 現実世界の人間における状態(例えば、風邪気味であったり頭が痛かったり眠かったり)はいろんな状態が混ざり合っていて それは定義することすらできない複雑なものなのかもしれないですけど、 例えばこういうゲームをプログラムで作成するとなると、ある程度大雑把になろうが定義できないと作れないわけで、 それを今が探してる段階というか(まあそもそも見当違いかもしれませんが・・・) ところで書籍を紹介して頂きありがとうございます。 さっそく参考にしてみたいと思います。
お礼
ご回答ありがとうございます。 なるほど、やはりそういう感じになりますか。参考になりました。 例えば(何でこんな例しか思いつかないんだろう・・)、 「筋肉が増した」状態(攻撃力がアップ) 「足怪我」状態(守備力がダウン) があってその2つとも所有していた場合、 「筋肉が増した」+「足怪我」(素早さがダウン) の新たに第3の状態(というかアクションに付加する処理)ができたとします。 これは素早さがダウンなので、名前的には前の2つの状態をあわせた名前ですが 付加処理が違うので、前の2つの状態の付加処理も行ってもいいと思うんです。 なんかこういう感じでやっていけばいけそうな気がするんですけど、まあでも大変ですよね・・・^^ ところでlv4uさんは何かお勧めの書籍などはありますでしょうか? できればプログラム関係でお願いしたいのですが