デザインパターンで安全にタスクシステムを構築できるということについて
具体的に否定できるヤツは一人もいないなw
ファビョって否定してるやつが数人いるようにみえるがID変えてるだけのアホが一人なんだろうな。
>>823 んーちょっとよくわかんない
> このゲームオブジェクトが本来受け取りたいイベントというのは行動開始時間で、
> VSYNCイベントとの依存関係は本来はない。
より高い抽象度では、「時刻Xになったら行動開始」だけども、
実装レベルでは、結局のところVSYNC割り込み単位でのフレーム更新という
「必要」が先立ってるんでしょ?
コルーチンや継続のようなものが使えるのなら、VSYNC単位で微分せずに
もっと自然に書けるだろうけども、
「その設計では」VSYNCイベント通知が「必要」だから、そう作ってあるのでは?
設計の良し悪しとか、無駄が多いとかはいくらでも言えると思うんだけど、
「オブザーバとは違う」ということにこだわることにそんなに意味があるようには
俺には見えないけどねえ
オブザーバは単なるパターンだから、
オブザーブする対象は別にVSYNCイベントだろうがタイマだろうが構わないわけでしょ
>>824 安全には無理でしょ
明示的な引数がない時点でプログラムの作法云々なんてもう
気にしない組み方だと思うけどね
update();
で動くのと
update(jiki,teki,tama,unko);
で動くのとじゃ
プログラムとしては絶対に下のがいい
上は結局、その関数で何が必要なのか要素がまったくわからない
アクセスしているものを知るには中身のプログラムを読むしかないし
趣味プログラマにはワケワカメw
ごった煮リスト+メッセージプロシージャでこれからも頑張っていきます( ^ω^;)ゞ
>>826 > プログラムとしては絶対に下のがいい
あんた、どんだけ素人なんだ…。
本当にプログラムが1行でも書けるのか。
素人目にも、上( update(); )のほうがよさそうに見えるのだが、俺はおかしいのか。
いや、やはり下の方がいいね。
下手にクラス変数に他のオブジェクトへのポインタを持たせたり、グローバル変数(シングルトン含む)を使ってしまうと、
オブジェクト間の関連が複雑になって、わけがわからなくなる。
各メソッドに処理に必要な情報(メディエータとか)渡すようにすると、
末端のオブジェクトは実にシンプルになる。
JavaのAWT/Swingでよく使うpaint(Graphics g)みたいにね。
>>828-829 素人もいいところだな
じゃ、上(update();)でアクセスしてるデータは何と何?
update関数の中をすべてみないとわからないでしょ?
これがグローバル変数の最大の害
下(update(jiki,teki,tama,unko);)ならjiki,teki,tama,unkoインスタンスにアクセスしていて
仮にソースでグローバル変数を使わないという決まりをきちんと守っていた場合
update関数によってこれら以外のインスタンスが変更されることは絶対にないという
ソースを読む側の確信がもてる
これはバグを追う場面でも強力に働く
これがキチンと守られているかどうかで
開発期間やプロジェクトで出る総バグ数がまるで違う結果になる(俺の経験)
だからタスクシステムはバグが増える
ヘッダファイルにしても同じ
必要なヘッダを必要な分だけインクルードしてるソースなら影響するクラスがわかりやすい
が、タスクシステムはごった煮ソースになるので
ほぼ全クラスを一括インクルードしなければ動かないとかかなり糞
これではバグがいつまでたっても減りませんわw
安全性ってのは2つの観点があるんじゃね?
・メモリ上のオブジェクト寿命の保証
・データ確定性の保証
データ確定性というのは「今このデータ更新中だからアクセスしないでね」っていう期間に
ちゃんと排他制御出来てること(更新してるやつ以外はアクセスしないで待ってること)。
上(update();)はスマートポインタ使うから安全って言いたいのかもしれないけど、
スマートポインタが解決できるのはメモリ上のオブジェクト寿命の保証のみ。
データ確定性の保証は別途対策が必要。
それに比べて下はオブジェクト間の関係性が単純化できているから
データ確定性の保証もしやすい。
だから下が良い。
>>831 >>ほぼ全クラスを一括インクルードしなければ動かないとかかなり糞
お前タスクシステムで組んだこと一度もねーだろ
>>833 あるし、ちゃんとタスクシステムでゲームを3本ぐらいコサエタよ
どれも途中参加だったけどかなり苦痛だったな
>>834 じゃあソースの分け方が下手なだけだ、タスクシステムだからって理由でそんなことには絶対ならない
>>835 ふーん、じゃそれでいいよ
ただ、そうしないとごった煮判別のところでやりにくいだけだと思うけどね
どうせわからないんだから一括でインクルードしちゃえばいいじゃん
って俺は思ったけどね(タスクシステムプロジェクトに参加してるときは)
グローバル変数も当然のように使いまくりになるしいまさらそんなところ争っても
大した違いにならないのでこの話(ヘッダの話)はここで終わりにしてくれ頼む
>>830 > JavaのAWT/Swingでよく使うpaint(Graphics g)みたいにね。
Graphicsのような汎用contextを引数として渡すのは構わない。
826はupdate(jiki,teki,tama,unko);のようにそのゲーム専用でかつそのタスク専用のcontextを渡しているわけで、
そんなものを渡すとその部分を抽象化できなくなる。
>>831 > じゃ、上(update();)でアクセスしてるデータは何と何?
> これがグローバル変数の最大の害
グローバル変数を使うだなんて一言も言ってないし、実際updateメソッドのなかでは
グローバル変数なんかにはアクセスしない。
> update関数によってこれら以外のインスタンスが変更されることは絶対にないという
> ソースを読む側の確信がもてる
そんなことは全く保証されないし確信が持てるわけではない。
updateメソッドでは自由に他のインスタンスにアクセスできる。
>>836 そうやって一括でインクルードするからわからなくなるって自分で言ってんじゃないの?矛盾してるよ、
ソースがしっかり分かれていればupdateの引き数で判断しなくても何がインクルードされてるかで判断できるよ。
>>832 > 上(update();)はスマートポインタ使うから安全って言いたいのかもしれないけど、
そんなことは俺は言っていない。
updateの引数として、汎用性のないcontextを渡すと呼び出し部分も呼び出される部分も抽象化できない。
それが最大の弊害であり、やるべきではない理由である。
>>833 実際831はド素人。「タスクシステムはごった煮ソースになる」の時点でド素人。
template<class Context> class Task
{
virtual void update(Context context);
};
こう抽象化しておいて、std::list<Context> tasksに対してforeachでupdateを呼び出すだけ。
「ほぼ全クラスを一括インクルードしなければ動かない」なんでド素人もいいところ。
>>836 > ただ、そうしないとごった煮判別のところでやりにくいだけだと思うけどね
「ごった煮判別」ってなんだ?
> グローバル変数も当然のように使いまくりになるしいまさらそんなところ争っても
タスクシステムを使ってようが、グローバル変数なんてひとつも使わないぞ。
あんたみたいなド素人、仕事に参加されると迷惑きわまりない。
俺が教えてやるから、何がしたいのか詳しく書いてみな。俺がお手本を見せてやんよ。
一括インクルードとかグローバル変数使いまくりとか、
単にそのチームが酷かっただけのように思えるが。
ID:rIovvj90は途中参加って言ってるし。
ゼロからタスクシステムを組んでみたら、どういうものか分かると思う。
そうすればグローバル変数とか関係ないことがわかる。
>>841 そういう理由があっても、それがすべてと思って批判するのはどうかと思う。
いや発言のレベルが流石に低すぎるから
ただの釣りか、BASICやHSPぐらいしか知らないド素人なんでしょ
OOPを知らないのは確実だけど、Cでもちょっと抽象化したコードを
読み書きしたことはなさげ
>>839 だからそれってソースをみたときに何が渡されてるのか確定できんの?
単純に
>>826の問題は解決してないように見えるけど?
>>832 何故そこでスマートポインタが出てくるのか意味不明なんだが、
updateで引数がないということは次のようになっていると俺は想定している。
template <class Context>
class Task
{
public:
Task(const Context& context_) : context(context_) {}
virual void update();
private:
Context context;
};
それぞれのタスクはこのTaskクラスから派生させる。
updateメソッドでは、this.context に対してアクセスするので、スマートポインタなんか使う必要はないし
グローバル変数も使わない。
>>846 >、this.context に対してアクセスするので、
これが何か知らないんだけど説明してくれない?
>>844 > だからそれってソースをみたときに何が渡されてるのか確定できんの?
何が言いたいのかよくわからん。
「何が渡されてるか」って、updateの引数がないのだから、何も渡されていない。
C++でメソッド呼び出しなら、thisが渡されている。
と確定できるが?
>>848 仮に呼び出し側で
>>862のような違いを
見つけたいソース(=引数を明示的にしたい)を書きたい場合にはどうするの?
>>847 >>、this.context に対してアクセスするので、
> これが何か知らないんだけど説明してくれない?
templateになっていて、ゲームを作りたいなら、そのゲームでタスクにとって必要なcontextを入れるんだ。
例えば、シューティングゲームで敵の弾だけ列挙したくて、それをタスクリストからdynamic_castなどで型判別するのが
嫌なら、このcontextに
std::list<Task*> enemy_shot_tasks;
というstd::listを持たせておく。そうすれば、敵の弾に対してforeachしたりできる。
描画するのにGraphicsクラスが必要なら、それもこのcontextに持たせておく。
>>849 > 仮に呼び出し側で
>>826のような違いを見つけたいソース
> (=引数を明示的にしたい)を書きたい場合にはどうするの?
呼び出し側は、単に、一定のタイミングごとにupdateを呼び出すだけの存在であって、
呼び出し側に違いが現われるのは論理的におかしく、設計がまずい。
だから、呼び出し側にそのような違いを見いだそうとすること自体がおかしい。
>>849 詳細で個別的なメソッド呼び出しが必要で適している状況ではそうすればいいし、
そうするだけだよ
上のupdate()のような例では、呼び出し側は、オブジェクト間の差異を
意識したくない、統一的に扱いたいから、より抽象度が高い方法でやるだけさ
差異を選択的に無視して、問題をそれに適した/適切な抽象度で
扱えるようにすることが「設計」でしょ
車の運転をしているときに、エンジンに使われているネジのことなんぞ
いちいち気にしたくはないわけだ
>>842 俺もそう思うよ。
批判すべきはチームの設計・開発体制であって、タスクシステム自体の是非はまた別の問題。
ID:rIovvj90はそのへんの問題点の切り分けが出来てないんだな。
坊主憎けりゃ袈裟までってやつか。
途中参加した3本が全部別々のチームなんだったら、流石にその不運には同情するが。
俺には 826 で何がしたいのかいまひとつわからないのだが、タスクシステムから
callbackがかかるタイミング(ビデオゲームならV_SYNCごととでも思っとくれ)
以外のタイミングで、他のタスクの更新メソッドを呼び出してやる必要がある場合を
考えてみる。
普通、ゲームでそういうコードが必要になることは稀ではあるが、そのとき、
updateメソッドのなかで他のupdateを呼び出す、
> update(jiki,teki,tama,unko);
のようなことがしたい、というなら動機はわからなくはない。
この場合、jiki,teki,tama,unkoの4つのオブジェクトが生きていることを
保証してやる必要がある。
C#やJavaのような言語であれば、オブジェクトが生きていることは
保証されるのでDisposeされていないかをチェックする。
C++なら、オブジェクトが生きていること自体が保証されないので例えば、
Task chainに対して、
tasks.isContain(jiki)
のような判定が必要である。
std::listに対してこれをやると結構のオーバーヘッドになり得るので、
特定の型のstd::listを用意するか、もっと高速に判定できるコンテナ
(std::mapなど)をContext(
>>846)に持たせておきこれをチェックするのが普通。
>>846 そのContextとやらでポインタ使うことになるっしょ。
そこでスマートポインタ使うから安全って言うのかと思った。
そうでなければどういう意味で安全といったの?
std::list< Task<Enemyとか> *> m_bullets;
init(*jiki,*teki,*tama,*unko){ ←こことか、
m_bullets.push_back(new Task<Enemyとか>(jiki,teki,tama,unko)); ←ここで外部データへのポインタ使ってる。安全じゃない
}
enterFrame(){
foreach bullet (m_bullets) {
bullet.update(); ←ここで渡すのを初期化時に移しただけ。状況はむしろ悪化してる
}
}
#template分かんねえwww
#newまわり恐らく書き方違うだろうけど意図は大体通じるよね?
>>856 > そこでスマートポインタ使うから安全って言うのかと思った。
> そうでなければどういう意味で安全といったの?
すまんが、俺は「安全」という言葉は一言も言ってない。
「安全」と言ったのは俺じゃない。
俺は、オーバーヘッドがあるから、スマートポインタを使わなくて済むところでは
使わないし、この手のタスクをコントロールするのにスマートポインタは持ち出さない。
またオブジェクトが生きているかどうかの判定については
>>855 で書いた。
ただ、
> Contextとやらでポインタ使うことになるっしょ。
については、このContextが保持しているstd::listなりstd::mapなりは、
Taskの解体の時に自動的にここから該当タスクのポインタをremoveするので
このstd::listなりstd::mapなりにあるオブジェクトが実在して、かつ生きている
ことだけは保証される。だから
>>855 の方法で問題ない。
いま読み返してみたら、855の書き方が悪かったので以下補足&修正とお詫び。
> Task chainに対して、
> tasks.isContain(jiki)
> のような判定が必要である。
だけど、tasksから一度removeされて、同じメモリがオブジェクトに割り当てられる
ことがあるので、このjikiというのは、オブジェクトのアドレスやポインタではなく
incremental ID(いわゆるhandle)だ。
誤解を生じさせる書き方をして済まない。
contextって俺は長命オブジェクトをイメージしてるんかと
思ったけど、違うの?
マネージャなりメディエータなり
よくある子に親への参照を持たせているようなケースは、
親が先に死なないデザインなら安全
オブジェクト間の相互参照グラフが無軌道に複雑化する場合は、
確かにGCなしでは酷いことになるだろうし
IDルックアップを毎回やるのは遅そうだな
>>852 その状態でなんで
>>826,830-832の内容を否定するの?
俺が書いたのは
>>831のみだけど
その状態をダメだって言ってるのが
>>826,830-832の内容なわけ
ソースの記述で引数を明示的に表現しなければならないって話なのよ?
タスクシステム使うと完全に
>>826,830-832の問題をどうにもできないでしょ?
ソースの記述で引数を明示的に表現できる?
(ま、別にする必要がないって主張をもってるからタスクシステム派なんだろうけどさw)
>>859 > contextって俺は長命オブジェクトをイメージしてるんかと
> 思ったけど、違うの?
もちろん、Contextオブジェクトの寿命はあらゆるTaskよりは
長いことは保証しなければならないし、保証されると仮定してプログラムする。
でもタスクフレームワークとして見たとき本質的なのはそこじゃなくて、
そのアプリ固有のcontextをTaskから切り離してTaskの呼び出しを抽象化するのに
ここをtemplate classにする必要があるということ。
> よくある子に親への参照を持たせているようなケースは、
> 親が先に死なないデザインなら安全
>>858 のようにhandleを持たせるのが嫌なら、boost::shared_ptrを用いて
std::list<boost::shared_ptr> tasks;
としておくというのは一理あるんだな…でも、
> オブジェクト間の相互参照グラフが無軌道に複雑化する場合は、
循環参照するようなケースはshared_ptrではどうにもならないし
GCがある言語で書いたほうがスマートではあるな。
> IDルックアップを毎回やるのは遅そうだな
実際はオブジェクト数の上限は決まっていると仮定して良くて、
hash tableを使って実装するので、その部分は実はそんなに遅くはないよ。
たいていはhash値を計算してメモリを一度lookupするだけで済むので。
だから、ID lookupで実装するのがスマートだと思うな。
>>860 > ソースの記述で引数を明示的に表現しなければならないって話なのよ?
何故そんな必要があるのか、そのメリットがどこにあるのか俺には全く理解不能だ。
なんとなく論点のズレが分かった
ID:rIovvj90
は、タスクマネージャからのupdate()呼び出し
以外の関数呼び出しは一切存在しない(あらゆるオブジェクト間の相互作用を
それで解決する)プログラムを仮定しているように見える
他の人は(ID:9d5EHsE6)は、誰もそのようなものは仮定していない
普通に引数を使うところでは勿論使うわけで、
あくまでジェネリックなupdate()を書く時の作法として
どっちが適切かという話をしていただけでしょ
>>863 違うってなんで曲解するんだ?
update関数の中でアクセスするインスタンスがソースの記述から
わからないのがダメだって言ってるんだ(もちろんダメだと思わなければそれまでだがw)
>>826,830-832で言ってることがすべてだって
これがわからないって経験値が低いぜ
わかってやってるってのも俺的にはちょっと考えにくいんだけどね
>>865 > update関数の中でアクセスするインスタンスがソースの記述から
> わからない
???
C++だからthisポインタ越しに、オブジェクト参照をたどるだけでしょ
参照はメンバ変数に持っているのだし
何が「わからない」のかが分からない
>>865 > update関数の中でアクセスするインスタンスがソースの記述から
> わからないのがダメだって言ってるんだ
何度でも言うが、そんなものはわかる必要がないんだ。
Javaのupdate(Graphics g)メソッドひとつにしても
updateメソッドのなかでGraphics以外のオブジェクトにアクセス
することは当然あるし、それを禁じる方法はないし、
update(Graphics g)というシグネチャを見てGraphicsにしかアクセス
しないと判断することは出来ないし、
実際そんなコーディングスタイルで書く必要もないし、
そんなコーディングスタイルを強要されるとまともにプログラムを書けない。
すべてのタスクファイルのヘッダをincludeして
それが普通だと思っているID:rIovvj90みたいなド素人と
話していても時間の無駄なので俺は、ID:8CtHI7i5と勝手に話を続ける。
■ ID lookupの周辺。
typedef TaskHandle unsigned int;
struct HashEntry
{
TaskHandle handle;
Task* task_ptr;
};
typedef HashTable HashEntry[max_of_hash_entry];
Taskクラスのコンストラクタでは、TaskHandleとしてincremental IDを付与して
HashTableに登録するコードを書く。Taskクラスのデストラクタでは、HashTable
からremoveするコードを書く。
TaskHandleから具体的な型に変換するのは
template<class TaskClass>
TaskClass* TaskHandleToPtr(TaskHandle h)
{
Task* p = HashTableからhのTaskを取得();
if (typeid(p)!=typeid(TaskClassのインスタンス))
return null;
return (TaskClass*)p; // このcastは安全
}
こうなるな。
>>869 の続き。
TaskHandle enemy1;
TaskHandle enemy2;
に対して
EnemyTask* enemyTask1 = TashHandleToPtr<EnemyTask>(enemy1);
EnemyTask* enemyTask2 = TashHandleToPtr<EnemyTask>(enemy2);
if (enemyTask1!=null && enemyTask2!=null)
{
// これらのタスクは生存している
update_something(enemyTask1,enemyTask2);
}
という生存チェックが必要になるな。仕方ないと言えば仕方ないが、
使う前に必ず必要なのがちょっとうざい気はする。
>>869 まあ概ね想定どおりっすね
俺の考えを言うと、
・C/C++なら、そもそもオブジェクトの相互参照グラフを整理する
のがベスト
・問題が不可避な場合にはGC, スマポ, ID管理などの解決方法があるが、
スマポは良く知られているように巡回参照には無力(そもそもグラフが
複雑化したから欲しくなったはずなのだから、それでは意味が無い)
ID管理が一番シンプルでポータブル、仮に全てが利用できると
仮定した場合、性能や特失に関しては研究の余地あり(ただ、ゲームでは
いずれにせよGCは利用しにくいでしょうね)
ってとこかしら
>>871 >(ただ、ゲームではいずれにせよGCは利用しにくいでしょうね)
これは何故?俺は、C#で商用ゲームプログラムを書いたけど
別にGCがあるので困るということはなかったのだが。
どんなことが問題だと思ってるの?
>>872 優秀な世代別GCで、決してワールドがストップしないと
確信できるケースでは、問題ないかもしれません
その確信が持てないなら、ゲームでは難しいかもしれません
ということです
>>873 > 優秀な世代別GCで、決してワールドがストップしないと
> 確信できるケースでは、問題ないかもしれません
それが確信できないとそもそもXNAでプログラミングなんて出来ないわけで…。
あ、もちろん、大きなリソースを解放するときは要注意なのでそのタイミングは
なるべくコントロールしてやる必要があるけども。それはC++でも一緒なわけで…。
>>870 の続き。
結局、TaskHandleという汎用型があまりよくない気がするな。
TaskHandle<T>にして、operator T*() を用意して暗黙で変換できる
ようにしたほうがいいかもね。
これなら
TaskHandle<Enemy> enemy1;
TaskHandle<Enemy> enemy2;
に対して
try {
// これらのタスクが生存していなければ暗黙の変換のときに例外が飛ぶ
update_something(enemyTask1,enemyTask2);
// update_somethingのシグネチャは、update_something(Enemy*,Enemy*)
} catch {}
とか。こっちのほうが少しシンプルかも。これは、どう? > ID:8CtHI7i5
>>874 いやその、「C#でゲームプログラミングは不可能である」という主張ではないので
誤解無きよう
そういう意味では、「GCはゲームではありえない」と読み取れるので、
強い主張すぎたかな、訂正します
>>876 いやまあ、C#で弾幕シューティング作ったら、やっぱり
GCが変なタイミングで動いてときどきカクカクなるから
object poolingするコードをtemplateで書きまくったとか
そういうことは日常茶飯事なので、GCに対して世間の目が
冷たいのはよくわかってるつもり。
このスレの趣旨に見合う形で話を戻すと、GCつきのほうが
タスクのinteractionは楽に書けることは書けるけども、
それでもC++でも
>>875 のように書けるなら、さほど手間は
変わらないと思う。
>>866 それは別クラスのポインタの保持をするってこと?
まあ、ようは関数読んだときにアクセスしてるクラスがわからないようなのはダメってことよ
当然そういうメンバに別クラスのインスタンスのポインタを保持させるようなのも禁止だよ
>>867 それができるんだよ
グローバル変数・関数の使用禁止(当然スタティックとかも禁止)とか徹底して
メンバに別クラスのインスタンスの保持禁止にするのをちゃんと徹底すれば
後、可能性があるのは引数とメンバ変数ぐらいだろ?
こうするとバグが比較にならないほど減るぞ
自分で一度この方式でプログラム組んでみてほしいね
決して面倒臭くない
むしろ開発が進むにつれこっちのが作りやすいとわかるはず
しかも読む人間もソースを追いやすいからバグの発見も早い
>>878 > メンバに別クラスのインスタンスの保持禁止にするのをちゃんと徹底すれば
> 後、可能性があるのは引数とメンバ変数ぐらいだろ?
別クラスのインスタンスの保持をすると何故いけないと思うんだ?
親オブジェクトが子オブジェクトのstd::listなんかを持っているのは普通であり、
当たり前だろ?これを禁止するなら、どのオブジェクトがある親オブジェクトに
対する子オブジェクトを持っていると言うんだ?具体的にソース書いて見せてくれ。
>>857 了解しました。スマートポインタは無かったことに。
あと「メモリ上のオブジェクト寿命の保証」が対策済みで問題ないのは了解しました。
それはそれとしてゲームオブジェクト間の「データ確定性の保証」
もしくはオブジェクト間関係の見える化の推進度合いの観点では
依然として「引数を明示的に表現する」ほうがupdate(void)よりも分かりやすい
と考えてるんですが、その点どうお考えですか?
update(void)メソッド呼び出しの中で他のupdateを呼び出す場合に
update(jiki,teki,tama,unko);と書くこと自体はその是非はともかく存在は想定されてますよね。
以下のように
Scene::update(void);
Player::update(void);
Enemy::update(void);
Bullet::update(void);
と全てをタスク化するのではなく、タスクの数はもっと減らして
Scene::update(void) { ←タスクはこいつだけ
player.update(jiki,teki,tama,unko);
enemy.update(jiki,teki,tama,unko);
bullet.update(jiki,teki,tama,unko);
}
としてやればだれがいつjiki, teki等を使っているのかが見やすくなる。
また、playerはTaskを継承しなくて良くなるのでPlayerクラスだけを抜き出して
test_player_class.cppとかいうコードを作ってPC上でクラスの単体テストすることもかなりやりやすくなる。
思うにTask導入しちゃうとタスクフレームワーク使わなければ単体テストすら満足にできなくなってるんじゃないかと。
update(void)を自動的に定期的に呼び出すのは要らないと言っているのではなく、
それを使った上でさらにそのメソッド内でupdate(jiki,teki,tama,unko)を導入することの利点について考えたこと有りますか?
きちんと比較したことありますか?という辺り
>>878 ひょっとして
> メンバに別クラスのインスタンスの保持禁止にする
の「インスタンス」には「std::list<EnemyTask*>」のようなものは
含まれないのか?これも立派な「インスタンス」だと思うんだが。
何かコンピュータ用語以前に、ID:rIovvj90は日本語が不自由で
何が言いたいのかよくわからない。
そもそも俺にしても
>>875 のようなスタイルで書いているわけで
別のTaskのインスタンスなど(compositionでの)保持はしない。
しかしHandleは保持する。それは、参照するのに必要だからである。
>>880 あなたの想定しているように、ゲーム画面ではプレイヤと弾と敵が
存在するが、タイトル画面ではそんなものは存在しないという場合、
タイトル画面を構築に不要なものを排除して、タスク間のinteractionを
なるべく簡素で見える形にしたいという動機は当然ありえる。
それでも
> Scene::update(void) {
> player.update(jiki,teki,tama,unko);
> enemy.update(jiki,teki,tama,unko);
> bullet.update(jiki,teki,tama,unko);
>}
は良くない。この書き方はド素人くさい。
>>882 >は良くない。この書き方はド素人くさい。
良くない理由はなんで?
>>882 そう、結局そこに帰結する。
>この書き方はド素人くさい。
この1点の是非
メンバもグローバルも駄目なら、
update(jiki,teki,tama,unko)を呼ぶ側のクラスは
jiki,teki,tama,unkoをどこに持ってるんだ?
main関数のローカルに持って、延々と渡していくのか?
まあ、そんなことより、
ID:9d5EHsE6のタスク周りの実装をもう少し聞きたいな。
新しいContextを追加すると、Taskのリストがひとつ増えるんだよね?
そのリストは誰がどういうふうに持つの?
>>882 そういう動機なら
class SceneXXXContext
{
GameGlobalContext* globalContext;
Jiki jiki;
Teki teki;
Tama tama;
Unko unko;
}
こう書いて、
public SceneXXX<GameGlobalContext> : public Task<GameGlobalContext>
{
virtual void update(const GameGlobalContext& globalContext)
{
this->grobalContext = globalContext;
my_task.update(this.context);
}
TaskList<SceneXXXContext> my_task;
SceneXXXContext context;
};
こう書くべきだろう。
>>886 の続き。さらに抽象化して、
template <class GlobalContext,class SceneContext>
public Scene : public Task<GameGlobalContext>
{
virtual void update(const GameGlobalContext& globalContext)
{
this->grobalContext = globalContext;
my_task.update(this.context);
}
TaskList<SceneContext> my_task;
SceneContext context;
}
こうとか。
ID:rIovvj90,ID:8N26Dxd2 の設計と上の設計との大きな違いは、このクラスが
使い回せるし、他の具体的などのTaskクラスにも依存していないということだ。
>>885 > ID:9d5EHsE6のタスク周りの実装をもう少し聞きたいな。
> 新しいContextを追加すると、Taskのリストがひとつ増えるんだよね?
増えない。Contextはタスク階層ごとに必要となるだけ。cf.
>>886
>>880 > オブジェクト間関係の見える化の推進度合い
詳細を「隠したい」場合と「見たい」場合があるはずなので、
その場合はどっちですか、という話になるだけでは。
ネジ一本のツクリまで気にしなければ運転できないような車は設計の欠陥で、
「見える」ことが常に良いことである、という発想は誤りです。
OOPのカプセル化やポリモーフィズムの意味を理解してください。
>>885 直接のインスタンス参照ではなく、事実上参照代わりに使えるハンドル(ID)を
持っていると書いてあったと思うけど。
勿論それは寿命が微妙な短命オブジェクトの話で、
寿命管理が明確な長命オブジェクトは直接参照でしょう。
>>889 ネジ一本のツクリまで気にして設計してある車でなければ運転したくないです。
運転手の話したいの?
設計者の話でしょ?
>>890 クラスAを作るとき、クラスAを利用する人間は、クラスAのユーザになる。
どちらも同じ人間かもしれないが、クラスはユーザに対しては詳細を
隠蔽するように働き、汚い泥仕事はその中に閉じ込め、問題の局所性を
高める。
本当にネジを気にしなければならないコードを局所化するわけだ。
OOPどころか、構造化プログラミングの基本中の基本ですので、
一から勉強しなおして下さい。
>>891 > 問題の局所性を高める。
そのためには、あるオブジェクトが実装のために利用するオブジェクトの
種類について
>>882 のように制約をかけたいという欲求はあるのでは。
だからこそ俺は、template <class Context>とContextを用いて、
こいつにしかupdateメソッドのなかではアクセスしないという制約
のかけ方を示し
>>886 、さらにそれを抽象化して
>>887 、
何故、update(jiki,teki,tama,unko);と書くのが良くないのかに答えた。
意味論的にupdate()をどう捉えるかでしょう
update()の抽象的意味が、「VSYNCを通知する」ということであれば
(というか多分そうだと思うのですが)
jikiだのtekiだのtamaだのunkoだのといった情報は抽象的意味とは無関係な
代物、ということになります。
引数jiki, teki, tama, unkoを取るupdate()の「意味」は何でしょうか?
>>888 Task<GameGlobalContext> のリストは TaskList<GameGlobalContext> で、
Task<SceneXXXContext> のリストは TaskList<SceneXXXContext> なわけでしょ?
Contextが増えたらTaskListが増えてるじゃん。
Contextの種類数=リストの種類数ってことなんじゃないの?
>>893 > update()の抽象的意味が、「VSYNCを通知する」ということであれば
> jikiだのtekiだのtamaだのunkoだのといった情報は抽象的意味とは無関係
ああ、それは正しいね。通知するだけなら、引数は必要ないね。
ただ、世の中のイベントハンドラがすべてそうであるように、
そのハンドラのなかで使いたいであろうもの(マウスイベントならばマウスの状態、
キーボードイベントなら押されたキーの情報)を引数として渡すのは常識的なことなので
今回のケースも、updateなら何らかのContext(それは描画のためのものであったり、
そのSceneで使うものであったり)が付随していてもそれはおかしくはないと思うけど。
>>895 ええ、そうですね
Cならば、コールバックの第一引数として、いずれにせよthisポインタ代わりのもの
を使うのが普通ですし
ところで、違う目的のものに同じ"Context"という用語を用いているのは
混乱の元のように見えます
引数渡しをする場合には、テンポラリな情報のジェネリックラッパーを仮定していて
依存性をオブジェクト生成時に注入している場合には、長命オブジェクトへの
参照を仮定しているのではありませんか?
>>885 メンバはダメじゃないだろ
ただ、別クラスのインスタンスのポインタの保持が禁止なだけ
それとupdateの例ではなにやらゲームのオブジェクトのクラスに
同じゲームオブジェクトのjiki,teki,tama,unkoを突っ込んでるようなソースに
なってしまっているがこれも間違いでありえない
仮にシーンクラスなんてのがあったらそこのメンバとして
jiki,teki,tama,unkoをもつ
ちなみに俺が最初に書いたソースのupdateはゲームオブジェクトの更新処理のつもりの
updateだったが引数でゲームオブジェクトを入れてるのは間違い
必要な情報だけ入れる
まあ、この場で話す分にはあんまり気にしなくておk
>>894 俺は、
>>888 では
>>885 の
「新しいContextを追加すると、Taskのリストがひとつ増えるんだよね?」
の「リスト」を何故かstd::listのようなものではなく、表のようなものを
指しているのかと勘違いしてトンチンカンなことを書いてしまった。
これについては、謹んでお詫びしたい。
なるべくならこういうときは「std::listのようなもの」と書いてもらえるとありがたい。
ただ、
> Contextが増えたらTaskListが増えてるじゃん。
Contextに対応したTaskListが必要とは限らない。
class TitleSceneContext : GlobalContext {};
class GameSceneContext : GlobalContext {};
class PlayerSelectSceneContext : GlobalContext {};
このように(あとで拡張できるように各Sceneに対応するContextのclass
だけ生成しておいて)、実際は
TaskList<GlobalContext> taskList;
こう書くかも知れない。こう書くメリットは、TaskListの種類を減らして
生成されるコードを縮めることにある。
>>896 > ところで、違う目的のものに同じ"Context"という用語を用いているのは
> 混乱の元のように見えます
どれとどれが違う目的?
また、どう呼ぶべきだと思う?
ここで言うContextはtemplate class名なのでtemplate <class T>のTみたいなもので
それほど厳密な意味や名前は必要ないと思うのだけど。
(あったほうが理解したり、読みやすかったりするんだろうけど)
また、updateメソッドはContextとメンバ変数の状態のみによって、
次のContextの状態を作り出すことが出来るので、DFAと見なせる。
だもんで、CFTG(context-free tree grammar)とかの "context" という用語を
ここに持ってくるのはおかしくないと思ったんだけどな。
>>900 番号はずれてない。
>>886 の「そういう動機」の「そう」が指すのは、
>>882 の 「あなた」から「したいという」まで。
わかりにくくてすまん。
>>898 その場合、呼ばれる側はdynamic_castか何かで必要なContextの型に変換するんだよね。
呼ぶ側がどのContextを渡すかはどうやって判断するの?
俺、実は主婦なので、いまから買い物にいかなきゃならん。
そんなわけで、明日まで、返答できない。
半額セールがはじまる時間なんだ…。
>>902 > その場合、呼ばれる側はdynamic_castか何かで必要なContextの型に変換するんだよね。
この場合は、しない。
place holder的に、Contextの派生型だけ宣言しておく(
>>898 )という使い方がありうると言いたかっただけ。
902を混乱させたようで悪かった。些末な問題なので、忘れてもらって構わない。
>>899 > (あったほうが理解したり、読みやすかったりするんだろうけど)
無論、議論の参加者にとってはそのほうが良いわけですから、
その点の指摘に過ぎませんよ。
> また、updateメソッドはContextとメンバ変数の状態のみによって、
> 次のContextの状態を作り出すことが出来るので、DFAと見なせる。
updateメソッドの引数としてのcontextという名前が非常に典型的であるのは
その通り(特にその中身がopaqueで、その意味内容をその場で論じたくない場合)
ですが、一方クラスメンバの名前としては、「context」は適切で意味が
明快な名前であるとは言いがたいでしょう。
タスクシステム信者いたんだ?w
なんかタスクシステムがヤバクなるとスレを流しにかかるよねw
>>826,
>>830-832からの一連のレスは面白いから次のスレにも貼ろうw
>>901 Thanks!
なぜド素人くさいと言ったか分かった。あなたはSceneについてのみ話している。
私はゲームオブジェクトについてのみ話している。そこがまず違う。
まずupdate()という名前のメソッド群があったらそれらを抽象化してまとめて扱いたくなるのは当然。
でもupdate()に引数付けただけの段階ではまだ途中なんだよ。その次の段階に進むことでうまみが出る。
次の段階というのはupdate()を複数のメソッドに分割できるようになること。
つまり
update(jiki, teki, tama, unko);
は下のように分割できる。
Jiki::hit_check();
Jiki::set_hp();
Jiki::get_hp();
Teki::get_hit_area();
Teki::get_attack_point();
Tama::get_hit_area();
Tama::get_attack_point();
...
それらをこうやって使う。
foreach t (teki) {
if (jiki.hit_check(t.get_hit_area())) {
jiki.set_hp(jiki.get_hp() - t.get_attack_point());
}
}
(続く)
(続き)
最後にSceneと統合すると
SceneXXX::update(void) { ←タスクはシーンだけ
foreach t (teki) {
if (jiki.hit_check(t.get_hit_area())) {
jiki.set_hp(jiki.get_hp() - t.get_attack_point());
}
}
foreach t (teki_tama) {
if (jiki.hit_check(t.get_hit_area())) {
jiki.set_hp(jiki.get_hp() - t.get_attack_point());
}
}
...
}
こうなる。
Scene::update(void) は
>>886 >>887 みたいなやり方で実装するのはアリ。
というかむしろそうしないと確かに「ド素人くさい」。ただそれはSceneにおいての話。
ゲームオブジェクトはSceneとは別枠で扱うべき。つまりゲームオブジェクトはタスクにすべきでない。
理由はゲームオブジェクト間は通信が多いので引数渡しを使用可能としたいため。
タスクでは引数渡しができないのが嫌。
私がupdate()で引数扱えるようにした方がいいって思ってるのはゲームオブジェクトについての話。
ただ、update()という名前で引数を扱うのはタスクシステムを脱出する過程で現れる過渡的、一時的な姿。
複数のメソッドに分割された後はupdate()なんて名前は消えてなくなる(たまたま別の意味付けで残ることはありえるけど)。
Sceneはタスクで良いのでupdate(void)で構わないが、
ゲームオブジェクトはタスクにすべきではないので(過渡的な姿という意味で)update(jiki,teki,tama,unko); が良い。
買い物から戻ってきた。
>>905 > ですが、一方クラスメンバの名前としては、「context」は適切で意味が
> 明快な名前であるとは言いがたいでしょう。
俺の書いた、
>>886では、そのメンバ変数の宣言は、
SceneXXXContext context;
となっているけど、
A) SceneXXXContextのメンバ名がcontextとなっているのが適切ではない
と言いたいのか、
B) SceneXXXContextという名前が十分わかりやすいものではない
と言いたいのか、どちら?
俺としては、SceneXXXContextの変数名がcontextなのは、
大きなクラスでない限りはそれくらいの省略は許されるべきだと
思うんだが。また、SceneXXXContextは、SceneXXXを構築するのに必要な
文脈という意味で、意味明瞭だと思うのだが。
>>909 ああ、申し訳ないっす。後のほうの話を念頭に置いてなかった。
そっち見りゃ、少なくとも「なんのつもりか」は分かるでしょうね。
>907-909
> 理由はゲームオブジェクト間は通信が多いので引数渡しを使用可能としたいため。
> タスクでは引数渡しができないのが嫌。
タスクでは引数渡しがなんで出来ないの?
template <class Context>
void update(const Context& context);
このcontextって引数渡しでない?
それとも、これはいわゆるタスクではないの?
「オブジェクト間の通信」って何がしたいのかよくわかんないのだけど、
Handle経由で参照すれば(
>>875)いいじゃん。
どうせ、ゲームオブジェクトにしてもそのオブジェクトの
生存の確認は必要なんだろうし、
>>875 のコードと何ら変わらないんじゃ?
>>912 実質グローバル変数渡ししかできないから。
foreach t (teki) {
if (jiki.hit_check(t.get_hit_area())) {
jiki.set_hp(jiki.get_hp() - t.get_attack_point());
}
}
これならそもそも生存してるやつ同士しか参照が発生しないから生存確認必要ないかと
>>912 明示的なって意味じゃねぇの?
だいたいそんな十把一絡な引数あってもしょうがねぇし
引数はソースコードに明示的にわかるようなものでないと俺は意味がないと思ってる
グローバルにしてテキトーに渡したほうが速いじゃん
>>913 > 実質グローバル変数渡ししかできないから。
何故、そう思ったのかは知らないが、俺の書いたソース(
>>869-870,875 )では
グローバル変数なんか経由していないし使ってもいないし、使うつもりもないのだが。
だから俺には、何が言いたいのかさっぱりわからない。
そもそも
>>908の「ゲームオブジェクト」が何を指すのかがわからない。
「タスク」とどう違うのか。
そもそも、俺の「タスク」(
>>846)とは「タスク」そのものが違うような気がする。
>>914 > 明示的なって意味じゃねぇの?
> だいたいそんな十把一絡な引数あってもしょうがねぇし
templateは実体化して使うだから
compile-timeには型が確定するし十分明示的じゃん。
templateが「十把一絡な引数」だと思うなら、お前はstd名前空間使用禁止な!
> グローバルにしてテキトーに渡したほうが速いじゃん
グローバル変数なんか使わないっちゅーの。
本当、ID:rIovvj90は、タスクシステム以前にプログラミング自体、ド素人なんだな。
そもそもまずいのはグローバル変数・関数じゃなくて
グローバル・スタティックの変数・関数、別クラスのインスタンスのポインタの保持、
シングルトン、アドレスジャンプ等を使用することによって
明示的な関連が全く見えなくなることだけどね
別にそこにこだわってないならいいんじゃないの?
どう組んでもw
>>915 > EnemyTask* enemyTask1 = TashHandleToPtr<EnemyTask>(enemy1);
これが実質グローバル変数。
これがダメ。
これをやめるべき。
これがグローバル変数とほぼ等価だと認識できないの?
俺の「ゲームオブジェクト」は
「自機と敵機の座標が重なっていたら自機にダメージ与える」という処理において言えば
「自機」と「敵機」がそれぞれゲームオブジェクト。
俺の「タスク」は
class Task {
public:
virtual void update(void)=0;
}
的なやつを実装したもの。update(Context?)=0;でもいいけど。
>>918 >> EnemyTask* enemyTask1 = TaskHandleToPtr<EnemyTask>(enemy1);
> これが実質グローバル変数。
> これがグローバル変数とほぼ等価だと認識できないの?
出来ないね。
それがグローバル変数に見えるならあんたの目が腐ってるとしか言いようがない。
ID:rIovvj90 と ID:8N26Dxd2 はいつも時間的に連続して出てきて、
二人とも極端にレベルの低いド素人プログラマなんだが、これは
同一人物なのか?
それとも、こんなド素人が二人も都合良くこのスレに出没してるのか?
>>918 >「自機と敵機の座標が重なっていたら自機にダメージ与える」という処理に
> おいて言えば「自機」と「敵機」がそれぞれゲームオブジェクト。
タスクとの違いがわからない。
> 俺の「タスク」は
> class Task {
> public:
> virtual void update(void)=0;
> }
> 的なやつを実装したもの。update(Context?)=0;でもいいけど。
「ゲームオブジェクト」とやらも、そのTask派生クラスでいいじゃん。
何がまずいの?
>>919 違うっつの
しかも、グローバル変数が問題じゃなくて
明示的な関連(アクセスかな?)が見えないのがまずいって言ってるだろ
関数の実行に必要なもんをソースみてわかるようにしろ
簡単だろ?
何もむずかしいこといってやしねぇだろ?
>>921の書き込みが ID:rIovvj90 でされているんだが、ID:8N26Dxd2 のほうでログインしなくて
良かったのかい?
まあ、それはいいとして、ひょっとしたら、ID:8N26Dxd2 は、
static std::map<TaskHandle,Task*> taskHandleToPtr;
static Task* TaskHandleToPtr(TaskHandle h)
{
return taskHandleToPtr[h];
}
こういうソースを想定して、taskHandleToPtrがglobal変数で
この変数を経由してTaskHandleとTask*が結びついているから
enemy1がglobal変数だと主張しているのかも知れないが、
よほどのド素人でもない限り、上のような馬鹿なソースは書かない。
実際は、
> EnemyTask* enemyTask1 = TaskHandleToPtr<EnemyTask>(enemy1);
このように実装できるように書くなら、Task基底クラスに
TaskHandleToPtrメソッドを持たせてTask派生クラスの初期化のときに、
HashTable (
>>869) のポインタを渡す。
だから「実質グローバル変数」ではない。
>>921 > 関数の実行に必要なもんをソースみてわかるようにしろ
についてだが、
> EnemyTask* enemyTask1 = TaskHandleToPtr<EnemyTask>(enemy1);
の「関数の実行(コンパイル?)に必要なもん」は、EnemyTask*で、
これはEnemyTaskクラスの定義してあるheaderが必要だ。
これは「みてわかる」と思うんだが、何をそんなに問題にしているのか
それがわからない。
いま気になったので、ID:rIovvj90 と ID:8N26Dxd2 の書き込みを
全部読み返したのだが、この二人(一人?)は極端に日本語が不自由だな。
この二人は専門用語の使い方もかなり間違いだらけで、
何が言いたいのか理解に苦しむ。
本当にこれで社会人なのか…。
というのはさておき、わかってないのはこの二人(一人?)だけみたいなので
IDも変わったことだし、俺はもう寝る。
>>924 文体が全然違うのに病気だろそれはw
ちなみに俺はID:rIovvj90ね
君たち、タスクシステムの話をしなさい。
素人だとかプロだとか、技術と関係ない感想は取り除いて話しなさい。
ID:8N26Dxd2 です
>>919 グローバル変数の嫌なとこは
プログラムのどこかで変数の内容が変更される可能性があるのにそれが把握できないことでしょ
「ポインタを全タスクにばらまいてどこからでも全データ参照できる機構」の嫌なとこは
プログラムのどこかで変数の内容が変更される可能性があるのにそれが把握できないことでしょ
俺には両者はほぼ等価でどちらも忌むべきものにしか思えないが
もし「グローバル変数」と呼称するのが受け付けられないだけなら呼び方はどうでもいいけど
技術者として設計に不吉なにおいを感じないか?
>同一人物なのか?
違うっつの
>>920 >タスクとの違いがわからない。
object: 物体, もの, 実物; 対象
task: コンピュータが処理する仕事の最小単位
*goo辞書より
>何がまずいの?
タスク間の通信は実質グローバル変数渡しでしかできないのにゲームオブジェクト間は通信たくさん必要でミスマッチなとこがまずい
#話全く関係ないけどgoogleびびったw
>>919 > 同一人物なのか?
人を陥れるために真似る奴もいるぜ
あまり単純に考えない方がいい
つか>826にあるコードの断片だけを見て
どっちがいいか判断できるなんてエスパーとしか思えん。
>>826 引数でどうしても縛りたいならupdate()のなかでupdate(jiki,teki,tama,unko)呼べば解決じゃねーのか。
何が問題になるんだ。
>>830 本題とは関係ないかもしれないが、
JavaのCanvas#paint(Graphics g)で、
g以外に何にもアクセスしないで何を描画するつもりなんだ?
Canvasインスタンスが持ってる他のオブジェクトへのリファレンスを経由して
他のオブジェクトにアクセスするからpaint(g)でうまくいくのであって、
決してgにしかアクセスしないわけじゃない。
826の下がいいと言うなら、
paint(Graphics g)だとCanvasが何をpaintするかわからないので、paintメソッドは
paint(Graphics g, jiki, teki, tama, unko)にしなければいけなくなる。
だからpaint(g)を例に出すのは間違ってる。
>>927 > 「ポインタを全タスクにばらまいてどこからでも全データ参照できる機構」の
> 嫌なとこはプログラムのどこかで変数の内容が変更される可能性があるのに
> それが把握できないことでしょ
std::list<Task*> tasks;
に対してforeachでupdateを呼び出していくだけで、
この呼び出し側しかtasksにはさわれないのに、
何故「ポインタを全タスクにばらまいて」いることになって、
「どこからでも全データ参照できる」んだ?
各Taskは、このtasksはいじれないんだぜ?
根本的に何か勘違いしてないか?
> タスク間の通信は実質グローバル変数渡しでしかできないのに
何度でも言うが、俺のソース(
>>875)はグローバル変数を経由している
わけでもなく、グローバル変数を使っているわけでもなく、大きなオーバーヘッドも
なく、他のタスクオブジェクトのメソッドを呼び出せているが?
>>875 の書き方が気にいらなければ、
std::list<boost::shared_ptr<Task> > task;
というタスクリストに対して
boost::weak_ptr<Enemy> enemy;
こうなってると考えてもらっても良いが。
いずれにせよ、グローバル変数は使ってないし、
「どこからでも全データ参照できる機構」もないし、大きなオーバーヘッドもないが?
932 :
929:2009/02/01(日) 02:06:31 ID:7YDjPQ+X
省略しすぎた
> EnemyTask* enemyTask1 = TaskHandleToPtr<EnemyTask>(enemy1);
上のやつで引数はとってくるって意味ね4つだから
Jiki* jiki = TaskHandleToPtr<Jiki>(jiki1);
Teki* teki = TaskHandleToPtr<Teki>(teki1);
Tama* tama = TaskHandleToPtr<Tama>(tama1);
Unko* unko = TaskHandleToPtr<Unko>(unko1);
this->update(jiki,teki,tama,unko);
>>922 俺が実質グローバル変数と言う判断基準は
「グローバル変数そのものではなくとも、
グローバル変数のように動き、グローバル変数のように扱えるのならそれはグローバル変数だ」
これ。
だから実装方法はとくに想定していないし、する必要も無い。
まぁグローバル変数という呼び方がだめならその呼び方はやめるよ。
問題点は
>初期化のときに、HashTable (
>>869) のポインタを渡す。
こんな感じでポインタをプログラム中にばらまくと
プログラムのどこかで変数の内容が変更される可能性があるのにそれが把握できなくなること。
「メモリ上のオブジェクト寿命の保証」は対策できてるから問題ないとして、
「データ確定性の保証」の観点から見た場合はどうなの?ってこと。
もしかしたら
>>886 の
> Jiki jiki;
> Teki teki;
> Tama tama;
> Unko unko;
を扱う記述例はどんな感じになる? と聞いた方が手っ取り早いのかもしれないが
>>924 >極端に日本語が不自由だな。
うん
ねもいにょ。俺ももう寝るにょ
>>933 >> Jiki jiki;
>> Teki teki;
>> Tama tama;
>> Unko unko;
> を扱う記述例はどんな感じになる? と聞いた方が手っ取り早いのかもしれないが
class SceneTask : protected Task
{
protected:
Jiki jiki;
Teki teki;
Tama tama;
Unko unko;
public:
SceneTask();
virtual ~SceneTask();
virtual void update();
};
何かこれで不満でも?
>>933 936の補足。
Jiki,Teki,Tama,UnkoがTask派生クラスとは限らないなら、そのへんはケースバイケース。
場合によっては
>>886 のように書いたりもする。
Jiki,Teki,Tama,UnkoがTask派生クラスであるなら、
boost::weak_ptrを用いるか、TaskHandle(
>>869) を用いるか、
>>886 のようにContextに持たせるか。
方法はいろいろある。
>>929 >>932 それ良いと思う。
>>934 >>908 の
>Sceneはタスクで良いのでupdate(void)で構わないが、
>ゲームオブジェクトはタスクにすべきではないので(過渡的な姿という意味で)update(jiki,teki,tama,unko); が良い。
は、つまり、
>>929 >>932 と同じようなこと言ってる。
急いで付け加えるとあくまで過渡的な姿に限定して言えばの話なので、その後
update(jiki,teki,tama,unko);
は
> foreach t (teki) {
> if (jiki.hit_check(t.get_hit_area())) {
> jiki.set_hp(jiki.get_hp() - t.get_attack_point());
> }
> }
> foreach t (teki_tama) {
> if (jiki.hit_check(t.get_hit_area())) {
> jiki.set_hp(jiki.get_hp() - t.get_attack_point());
> }
> }
> ...
みたいに変化すべきと思うけども
意味というか、論点がわかんねぇ……
>>938 その引用部分は、何のつもりだかはある程度は想像はつくが、
update(jiki, teki, tama, unko)
はどこでも呼んでいない
「update(jiki, teki, tama, unko)が欲しい」という主張じゃなかったの?
その引用部分のコードを、(引数のない)「update()」の中に
記述できない理由は何?
>>938 > ゲームオブジェクトはタスクにすべきではない
俺には、この根拠がいまだにわからない。
V-SYNCに応じてupdateを呼び出されたり、描画のために呼び出されたりするのだから、
少なくともTask派生クラスであるべきだと思うのだが。
それとも、その自機、弾、敵は描画には関係ないのか?
それとも、描画に関係あって、Task派生クラスではあるが、foreachでぶん回したいから
std::list<敵*> のようなものがいると言っているのか?
>>936-937 ども。不満は着目点がずれたまま議論してることです。
Jiki,Teki,Tama,UnkoがTask派生クラスじゃないという前提で私しゃべってます。
まず
void SceneTaskSTG::update() {
update(jiki, teki, tama, unko);
}
こういう書き方が良い。急いで付け加えると、なぜなら
void SceneTaskSTG::update() {
foreach t (teki) {
if (jiki.hit_check(t.get_hit_area())) {
jiki.set_hp(jiki.get_hp() - t.get_attack_point());
}
}
foreach t (teki_tama) {
if (jiki.hit_check(t.get_hit_area())) {
jiki.set_hp(jiki.get_hp() - t.get_attack_point());
}
}
...
}
こう進化させていけるから。
ド素人くさいとか泥臭い書き方が嫌という場合は劣化と受け取るかもですが。
あーそうか
ゲームオブジェクトのステートは
相互に依存していることが往々にしてあって、独立しているとは限らないから
おのおのについて「自分のステートを更新しろ」という号令を発する
モデルが常に適切とはいえない、という主張か
もう少しランクが上のクラスで(相互に関連した)ゲームオブジェクトのの
ステート更新をまとめて行う設計であれば、その配下のオブジェクトが
号令(VSYNC通知)を受け取る必要は無いし、むしろそれは無駄だと
そういう意味だと俺は解釈した
>>941 Task派生クラスではないならタスクではないのだから、好きなように書けばいいじゃないか。
それらは、いま議論しているタスクシステム(タスクフレームワーク?)とは何ら関係ないのだから、
タスクシステムの書き方に準拠する必要はないだろう。
しかし、どう見ても自機、敵、弾は描画の必要がありそうなのにTask派生クラスじゃないが
わけわかんないし、俺のタスクの書き方をどうして、タスクと関係のないプログラムの書き方を
持って否定されなきゃならんのか、まったくわけがわかんない。
>>940 >それとも、その自機、弾、敵は描画には関係ないのか?
関係ないっすね
Task派生クラスでも無いです
foreachでぶん回したいのはあります。
std::list<敵*> のようなものは要ります。
>>942 それは、俺も理解しているし、たぶんみんな理解している。
本人はそれをうまく説明する言葉を持ち合わせていなかったようではあるが…。
しかも、もしその理解が正しければ、ID:wxGi2uACの反論は誰に対する反論にもなっていない。
>>944 >> それとも、その自機、弾、敵は描画には関係ないのか?
> 関係ないっすね
なにー!それなら、いかにもシューティングみたいで、いかにも、描画します、みたいなものを例に
持ってくるのがわけわかんない。しかも接触判定とか、もろに描画考慮しているように見えるじゃん。
それなら最初から、
「主人公の体力」「主人公の生命力」「主人公の知力」(それぞれ描画は不要)
とか、そのくらいの説明は欲しいよ。
長々とつきあって損した。
>>943 >Task派生クラスではないならタスクではないのだから、好きなように書けばいいじゃないか。
Yes. 全くその通りです。
>それらは、いま議論しているタスクシステム(タスクフレームワーク?)とは何ら関係ないのだから、
>タスクシステムの書き方に準拠する必要はないだろう。
ここだったんですね、すれ違いの原因。
このスレではタスクシステムを使う=自機、弾、敵はTask派生クラスに絶対すべき、それこそがタスクシステム
という人が多かったのであなたもそういう考えの人だと誤解してました。でもそうではなかった。
この勘違いは私の落ち度です。ごめんなさい。思えばそういうそぶりは節々に出ていた。
あと、描画は別途foreach作ります。
void SceneTaskSTG::update(context) {
...
context.dev3dg->draw_model(jiki_gazou, jiki.x, jiki.y); ← 描画
foreach t (teki) {
context.dev3dg->draw_model(teki_gazou, t.x, t.y); ← 描画
}
...
}
> 俺のタスクの書き方をどうして、タスクと関係のないプログラムの書き方を
> 持って否定されなきゃならんのか、まったくわけがわかんない。
タスクの書き方というより自機、弾、敵をTask派生クラスにすることを否定したいと思ってました。
そのためにタスク使わなくても自機、弾、敵を扱えることを示していたつもり。
でもあなたはそもそもそこを重要視してなかったようです。ここにすれ違いの原因がありました。
SceneをTask派生クラスとするのは問題ないと思います。
夜遅くなっちゃいました。そろそろほんとに寝ます。
>>945 >たぶんみんな理解している。
それは・・・
そういうスレになったらうれしいですね
>>947 話はわかった…。お疲れさま&おやすみ。
話がさっぱりわからないけど
ゲームオブジェクトまたはその他もろもろのごった煮リストとは
違うタスクの話を始めたの?
眠れねー。
どうでもいいけどforeachでヒットチェックしてるとこ、べた書きはないだろ。
判定対象は同士は同じメソッド持ってんだし。
×判定対象は同士
○判定対象同士
ちなみに俺 ID:rIovvj90ね
俺的には
>>936もいいとは言えないね
jiki,teki,tama,unkoと同じ問題が今度はシーンにうつるだけだ
シーンをタスクにいれて何のごった煮になるのか知らないけど
仮にタイトルシーン、シューティングシーン、リザルトシーン、・・・という種類のあるものなら
当然それに必要なupdateの引数も異なる
だからダメだね
まったく汎用性のない糞クラスだ
954 :
名前は開発中のものです。:2009/02/01(日) 04:20:37 ID:3I9VPm/8
ID:rIovvj90=ID:rVEgp4cMって本当にプログラム書いてんのか?
どう見ても役に立ちそうにないんだけど。
俺の現場にこんな奴来たら、即クビにしちゃうけどな。
いま本当に仕事やってんの?ニートとかじゃなく?
シーンなんて最も抽象化しやすいものの1つだと思うんだが……。
>>951 自機も敵も弾も全て同じ当たり判定してるなら同じメソッドでしょうから
1つのforeachにまとめてもいいかもしれませんね。
ただ、自機、敵、弾のリストを当たり判定リストとして統合した1フレーム限りの寿命のリストを作るのが良いかどうかという問題が発生しますが。
オブジェクト毎に異なる当たり判定処理を導入しやすいという利点を得た代わりにどんくさい見た目になってると考えればまぁ許容範囲かと。
あと、この手法が真価を発揮するのはひとくくりに抽象化できないもの同士の情報やり取りが楽になることです。
つまり当たり判定という「処理」部分の中に閉じた話ではなく、
「入力」、「処理」、「描画」を簡単に接続できるとこに真価を発揮します。
例1:自機を動かす
・InputDevice(イベントハンドラとかで1フレーム分キー入力とかをバッファリングするオブジェクト)
・jiki
・OutputDevice
というオブジェクトがあった場合に
・InputDevice・jiki間はswitch (InputDevice->popKey()) ~
・jiki・OutputDevice間はcontext.dev3dg->draw_model(jiki_gazou, jiki.x, jiki.y);
と書ける。
例2:メニュー画面を操作する
・InputDevice
・menu(キー情報を入力として保持カーソル位置情報を変更)
・menu_draw_model(menuを入力として1フレーム分の描画内容を決定)
・OutputDevice
この場合もオブジェクト間で発生するデータのやりとり処理が素直に思いつきますよね。
オブジェクト間にまたがるデータ通信は無理やりどこかのオブジェクトに押し込めるのではなく、
Sceneクラスが受け持つ。そしてC入門書よろしく関数呼び出しを延々と書いていく。構造化プログラミングに逆戻りです。
利点はオブジェクト間の通信処理がC初心者でも書ける位に簡単化されることです。
(私はゲームオブジェクトにタスク導入反対派なので、こう記述すればタスク使うより楽にプログラミングできますよねという意図で書いてます)
そういえば
> ・InputDevice・jiki間はswitch (InputDevice->popKey()) ~
> ・jiki・OutputDevice間はcontext.dev3dg->draw_model(jiki_gazou, jiki.x, jiki.y);
これらをそれぞれタスク化するのはありかも。
処理だし
なんだこの損した感は
960 :
名前は開発中のものです。:2009/02/01(日) 20:36:02 ID:3I9VPm/8
ID:rVEgp4cMには
>>954の返答がもらえないみたいなんだが
無視して
>>958みたいに次スレ立ててるところを見ると本当にニートなのか
それは悪いことを聞いたな・・
前スレ、前前スレから、ひとりだけ異様にレベルの低いことを言う、
プログラムが少しも出来そうにもない、日本語の読み書きが不自由そうな
タスクシステム否定派が混じってると思っていたんだが、
それがID:rVEgp4cMだったのか・・・
>>960 一応、本職のPGですわ
ってなんでお前にそんなこと言わなきゃいけないのか?
内容でレス返せなくなったから人格攻撃に移ろうと思ったの?
育ちが悪いなぁ
962 :
名前は開発中のものです。:2009/02/01(日) 21:29:27 ID:3I9VPm/8
>>961 あんたの書いた
>>831とか
>タスクシステムはごった煮ソースになるので
>ほぼ全クラスを一括インクルードしなければ動かないとかかなり糞
この一文を見ただけであんたがとんでもない大馬鹿野郎で相手するだけ無駄な
三下プログラマだと誰が見てもわかるんだが。
なんでそこまで自覚無いのか知らないが、このスレであんた一人だけレベルが
異様に低いんだよ。
こんな場末のスレだってまともな人がたまに遊びに来て、
有意義なことを言ってくれるのに、あんたみたいな
クズが居たら議論の妨げにしかならない。ほんと、いい迷惑。
自分が三下ではないと思うのならぜひ
>>497 の問いかけに答えてみて欲しい。
いつのまにかタスクシステムありきで話が始まるんだから恐ろしい。
>>962 全然主旨と違うところに噛み付いてるけど
ソースに明示的に引数を書いて関数に必要なインスタンスをわかるようにしろ
ってのがメインなんだけどね
ここをあんまりずらしてほしくないな
あと、インクルードだってできるできないは別にしてもインクルードしてるんでしょ?
だって折角引数なくしてごった煮にしたのにそんなところに制限あっても
邪魔なだけもんねぇw
それとタスクシステムで組むなら俺はインクルードしちゃうべきだと思うよ
制限があることにもはやなんの意味もないわけだし
あえて言えばタスクシステムにしたのが運の尽きでしょw
プログラムの組み方(主に引数)によるクラスや関数の関連・アクセスの限定が
できないわけだしもうなんでもやっちゃったらいいよw
>>953 > jiki,teki,tama,unkoと同じ問題が今度はシーンにうつるだけだ
まあね。
> まったく汎用性のない糞クラスだ
ほどほどに汎用性のないちょっとだけダーティクラスだ
程度の言い方の方が良いのでは?
いつまでも通用する完全に汎用的なライブラリとして整備するかどうかは別として
似たようなゲーム量産する時にこういうクラスを作っとくのは実用上アリと考えても良いかと。
シーン間にまたがった画面遷移させたいというような要求が出て「やっぱシーン間も引数渡ししたいね」
となってから使うのやめたとしても、ゲームオブジェクトを脱タスク化する仕事量と比べたら傷は無いに等しい。
だから導入する人の直近の実用上に問題無いなら問題無いか、という姿勢で自分はOKと思った。
>>953 > 俺的には
>>936もいいとは言えないね
(snip)
> まったく汎用性のない糞クラスだ
あんた、本当に頭がおかしいんじゃないの?
936は、具体的なシーンを構築するためのクラスであって再利用するわけでもなく、
汎用性なんか必要ないんだが。
何故理解していないものを批判できると思うのか。
Taskはより抽象的なクラス。こちらは汎用的である必要がある。
SceneTaskはより具象的なクラス。こちらは汎用的である必要はない。
>>968 したらどうして継承して~Taskクラスである必要があるのん?w(俺はないと思うんだけどw)
ばっかじゃねぇんw
まさかの第2ラウンドwww
遅刻の予感www
名前の話か? それは単にサンプルだからだろ。
実際にはSceneTaskなんて何も表してない名前は使わないだろう。
Scene全体のスーパークラスとしてはあり得るかもしれんが。
俺はID:rVEgp4cM(スレ主)と議論するのは時間の無駄だと思うので
よそのスレでやりたいわ。
あるいは、ID:rVEgp4cMを無視して話を続けてもいいだろうが。
973 :
名前は開発中のものです。:2009/02/02(月) 01:38:35 ID:lhNqT94l
いいスレなのに、スレ主がいるだけ邪魔というのが惜しいな
>>972 アレアレ?
敗北宣言?w
まあ、いいけどダメな組み方なのわかってて
それを人に押し付けるのやめてね
その先に進歩ないからw