使いやすいタスクシステム設計・実装にご協力下さい

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
アバター
サウス
記事: 78
登録日時: 9年前
住所: 千葉
連絡を取る:

使いやすいタスクシステム設計・実装にご協力下さい

#1

投稿記事 by サウス » 8年前

追記
タスクシステムのメリット、デメリット、求める機能、問題点のまとめ

求める機能
・C++としてクラスを用いて実装する。その際Javaに近い方向で設計を行えば派生して使いやすいクラスになると考えている。
・派生させやすくて使いやすいクラス

メリット
・空領域のリスト(以後フリーリスト)をあらかじめ用意しておき、そこからメモリの確保を行うので
単純にnewなどで生成するより高速に処理できる。
・リスト化されているので切り離し、追加が簡単で高速

デメリット
・近年のPCハイスペック化により高速化を意識する重要性が薄れている(タスクシステム自体いらないんじゃないか)
・std::listもアロケーターいじれば高速化可能で、タスクシステムと同様の事ができる。(これを使えばタスクシステム再設計の手間いらず)
その他STL、boostに応用できるものがある(std::list,std::vector,boost::function,etc...)。
・フリーリストを用意するとフリーリストの存在を意識しないといけない。(関係性の管理とコーディングミスの誘発につながる恐れあり?)

とくにこだわっていない部分
・フリーリストの存在(なくなると綺麗に設計できそう、そうすると本当に標準のものでOKな展開に...)
・タスクシステムという名称。

--------------------------------------------------------------

リンクが張られたため情報の混雑を防ぐためにリンクを張っておきます。
http://hibari.2ch.net/test/read.cgi/gamedev/1285930493/

念のために補足しておくと私の目指す所は
タスクシステムを基盤としているのであくまでタスクシステムという言葉を使いますが
「コンテナ」と脳内置換して眺めてもらっても良いかもしれません。

--------------------------------------------------------------

【※タイトル変更】 使いやすいタスクシステムについて → 使いやすいタスクシステム設計・実装にご協力を

タスクシステムという不明瞭なモノを作る前に、何が本当に必要なのかを明確にするべきだという意見を貰ったので
冒頭に私の思うタスクシステムの利点を書いておきます。

・タスクシステムには汎用的な領域をリスト化して高速に処理できるメリットがある。
・クラスとしての特性を失うような設計や複数のクラスと関係性を持つようなデメリットは避けたい。

私にも明確な方向性が決まっていない部分が多数ありますので、方向性が変わるかもしれません。
利点への指摘、追加、設計案など色んな意見が欲しいです。

--------------------------------------------------------------
現在C++にてclass Taskを作成しようと試行錯誤しております。

タスクシステムというのはシューティングではよく使われている技法のようで
検索すればどんなものか、さらにコードも見つける事が出来るくらい有名なものです。
しかし実際に使ってみると汎用性に欠けていたり使いづらかったりするので再設計したいと考えています。

・タスクはあらかじめ空き領域を用意しており使用する場合に、そこから切り離して使う場合が多いです。
→ 空き領域をグローバル領域に配置する場合が多いようですが、理想としてはTaskクラスの派生先で用意できれば汎用性が高まると思っています。

・空領域から切り離して使用するので型が不明確。もしくはタスク用のイテレーターを別途用意する事で型が不明確になる場合が多い。
→ テンプレートを用いて解決できないかと模索中(テンプレート勉強不足、テンプレートでどこまでできるのか...)

個人的に使いやすいんじゃないかというTaskクラスの形式として理想のTask使用例を書いておきます。
使いにくそう、どうすれば使いやすくなりそう、車輪の再発明などの意見・情報・アドバイスが貰えればと思います。

コード:

class Move : public Task  // タスククラスを継承すれば派生クラスはタスクシステムの機能を持てる
{
    Move() : Task( 100 ) {}  // 初期化時にMoveの空き領域確保ができれば使いやすい? この場合Move領域が100個確保される
    
    virtual void run() = 0;  // 適当な処理
    int      x;   // 適当な値
    int      y;   // 適当な値
};

Moveクラスからさらに派生させるのを考慮するとTaskにsetHeader,hasNext,next等のイテレーター機能を持たせていた方が使いやすい?

コード:


class Move : public Task 
{
    Move( int task_num ) : Task( task_num ) {}
    
    virtual void run() = 0;
    int      x;
    int      y;
};

class EnemyMove : public Move  // 敵
{
    EnemyMove() : Move( 100 ) // EnemyMove領域100個確保
    void run();    // 敵の動き
}

class BulletMove : public Move // 弾
{
    BulletMove() : Move( 5000 ) // BulletMove領域5000個確保
    virtual void run();    // 弾の動き
}

int main()
{
    BulletMove* bullet_move = new BulletMove();
    /* ~ bullet_move値追加処理(省略) ~*/
    for ( bullet_move->setHeader(); bullet_move->hasNext(); )   // 参照位置を先頭にして次値が有るか
    {
        bullet_move->next();   // 参照位置を次に
        bullet_move->run();     // 処理を行う
    }
    
}

イテレーターはTaskクラスとは別にする?...使用時に型をキャストする必要性がありそうだし良い方法も思いつかないので省略




 
最後に編集したユーザー サウス on 2011年5月07日(土) 07:27 [ 編集 4 回目 ]

アバター
サウス
記事: 78
登録日時: 9年前
住所: 千葉
連絡を取る:

Re: 使いやすいタスクシステムについて

#2

投稿記事 by サウス » 8年前

自分で書いといてあれですがTaskを意識せずにbullet_move->run();の処理は無理そう...

とりあえず、私はTaskクラスの内部実装を行いたいと思います。
C++の技術的な話ですが基底クラスが派生先のクラスサイズを取得する事は可能でしょうか?
現在テンプレートに答えを求めて勉強中です...

コード:

class Task
{
    Task( int task_num )    // 派生クラスのサイズが分らない場合は、引数で取得するしか方法が無いので、パターン化した無駄なコーディングが増えることになる....
    {
        BYTE free_space = new BYTE[ sizeof( 派生クラス ) * task_num]; // Task内部で空き領域確保。
    }
}

アバター
MNS
記事: 35
登録日時: 9年前

Re: 使いやすいタスクシステムについて

#3

投稿記事 by MNS » 8年前

>C++の技術的な話ですが基底クラスが派生先のクラスサイズを取得する事は可能でしょうか?
こうすることで可能ではあると思います。

コード:

template <class T>
class Task
{
    Task( int task_num ) 
    {
        BYTE free_space = new BYTE[ sizeof(T) * task_num];
    }
}
※派生クラス

コード:

class X :public Task<X>
{
    X() : Task(199)
    {}
};

名前は開発中のものです。

Re: 使いやすいタスクシステムについて

#4

投稿記事 by 名前は開発中のものです。 » 8年前

「タスクシステム」などという名前からして不明瞭なモノを作る前に、何が
本当に必要なのかを明確にするべきだと思います。

そうして必要なものをはっきりさせれば、既存のライブラリやアイディアから
何を使うべきかが見えてくるものです。

例えば、・・・
汎用的なコンテナが欲しいなら C++ 標準コンテナを使いましょう。
いろんなオブジェクトについての処理を統一的に扱いたいのなら
boost::function などを使いましょう。
オブジェクト生成のコストが気になるならまず実測し、許容できないのなら
その部分についてプールを作りましょう。
プールをいろんな型について作ることになったらテンプレートにしましょう。
・・・などなど。

「よく使われている技法のよう」とか「有名」だとかいういい加減な理由で
新しいクラスやシステムを作っていると、何が必要で何が必要でないのか、
どこまで汎用的にするべきなのか、などという基本的なところでいつまでも
悩みの種が尽きないのは、当然の話です。

http://hibari.2ch.net/test/read.cgi/gamedev/1285930493/

アバター
サウス
記事: 78
登録日時: 9年前
住所: 千葉
連絡を取る:

Re: 使いやすいタスクシステムについて

#5

投稿記事 by サウス » 8年前

> 可能ではあると思います。

ありがとうございます。
さらに派生させて使うようなクラスには以下の様にコーディングすることになるのでしょうか?

テンプレートをまともに使った事が無いので派生クラスと
派生からさらに派生させたクラスの記述が異なっているのが気になります...

コード:

template <class T>
class Test
{
public:
    Test() 
    {
		std::cout << "sizeof:" << sizeof( T ) <<std::endl;
    }
};

template <class T>class TestSub : public Test<T>
{
	int a;
	int b;
};

class TestSubSub : public TestSub<TestSubSub>
{
	int c;
	int d;
};


int main()
{
	TestSubSub a;

	return 0;
}

アバター
Justy
副管理人
記事: 122
登録日時: 9年前
住所: 神奈川県

Re: 使いやすいタスクシステムについて

#6

投稿記事 by Justy » 8年前

>サンプル
 内部のコンテナとしては個々の特定1クラスのみを扱うわけですね。


>タスク用のイテレーター
 ユーザー側でタスクリストを辿ったり、元の型に復元してまわるようなことは面倒ですし
あまりするべきではないかと思います。
 つまりそのサンプルでいうなら

コード:

    BulletMove* bullet_move = new BulletMove();
    ....
    bullet_move->run();    // 全ての BulletMove(?)を実行
の方が楽ですし、間違えにくいのではないでしょうか。


 ところで
> BulletMove領域5000個確保 / EnemyMove領域100個確保
 最初のBulletMoveを生成した段階で指定数確保したとして、/* ~ bullet_move値追加処理(省略) ~*/で
追加するための BulletMoveを生成したら、またその中で指定数分確保されてしまいませんか?

アバター
サウス
記事: 78
登録日時: 9年前
住所: 千葉
連絡を取る:

Re: 使いやすいタスクシステムについて

#7

投稿記事 by サウス » 8年前

> 名前は開発中のものです。さん

確かにタスクシステムは不鮮明な部分が多いですよね。
実際タスクシステムという名称も正しく定義されていないとも聞きます。
私の記憶だと初期のタスクシステム実装時はC言語であって
それをクラス化しようという方向性が間違っているのかもしれません。(私の中に明確な答えがあるわけではないです)

それを踏まえたうえで決して有名だからとかいい加減な理由で使おうと思っているわけではなく
タスクシステムには汎用的な領域をリスト化して高速に処理できる。という利点があるので使いたいと思っています。

この利点を活かしたクラスを作成する。あくまで使いやすいタスクシステムを作成したいというのがこのスレッドの趣旨と解釈してもらえれば助かります。

現在ネットや参考書で紹介されているタスクシステム(C++による)というのは、私の見た限りだと
関係性のあるタスクシステム関連のクラスが3つあったり、静的領域を予め用意しておいて随時参照するなど
正直まともに使い物になるのが有りませんでした。
この時点でタスクシステムから離れて別の方向性に進んだ方がいいのかもしれません。
しかし、繰り返しますが私の中に明確な答えがあるわけではないので”使いやすい”タスクシステムを再設計したいと思っています。


もしよかったら、タスクシステムに精通した方や実装経験のある方、興味のある方達から
消してはいけないタスクシステムのメリット
使いにくさを解消するための設計
どうすれば使いやすくなるか
私が間違った方向性にコーディングしているんじゃないかなど
アドバイス、指摘、色々ご意見いただけたらと思っています。
 

アバター
サウス
記事: 78
登録日時: 9年前
住所: 千葉
連絡を取る:

Re: 使いやすいタスクシステムについて

#8

投稿記事 by サウス » 8年前

Justy さんが書きました:>サンプル
 内部のコンテナとしては個々の特定1クラスのみを扱うわけですね。
内部コンテナは特定の1クラスを扱う方向性にした方が良いと思っています。
Justy さんが書きました: >タスク用のイテレーター
 ユーザー側でタスクリストを辿ったり、元の型に復元してまわるようなことは面倒ですし
あまりするべきではないかと思います。
 つまりそのサンプルでいうなら

コード:

    BulletMove* bullet_move = new BulletMove();
    ....
    bullet_move->run();    // 全ての BulletMove(?)を実行
の方が楽ですし、間違えにくいのではないでしょうか。
bullet_move->run();の方が使いやすそうですね。
ただBulletMove::run()内部ではどちらにしろイテレータのループ処理を書くことになるのでしょうか?
Justy さんが書きました:  ところで
> BulletMove領域5000個確保 / EnemyMove領域100個確保
 最初のBulletMoveを生成した段階で指定数確保したとして、/* ~ bullet_move値追加処理(省略) ~*/で
追加するための BulletMoveを生成したら、またその中で指定数分確保されてしまいませんか?
確かにBulletMoveを再度生成すると指定数分確保されるので、悩ましいところです。
派生クラスで別途createメソッド(別名insert,appendなど?)を用意する必要があるのかと思います。
その際、領域確保の処理をcreateメソッド内に記述することになりそうなのが不安ですが...

名前は開発中のものです。

Re: 使いやすいタスクシステムについて

#9

投稿記事 by 名前は開発中のものです。 » 8年前

> タスクシステムには汎用的な領域をリスト化して高速に処理できる。という利点があるので使いたいと思っています。

単なる std::list を使った場合に、そのうちのメモリ確保・解放の時間が
実際の run() 全体に対してどれだけの割合になるか、実測して確認されている
でしょうか?多くの場合には1割程度に収まるのではないかと思います。

メモリのプールを実装されようとしていますが、少なくともサイズに基づいた
プール程度であれば単なる std::list で使われるコンパイラ提供の
operator new などにも組み込まれている可能性が高いですし、その実装の
信頼性もずっと上です。

あなたの思いついた高速化手法を、 std::list の実装者や operator new の
実装者はきっと思いつかない・実装できないと考えられるでしょうか?

また、本当に速度が最重要となる場合には、連結リストというデータ構造が
邪魔になるはずです。ただの配列か、 std::vector にしたほうが速くなります
からね。
http://www.aya.or.jp/~sanami/peace/memo ... tml#CODE44

メモリ確保・解放の頻度、速度の重要性はシューティングゲームの中でも
自機・敵・弾などの種類によって異なるでしょう。それらを統一的に扱うことを
大前提にしてしまうと、本当の意味での最適化の機会を失うことにもなりかね
ません。
http://www.google.co.jp/search?q=%22pre ... lr=lang_ja

> この利点を活かしたクラスを作成する。あくまで使いやすいタスクシステムを作成したいというのがこのスレッドの趣旨と解釈してもらえれば助かります。
...
> この時点でタスクシステムから離れて別の方向性に進んだ方がいいのかもしれません。
> しかし、繰り返しますが私の中に明確な答えがあるわけではないので”使いやすい”タスクシステムを再設計したいと思っています。

個人的にはリスト構造を作るなら std::list が「使いやすい」を含めてほぼ
最適と考えます。メモリ周りのカスタマイズのためにアロケータをいじることに
なるとちょっと面倒ですが、それによって元々のリストとしての使いやすさを
犠牲にする必要が無いのは大きなメリットです。

自機などは生成・破棄の時間が問題になることはありませんし、専用の処理も
多くなるので、コンテナに入れないで独立したオブジェクトにしてしまった
ほうが「使いやすい」だろう、とも思います。

> 私が間違った方向性にコーディングしているんじゃないかなど
> アドバイス、指摘、色々ご意見いただけたらと思っています。

「タスクシステム」という、必要性の示されていない謎の枠が、なぜか強力な
足枷になってしまっているように見えます。

まずは動くものを平易なコードで組み、そこから問題(足りないもの・必要な
もの)を見出し、それを解決するための手段を個別に組み込んでいくのが
良いのではないかという意見です。

アバター
lriki
記事: 88
登録日時: 8年前

Re: 使いやすいタスクシステム設計・実装にご協力を

#10

投稿記事 by lriki » 8年前

タスクシステムの趣旨というか重要な点は「高速にタスクの生成・解放が行える」ということですよね。
名前は開発中のものです。さんのおっしゃっていることに関係しますが、
昨今の PC で new が速度に響いてくるは稀です。(よほど大きな領域を確保する時は別ですけど…)

私も低スペ PC 向け!…なんて考えて作ってましたけど、結局 STL の速度には勝てませんでした。



「それでも作りたいんだ!」っていう場合は以下の書籍が参考になると思います。

 「シューティングゲームプログラミング」
 「弾幕 最強のシューティングゲームを作る!」

クラスを3つ使って実装する方法で解説されています。
ちゃんと読んで、サンプルのライブラリのタスククラスを実際に使ってみれば、
なんで3つも必要なのかがわかると思います。


> アドバイス、指摘、色々ご意見いただけたらと思っています。
検索されたのでしたらタスクシステムの危険性の記事も見つかったとおもいますが、
現在のサウスさんはそれにはまり込んでしまっているような気がします。

アバター
Justy
副管理人
記事: 122
登録日時: 9年前
住所: 神奈川県

Re: 使いやすいタスクシステム設計・実装にご協力を

#11

投稿記事 by Justy » 8年前

サウス さんが書きました:ただBulletMove::run()内部ではどちらにしろイテレータのループ処理を書くことになるのでしょうか?
 そうですね。
 具体的にどこでどうやって等の詳細はもっと詰めないとなんとも言えませんが、
基底側でループ処理をするのが望ましいです。

サウス さんが書きました:BulletMoveを再度生成すると指定数分確保されるので、悩ましいところです。
 役割がコンテナとその要素、というように明らかに別モノですよね。
 別クラスにするのがいいかと思います。

サウス さんが書きました:派生クラスで別途createメソッド(別名insert,appendなど?)を用意する必要があるのかと思います。
その際、領域確保の処理をcreateメソッド内に記述することになりそうなのが不安ですが...
 派生側に書かなくて済む、或いは書くにしても最小限の定型で済ます方法もあるとは思いますが、
標準のコンテナと同じことをしている気がします。


 うーんどういうあり方がいいのか、何が使いやすいのかというのは人それぞれあって、
なかなか難しいところがあります。
 とりあえず、領域X個確保とか、単一クラスのみとかまずは忘れて、世の中でよく見かける
Taskクラスを継承したポインタのリストをまわす構造のオーソドックスのものを
作ってみるのもいいのかもしれません。

 それを実際に運用テストし、問題点を洗い出して改善していった方が自分にあったものが
出来るでしょうし、ひょっとしたら不要だったということになるかもしれません。

アバター
サウス
記事: 78
登録日時: 9年前
住所: 千葉
連絡を取る:

Re: 使いやすいタスクシステムについて

#12

投稿記事 by サウス » 8年前

名前は開発中のものです。 さんが書きました:単なる std::list を使った場合に、そのうちのメモリ確保・解放の時間が
実際の run() 全体に対してどれだけの割合になるか、実測して確認されている
でしょうか?多くの場合には1割程度に収まるのではないかと思います。
STLやboostなどについては現段階では調査不足です。
参考書やWeb上に転がっている”処理が遅い”という情報を鵜呑みにしている面があり
”タスクシステム”を組んだ方が高速に処理できるというのを信じている面もあります。

ここでのタスクシステムも実装できていないので計測は後回しでも良いかと思っていましたが
あまり差が無いというのであれば計測の必要がありそうですね。
計測結果次第でタスクシステム再設計の手間も考慮して既存のライブラリの使用も考慮した方がいかもしれません
名前は開発中のものです。 さんが書きました: 個人的にはリスト構造を作るなら std::list が「使いやすい」を含めてほぼ
最適と考えます。メモリ周りのカスタマイズのためにアロケータをいじることに
なるとちょっと面倒ですが、それによって元々のリストとしての使いやすさを
犠牲にする必要が無いのは大きなメリットです。
vectorとlistはSTL勉強がてら程度に使った事はあるのですが
タスククラスとして派生させて使用するには設計思想が合わないと思いました。
名前は開発中のものです。 さんが書きました: 「タスクシステム」という、必要性の示されていない謎の枠が、なぜか強力な
足枷になってしまっているように見えます。
謎の枠というのは確かに埋める必要性がありますね。
レスの最後にまとめたいと思います。

アバター
サウス
記事: 78
登録日時: 9年前
住所: 千葉
連絡を取る:

Re: 使いやすいタスクシステム設計・実装にご協力を

#13

投稿記事 by サウス » 8年前

梨樹 さんが書きました:タスクシステムの趣旨というか重要な点は「高速にタスクの生成・解放が行える」ということですよね。
名前は開発中のものです。さんのおっしゃっていることに関係しますが、
昨今の PC で new が速度に響いてくるは稀です。(よほど大きな領域を確保する時は別ですけど…)

私も低スペ PC 向け!…なんて考えて作ってましたけど、結局 STL の速度には勝てませんでした。
私も実行速度を語るならまず既存のSTLの計測くらいはして置くべきでしょうね。
梨樹 さんが書きました: 「それでも作りたいんだ!」っていう場合は以下の書籍が参考になると思います。
 「シューティングゲームプログラミング」
 「弾幕 最強のシューティングゲームを作る!」
弾幕は読んでませんが「シューティングゲームプログラミング」のタスクシステムは読みました。
書籍で読んだことのあるタスクシステムはこの本しかないのですがWeb上の情報と比べて
この「シューティングゲームプログラミング」が一番使いやすいと感じました。
ですが一番使いやすいという話で、TaskとTaskListの関連性を持たせたコーディング法が使いにくいです。



この書籍のタスクシステムとSTLのListとvectorくらいは計測しておいた方が良さそうですね。

アバター
サウス
記事: 78
登録日時: 9年前
住所: 千葉
連絡を取る:

Re: 使いやすいタスクシステム設計・実装にご協力下さい

#14

投稿記事 by サウス » 8年前

> Justyさん
色々ありがとうございます

標準コンテナの利用考慮と、叩き台のとなるタスクシステムの作成が必要そうですね

---

最後にまとめるつもりでしたが、眠気で頭が回らないので時間を置かせてもらいます。

アバター
サウス
記事: 78
登録日時: 9年前
住所: 千葉
連絡を取る:

Re: 使いやすいタスクシステム設計・実装にご協力下さい

#15

投稿記事 by サウス » 8年前

トピ先頭に要点をまとめてみました。まだまだ不鮮明な部分があるかもしれません。

ライブラリを使用する事で派生させやすくて使いやすいタスククラスを実現できればそれはありだと思います。
もしかしたらタスクシステムの危険性というのにはまっているのかもしれません。
まだ私の中では問題点を洗い出し切れていないと感じています。

正直 Bullet* bullet = new Bullet( 座標X, 座標Y)といった形で自己(リスト化や配列)管理した方が、最終的に使いやすいんじゃないかという考えもあったりします。

しかし、タスクシステムというメモリ参照の効率化、さらなる使いやすさとなどの”ロマン”を求めてるのが現状なのかなと今は思っています。
この場合”ロマン”を実装したいというのがこのスレッドの趣旨であるわけですが...

たいちう
記事: 418
登録日時: 9年前

Re: 使いやすいタスクシステム設計・実装にご協力下さい

#16

投稿記事 by たいちう » 8年前

「名前は開発中のものです。」さんの意見に尽きると思います。

> > 「タスクシステム」という、必要性の示されていない謎の枠が、なぜか強力な
> > 足枷になってしまっているように見えます。
>
> 謎の枠というのは確かに埋める必要性がありますね。
> レスの最後にまとめたいと思います。

「名前は開発中のものです。」さんは、埋めるべき枠ではなく、
取り外すべき枠という意味で使っていると思います。


> まずは動くものを平易なコードで組み、そこから問題(足りないもの・必要な
> もの)を見出し、それを解決するための手段を個別に組み込んでいくのが
> 良いのではないかという意見です。

↑このアドバイスがスルーされていますが、よほど気に入らないのですか?


> この場合”ロマン”を実装したいというのがこのスレッドの趣旨であるわけですが...

ロマンもいいけど現実も見ましょう。
何も考えないで作り出すことは良くない事ですが、
理想的な完成形を追い求めるあまり作り始めることができないのも問題です。

もしあなたが今シューティングゲームを作れないとしたら、
それはタスクシステムのせいではありません。
タスクシステムがまともに使い物にならないと言う前に、
タスクシステムなしで作ってみましょう。

アバター
MNS
記事: 35
登録日時: 9年前

Re: 使いやすいタスクシステム設計・実装にご協力下さい

#17

投稿記事 by MNS » 8年前

タスクシステムなんてのは一つのアプローチに過ぎなくて、最終的に何を求めるかですよね。

求める機能に「使いやすいクラス」とありますが、
メリットに書かれている恩恵を得るような設計を得るには、
どうしても使いにくくなってしまうような箇所が出そうなように思えまし、
やっぱりそのような微妙な速度差が実際の問題になるようにはならないでしょうね。


ところで、コードややり取りから見ると、どうやら派生する対象をいわゆるところのタスクシステムにしてしまっているような印象を受けるのは気のせいでしょうか?
コンテナクラスでいうならば、
std::list<Task*> TaskSystem;
という形で、派生するのはTaskクラスであって、std::list<~>ではないという認識はありますかね?
まあ恐らく私の勘違いであるとは思いますが、
>vectorとlistはSTL勉強がてら程度に使った事はあるのですがタスククラスとして派生させて使用するには設計思想が合わないと思いました。
あたりの返答で、若干そういう印象を持ったので、一応確認だけさせてください。


最も簡単でかつ汎用性のありそうなタスクシステムは、何方かも言ってらっしゃるように、
タスククラスとしてTaskという単純なクラスを作り、そこからEnemyクラス、Bulletクラスを派生させ、
それを全てstd::list<Task*> tasksystem;というリスト構造に突っ込む形です。
まあ確かにロマンはないのですが、扱いやすさではやはり一番じゃないでしょうか。

dic
記事: 561
登録日時: 9年前
住所: 宮崎県

Re: 使いやすいタスクシステム設計・実装にご協力下さい

#18

投稿記事 by dic » 8年前

「ロマン」とあったので、私からの提案としては そのロマンに求めるものを書き出してはどうでしょうか?

<例>
ロマンの条件
・みやすい
・派生することで利用可能
・メモリの効率がいい
・修正しやすい

より具体的に
・みやすいとは・・・
・派生させるので必要となる宣言は・・・
・メモリの効率とは、派生したクラスの・・・

と、私もタスクシステムを考えていますが、作っている途中でブレてしまい
いつも失敗に終わります

アバター
サウス
記事: 78
登録日時: 9年前
住所: 千葉
連絡を取る:

Re: 使いやすいタスクシステム設計・実装にご協力下さい

#19

投稿記事 by サウス » 8年前

計測は後回しにしていますが、現在叩き台になるタスクシステムを作成中です。

> たいちうさん

私はタスクシステムを使ってシューティングを作りたいと思っているわけではなく
使いやすいタスクシステムを作りたいと思っています。

確かに現在抽象的な事しか書いていないので議論が成り立つレベルにないのかもしれませんが
その抽象的な部分を皆で埋められればと思っています。
なので個人的にはタスクシステムから離れろという意見ではなくて
タスクシステムはこうすれば良くなるという方向性の意見の方が欲しいです。
今はこの段階なのかなと思っています。

私は日頃、STLやboostを積極的に使う習慣がないので
既存のライブラリの方が使いやすいと言われても調査から入らないといけなく手間が大きいです。

といっても空想のタスクシステムと既存ライブラリを比較する事が出来ず、今以上の意見を貰えなさそうなので
現在はタスクシステムを作成しています。

スルーしているという話は、基本的に私はすべて読んでいます。回答出来ていないものは明確な答えが出せずにいます。


 

アバター
サウス
記事: 78
登録日時: 9年前
住所: 千葉
連絡を取る:

Re: 使いやすいタスクシステム設計・実装にご協力下さい

#20

投稿記事 by サウス » 8年前

MNS さんが書きました:タスクシステムなんてのは一つのアプローチに過ぎなくて、最終的に何を求めるかですよね。
タスクシステムがここまで批判されているのを見ると、やはりタスクシステムというのはlistで十分なのかな...
と思ってはいますが...あくまで、これからは”ロマン”を求める事にします。
それで最終的に、ロマンはやはりロマンであったとか、やっぱりlistで良かったという事になれば
記録に残せるので良いのかなと思います。
もし既にそういった記録があれば紹介して欲しいです。
MNS さんが書きました: std::list<Task*> TaskSystem;
という形で、派生するのはTaskクラスであって、std::list<~>ではないという認識はありますかね?
一応テンプレートで言うlist<int>とすればint型のlistが生成されてlist<string>とすればstring型のlistが別に生成されるという認識はあります。
MNS さんが書きました: タスククラスとしてTaskという単純なクラスを作り、そこからEnemyクラス、Bulletクラスを派生させ、
それを全てstd::list<Task*> tasksystem;というリスト構造に突っ込む形です。
まあ確かにロマンはないのですが、扱いやすさではやはり一番じゃないでしょうか。
現在だとlistが一番使いやすそうですね。
これからタスクシステムの設計が途中で破たんするかもしれませんが、listで扱いやすいという部分をTaskシステムに組み込めないかアドバイスお願いします。

アバター
サウス
記事: 78
登録日時: 9年前
住所: 千葉
連絡を取る:

Re: 使いやすいタスクシステム設計・実装にご協力下さい

#21

投稿記事 by サウス » 8年前

> dicさん

ありがとうございます。
今更になってロマンを追い求めてる事に気づいて、私もロマンの洗い出しをしようと思っているのですが
ロマンの定義がブレブレでした。
なので標準ライブラリのこれが良いと言われても比較のしようがないですよね。

私の方でも明確になっていない部分が3つほどあり
・使いやすいタスクシステムという定義が決まっていない。
・タスクシステムをlistで実装した場合のデメリットはなに?
・ロマンはあるけど実装できる?

掲示板で意見を貰えないかと思っていましたが、今頂いている意見を参考に
とりあえず”ロマン”になっている部分を実装してみようと思っています。
まともな話ができるようになるのはこれが終わってからですかね^^;
そでも、3つの不明確な部分は引き続き情報が欲しいと思っています。

 

アバター
MNS
記事: 35
登録日時: 9年前

Re: 使いやすいタスクシステム設計・実装にご協力下さい

#22

投稿記事 by MNS » 8年前

>list<int>とすればint型のlistが生成されてlist<string>とすればstring型のlistが別に生成されるという認識はあります。
ああ、そういうことではなく、list構造によるタスクシステムにおける「タスク」はstd::list<X> hoge_listにおけるXなのかstd::list<~>なのか、
つまりタスククラスとして派生させるのはXかstd::list<~>なのかということが認識としてズレてるように思えたのです。

そもそも、タスクシステムという言葉が不明確なのに、そのロマンを求めるなんてことは不可能では?
結局のところサウスさんが求めている機構というのはどういう機能なんでしょうか?
使いやすいとかオブジェクト指向に即しているとかはあくまで性質的なもので、
そのタスクシステムが一体どんな機能をするのか、どういう働きをすることを望んでいるのか、
それが分からないと、何ともいえません。

一番良いのは、ロマンも扱いやすさも無くてもよいですから、
サウスさんが組んだ単純な、簡単なタスクシステムを見せて頂くことです。
そうすれば具体的な案やら設計やらを示せる気がしますし、議論も捗ると思いますよ~

名前は開発中のものです。

Re: 使いやすいタスクシステム設計・実装にご協力下さい

#23

投稿記事 by 名前は開発中のものです。 » 8年前

> 私の方でも明確になっていない部分が3つほどあり
> ・使いやすいタスクシステムという定義が決まっていない。
> ・タスクシステムをlistで実装した場合のデメリットはなに?
> ・ロマンはあるけど実装できる?

問題が無ければシステムは不要であり、定義などできるはずもありません。
定義が無いものについて「~で実装した場合」とか「実装できる?」というのも
輪をかけてナンセンスです。

> 掲示板で意見を貰えないかと思っていましたが、今頂いている意見を参考に
> とりあえず”ロマン”になっている部分を実装してみようと思っています。

何であれ動くコードを書いて見せてもらえれば、もっと具体的な問題点なり
意見なりが出てくるものと思います。今まで示されている情報から考えると
まったくお勧めできないのですが、僕の予想が外れるようならそれはそれで
面白いですし。

アバター
サウス
記事: 78
登録日時: 9年前
住所: 千葉
連絡を取る:

Re: 使いやすいタスクシステム設計・実装にご協力下さい

#24

投稿記事 by サウス » 8年前

> MNSさん
正直listを使ったタスクシステムの実装は、現段階では深く考えていないませんが
hoge_listにおけるXを派生?というか生成する事になると考えています。

ロマンに関しては適当な用語が見つからなかったので
不確定だけれど実装したい理想形をロマンといってるつもりで、できればロマンを語っていただきたいです。

タスクシステムは理想を追いながら外枠から組んでます。

> 名前は開発中のものです。

おっしゃるとおりです。
そろそろ能書きは止めてコードレベルの話をした方がいいですよね。

※ 現在、理想の設計に折れかけています。
  理想を追ったものの結局list使った方が見やすいということになりそうな予感がしています。

アバター
サウス
記事: 78
登録日時: 9年前
住所: 千葉
連絡を取る:

Re: 使いやすいタスクシステム設計・実装にご協力下さい

#25

投稿記事 by サウス » 8年前

内部実装を行いながら現実的は方向で理想を追求しています。
とりあえずメソッドの定義
Taskクラス

コード:

class Task
{
public:
    Task( int task_num );                 // task_num:確保するタスクの数(フリーリスト容量)
    virtual void runFunction() = 0;    // アクティブ時に行う処理のを定義
    void run();                               // アクティブタスクをすべて処理する
    virtual bool remove();              //  default return false, 戻り値 true時はrun()実行時にタスクをフリーリストへ
    void* getTask();                       //  フリーリストからタスクを確保
};
removeを派生先でtrueを返すように処理する事でアクティブクリストから解放できるという動作は微妙?

コーディング例(プロトタイプ)

Taskクラスを使用してMoveを派生

コード:

class Move : public Task
{
public:
	Move( int task_num )	// 確保数は継承先に任せる
		:	Task( task_num )
	{
	}

	~Move()
	{
	}

	virtual bool remove()	// 削除する条件を記入 削除:true, 維持:false
	{
		if ( ( x < 0 ) || ( x > 640 ) || ( y < 0 ) || ( y > 480 ) )	// 画面外は削除
		{
			return true;
		}
		
		return false;
	}

	virtual void move() = 0;

	virtual void runFunction()
	{
		move();
	}

	void copy( Move* move )
	{
		x     = move->x;
		y     = move->y;
		angle = move->angle;
		range = move->range;
		speed = move->speed;
	}
	
protected:
	float x;
	float y;
	float angle;
	float range;
	float speed;
};
MoveクラスからBulletMoveやPlayerを派生させる。

コード:

class BulletMove : public Move
{
	// static int* texture;
public:
	BulletMove( int task_num )	
		:	Move( task_num )
	{
		// 画像読み込み? 実際はタイプ毎に派生させるべき
		//if ( texture == NULL )
		//{
		//	 texture = new int(画像枚数);
		//	 texture[0] = 画像読み込み;
		//}
	}

	~BulletMove()
	{
		//if ( texture != NULL )
		//{
		//	delete[] texture;
		//}
	}

	virtual bool remove()	// 削除する条件を記入 削除:true, 維持:false
	{
		if ( Move::remove() ) return true;	// 画面外の判定
		
		return false;
	}

	void create( Move* move )
	{
		BulletMove* task = (BulletMove*)this->getTask();	// タスク確保
		if ( task == NULL ) return;
		task->copy( move );		// Moveのコピー
	}

	virtual void move()
	{
		x = cos( angle ) * speed;
		y = sin( angle ) * speed;
	}

	virtual void draw()
	{
		/* ~ 描画 ~ */
		// 	描画( x, y, angle );
	}
	virtual void run()
	{
		move();		// 移動
		draw();		// 描画
	}
};

class Player : public Move
{
	PlayerBulletMove*	player_bullet_move;
public:
	Player()
		: Move( 1 )
	{
		player_bullet_move	= new PlayerBulletMove();
	}

	~Player()
	{
		delete player_bullet_move;
	}

	virtual void move()
	{
		//if ( 押下情報TRUE )
		//{
		//	x += 入力方向;
		//	y += 入力方向;
		//}
		/* if ( shot_flag ) */
		{
			player_bullet_move->create( this );
		}
	}

	void runFunction()
	{
		/* アクティブリストの実行 */
		player_bullet_move->run();
	}
};
BulletMoveクラスからも派生

コード:


class PlayerBulletMove : public BulletMove
{
public:
	PlayerBulletMove()
		: BulletMove( 100 )		// PlayerBulletMoveを100個確保
	{
	}
};

PlayerクラスはMove継承しており、MoveはTaskを継承しているので(タスクシステムで動かすことが可能)
PlayerクラスはPlayerBulletMoveも内包するなど階層化も考慮しやすい?

コード:

int main()
{
	Player* player = new Player();

	player->run();  // 弾計算、player移動制御が含まれる

	delete player;

	return 0;
}

アバター
サウス
記事: 78
登録日時: 9年前
住所: 千葉
連絡を取る:

Re: 使いやすいタスクシステム設計・実装にご協力下さい

#26

投稿記事 by サウス » 8年前

move()のような処理をどこで管理するかという事でまったく別の設計になり
私はmoveの動作は各クラス内で自己管理する方が修正が入れやすいと思いながら書きました。
当たり判定の処理をどうすれば記述しやすいか悩み中です。
MSN さんが書きました: 最も簡単でかつ汎用性のありそうなタスクシステムは、何方かも言ってらっしゃるように、
タスククラスとしてTaskという単純なクラスを作り、そこからEnemyクラス、Bulletクラスを派生させ、
それを全てstd::list<Task*> tasksystem;というリスト構造に突っ込む形です。
まあ確かにロマンはないのですが、扱いやすさではやはり一番じゃないでしょうか。
まあでも、いまのところlistを使った方が一番よさそうですね。


タスクシステム未実装ですが現段階の進捗として途中のコードを張っておきます。
-----------------

コード:

#include <iostream>
#include <string>
#include <assert.h>

typedef unsigned char	BYTE;

static int task_size;	// タスクサイズ取得用
class Task
{
	Task*		active_header;
	Task*		free_header;
	Task*		prev;
	Task*		next;
	Task*		current;
	BYTE*		buf;
	int			task_size;
	int 		free_num;
	int 		max_num;
	
public:
	Task( int max_num )
		:	active_header( NULL )
		,	free_header( NULL )
		,	prev( NULL )
		,	next( NULL )
		,	current( NULL )
		,	task_size( ::task_size )
		,	free_num( max_num )
		,	max_num( max_num )
		,	buf( NULL )
	{
		std::cout << "task_size:" << this->task_size << std::endl;
		std::cout << "max_num:" << this->max_num << std::endl;
		this->buf = new BYTE[this->task_size * ( this->max_num + 2 )];

		this->active_header = this->getTaskAddr( 0 );
		this->active_header->prev = this->active_header->next = this->active_header;

		this->free_header = this->getTaskAddr( 1 );
		for ( int i = 1; i < this->max_num + 1; i++ )
		{
			this->getTaskAddr( i )->next = this->getTaskAddr( i + 1 );
		}
		this->getTaskAddr( this->max_num + 1 )->next = this->free_header;
	}
	virtual ~Task()
	{
		delete[] this->buf;
	}
	
	inline void* getTask()
	{
		assert( this->free_num > 0 );

		if ( this->free_num <= 0 )
		{
			return NULL;
		}

		Task* task = this->free_header->next;
		this->free_header->next = task->next;
		this->free_num--;
		return task;
	}
	
	inline void deleteTask( Task* task )
	{
		task->next = this->free_header->next;
		this->free_header->next = task;
		this->free_num++;

		assert( this->free_num <= this->max_num );
	}
	
	virtual bool remove()
	{
		return false;
	}
	
	virtual void runFunction() = 0;
	
	void run()
	{
		for ( this->setHeader(); this->hasNext(); )
		{
			Task* task = this->getNext();
			task->runFunction();
			if ( task->remove() )
			{
				this->deleteTask( task );
			}
		}
	}
	
	inline int getMaxSize() const
	{
		return max_num;
	}
	
	inline int getFreeNum() const
	{
		return free_num;
	}
	
	inline int getActiveNum() const
	{
		return max_num - free_num;
	}
	
	inline Task* setHeader()
	{
		current = active_header->next;
		return current;
	}
	
	inline Task* getNext()
	{
		current = current->next;
		return current;
	}
	
	inline bool hasNext()
	{
		return current->next != active_header;
	}
public:
	void* operator new( size_t size )
	{
		::task_size = size; 
        return ::operator new( size );
	}
private:
	Task* getTaskAddr( int index )
	{
		return reinterpret_cast<Task*>( this->buf + this->task_size * index );
	}
};

class Move : public Task
{
public:
	Move( int task_num )	// 確保数は継承先に任せる
		:	Task( task_num )
	{
	}

	~Move()
	{
	}

	virtual bool remove()	// 削除する条件を記入 削除:true, 維持:false
	{
		if ( ( x < 0 ) || ( x > 640 ) || ( y < 0 ) || ( y > 480 ) )	// 画面外は削除
		{
			return true;
		}
		
		return false;
	}

	virtual void move() = 0;

	virtual void runFunction()
	{
		move();
	}

	void copy( Move* move )
	{
		x     = move->x;
		y     = move->y;
		angle = move->angle;
		range = move->range;
		speed = move->speed;
	}
	
protected:
	float x;
	float y;
	float angle;
	float range;
	float speed;
};

class BulletMove : public Move
{
	// static int* texture;
public:
	BulletMove( int task_num )	
		:	Move( task_num )
	{
		// 画像読み込み? 実際はタイプ毎に派生させるべき
		//if ( texture == NULL )
		//{
		//	 texture = new int(画像枚数);
		//	 texture[0] = 画像読み込み;
		//}
	}

	~BulletMove()
	{
		//if ( texture != NULL )
		//{
		//	delete[] texture;
		//}
	}

	virtual bool remove()	// 削除する条件を記入 削除:true, 維持:false
	{
		if ( Move::remove() ) return true;	// 画面外の判定
		
		return false;
	}

	void create( Move* move )
	{
		BulletMove* task = (BulletMove*)this->getTask();	// タスク確保
		if ( task == NULL ) return;
		task->copy( move );		// Moveのコピー
	}

	virtual void move()
	{
		x = cos( angle ) * speed;
		y = sin( angle ) * speed;
	}

	virtual void draw()
	{
		/* ~ 描画 ~ */
		// 	描画( x, y, angle );
	}
	virtual void run()
	{
		move();		// 移動
		draw();		// 描画
	}
};

class PlayerBulletMove : public BulletMove
{
public:
	PlayerBulletMove()
		: BulletMove( 100 )		// PlayerBulletMoveを100個確保
	{
	}
};

class Player : public Move
{
	PlayerBulletMove*	player_bullet_move;
public:
	Player()
		: Move( 1 )
	{
		player_bullet_move	= new PlayerBulletMove();
	}

	~Player()
	{
		delete player_bullet_move;
	}

	virtual void move()
	{
		//if ( 押下情報TRUE )
		//{
		//	x += 入力方向;
		//	y += 入力方向;
		//}
		/* if ( shot_flag ) */
		{
			player_bullet_move->create( this );
		}
	}

	void runFunction()
	{
		move();
		/* アクティブリストの実行 */
		player_bullet_move->run();
	}
};

int main()
{
	Player* player = new Player();

	player->run();

	delete player;

	return 0;
}

たいちう
記事: 418
登録日時: 9年前

Re: 使いやすいタスクシステム設計・実装にご協力下さい

#27

投稿記事 by たいちう » 8年前

> 私はタスクシステムを使ってシューティングを作りたいと思っているわけではなく
> 使いやすいタスクシステムを作りたいと思っています。

なるほど私が考えていたのとは手段と目的が逆ですね。
タスクシステムが目的なのは判りましたが、
少なくとも使いやすさを評価するための手段としては、
シューティングゲームを作るのですよね?
(タスクシステムを使った別のゲームやシステムでも良いけど)

タスクシステムを使わなければシューティングを作れるのですか?
それなりの規模のシューティングを作ったことがない人が、
使いやすいタスクシステムなど作れるはずがないと思うのですが、
ご自分の経験や技術をどのように評価していますか?

アバター
サウス
記事: 78
登録日時: 9年前
住所: 千葉
連絡を取る:

Re: 使いやすいタスクシステム設計・実装にご協力下さい

#28

投稿記事 by サウス » 8年前

2、3年前にSTGの館を参考に一度作った事がある程度です。規模は小規模になるとおもいます。
龍神録の館の内容は把握しているつもりです。
2年くらい内容の更新は止まっていますが支館という名目で
弾幕など楽にコーディング出来ればな~程度に作ったサイトを維持中(更新していないので)です。
http://sinkai.net/

確かにタスクシステムはなくてもプログラミングの方法なんて色々あるのでいくらでも書きようが有ると思います。
タスクシステムの利点もデメリットも分ってなさそうな人が使いやすいタスクシステムを設計というのはおかしいかも知れません。

ただこのトピックでは使いやすいタスクシステムの設計・実装に目的を置いており
私がシューティングゲームを作るという目的などは除外視したて使いやすいタスクシステムを実装できたらと思います。

たいちう
記事: 418
登録日時: 9年前

Re: 使いやすいタスクシステム設計・実装にご協力下さい

#29

投稿記事 by たいちう » 8年前

サウスさんが何をしたいのか益々分からなくなりました。
おそらく私の書いたことは理解されているとは思いますので、
これ以上は平行線でしょう。退散しようと思います。

ご回答ありがとうございました。

アバター
サウス
記事: 78
登録日時: 9年前
住所: 千葉
連絡を取る:

Re: 使いやすいタスクシステム設計・実装にご協力下さい

#30

投稿記事 by サウス » 8年前

初めに書いたことから、目的は変化していると思います。

というのも初めはタスクシステムのようなものはlistで十分という認識はありませんでしたし
タスクシステムというのがここまで使えないものとされている認識もありませんでした。
この認識を得た時点で、トピックを閉じてもよかったかも知れません...

なぜいまだ閉じていないかと言うと...当初の目的が使いやすいタスクシステムを設計ということだったのに
結局なにもやっていなかったからで、とりあえず最終的には”使えない”タスクシステムになるにしろ続けているのが現在です。
いまだにタスクシステムという用語を使っているのも色々ぶれている原因かもしれません。
 
私の方こそ方向性を示唆してくださいって感謝しています。
気になるような事があればぜひ書き込んでください。
 

閉鎖

“C言語何でも質問掲示板” へ戻る