プロが教える店舗&オフィスのセキュリティ対策術

PHPの環境で試しているので、このジャンルでお願いします。

例えば、プレーヤー(Player)がいてプレーヤーには状態(State)があり
行動(Action)するとします。そして状態と行動は複数個あります。

[State]
・状態1
・状態2
・状態3

[Action]
・行動タイプ1
・行動タイプ2
・行動タイプ3

それぞれをどちらかを1つのクラスして設計(状態1クラスかもしくは行動タイプ1クラスなど)した場合に
どちらかはswitch文などで分岐して処理を記述しなければならないと思うのですか
どちらを基準にした方が良いのでしょうか?

例えば、
状態をクラスにするなら

状態1クラス{
function doAction(){
switch($this->action){
case 行動タイプ1: /* 行動タイプ1を実行 */ break;
case 行動タイプ2: /* 行動タイプ2を実行 */ break;
case 行動タイプ3: /* 行動タイプ3を実行 */ break;
}
}
}

行動タイプをクラスを基準にするなら

行動タイプ1クラス{
function doAction(){
switch($this->state){
case 状態1: /* 状態1に即した行動をする */ break;
case 状態2: /* 状態2に即した行動をする */ break;
case 状態3: /* 状態3に即した行動をする */ break;
}
}
}

そもそもこれはRPGの戦闘のシステムを自分なりに設計してみたいというところから始まっていまして、
ドラクエモンスータズの戦闘システムです。この戦闘システムで難しそうなところ(特技)は
・2回行動
・跳ね返し
・敵の行動を真似する
・次のターンまで必要とする(たかくとびあがる、など)
・攻撃でもない回復でもない攻撃補助でもないジャンル分けし難い特技
など他にもあるのですが、これらがあるだけで全く違う作りにしないといけないというか、とにかく難しくなると思います。
そこに複数の状態も絡んでくるのでどう設計すればいいのか悩んでいます・・・
できればそこらへんも含めてアドバイスして頂きたいです。

A 回答 (40件中11~20件)

■なぜ秒数という概念を取り入れる必要があるの


仰るとおり、別の言語で作った場合にも大体大枠はそうなるかなという想定で書いてみました。
PHPでも結構長い処理を行うプログラムを作るとき(何かのステータスを読んで別の何かに書きだしたりとか)
<?php
while(true){
}
?>
とか、無限ループにして、ある一定の状態遷移に着たらbreak;するなりexit;するなりしてループを終わらすという処理を行ったりします。
Apacheを通さない、PHPを素のスクリプトとして走らせる場合ですね。
その時に結構長い処理なのに、sleep入れないで走らせると、CPU使用率限界の速度でループするので、ある程度間隔をあけてやったりします。

■Viewクラスに関して
これは多分何でもいいと思います。今後も同じプログラムを作っていく、という事であれば何か特別な名前を付けてもいいと思いますが、今回はつくってみる、なので、単純に「View」でも、「Application_View」でも。
他で扱うものでないなら、面倒ですし、CakePHPのCoreクラスのように、「Controller」「View」「Model」という単純な名前でも良いと思います。

■ステートマシンに関して
>兵士クラス(プレーヤー)にステートマシンを所持させる形では駄目なんですか?
所持させますね。
<?php
class Solder extends Actor{

public function __construct(){
$this->statemachine = new SolderState($this);
}

}
?>
とか。

自分自身のメンバ変数に持たせ、ステートマシンに自分自身のインスタンスを渡してやる感じですね。
このActorに持たせる理由は、Actor自身が、Modelで、このModelのなかでControllerをもつので、そのControllerが使うModelというところです(MVC的にこれはいいのか、と言われると微妙な気もしますが。)

で、ステートマシンの実装部分ですが、もうすこしちゃんと書くと
<?php
class SoldierState extends StateMachine{
protected $fields = array(
'hoge', 'moge'
);
protected $mappings = array(
'XX' => array('hoge'=>1, 'moge'=>0),
'YY' => array('hoge'=>0, 'moge'=>1),
);
public function __construct(Actor $soldier){
$this->stateobject = $soldier;
}
public function onXX(){
return $this->stateobject->hogeAction();
}
public function onYY(){
return $this->stateobject->mogeAction();
}
public function execute(){
$result = array();
foreach($this->fields as $f){
$result[$f] = call_user_func(array($this->stateobject, "is".ucfirst($f))) ? 1 : 0;
}
foreach($this->mappings as $actid=>$check){
$isOK = true;
foreach($check as $key=>$flag){
if($result[$key] != $flag) break;
}
if($isOK) return call_user_func(array($this, "on{$key}"));
}
}
}

こんな感じです。XXとかYYは、ただのIDです。意味を持ちません。Hoge,Mogeが、Poison,Paraizeとかそういう奴です。
これはYamlとかCSVとかINIとかといった設定ファイルからクラスを自動生成したときに効果があるパターンですね。

■lv4uさん
PHPはネームスペースという概念を持たないので、SolderStateMachineとか、長い名前を必要とします。
他の言語だったら、
package app.state
class Solder extends Machine{
}
とかでいけるんでしょうけども。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。
なるほど、これも良さげな設計ですね。
ただプログラムの細かいことに質問があるのですが、
executeメソッド内の「$isOK」というのは何のために存在してるのでしょうか?
foreachのあとに$isOK = true;とされて、それ以降falseとかを注入した様子がないのですが。
あと「if($result[$key] != $flag) break;」は「if($result[$key] == $flag)」だと思うですが、
$resultの中にはis~が存在していれば1が入ってるわけですよね?
例えば
foreach($check as $key=>$flag){
if($result[$key] != $flag) break;
}
の$keyがmogeの場合に、$result[$key]=1 $flag=1ですからループから抜けて
onYYが実行されると思うのですが(つまりそれが期待する動作)
それともなにか自分の解釈に間違いがありますでしょしょうか?

それとNo.29であったというか今までの状態に対する基本的な考え方として

>ACTION_ID,METHOD,Hoge,Moge
>XX,hogeAction,1,0
>YY,mogeAction,0,1
>とかといった、遷移IDと、遷移時のメソッド名と、各フラグのONOFFを書いてやって

これは攻撃をする側の状態のみの状態遷移プログラムが作成されると思うのですが、
攻撃を受ける側の状態も考慮した場合はNo.29で提案されたテーブルはどのようになるか分かりますでしょうか?
自分の想像ではフラグが倍に増えACTION_IDがさらにもっと増えそうな気はしてるのですが

お礼日時:2011/02/12 03:49

No.27補足より



>>class State_Poison extends State {// 毒の状態クラス

という「毒の状態クラス」という考え方は、やはりおかしいと思います。
もちろん、目的のプログラムをどのようなクラスの集合体にするかということは、個人の考え方やプログラムの利用目的などにより、「こういうクラスにしないとダメである」ってルールはもちろん無いと思います。

が、今回の例で扇風機を兵士に置き換えて考えてみれば、兵士の状態の全てを把握するためにステートマシンを使うわけです。兵士クラスを作る過程で、「毒ってのもイロイロある。ただ麻痺するだけの催涙ガス程度のものから、目や口の機能を失わせるもの、すぐに死んでしまうものなどもある」と考えて毒の「状態」ではなく「毒」というオブジェクトのために「毒クラス」にしたほうがいいかな?と考えるのならわかりますけどね。

もちろん「すべての存在には、元になるクラスが絶対に必要である!」というクラス至上主義(クラス教条主義?)で作られるなら、「毒の状態のクラス」もありかもしれません。が、今回作成しようとしているプログラムでは必要以上にプログラムを複雑化させるだけというか、「クラスのためのクラス」のように思えます。


No.29のhogehoge78さんの回答より

>>PHPでオブジェクト指向でやるとしたら、このfanfsmfsm.cの中身全部を保有するStatusクラスをつくってやって、ステータスの変動の度にそこに登録してあるファンクション(プロシジャ?サブルーチン?メソッド?)を呼び出してやるってことですよね。

そうです。ただ、クラス化するならネーミングとしては、Statusクラスよりも、Soldierなど、もっとかっこいいというか、オブジェクト指向プログラムっぽい名前にしたいところではありますが。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。
う~ん、そうですか。なんで自分はクラスにしたいというか拘っているかというと
例えば毒の状態になった時に
Battle::addMessage($this->owner.'は毒の状態になった');
とか
解除された時に
Battle::addMessage($this->owner.'の毒は消え去った');
とかまあこれはメッセージだけですけど、なにか処理を加えたいと思ったわけです。
そうする場合にクラスにしてたほうが、まとまりがあるというか自分としては分かりやすい気がしたので。
public function enter()でその状態になった場合の処理を記述し
public function exit()でその状態が解除なった場合の処理を記述できますし
ほかにもいろんな場所(メソッド)を設けてもいいかもしれません。
それが本当に戦闘に必要かというのはありますが、もしこのような処理をクラスを用いないでやろうとした場合に
lv4uさんなら具体的にどこにこのような処理を記述するのでしょうか?


それとすいません、状態遷移表におけるhogehoge78さんが示した例の遷移ID(ACTION_ID)の確認なのですが
ちょうど状態と事象(イベント)が交差する場所が遷移IDという認識で良いのでしょうか?

┌──────┬──────────┬──────────┐
│事象(イベント)→│   通常攻撃     │    ギラ       │
│状態↓     │              │              │
├──────┼──────────┼──────────┤
│         │ここがhogehoge78さんの│例えばここなら     │
│   眠り   │例のonXX() やonYY()に│onSleepGira()とか   │
│         │相当する場所?     │              │
├──────┼──────────┼──────────┤
│         │ここで遷移先(状態)の│               │
│    毒   │変更があればそれも行う│              │
│         │              │              │
├──────┼──────────┼──────────┤

お礼日時:2011/02/11 17:32

■戦う場所というか戦う前の準備は先ほどの構造でいうとどのクラスで行うの


これは、先の回答で書いたソースを見ていただきつつで。
最終的に物を取得してその物の振り分けを行うのが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に組み込んで触ってみてますが、非常に感触は良いです。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。
>最終的に物を取得してその物の振り分けを行うのがControllerなので、
BattleControllerで全部もち回す感じでしょうね。
なるほど、たしかに「戦闘をする(戦闘処理)」というのはモデルではないですよね?
ちょっと(というかかなり)MVCに対する理解も曖昧なので
やっぱりこれも何かで勉強した方がいいですね・・・

>なんかしらのViewクラスを作り、
これはモデルが
Application_Model_Player
とかなので
Application_View~
という形になるのでしょか?(なんかそういうどうでもいい(?)ことばかり気になってすいません・・・)


>兵士クラスないにステートマシンのようなものを組み込んでやってね、ってことですよね
すいません、確認なのですがそもそも兵士クラスってhogehoge78さんのところの
Actorとかplayerとかとイコールですよね?
だとしたら兵士クラス(プレーヤー)にステートマシンを所持させる形では駄目なんですか?

class Actor{
protected $stateMachine = new StateMachine();
}

>Doctrineを使うのが良いかなと思います。
>ZendFrameworkに組み込んで触ってみてますが、非常に感触は良いです。
Doctrineですか、今後なにか機会があれば使ってみたいと思います。

お礼日時:2011/02/11 04:50

とりあえず、私のイメージした全体的な流れを書いてみました。


多分プログラムのエントリポイントのようなところで、ゲームが終了するまでループさせ続けると思います。
このループで、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();
}
}

?>
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。
なるほど(とは言ってますがまだちゃんと理解できてるわけではないです^^)
これも凄いですね。。こうもきちっと設計できるもんですね。
>1秒間に何回描画するか
なぜ秒数という概念を取り入れる必要があるのでしょうか?
1アクションに対して一回の描画ではないのでしょうか?
もしかしたらweb(PHP)以外でのことを想定しているのでしょうか?


それとすいません、No.29に対する質問をここでするのはあれかもしれませんが
後で疑問が出てきたものでここで質問させてください。
>遷移IDと、遷移時のメソッド名と、各フラグのONOFFを書いてやって
onXXやHogeMogeを具体的(毒状態やそれに対するアクション時の付加処理など)な記述で
書いてもらえないでしょうか?onXXやonYYが何をさすものなのかがいまいち分からないですが

お礼日時:2011/02/11 05:17

>>なのでfanfsm_on1at0関数などの各ステートが自分としてはそれぞれ個別にクラスで作った方がいいんじゃないかと思いまして。



うーん、そのような考え方をされるってことは、takagoo100さんはオブジェクト指向プログラミングというものが分かっていないのではないでしょうか?

生成された関数をまとめて、例えば「兵士」というクラスにとりまとめるならわかりますが、fanfsm_on1at0という1つの関数を取り出してクラスにするのは全く意味がないことだと思います。

また、別の観点からいえば、このステートマシンはfanfsmstateが重要な役割を持っている変数ですが、グローバル変数になっています。もしfanfsm_on1at0のような関数単位でクラスを作るならば、それぞれのクラスが、このグローバル変数によって強く結び付けられてしまいます。それは、嫌われものであるgoto文を使ったプログラムよりもたちの悪いものになりますし、クラスを元に独立した複数のインスタンスを作ることができなくなり、オブジェクト指向プログラムとはいえないしろものになってしまいます。

>>もしそうではないとすると、lv4uさんの仰る各ステートというのは
具体的にどこに(どのファイル、どのクラスの中)用意すれば良いのでしょうか?

サンプルでは、ステートを表現(保持)しているのは、「fanfsmstate」の持つ値になります。各ステートから何らかの攻撃(原因)等で別のステートに移動するときに、ひとつながりのアクションというかなんらかの処理をさせたりするわけですが、それらを「fanfsm_on1at0」などの関数の中に記述することになります。

なお、このサンプルプログラムでは、敵味方の複数兵士を表現しようとすると、とりあえずはfanfsmstateを配列にして対応するコーディングになると思います。(各種フラグなどがあれば、それらも配列にする必要があります)
でも、スマートに作成するなら、兵士クラスのような形にまとめるほうがいいと思います。

この回答への補足

補足を借ります。。

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で管理してやればいいと思うのですが
こういうクラスの使い方もおかしなやり方なのでしょうか?

補足日時:2011/02/11 03:29
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。
すいませんちょっと勘違いしてたかもしれません・・・(たぶん)
fanfsm_on1at0などのひとつひとつのアクションをクラスにしたいと思っているのではなく

┌──────┬──────────┬──────────┐
│事象(イベント)→│   通常攻撃     │    ギラ       │
│状態↓     │              │              │
├──────┼──────────┼──────────┤
│         │眠っていて攻撃できない│眠っていて呪文と唱える│
│   眠り   │というメッセージを追加 │ことができない(以下略│
│         │するアクション      │              │
├──────┼──────────┼──────────┤
│         │例えば攻撃力を増す  │何も付加処理を    │
│    毒   │処理を施すアクション  │行わない(スルー)   │
│         │              │              │
├──────┼──────────┼──────────┤

自分がクラスにしたいと思ってるのは上の表の行(状態)で、
例えば

お礼日時:2011/02/11 03:28

>>例を何か一つ具体的なプログラムで記述していただけないでしょうか?


そうすればイメージできそうなのですが

以下のURLには雑誌インターフェース2001年5月号で紹介された状態遷移表コンパイラの記事を一部修正して著者の方がアップされています。
http://homepage3.nifty.com/~masumoto/embedded/st …

以下のURLからは、状態遷移表コンパイラと簡単なサンプルがダウンロードできます。
http://homepage3.nifty.com/~masumoto/embedded/st …

ダウンロードした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で対応したり、オープンクラス(モンキーパッチング)が強力な拡張性を提供しているようです。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。
実際に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

お礼日時:2011/02/10 16:29

■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さんはどこら辺が良いと感じたのでしょうか。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。
>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つのアプリケーションを作成するにあたって「そういう機能があったら楽だよなぁ」って感じで
自分に合う感じでした。使用するライブラリを選択することによって自由度も調節できるようだし(この機能については自分の理解力じゃよく分からないですけど^^)
全然有名じゃないですけどこれ結構凄いんじゃないのかなぁと思いまして

お礼日時:2011/02/10 02:58

>>つまりその(眠り)カウンタがフラグの代わりってことですか?


カウントが0ならフラグでいうところのfalseで0より大きい値のならtrue

そうです。ただ眠りから覚めるのが時間ではなく、なにかのイベント(味方からの解毒剤投与など)と設定するなら、0か1の単なるフラグになるでしょうね。

>>もし「状態C」から眠りフラグが解除された場合、毒フラグは残ったままなので例えば「状態D」とかになるのでしょうか?

それは、「どういう設定に自分がしたいか?ストーリの整合性がそれで納得できるか?」などで決まると思います。つまりシンプルに「状態B」に戻るだけにしたいか、それとも「状態D]にするかは、ゲームデザイナとしてどちらが良いと思うかで決めればいいと思います。
もちろん、「状態D」があるほうが遷移表は大きくなり、複雑度は上がりますね。

>>「各ステートの中」が具体的にどこのことなのかが分からないのですが、
やっぱりこれはクラスになるんじゃないんですか?

それは、状態Aとか状態Bの値を保存する「状態変数」の値で表現することになります。状態が変わるごとに、「状態変数」に「状態A」とか「状態B]の値を代入していくわけですね。で、状態変数とイベント値(原因)の組み合わせによって、次の移動先の状態が、状態遷移コンパイラが生成したテーブルの値に従って決まってゆくことになります。
まあ、このあたりは、実際にステートマシンを動かしてみないと理解が難しいかもしれません。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。
>つまりシンプルに「状態B」に戻るだけにしたいか、それとも「状態D]にするかは、
ここでいう「状態B」というのは眠りフラグが設定されていた(つまりtrue)状態をさすと思うので
眠りフラグが解除(false)されているのに「状態B」に戻るというのはおかしくないですか?
この場合の、「状態B」って
状態の前後の推移関係なく眠りフラグが設定されたら「状態B」になるのか
それとも
健康な状態Aから、眠り攻撃を受け「眠りフラグ」が設定されたら「状態B」になるってことですか?

>共通的(横断的)な処理は関数化して、各ステートの中で呼び出せばいいように思うのですが・・・。
「~の中で呼び出す」というのは例えば関数の中とかで

function func1() {
// この中で呼び出す
}

というイメージなのですが、
>状態Aとか状態Bの値を保存する「状態変数」
それが変数だとしたら中で呼び出すというのがいまいちよく分からないのですが、
関数ならさきほどのようにその中でなにか別の処理を呼び出せますよね?
lv4uさんが考えいてる
>実際のアクションで、共通的(横断的)な処理は関数化して、各ステートの中で呼び出せばいいように思うのですが・・・。
の例を何か一つ具体的なプログラムで記述していただけないでしょうか?
そうすればイメージできそうなのですが

お礼日時:2011/02/10 01:56

>>class State_Sleep {// 眠りの状態クラス



「眠りの状態のクラス」ってのは、私からみると変なクラス分けだと思います。
普通に作成すれば、例えば兵士クラスに「眠りフラグ」を作るのではないでしょうか?

>>複合状態の場合はクラスとして用意するとしても、単独状態のようなカウントはないわけですよね。

複合状態は、クラスとして表現されるのではなく、ステータスとして表現します。状態遷移図で兵士オブジェクトが健康な状態Aから、眠り攻撃を受けたら「眠りフラグ」が設定された「状態B」に移行し、さらに毒攻撃で「毒フラグ」がセットされて「状態C」に移行するって感じですね。
攻撃を受ける順序により状態が別になる場合は、遷移図でそれぞれ別のステータス移動になるように設定します。

またカウンタも兵士オブジェクトに持たせておけばいいでしょうね。大きなループを作成して、その世界の「時の流れ」をカウントし、個々の兵士オブジェクトは、それに同期して、それぞれの持つカウンタを増やせばいいと思います。

例えば、眠りをカウンタで表現し、眠り攻撃を受けたら100をセットし、時間の流れに合わせてカウントダウンして、0になったら回復とかすればいいと思います。

>>そもそも複合状態ってなんなんだ?っていうのがあって^^

上にも書きましたが、それは状態変数によって表現されます。兵士の例でいえば「状態A=0」「状態B=1」「状態C=2」みたいになりますね。この0~2の値を人間が割り当てるのは面倒ですので、状態遷移表をエクセル等で作成して、それをCSVファイルにして状態遷移コンパイラーのようなツールに入力して作ったりします。
この過程で、状態遷移が決定できないような矛盾があれば、エラー表示が出てきたりします。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。
>、眠りをカウンタで表現し、眠り攻撃を受けたら100をセットし、時間の流れに合わせてカウントダウンして、0になったら回復とかすればいいと思います。
つまりその(眠り)カウンタがフラグの代わりってことですか?
カウントが0ならフラグでいうところのfalseで0より大きい値のならtrue

>兵士オブジェクトが健康な状態Aから、眠り攻撃を受けたら「眠りフラグ」が設定された「状態B」に移行し、さらに毒攻撃で「毒フラグ」がセットされて「状態C」に移行
もし「状態C」から眠りフラグが解除された場合、毒フラグは残ったままなので例えば「状態D」とかになるのでしょうか?
つまり全てのケースの状態遷移図を作成するということでしょうか?(そもそも状態遷移図とはそういうものかもしれませんが)

>「眠りの状態のクラス」ってのは、私からみると変なクラス分けだと思います。
>普通に作成すれば、例えば兵士クラスに「眠りフラグ」を作るのではないでしょうか
だとすると、No.21で回答して頂いた
>大きな流れというか制御はステートマシンで構成し、実際のアクションで、共通的(横断的)な処理は関数化して、各ステートの中で呼び出せばいいように思うのですが・・・。
の「各ステートの中」が具体的にどこのことなのかが分からないのですが、
やっぱりこれはクラスになるんじゃないんですか?

>攻撃を受ける順序により状態が別になる場合は、遷移図でそれぞれ別のステータス移動になるように設定します。
なるほど、そこまでの仕様にするなら大変なことかもしれませんが
たしかにそうする必要がありますね。自分はとりあえずそこまでは考えてないですけど

お礼日時:2011/02/09 16:34

■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とか、何かの完成品のソースを眺めてみるのもまた何かを得るチャンスです。

■基本的なことがある程度理解できてれば、毎回同じようなことでの質問の回数も減らせそうな感じがするんですけど
結局はそういった理解も、自分で体験していかないとなかなか覚えられないですし、何度も体験していれば、そこから派生で応用も出来るようになるのかな、と思います。聞いたり見たりしたことはとにかく実際に入力して実行してみないと、結構すぐ忘れちゃったりしますね。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。
なるほど、いろいろなフレームワークを見てみるとたしかになんらかしら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さん的にはこのフレームワークについてなにか感想はありますか?(急に言われてもあれだと思いますが^^)

お礼日時:2011/02/09 03:34

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!