Re: クラスtoka ( No.1 ) |
- 名前:アリ 日時:2011/09/08 03:12
friendクラスにしてビュークラスにモデルクラスごと渡してしまうとか
オブジェクト指向的にはあんまりよろしくないですけど・・・
|
Re: クラスtoka ( No.2 ) |
- 名前:tare 日時:2011/09/08 10:56
モデルインスタンスが複数あるなら、モデルクラスにビュークラスを統合してしまったほうがややこしく無いと思われます。
どうしても分離したいならば、ビュークラスにモデルクラスへの参照ModelClass*を持たせましょう。
void ViewClass::RegistModelClass (ModelClass *_model) {
m_Model = _model; // m_ModelはViewClassのメンバ変数
}
あとはViewClassの描画関数を呼び出した際に関数内でm_Model->GetHP()とすればいちいち書く必要はなくなります。
ViewClassインスタンスはモデル毎に用意していてもいいですし、描画配列vector<ModelClass*>などにModelClassを逐一登録して、入れ替わり描画しても良いと思います。
|
Re: クラスtoka ( No.3 ) |
- 名前:クラスクラス 日時:2011/09/08 16:36
私なら、表示のたびにHPを問い合わせるのではなく、
HP表示オブジェクトを作って、モデル側からHPが変化したら、HPを渡すメッセージを出す
(ただのメソッドコールかもね)
とすれば、毎フレームごとにHP表示のたびの問い合わせが無くなる。
表示が軽い、HP表示非表示はモデルに関係なく出来る。HP変化のアニメも表示側で出来る。
等々の利点があり、分離性もよくなる。
実装は、モデル側にHPのオブジェクトポインターを持つか。(あまりお勧めしない、が、簡単なものなら何でもいい)
本当のメッセージキューを作って、完全に分離も出来る。
私は後者を実装している。
|
Re: クラスtoka ( No.4 ) |
- 名前:獅子 日時:2011/09/08 18:17
アリさん、tareさん、クラスクラスさん、返信ありがとうございます。
どれも興味深いのですが、クラスクラスさんの言うものにちょっと驚きました。
初心者なのでびっくりしました。それって、具体的にどう実装したらよいのでしょうか?難しくてわかりません。observerパターンのようなものでしょうか?
よろしければ教えて頂けないでしょうか
|
Re: クラスtoka ( No.5 ) |
- 名前:獅子 日時:2011/09/08 19:13
すいません具体的に言うとメッセージキューとはどう使えばいいのでしょうか?
|
Re: クラスtoka ( No.6 ) |
- 名前:クラスクラス 日時:2011/09/09 12:55
広義の意味でobserverパターンと言ってもいいかもしれません・・・が、
誤解を受けそうな気もするので、私はそうは言いたくは無いですね
単純に言うと、表示ループとゲーム処理ループを別に持った場合の
実装方法の一つです。
ゲーム処理ループ 表示処理ループ
| |
| 何かのキューが有るかチェック
HP変化した → キューを出す → キュー有り対応する処理を行う
| |
次の処理を行う OnUpdateする
| OnDrowする
ゲーム処理ループに戻る 表示処理ループに戻る
このキュー処理に独自の処理を作ってもいいし。List を活用してもいい。
私は高速化のために、独自のキュー処理を実装している。
(new delete 遅いし管理大変だから、独自のキューバッファー処理込みで
高速なキュー処理を)
この方法の利点は、HP表示方法がどんなものでも、モデルに関係が無い点です。
HPが変化した時、表示部分でグニューとHPバーが移動してもいい、位置が変
わってもいい。独立性がとても高い。
今日プロバイダ変わるので今日の13時に撤去。
次にネットにつなげられるのが、1週間以上先かも。
なので、何か有りましたら、お返事できるのが遅いです。
切断前の追記。
表示ループとゲーム処理ループが同一スレッド(同一ループ内)でも、別スレッドでもかまわない。
|
Re: クラスtoka ( No.7 ) |
- 名前:獅子 日時:2011/09/09 13:06
クラスクラスさん返信ありがとうございます。
すみません、根本的な点なのですが、二つのループ処理を別に持つとはどういう風に実装したらよいのでしょうか…(並列処理?ってどうするんでしょうか)
あと、new演算子だと遅いから独自のキューバッファを実装とのことですが、こちらはどう実装したらいいか想像もつきません(´・ω・)スミマセン
よろしければもうすこし具体的に教えて頂けないでしょうか?暇な時で全然いいのでお返事よろしくお願いします
|
Re: クラスtoka ( No.8 ) |
- 名前:クラスクラス 日時:2011/09/15 18:18
今日新しいプロバイダにつながり。
言葉で説明するより、動くソースをのせます。
ただし!!!!!!
最小限の機能のみなので、固定数バッファーだったり、デバッグ機能はない。
本当ならいろいろな管理機能が有るが、一切排除して、最小のプログラムに。
なので、参考としてみて欲しい。
以下は、2つのスレッドで、ゲーム処理ループでは1秒ごとにHPがランダムに決まり、表示ループにキューで送信している。
#include <windows.h>
#include <process.h>
#include "DxLib.h"
//== キュー data =====================================================
class que_data
{
private:
int m_qid; // キューの種類
int m_p1; // キューのパラメータ1 この場合はHP
que_data * m_next; // next
public:
que_data() { this->init(); }
~que_data() { }
void init() {
this->m_qid = 0;
this->m_p1 = 0;
this->m_next = NULL;
}
int get_qid() { return this->m_qid; }
int get_p1() { return this->m_p1; }
void set_used() { this->m_qid = 999; }
void set_data(int qid,int no) {
this->m_qid = qid;
this->m_p1 = no;
}
que_data * get_next() { return this->m_next; }
void set_next(que_data * qdat) { this->m_next = qdat; }
};
//== キュー 管理 =====================================================
#define QMAX (100)
class que_manager
{
private:
CRITICAL_SECTION m_cs; // クリティカルセクションオブジェクト
que_data m_qbuf[QMAX]; // キューバッファー
que_data * m_qstart; // キューの開始
que_data * m_qend; // キューの終了
public:
que_manager() {
InitializeCriticalSection(&this->m_cs);
this->m_qstart = NULL; // キューの開始
this->m_qend = NULL; // キューの終了
}
~que_manager() { }
// キューデータ確保------------------------
que_data * alloc_que() {
que_data * qdp = NULL;
EnterCriticalSection(&this->m_cs); // クリティカルセクションに入る。
for(int i=0;i<QMAX;i++) {
if(this->m_qbuf[i].get_qid() == 0) { // キューIDが0の時は空きデータ
this->m_qbuf[i].set_used(); // キューデータが使用された
qdp = &this->m_qbuf[i];
}
}
LeaveCriticalSection(&this->m_cs); // クリティカルセクションを出る。
return qdp;
}
// キューデータ解放------------------------
void free_que(que_data * qdat) {
EnterCriticalSection(&this->m_cs); // クリティカルセクションに入る。
qdat->init();
LeaveCriticalSection(&this->m_cs); // クリティカルセクションを出る。
}
// キューを入れる (FIFO) ------------------
void push_que(que_data * qdat) {
EnterCriticalSection(&this->m_cs); // クリティカルセクションに入る。
if(this->m_qstart == NULL) {
this->m_qstart = qdat;
this->m_qend = qdat;
} else {
this->m_qend->set_next(qdat);
this->m_qend = qdat;
}
LeaveCriticalSection(&this->m_cs); // クリティカルセクションを出る。
}
// キューを取る (FIFO) -------------------
que_data * pop_que() {
que_data * qdp = NULL;
EnterCriticalSection(&this->m_cs); // クリティカルセクションに入る。
if(this->m_qstart != NULL) {
qdp = this->m_qstart;
this->m_qstart = qdp->get_next();
}
LeaveCriticalSection(&this->m_cs); // クリティカルセクションを出る。
return qdp;
}
};
//========================================================================
que_manager g_que_dat;
//== ゲーム処理ループ ====================================================
unsigned __stdcall game_loop(LPVOID lpParameter)
{
int hp;
que_data * qdp;
while(1) {
hp = GetRand(100); // 1秒ごとに、ランダムにHPを変化させる
// HPが変化したことを表示部にキューで送信する----------
qdp = g_que_dat.alloc_que(); // キューデータ確保
qdp->set_data(1,hp); // キューデータ設定
g_que_dat.push_que(qdp); // キュー送信
Sleep(1000);
}
}
//== main (表示部ループ)================================================
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow )
{
// Dxlibの初期設定----------------
ChangeWindowMode( true ); // ウインドウモードで起動
if( DxLib_Init() == -1 ) return -1; // DXライブラリの初期化
SetDrawScreen( DX_SCREEN_BACK ) ; // 裏画面を描画対象にする
que_data * qdp;
char wstr[200];
// データの初期設定----------------
int new_hp = 100;
int hp = 0;
int hp_color = GetColor( 0 , 0 , 255 ) ; // 青色の値を取得
int FontHandle1 = CreateFontToHandle( NULL , 20 , 3 ,DX_FONTTYPE_NORMAL); // : ノーマルフォント
// ゲーム処理ループのスレッド起動----------------
HANDLE thread_h; // スレッドハンドル
thread_h = (HANDLE)_beginthreadex(NULL, 100000, game_loop , NULL, 0, 0);
if(thread_h == 0) {
return -1; // スレッドが起動できない
}
// 表示部ループ--------------------------------
while( ProcessMessage() == 0 )
{
// キュー処理-------------------------------------
while((qdp = g_que_dat.pop_que()) != NULL) { // キューデータ取出し
if(qdp->get_qid() == 1) {
new_hp= qdp->get_p1(); // HP変化データの受信
}
g_que_dat.free_que(qdp); // キューデータ解放
}
// OnUpdate処理 -----------------------------------
if(hp < new_hp) {
hp += 2;
if(hp > new_hp) {
hp = new_hp;
}
} else if(hp > new_hp) {
hp -= 2;
if(hp < new_hp) {
hp = new_hp;
}
}
// OnDrow処理 -------------------------------------
ClearDrawScreen() ; // 画面を初期化
sprintf_s(wstr,200," HP = %3d / 100",hp);
DrawStringToHandle( 100 , 100 , wstr, GetColor( 255 , 255 , 255 ) , FontHandle1 ) ;
DrawBox( 100 ,120 , hp * 4 + 100 , 150, hp_color , TRUE) ; // 四角形を描画
ScreenFlip() ; // 裏画面の内容を表画面に反映する
Sleep(5);
}
DxLib_End() ; // DXライブラリの後始末
return 0 ;
}
//========================================================================
|
Re: クラスtoka ( No.9 ) |
- 名前:クラスクラス 日時:2011/09/15 18:20
次のプログラムは、1つのスレッドでゲーム処理と表示処理を行っている。
#include <windows.h>
#include <process.h>
#include "DxLib.h"
//== キュー data =====================================================
class que_data
{
private:
int m_qid; // キューの種類
int m_p1; // キューのパラメータ1 この場合はHP
que_data * m_next; // next
public:
que_data() { this->init(); }
~que_data() { }
void init() {
this->m_qid = 0;
this->m_p1 = 0;
this->m_next = NULL;
}
int get_qid() { return this->m_qid; }
int get_p1() { return this->m_p1; }
void set_used() { this->m_qid = 999; }
void set_data(int qid,int no) {
this->m_qid = qid;
this->m_p1 = no;
}
que_data * get_next() { return this->m_next; }
void set_next(que_data * qdat) { this->m_next = qdat; }
};
//== キュー 管理 =====================================================
#define QMAX (100)
class que_manager
{
private:
que_data m_qbuf[QMAX]; // キューバッファー
que_data * m_qstart; // キューの開始
que_data * m_qend; // キューの終了
public:
que_manager() {
this->m_qstart = NULL; // キューの開始
this->m_qend = NULL; // キューの終了
}
~que_manager() { }
// キューデータ確保------------------------
que_data * alloc_que() {
que_data * qdp = NULL;
for(int i=0;i<QMAX;i++) {
if(this->m_qbuf[i].get_qid() == 0) {
this->m_qbuf[i].set_used();
qdp = &this->m_qbuf[i];
}
}
return qdp;
}
// キューデータ解放------------------------
void free_que(que_data * qdat) {
qdat->init();
}
// キューを入れる (FIFO) ------------------
void push_que(que_data * qdat) {
if(this->m_qstart == NULL) {
this->m_qstart = qdat;
this->m_qend = qdat;
} else {
this->m_qend->set_next(qdat);
this->m_qend = qdat;
}
}
// キューを取る (FIFO) -------------------
que_data * pop_que() {
que_data * qdp = NULL;
if(this->m_qstart != NULL) {
qdp = this->m_qstart;
this->m_qstart = qdp->get_next();
}
return qdp;
}
};
//========================================================================
que_manager g_que_dat;
int time_old;
//== ゲーム処理 ====================================================
void game処理()
{
int hp;
que_data * qdp;
int time_new = GetNowCount();
if((time_new - time_old) > 1000) {
hp = GetRand(100); // 1秒ごとに、ランダムにHPを変化させる
// HPが変化したことを表示部にキューで連絡する----------
qdp = g_que_dat.alloc_que(); // キューデータ取出し
qdp->set_data(1,hp); // キューデータ設定
g_que_dat.push_que(qdp); // キューの送信
time_old = time_new;
}
}
//== main (表示部ループ)================================================
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow )
{
// Dxlibの初期設定----------------
ChangeWindowMode( true ); // ウインドウモードで起動
if( DxLib_Init() == -1 ) return -1; // DXライブラリの初期化
SetDrawScreen( DX_SCREEN_BACK ) ; // 裏画面を描画対象にする
que_data * qdp;
char wstr[200];
// データの初期設定----------------
int new_hp = 100;
int hp = 0;
int hp_color = GetColor( 0 , 0 , 255 ) ; // 青色の値を取得
int FontHandle1 = CreateFontToHandle( NULL , 20 , 3 ,DX_FONTTYPE_NORMAL); // : ノーマルフォント
time_old = GetNowCount();
// 表示部ループ--------------------------------
while( ProcessMessage() == 0 )
{
// ゲーム処理呼び出し-----------------------
game処理();
// キュー処理-------------------------------------
while((qdp = g_que_dat.pop_que()) != NULL) {
if(qdp->get_qid() == 1) {
new_hp= qdp->get_p1();
}
g_que_dat.free_que(qdp);
}
// OnUpdate処理 -----------------------------------
if(hp < new_hp) {
hp += 2;
if(hp > new_hp) {
hp = new_hp;
}
} else if(hp > new_hp) {
hp -= 2;
if(hp < new_hp) {
hp = new_hp;
}
}
// OnDrow処理 -------------------------------------
ClearDrawScreen() ; // 画面を初期化
sprintf_s(wstr,200," HP = %3d / 100",hp);
DrawStringToHandle( 100 , 100 , wstr, GetColor( 255 , 255 , 255 ) , FontHandle1 ) ;
DrawBox( 100 ,120 , hp * 4 + 100 , 150, hp_color , TRUE) ; // 四角形を描画
ScreenFlip() ; // 裏画面の内容を表画面に反映する
Sleep(5);
}
DxLib_End() ; // DXライブラリの後始末
return 0 ;
}
//========================================================================
|
Re: クラスtoka ( No.10 ) |
- 名前:クラスクラス 日時:2011/09/15 20:52
この2つの内、前者は2つのスレッドで実際に並列に動いている。
この利点は、表示のFPSにまったく影響されずにゲーム処理部分が書ける。
その他にも多くの利点があるが、ここでは割愛する。
私は、常に前者の設計をしている、その方が楽だし簡単だから。
しかし、技術ノウハウや設計ノウハウは多少高いかもしれない。
並列処理は慣れるまで大変だが、慣れたら楽です。
追記。
ちなみに、何故new deleteを使わないかというと。
遅い理由もあるが、主に、シーンが変わったり、終了や、何かの突然の状態遷移の場合。
表示部に対すキュー処理を一時中止やキャンセルしたい場合。
キューを使うところでnewすると、そのdeleteが大変。
一括管理していれば、キューマネージャに、「これから一時中止ね」とか、「今までのキュークリアして」とか
「シーン変わるから、キュー処理中断」とか簡単にできるのですよ。
キューに対する管理を一括で管理できる。
これの利点は計り知れない。
|
Re: クラスtoka ( No.11 ) |
- 名前:クラスクラス 日時:2011/09/15 23:54
おっと、キューバッファーのオバーフロー処理チェックがないから。
窓が後ろになるとエラーおこります orz
その他にもエラーチェックしていないのできおつけてください。
このエラー起こさないようには下記2行追加。
追加−> if(qdp != NULL) {
qdp->set_data(1,hp); // キューデータ設定
g_que_dat.push_que(qdp); // キュー送信
追加−> }
|
Re: クラスtoka ( No.12 ) |
- 名前:獅子(解決) 日時:2011/09/17 21:38
おお、凄い!返信ありがとうございます!
ナカナカ難しそうですが使えたら便利そうです。頑張って勉強してみます。わざわざソースコードまでありがとうございました!
|
|