トップページgamedev
935コメント361KB

OOとゲームプログラミング

■ このスレッドは過去ログ倉庫に格納されています
0001名無しさん@お腹いっぱい。01/11/07 23:55ID:HnYWCQK1
OOをどのように用いれば美しくゲームプログラミングを
行うことが出来るのか語り合うスレです。
0139名無しさん@お腹いっぱい。01/11/10 23:04ID:???
>>138
コンパイラなのにアセンブラソースが見えると書いてるよ。
分かるかね?
>吐き分けてどうしようって言うんだ、一体?
どうするのか分からんなら聞かなくていいよ。(藁
0140名無しさん@お腹いっぱい。01/11/10 23:08ID:???
ふう。このスレも某3Dプログラミング系ページに晒されそうだな。
0141名無しさん@お腹いっぱい。01/11/10 23:12ID:???
>>139
それは実際の実行プロセスに非常に近いという事だろう。
0142名無しさん@お腹いっぱい。01/11/10 23:18ID:???
>>141
・・・ハァ?
>それは実際の実行プロセスに非常に近いという事だろう。
言ってることが(危)レベルに達してますな。
0143名無しさん@お腹いっぱい。01/11/10 23:21ID:???
もー全然OOとゲームプログラミングぢゃねーよ
Game Programming Gems並みのネタをキボンヌ
0144名無しさん@お腹いっぱい。01/11/10 23:21ID:???
>>142
ハァ?
分からんの?
0145名無しさん@お腹いっぱい。01/11/10 23:33ID:ArMYUgNW
うう良いスレだったのに
0146名無しさん@お腹いっぱい。01/11/10 23:37ID:???
とりあえずアセンブラソースを実行なんたらと
勘違いしている>>144を放置してOOPに関する
話題で再開しましょうか。
01476001/11/10 23:37ID:???
貴方がなぜC++を使わないゲームプログラミングをしているのか
理由を書いてくれる人 他にはいないの?
0148名無しさん@お腹いっぱい。01/11/10 23:39ID:???
>>147
あなたって?
0149名無しさん@お腹いっぱい。01/11/10 23:40ID:???
貴方って誰?
0150名無しさん@お腹いっぱい。01/11/10 23:47ID:???
>>146
実行なんたらってなんだ?
あほか?
01516001/11/10 23:59ID:???
「ゲームプログラミングをする時にC++を選択しませんでした。」
という人がいるなら、その理由を聞きたいわけですよ
非常に興味深い

まぁ、「対象となるターゲット用C++コンパイラがありません」つーのは
ご愁傷様としか言えませんが
コンパイラ移植するって手もあるけどね
0152とむ ◆TMwDpCZo 01/11/11 00:00ID:???
やあ、今ログ読んでました。
すみませんね、荒れる原因みたいになっちゃって。

でも全体的に停滞気味の板だから、たまにはいいかな?(笑)
0153名無しさん@お腹いっぱい。01/11/11 00:33ID:kQ4U2tS+
私、ゲーム専用機対象にプログラミングしたこと無い門外漢ですからサパーリで
興味本位に質問ですけど、C++コンパイラ使えるようになったのって
どのあたりからですか?
  +->PCE   +->DC
FC->SFC->PS->PS2
          +->64->GQ
見たいな流れの中で
0154名無しさん@お腹いっぱい。01/11/11 01:17ID:???
3DO,PSあたり?
0155名無しさん@お腹いっぱい。01/11/11 01:30ID:S9ESG1BU
>>151
C++コンパイラの最適化性能が低くて、糞なコードを量産する
そもそも、まともにC++の言語仕様が実装できていない

なんて理由もあるかもね。
015647で6601/11/11 02:41ID:???
>60
該当者なんで、私もレス書きまーす。
前もって書きますが、私自身、流行の思想については使いこなせれば
良いなーと羨ましいですし、OOについて偏見もありません。

私はCのみでゴリゴリのヒトです。
理由ですが、必要ないから(必要性を感じる局面に遭遇してない)
という事と、やはり
「始めに物ありき」の考え方よりも、
「始めに物事の振る舞いの、法則ありき」の方が、私自身、
皮膚感覚的に馴染めるからです。これは、食わず嫌いではなく、
プログラムに限らず、何かを考える際、「物」(主語、目的語)
ではなく「事」(動詞)から先だって考えてしまう癖が
あるからのようです。

おそらく、このような思考方法の人間が、無理やり似非OO的
プログラムをしたところで、欠陥のある設計しかできないに
違いありません(笑)
そんなわけで、私は大人しくOOには触れずにおります。
0157名無しさん@お腹いっぱい。01/11/11 02:59ID:+9QMetsd
>おそらく、このような思考方法の人間が、無理やり似非OO的
>プログラムをしたところで、欠陥のある設計しかできないに
>違いありません(笑)
>そんなわけで、私は大人しくOOには触れずにおります
 押しつけるつもりはないけど、一通り勉強してから
結論出しても遅くないと思うけど・・・。
なんかもったいない
015815601/11/11 03:23ID:???
いやいや、現状で「そうだ」というだけで、決して結論を出した
わけではありません(笑)

さらには、まったく勉強してないわけでもありません。
勉強不足の度合いと、それが原因か否かはさておき、
現状では私の仕事に役立っていないというだけの話でして、
今でも関連書籍を読んだり、テストプログラムは書いて
みたりはしております。
0159名無しさん@お腹いっぱい。01/11/11 04:37ID:???
>>158
大規模なものを組み始めるとOOな手法が管理しやすいのだけど
もともとOOはSAと対立する技術ではないので、SAの技法もきちんと
勉強するのも大切なことだと思いますよ。
地盤が脆い状態で高層ビルを立てるのが不可能なように
コードを何十万行も書いて、経験を積んでSAもOOD/OOAも実装も
コストも利点も理解したうえでターゲットに最善な手法を
選べるようになれば結構いい感じになるんじゃないでしょうかね。

ぶっちゃけた話、木造二階建てを作るのにツーバーフォーな手法よりも
大工職人がトンカンやったほうが、ツーバイフォーな会社を作るより
小回りが効くし、でも出来てしまえばツーバイフォーは早い納期で
高層ビルだって立てられる。でもそれを設計するには習熟と経験が
必要不可欠なわけで。

うーん。私も勉強しなくては…。
勉強不足な自分への自戒の意味もこめて。
0160名無しさん@お腹いっぱい。01/11/11 04:42ID:???
今の会社はCマンセー。

Cを使うにしても、使いまわされることを前提に作られているなら
まだいいが、プロジェクト毎にソースコピペかYO!

3年以内に効率の悪い開発体制を変えられなかったら、
会社辞めることになるな。

ま、Cしか使えない厨房は家でおとなしくしてないさい、ってこった。
0161名無しさん@お腹いっぱい。01/11/11 05:09ID:???
プロジェクト毎にソースコピペかYO!

ソースコピペかYO

YO
0162名無しさん@お腹いっぱい。01/11/11 05:18ID:???
160
(・∀・)カコイイ! ゲラゲラ
0163がばろん@Mたんちゅきちゅき01/11/11 06:53ID:???
>>113-114 60たん。114たん。

とりあえず分散オブジェクトと
デザインパターンのとこから読み進めてみる。

ふたりのおかげで気力が、わいてきたよ。
ありがとう。☆⌒(*^-°)v Thanks!!
// こんど開発状況報告スレにも、いってみようかな。

>>50 とむたん。>>156たん。

いっそ、その「物事の振る舞い」だけを集めたクラスを作って
オブジェクト指向してみるって、どうかしら。

つまり仮想関数だけで構成される
通常のメンバ関数もメンバ変数も持たないクラス。

で、その「物事の振る舞い」が、
なにかのデータ群(つまりクラス)で必要になったら、

そのデータ群に仮想関数だけのクラスを継承させて、
そのデータ群に特化した処理を実装するの。

Javaにおけるインターフェイス的な考え方だけど、べんりだよ。
このクラスは、いっさいデータをふくまないから、
多重継承になっても、ややこしさが発生しないし。
// ただ問題は処理速度かも。。。

ところでJavaでゲームを作ろうとしてるおれは、
もしかして、ここではスレ違いで逝ってよしなのでしょうか。

// いや、それ以前にJavaの処理速度で逝ってよしという気もする。
// 鬱朶思王。。。でも氏なないで、がんばる。
// でも、おれが逝くまえにJavaが先に逝きそうな気もするよ。(TT
0164名無しさん@お腹いっぱい。01/11/11 07:03ID:???
インラインアセンブラを多用したプログラムを組みたい
ヘッダをインラインまみれにしても極限まで自分でチューニングしたい
いわゆる汚い泥作業も厭わない任侠気あふれるOOなら C++
0165名無しさん@お腹いっぱい。01/11/11 07:22ID:???
>>163
複数の人が別々にコーディングする場合はその方が吉だろうね。
一人でやる場合はOOPにこだわる必要はあまり無いとは思うけど。
関数の集合クラスを作ったり継承したりっていう余分な作業は一人で
やる時は得るものが少ないんじゃないかな。
JAVAはアメリカでは終わりだとか言われてるけど(MSがサポートを辞めた)
日本ではまだ元気があるみたいだし、しばらくは大丈夫かも。
速度は問題だよね。技術が足りないからだとか言われるけどそこそこの
技術力でももう少し速くなればいいのに思うよ。
0166名無しさん@お腹いっぱい。01/11/11 08:14ID:???
Cでゴリゴリやってる人にはC++はかなり泥臭くて似合ってると思うのだが。
C言語が好きじゃない俺は、C++はいくらOO言語だからっていやだけど
0167モデルやテクスチャのオプティマイズ>CPUのオプティマイズ01/11/11 09:56ID:???
先生!とりあえずC++が遅いって言うのは論外だと思うYO!
どうしても速度的に問題ある部分は、モジュール分けてCで書くっていうのじゃまずいの?
俺はそんなことはしませんが。使える環境なら必ずC++を選択するよ。

っていうかC++ワカランってところってまだ結構あるよね。
この前もローカライズ担当してる下請けがC++解りませんって泣きついてきたよ。(ワラ
C++駄目って奴はとりあえずC++で一本作ってから
反論した方が恥をかかないで済むと思われ。
0168名無しさん@お腹いっぱい。01/11/11 10:38ID:???
>>166
プロのゲーム開発の現場に限ったらASM,C,C++以外に選択肢は無いのでは?
C/C++に負けない実効速度を出せるOOPLってあるのだそうか?
純粋な質問。
0169名無しさん@お腹いっぱい。01/11/11 11:27ID:???
>167

どうも、このスレの流れとして
一部をCとかアセンブラで書くと良いじゃないの? -> やっぱり遅いってことじゃない。アヒャヒャヒャ
って流れらしい。

おれは楽できるところは楽すれば良いと思うけどな。
0170名無しさん@お腹いっぱい。01/11/11 11:32ID:???
一部をCとかアセンブラで書くと良い = 最適化して書く
が前提。
0171名無しさん@お腹いっぱい。01/11/11 11:33ID:???
>>168
コンシューマだとないだろうな。そもそも処理系がない。
0172名無しさん@お腹いっぱい。01/11/11 12:32ID:TZbrGtZW
>>60
>C++で実際に実行される順番が異なってしまう状態例を
>あげてくれると非常にうれしいです。感激しちゃう。

グローバル変数のコンストラクタ/デストラクタ。
0173名無しさん@お腹いっぱい。01/11/11 13:12ID:???
それは最初から不定なので問題なし。
0174_01/11/11 15:07ID:6XiTGObp
>>169
C++が遅いと言われても、そんなことは絶対にないと断言は出来ない。
もしかしたら自分の知らないケースで、めちゃ遅くなるかもしれないし。
そういうことで、逃げうってるだけだと思われ。
0175名無しさん@お腹いっぱい。01/11/11 15:16ID:???
C++がCより遅くなる、具体的なコード例を挙げられる人居る?
仮想関数によるポリモーフィズムは遅いと言われがちだけど、
同じ事をCでやるとif文やら関数ポインタやら使うわけで、少なくともC++より高速に動かせるという保証はないと思われる。
0176名無しさん@お腹いっぱい。01/11/11 16:07ID:???
どっち派でもいいんだけどさ、互いにコッチだ!って啓蒙しあう
必要ないでしょう。(そーいうのはヨソでやろう)
このスレは、前提としてOOで、どの様に良い設計をしていくか
がテーマなんだから、そういう話をしないと
「まったく意味がない」
0177名無しさん@お腹いっぱい。01/11/11 16:18ID:???
確かにCで一つ一つ全て別々に作っていたものを
C++で基本部分からの継承で作ると結構遅くなる。
0178名無しさん@お腹いっぱい。01/11/11 16:54ID:???
深い継承は、その時点で設計が間違ってることが多い(MFCとか)。
デザインパターンを勉強するよろし。
0179名無しさん@お腹いっぱい。01/11/11 17:53ID:???
多少遅くなったぐらいで実際に影響が出る環境で作ってる人が多いみたいですね。
コンシューマの方ばかりなのかな?
01801901/11/11 19:53ID:???
業界人手あげてー!

現場ではCとC++の使用比率どれくらいですか?
て前にきいたけど、だれも答えてくれない(泣
01811901/11/11 20:29ID:???
ところで、以前「モジュールの勉強って何?」ときいて恥をかいた漏れです。
結構Cは書いてきたので、処理とデータをまとめてドメインの独立性を高める
努力は必然としてやっていたんですが「構造化プログラミンの勉強」っていうのを
真正面からやったことは確かにない。
で「構造化プログラミング」の参考書を探しに本屋にいったんだけど無いですね、
全然。オブジェクト指向ばっかり。
ためしにオライリーの「C言語実践プログラミング」をぱらぱらと見たら、一応
モジュールとかデータ構造とか書いてあったけどなんか、いまさらなことばかり。
今までやってきた文法以上のCの訓練が実は構造化プログラミングだったてこと
でしょうか。
構造化プログラミングを極めるための本とか、構造化デザインパターンみたいな
ものってあるのでしょうか?
スレ違いでスマソ。

・・・何も買わないのは癪だったので「Game Programing Games」かってしまった。
0182名無しさん@お腹いっぱい。01/11/11 20:35ID:???
>>181
構造化定理って調べてみ
0183ラム01/11/11 20:42ID:BveFsL2V
ageるっちゃ。
01841901/11/11 20:44ID:???
>>182
サンクス、ザクザク出てきた。
この世界も奥深いね。べんきょー!べんきょー!
0185名無しさん@お腹いっぱい。01/11/11 22:05ID:HcMXioW1
仮想関数には関数ポインタのような柔軟性がないのが嫌。
以前、
CTask{
virtual void Update();
};
CEnemy extends CTask
CEnemy1 extends CEnemy
CEnemy2 extends CEnemy
CEnemy3 extends CEnemy
みたいな設計をして失敗した。

やっぱりコアは構造体と関数ポインタに限るよ。
0186名無しさん@お腹いっぱい。01/11/11 22:11ID:BveFsL2V
>>185
それがどういう不都合があったのか教えてもらえれば、
解決方法を提示できるかも風来のシレン。
0187名無しさん@お腹いっぱい。01/11/11 22:40ID:f4mbVjfS
>>186
・コーディング量が増え、生産性が落ちる。
関数ポインタの場合、関数を作ってアドレスを渡すだけだが、
仮想関数の場合、最低限classの定義、仮想関数の定義、コンストラクタの定義が入るため
余計なコーディングが必要になる。
ましてやクラスごとにcppとhを作っていた場合ファイル数がたいへんなことになる。

・動的なメモリ確保を強制される
関数ポインタの場合、関数アドレスを変更するだけで動作を変更できるが、
仮想関数の場合delete,newを呼び出す必要がある。
オーバーヘッドとメモリの断片化が問題となる。

・他クラスを参照している場合の解放タイミングの問題
参照先のタスクが解放済みか否かの判断が複雑になる。
静的に確保された構造体では、消去済みフラグを立てておけば
参照先が消去済みかはその参照先のフラグを調べることで判断できるが、
deleteされたクラスにこれをやると、不正なメモリアクセスになり落ちる。
もちろん解決方法はあるが、そのオーバーヘッドがばかにならない。

他にもいろいろあるがとりあえず代表的なものを。
01881901/11/11 22:49ID:???
ム板のゲーム開発スレでも出てたけど、
オブジェクトは必ずしも不要に成ったらdeleteしなければ成らないわけではない。
まとめてワーク用オブジェクトのプールを作っておいて、フラグ、ID管理をする
こともやろうと思えばできる。
これを実現すれば>>187の2,3番目の問題はある程度解消できるよね。

この変が、ゲーム開発的OOPの特徴にになってkるんじゃないかと、
素人ながらに思っています。
018918701/11/11 22:52ID:f4mbVjfS
後重要なものを忘れていた。
・動的な型情報の取得が簡単にできない。
関数ポインタの場合、function_address == Enemy1?で判断できるが、
クラスの場合CEnemy1クラスであるかどうかの判断が大変面倒になる。
0190名無しさん@お腹いっぱい。01/11/11 22:57ID:???
>>187
どういう場合に型情報が必要ですか?
基本的には抽象化された型情報の実際の型
を知らなくても、振る舞いをさせることが出来るのが
理想な気がしますが…。
0191名無しさん@お腹いっぱい。01/11/11 23:04ID:f4mbVjfS
>>190
特定のタスクを消したい、特定のタスクのみ更新したい。
こうした要求は少なからずある。
MFCでも、IsKindOf(RUNTIME_CLASS())という動的に型情報を取得するための
メソッドが用意されているでしょ?
0192名無しさん@お腹いっぱい。01/11/11 23:07ID:???
>180
んー、ツールではC++使うけど、製品には使わないねぇ。
基本的にCでなんでも出来ちゃうわけだから、「圧倒的に開発効率が良くなる」
とかいうシチュエーションにならない限り使わないと思うなぁ。
出来あがったソースコードはC++の方が綺麗だけど、
他の開発メンバーの書いたコードを理解するのはCの方が
しやすかったり、ヤバめのバグをアセンブラソースレベル
で追跡したりするハメになった時Cの方が楽だったり・・・

ちなみに、スレの本題の「OO」については、言語に
関わらず自然とそうなってる気がしますが。
0193名無しさん@お腹いっぱい。01/11/11 23:15ID:???
完全なOOなんて相当な大規模プロジェクトでない限り、
馬鹿げている。
01941901/11/11 23:19ID:???
>>192
ありがとうございます・・・
やっぱりまだこの業界では「これから」の技術なのでしょうか。
C++に移行してゆく保証もありませんけど。

>>188でもちょっと書きましたが、ゲーム開発にはゲーム開発の独特の
OOPが必要なのは間違いないはずです。
OOではデザインパターンというものが重要な地位を占めてますが、
ゲーム開発的デザインパターンがまとめられて、それに基づいた設計・実装
ができるようなクラスライブラリが整えばC++への移行もあるかも知れない
というところでしょうか。
0195名無しさん@お腹いっぱい。01/11/11 23:19ID:???
>>191
では、そのIsKindOf(RUNTIME_CLASS())(のようなもの)であるとか、
Updateのメソッド自体に更新の判断をさせるほうが良い気がするのですが…。
それではデメリットがあるのですか?
0196名無しさん@お腹いっぱい。01/11/11 23:23ID:+Lh2D+yi
>>195
IsKindOfを実装するには、これまたクラスごとに面倒なコーディングが増えるわけよ。
関数を作って、SetTask(Function)を呼び出すだけとい
お手軽さとは雲泥の差があるわけ。

Updateに判断させる場合、すべてのクラスにその判断処理を実装する必要があるわけでしょ?
それまた面倒でパフォーマンスも悪いわけ。
019718701/11/11 23:29ID:+Lh2D+yi
一応、現在の実装例を書いておこう
CTaskManager{
public:
TASK* SetTask(void* function_ptr);
void Update();
private:
TASK mTask[MAX_TASK];
};

CEnemy{
public:
static void Enemy1(TASK*);
static void Enemy2(TASK*);
static void Enemy3(TASK*);
};
0198_01/11/11 23:34ID:Tn36jYF+
>>192
俺もC++の可読性については、ちょっと同意かなあ。
ちらっと読んでもわからないんだよね、他人のソースが。
継承ツリーの根元にあるメンバなんか、いきなり出されても
作った本人しかわからないよねえ。
グローバル変数使いまくりのCでも、そっちの方が読みやすいというのはあるよねえ。
0199名無しさん@お腹いっぱい。01/11/11 23:34ID:???
C++(OO)は圧倒的にデバッグが楽。って思うのは俺だけ?
特に多人数での開発になると顕著だと思うんだけど。
他のプログラマによる不正な操作を制限しやすいのはかなりメリットだと思う。

Cでもキチンと書けば問題無いんだけど、
なんか致命的かつ再現率の低いバグが出やすい気がする。
↑これは自分がへぼいのは自覚してます。

あとまともに動けば例外処理もかなり強力だと思うざんす。
020019901/11/11 23:36ID:???
圧倒的ってほどでもないですね。誇大表現でした。
0201名無しさん@お腹いっぱい。01/11/11 23:36ID:???
>>196
>Updateに判断させる場合、すべてのクラスにその判断処理を実装する必要があるわけでしょ?
こういう場合、むしろ管理する側がチェックするよりも自分自身が判断する方が
むしろ軽くできそうなすみそうな気がしますけど…(適当でスマソ)
#もちろんこの程度ならC++使わなくても、Cだけでも出来ますけど。
class ab
{
virtual void update(const status& a)=0;
};
class con0 : public ab
{
virtual void update(const status& a){ do_update(); } // これはいつでも更新するクラス
}
class con1 : public ab
{
virtual void update(const status& a){ if(!a->pause){ do_update(); }} // pauseしてないときだけupdate
}

...
for(vector<ab*>::iterator i=obj.begin(); i!=obj.end(); i++){(*i)->update(current_status);}
020218701/11/11 23:46ID:+Lh2D+yi
>>201
いや、
if (pause){
 for (;;){
  if (task[i]->update == PauseTask){
    (*task[i]->update)(&task[i]);
  }
 }
}else{
 for (;;) (*task[i]->update)(&task[i]);
}

条件分岐をループの外に持っていけるし、妙な引数も必要ない分、
圧倒的に有利だよ。
まあPauseの用途に限定するならタスク構造体に属性を持たしておいた方が便利だけど。
0203名無しさん@お腹いっぱい。01/11/11 23:47ID:???
>>199
なんとなく同意です。
自分自身のヘボさを知るものとしてはCよりもデータへの不正な制限を掛けやすい
C++の方がいいかなぁ…と思ってます。
0204名無しさん@お腹いっぱい。01/11/11 23:50ID:???
>>199
俺も楽だと思うよ。
問題が局所化されるから見る範囲がかなり限定されてくる。
まー、何にせよ慣れでしょうね。
Cべったりな人たちには無理せず使い慣れたCがよござんしょ。
020519201/11/12 00:00ID:???
>>194
ツールではバリバリ使うんで業界的に(というか、うちの会社的に)
「これから」っていうわけでもないんですけどね。なんでツールで
使うかといえば、クラスライブラリが用意されてたりして「圧倒的
に効率が上がる」から。

ツールとか実用ソフトの場合、インターフェイスの統一性
が重要ですが、ゲームの場合は寧ろゲーム毎の独自性や面白さ
の方が優先されるわけで、その辺も難しいですね。

あと、コンシューマーの場合リソースの管理をかなり見通しが
効くようにしておかないといけないのが、楽に組めない要因で
すね。快適にプレイさせる為には必要になった時にリソース
確保じゃ遅い(先読み対応)とか・・・
メモリも、「メモリ128MB以上推奨」で済まないし、
「最悪仮想記憶で・・・」というわけにもいかない。
C++でやるにしても、メモリ管理は全部自前でしょうね。
まぁこれは、PCでもそうなんでしょうけど。

なんか、愚痴っぽいな。
0206名無しさん@お腹いっぱい。01/11/12 00:01ID:???
>>202
う〜ん、このあたりは判断すべき条件の場合分けの数であるとか、どこの
オブジェクトがその判断に責任を持つかっていう所で実装を分けるかも
しれないっすね。(そういう意味ではpauseは不適当だったか?)

私が>>202のようなソースを見たときに一番危惧するのは、条件分けが
多岐にわたったときに(pauseだけではなく、アドバタイズであるとか、
デバッグ用のモード(?)であるとか)updateの呼び出しが多個所になって
しまい、たとえばupdateメソッドの呼び出し方が変わった場合、変更が多岐
にわたってしまうんでないかい?とか思ってしまうのです。

#このあたりの考え方は好みに近いところだと思うので、なにが正解とも
#思いませんけど…。
0207名無しさん@お腹いっぱい。01/11/12 00:44ID:tWtzB3Ur
>>206
CTaskManager::Update()
{
 switch(mode){
 case UPDAETMODE_DEFAULT:
  for (;;) (*task[i]->update)(&task[i]);
  break;
 case UPDATEMODE_PAUSE:
  for (;;) (*task[i]->update)(&task[i]);
  break;
 case UPDATEMODE_DEBUG:
  for (;;) (*task[i]->update)(&task[i]);
  break;
 }
}

多岐にわたる理由などありませんが。
0208名無しさん@お腹いっぱい。01/11/12 00:45ID:???
>>207
ヲイヲイ・・・
0209名無しさん@お腹いっぱい。01/11/12 00:46ID:tWtzB3Ur
>>208
何か問題でも?
021020601/11/12 01:07ID:???
>>207
208の煽りは私ではないですよ〜。誤解しないでね。(ニコ

#以下は好みの話し程度に読んでください。
いや、>>202のpauseの時にその呼び出し側に関係ない処理は(OOP抜きとは関係ないですけど)
呼び出し側で制御すべきでないと思うんですよ。(それは例え>>207のような形であっても、結局
caseの数だけupdateメソッドを呼び出す個所が出来るわけで。)
もし一つ一つのオブジェクトで処理を判断するのが重いぜ、やってられないぜというのであれば、

// >>201からのつづきと考えてくださいね
// 文法的に嘘があっても無視の方向で(自虐藁
class ab_collection
{
vector<ab*> abc;
void add_ab(ab* _o){ abc.push_back(_o); }
virtual void update(const status& _stat) = 0;
};
// 見たいな形でオブジェクトをまとめるクラスを一段「かませ」て、
class free_ab_collection : public ab_collection // 判断のひつようなしクラス
{
virtual void update(const status& _stat){
for(vector<ab*>::iterator i=abc.begin(); i!=abc.end(); i++){ (*i)->update(_stat); }
}

class test_ab_collection : public ab_collection // pauseフラグをチェック
{
virtual void update(const status _stat){
if(stat->pause){ return; } // ここでチェックすることで、以下のabの各クラスではチェックする必要が無くなる
for(vector<ab*>::iterator i=abc.begin(); i!=abc.end(); i++){ (*i)->update(_stat);
}

みたいにしてやれば、見通しも良いまま、効率を保つことも可能ではないかと。
(親クラスはab_collectionの塊を持ってupdateを呼び出すだけ。)

実際にプログラムで重いところって大量のメモリのキャッシュミスと回数の非常に多い単純計算
だと思ってるんで、そこさえ気をつければ、見通しよくプログラムするためのコストって許されるんじゃ
ないかと思ってます。
#もちろん、見易さは人によってそれぞれだとおもうんで、>>207さんの方法も尊重します。
021120601/11/12 01:09ID:???
>>210間違い
それぞれのab_collectionから継承させたオブジェクトで
>for(vector<ab*>::iterator i=abc.begin(); i!=abc.end(); i++){ (*i)->update(_stat); }
を呼ぶのは愚の骨頂ですね。ab_collectionに置くべき処理でした。
失礼…。
0212名無しさん@お腹いっぱい。01/11/12 03:36ID:???
少なくともPCゲームのプログラムを組む場合、
C++を利用したことによるオーバーヘッドはほとんど
気にしなくていいような気がします。
0213名無しさん@お腹いっぱい。01/11/12 07:13ID:???
PC(Windows)のゲームでメモリの断片化気にして組まれてるのはどのくらいあるんだろ…。
0214名無しさん@お腹いっぱい。01/11/12 07:56ID:???
というか断片化の話はoperator newのオーバーロードで対処。
CTaskの継承クラスのサイズに最大128バイトとか制限かけて、
メモリプールから確保するようにする。
0215名無しさん@お腹いっぱい。01/11/12 21:17ID:???
>というか断片化の話はoperator newのオーバーロードで対処。
オーバーライドだと思うが…。
細かい突っ込みかな。

malloc自体が仮想メモリを利用することが前提の関数だし、おそらくnewもそうだろうと思います。
仮想メモリがある環境だと、あまりメモリの分断化は気にしないかも。
コンシューマだとガベージコレクトやメモリコンパクションやらを考えないと
いけないでしょうね。
メモリマップを始めにきっちりと作るというのも手だと思いますけど。
0216名無しさん@お腹いっぱい。01/11/12 21:24ID:???
>>215
オーバーロードでも対処できるよ。
newに特殊なパラメータ(フラグ)を取らせてそれによって、デフォルトのnewとは
動作を変える。
この場合、newの動作が暗黙のうちに最適化されるのではなく、プログラマが必要
に応じてメモリ割り当ての方法を選択できる。
うまくやれば、こっちの方が便利かもしれないよ。
0217名無しさん@お腹いっぱい。01/11/12 21:28ID:???
昔、擬似タスク処理をC++で実現しようとしたとき、処理とその処理で使う
メモリ領域をどうすり合わせるかで挫折した。

Cの場合は、構造体内部に関数へのポインタがあればそれを切りかえることで
簡単に実現できた。
C++の場合、上記メソッドのオーバーロードで簡単に実現できるのはひとつの
メソッドだけ。
無理やりやろうとすると、オーバーロードするメソッドの中で
処理するメソッドへのポインタを利用して処理を飛ばすという、
なんとも汚いものになった。
こんなことするくらいなら、構造体+関数へのポインタで処理させたほうがまし
だと思った。
0218名無しさん@お腹いっぱい。01/11/12 21:36ID:???
>>217
ごめん、ちょっとわかりにくい。
「オーバーロードで簡単に実現できるのは1つだけ」というのは
operator newのオーバーロードが1つだけってこと?
0219名無しさん@お腹いっぱい。01/11/12 23:02ID:aPZmJhKt
>処理するメソッドへのポインタを利用して処理を飛ばすという、
>なんとも汚いものになった。

あまり解釈に自信がないんだけど、例えばこういうの? 汚いかなあ。

typedef void(Task::*TaskFunc)();

class Task {
public:
  void SetMethod(TaskFunc updateFunc) { m_updateFunc = updateFunc; }
  void Update() { (this->*m_updateFunc)(); }
private:
  TaskFunc m_updateFunc;
};

class HogeTask : public Task {
public:
  HogeTask() {
    SetFunc((TaskFunc) Func1);
  }
  void Func1() {
    ...
    SetFunc((TaskFunc) Func2);
  }
  void Func2() { ... }
  void Func3() { ... }
};
022021901/11/12 23:06ID:???
こうやってTaskManagerから直接呼び出してもいいか。

class Task {
  friend class TaskManager;
public:
  void SetMethod(TaskFunc updateFunc) { m_updateFunc = updateFunc; }
private:
  TaskFunc m_updateFunc;
};
022121901/11/12 23:07ID:???
ギャース
s/Method/Func/g
0222名無しさん@お腹いっぱい。01/11/13 00:29ID:???
C++のソースはなぜこうも美しく無いのだろうか?
0223名無しさん@お腹いっぱい。01/11/13 00:30ID:???
>>222 Cで同じもん書いてみてみそ。
0224名無しさん@お腹いっぱい。01/11/13 04:25ID:???
Cの人はなぜ、継承だけで解決しようとしたり、
C++で関数ポインタ使うのはイヤみたいに言うのかわからん。
0225名無しさん@お腹いっぱい。01/11/13 10:18ID:???
とりあえず、タスクを1ステップ進行させるメソッドの名前は
みんなUpdateですか。
0226名無しさん@お腹いっぱい。01/11/13 13:13ID:ClbS1Bbe
もうvirtual関数無しではプログラム組めないっす。

ゲーム系だと特に期限ギリギりでの仕様変更が多いので(仕様書な
んてものが存在しない場合も多いけど)、パッチ当てる感覚でその場
がしのげて良いです。

その後の事はしらん(藁。
0227名無しさん@お腹いっぱい。01/11/13 14:05ID:FoEbYEta
>>219
Func1 は
typedef void (HogeTask::*FuncType)();
であって、
typedef void(Task::*TaskFunc)();
ではない。
メンバ関数へのポインタはクラスによってサイズが違うという話を聞いたんだが、
SetFunc((TaskFunc) Func1);
このキャストは安全なのか?
0228名無しさん@お腹いっぱい。01/11/13 14:08ID:???
典型的なC++のタスクと、Game Programming Gemsの3.0みたいな
マクロ使った状態マシン組みあわせてみない?
オレは今から試すにょ。
0229名無しさん@お腹いっぱい。01/11/13 14:17ID:???
>>227
安全じゃないからキャストしてると思われ(w
仕様上は安全じゃない(プログラミング言語C++第3版15.5.1)が、
C++の実装上は上手くいくのではないか。
ちとキモチワルイ感はある。
0230名無しさん@お腹いっぱい。01/11/13 14:36ID:???
ありゃ、キャストしたからってうまくいくのか?
エラーが出そうなものだが。

g++でコンパイルしてみたけど、駄目だった。
コンパイラの問題かな。
0231名無しさん@お腹いっぱい。01/11/13 14:58ID:???
こんなのどう?

#define DECLARE_TASK(TypeName) \
void (TypeName::*m_updateFunc)();\
public:\
virtual void Update(){(this->*m_updateFunc)();}\
private:\

#define SET_TASK_FUNC(FuncName) {m_updateFunc = FuncName;}

class Task
{
public:
  virtual void Update() = 0;
};

class HogeTask : public Task
{
  DECLARE_TASK(HogeTask)
public:
  HogeTask(){
   SET_TASK_FUNC(Func1);
  }

  void Func1(){
   …
   SET_TASK_FUNC(Func2);
  }
  void Func2(){};
};
023221701/11/13 15:31ID:4q6jfi+W
わたしが昔、書いたのは大体こんな感じでした。
219さんのソースを利用させてもらいます。

extern "C" {
#include <stdio.h>
}

//-----------------------------------------------------------
// 基本擬似タスク
//-----------------------------------------------------------
class Task {
public:
 Task() {};
 virtual ~Task() {};
 virtual void SetFunc(void) {}
 virtual void Update(void) {}
};

//-----------------------------------------------------------
// 擬似タスク定義部
//-----------------------------------------------------------
class HogeTask;

typedef void(HogeTask::*TaskFunc)(void);

class HogeTask : public Task {
public:
 HogeTask() { SetFunc(&HogeTask::Func1); }
 ~HogeTask() {};

 void SetFunc(TaskFunc updateFunc) { m_updateFunc = updateFunc; }
 void Update() { (this->*m_updateFunc)(); };

 void Func1() { printf("Func 1\n"); SetFunc(&HogeTask::Func2); }
 void Func2() { printf("Func 2\n"); SetFunc(&HogeTask::Func3); }
 void Func3() { printf("Func 3\n"); SetFunc(&HogeTask::Func1); }
private:
 TaskFunc m_updateFunc;
};
023321701/11/13 15:32ID:4q6jfi+W
続き
//-----------------------------------------------------------
// 擬似タスク実行部
//-----------------------------------------------------------
class ExecuteTask {
 Task *taskArray[100];
 int iTaskPoint;
public:
 ExecuteTask() { iTaskPoint = 0; }
 void SetTask(Task* prtTask) { taskArray[iTaskPoint++] = prtTask; }
 void execute(void) {
  {for(int i = 0; i < iTaskPoint; i++) {
   taskArray[i]->Update(); }
  }
 }
};
023421701/11/13 15:34ID:4q6jfi+W
最後

//-----------------------------------------------------------
// メイン
//-----------------------------------------------------------
int main(int argc, char** argv) {
 HogeTask hogeTask;
 HogeTask hogeTask2;
 ExecuteTask executeTask;

 printf("<<1st>>\n");
 executeTask.SetTask(&hogeTask); // タスク1セット
 executeTask.execute(); // タスク実行

 printf("<<2nd>>\n");
 executeTask.execute(); // タスク実行

 printf("<<3rd>>\n");
 executeTask.SetTask(&hogeTask2); // タスク2セット
 executeTask.execute(); // タスク実行

 return 0;
}
023521701/11/13 15:43ID:???
実際は、タスク実行終了時の資源の解放とか、タスク実行順序とか
いろいろ機能がありました。
ともあれ、結局構造体+関数へのポインタで実装したほうが
分かりやすい上に、パフォーマンスの上という理由で捨てました。
0236名無しさん@お腹いっぱい。01/11/13 15:46ID:???
>>230
VC だとメンバ関数のアドレスはその名前だけでよいが、
C++ の仕様だと &クラス名::関数名 でないとダメ
そこ以外でエラーはでないと思われ
023723001/11/13 20:52ID:???
>236
うむ確かに、うまくいった。
つうか、なぜ気がつかない、俺。

こうなると、219のコードが一番スマートなのかな?
キャストが嫌ってことなら、231になるか。
0238名無しさん@お腹いっぱい。01/11/13 21:09ID:xYb3Zs4n
試しにこんな感じにモデル化してみたんですが、どんなもんですかねえ。
これをいかに楽に記述する方法を考えるかみたいなー。
何かタスク記述言語でも作って、
C/C++ソースに変換するフィルタでも用意するのがええかのう。

タスク {
  タスク変数
  タスクの状態

  タスク作成時の処理
  タスク破棄時の処理
  汎用イベント処理

  状態1 {
    状態の初期化
    更新処理
    状態の後始末
    汎用イベント処理
  }
  状態2 {
    状態の初期化
    更新処理
    状態の後始末
    汎用イベント処理
  }
  ...
}
■ このスレッドは過去ログ倉庫に格納されています