class CA {
public:
int a;
} A;
class CB {
public:
int b;
} B;こんな感じでクラスのインスタンスを作成するとAからbを使う場合はCBとBを関数で言うプロトタイプ宣言する必要があります、可能でしょうか?
class CA {
public:
int a;
} A;
class CB {
public:
int b;
} B;こんな感じでクラスのインスタンスを作成するとAからbを使う場合はclass Kitai
{
/*機体を扱う基底クラス*/
};
class Jiki :public Kitai
{
/*
自機の位置座標、HPなどなど
*/
};
class Boss:public Boss
{
/*
Bossの位置座標などなど
*/
};
int Hantei(Kitai & kitaiA, Kitai & kitaiB)
{
/*
ここで色々判定をする。
*/
};(A.h)
class B;
class A {
protected:
B* m_pB;
public:
// コンストラクタ
A();
};
(A.cpp)
#include "A.h"
// コンストラクタ
A::A()
{
m_pB = NULL;
}
(B.h)
class A;
class B {
protected:
A* m_pA;
public:
// コンストラクタ
B();
};
(B.cpp)
#include "B.h"
// コンストラクタ
B::B()
{
m_pA = NULL;
}
(main.cpp)
#include "A.h"
#include "B.h"
A g_A;
B g_B;
このように記述するとAからB、BからAを参照できる仕組みになっているのが分かるでしょうか?class Kitai {
protected:
float x;
float y;
public:
float getAngle(const Kitai *target) {
return atan2(target->y - y, target->x - x);
}
virtual void Run() = 0;
virtual void SetAngle() = 0;
};
class Player : public Kitai {
float bossAngle;
public:
void Run() {
//
}
void SetAngle() {
bossAngle = getAngle(world.getBoss());
}
// その他必要なもの
};
class Boss : public Kitai {
// その他必要なもの
};
class World {
Kitai* player;
Kitai* boss;
public:
World() {
player = new Player();
boss = new Boss();
}
Kitai* GetBoss() {
return boss;
}
void Run() {
Player.SetAngle();
Player.Run();
Boss.Run();
}
};
手を抜いてクラス宣言に実装を書いていますので、hとcppとに分割してください。継承を使うのならば、基底クラスのポインタの配列にした方が良いのではないのでしょうか。
もしくはlistを使うとか。
------------------------
list<Kitai*> CharaList;
------------------------
そうしないと、オブジェクトが増えるごとにゴチャゴチャしてしまいます。
---------------------------------------------
class World {
Kitai* player;
Kitai* boss;
Kitei* item;
Kitei* Zako;
Kitei* Bullet;
public:
World() {
player = new Player();
boss = new Boss();
item = new Item();
Zako = new Zako();
Bullet = new Bullet();
}
Kitai* GetBoss() {
return boss;
}
void Run() {
Player.SetAngle();
Player.Run();
Boss.Run();
iten.Run();
Zako.Run();
Bullet.Run();
}
};
---------------------------------------------
あと、上記のコードではデストラクタが無いのでメモリリークしてしまいます。
>>チルチルさん
参考になればと。
http://www.tnksoft.com/reading/classgame/>>>自分で実装しても勿論問題ありません。
>>クラスの線形リストってどうやって作るのでしょうか?
ノードの中のデータをintにすればint型の線形リストになりますし、
ノードの中のデータをfloatにすればfloat型の線形リストになります。
同じように、ノードの中のデータを基底クラスのポインタにすれば、クラスの線形リストになります。
それだけのことです。 難しく考える必要はありません。
>>>まぁ、この場合は動的確保などもしていないので、オブジェクトの削除や追加が自由に出来ません。
>>ん?実行中に新たにインスタンスを作成する機会ってあるのでしょうか?
インスタンスを作成したいのであれば、
インスタンスを作成する関数を作って、その関数を呼べばよいのではないのでしょうか。
作成する機会を作りたいのなら、自分で作成する機会を作ればいいのです。
---------------------------------------------------------
void AddObject(Kitai* m){
if(Max < 20){
Object[Max++] = m;
}
}
---------------------------------------------------------
既に述べたように、単純に追加する処理しかしていないので、オブジェクトが20を超えると
追加が出来なくなりますし、削除する機能もありません。
「自由自在に削除や追加が出来ない」と言ったのはそういう意味です。
ともかく、削除や追加を自由に出来る様にしたいのならば、配列ではなくリスト構造にすればいいかと。
>>ああ確かにRunは継承元で宣言されていますね
>>しかしメンバなどの操作とはメンバの値を調べる事も含まれるのでしょうか?
>>座標が知りたいのに角度その他まで返されると困ってしまうのですが・・
オブジェクトを戻り値として受け取って、それからメンバアクセス演算子などでアクセスすればいいと思います。
-------------------------------------------------
・角度を受け取る関数
・位置を受け取る関数
・●●を受け取る関数
:
:
-------------------------------------------------
と言う風にしたら、メンバを追加した文だけそのメンバを取得する関数が増えることになりますし、
明らかに無駄です。
ポインタで受け取って、アロー演算子で特定のメンバを調べるなりした方がいいと思います。
-----------------------------------------
Enemy* e;
e = Enemy型のポインタを返す関数();
e->PtX
e->PtY
など。
-----------------------------------------
>>これは
>>「"Boss"というクラスがあるのは知っていますが、インスタンスされているか教えてください」って事でしょうか?
>>それとも
>>「"Boss"というクラスは存在するのでしょうか?存在するとしたらインスタンスされていますか?」って事でしょうか?
先ほどのコードだと基底クラス型のポインタの配列 Object[20]がありましたよね?
その配列の中に、派生クラスBossが入れられている場所を探し、見つかればそのポインタを返す、
と言う処理だと思って下さい。
>>>あくまでも、メンバを扱うのはそのメンバを持っているクラスです。
>>ではやはりGetBossX(),GetBossY(),GetBossAngle()とかをいちいち作るんでしょうか?
違います。
既に述べたように、メンバごとに関数を作るようにすると、メンバの数だけ関数が増えます。
オブジェクトのポインタを受け取って、そのポインタを通じてメンバを調べるなりした方が良いと思います。
ついでに、当たり判定などは
Playerクラス、Enemyクラスなどの内部で行うのではなく、
それらを一元管理するクラスの方で行った方が良いでしょう。
http://www.tnksoft.com/reading/classgam ... 00/005.php
当たり判定についてはここら辺で解説しているので、コレを参考にして下さい。
こちらでは書きません。
(長くなりますし、2度手間になってしまいます。)>>>その配列の中に、派生クラスBossが入れられている場所を探し、見つかればそのポインタを返す、
>>どうやって探すのでしょうか?
>>世界クラスは基底クラスの構造しか知らないようですが・・
typeid演算子を使えば、基底クラス型のポインタにどのオブジェクトが入っているかを
調べる事ができますよ。
あと、名前と基底クラス型のポインタを構造体にしてしまっておいて、名前で検索するという事も出来ます。
----------------------
typedef struct{
string name; //名前
Kitei* Object; //基底クラス型のポインタ
}ObjectList;
ObjectList Object[20];
----------------------
みたいな。
>>サイトは良くわかりませんでしたが
>>世界クラスの中で自機とボスのポインタから座標にアクセスして
>>当たっているか判断すれば良いって事でしょうか?
「よく分からなかった。」で諦めるのではなく理解できるまで頑張ってみてはどうでしょうか。
よく分からない→質問する→やっぱりよく分からない→質問する→それでもよく分からない→質問する
の繰り返しになって、半永久的に理解が進まない可能性があります。
よくあるのが、下記のようなパターンです。
回答は貰ったけど理解出来ないから諦める → 他の場所で質問しなおす
いわゆる、マルチポストという奴ですね。
ともかく、質問の前に考えることも大事です。
あと、ゲームには
Playerや、Enemy、Bossのようにキャラクターの役割をするものや、
Texture、Sound、Inputのようにキャラクターではないものも存在します。
なので、"世界"クラスに処理を持たせる、ではなく"世界"クラスから、
"キャラクター管理"クラスを派生させ、そのキャラクター管理クラスの中で処理をする、
という風にしたほうが良いでしょう。
>>>Enemy型のポインタを返す関数();
>>これだとボスのクラスに自機やザコのクラスの宣言が見える必要がある気がするんですが・・
そうですね。
先ほどのサイトのソースを見ても分かるように、CEnemyBase型のポインタを使いたい場合は
ヘッダをインクルードします。
------------------------------------------------
#include "EnemyBase.h"
void CPlayerBullet::Exec()
{
/* 弾の移動処理 - 省略 */
// 優先度から敵クラスを列挙する
CPrioEnum pe;
CGameObject *g;
CEnemyBase *e;
CreateEnumeration(ENEMY_PRIORITY, ENEMY_PRIORITY + 10000, &pe);
while(g = pe.GetNext()){
e = dynamic_cast<CENEMYBASE*>(g);
// CEnemyBaseが基底クラスに無ければ処理はしない
if(e == NULL) continue;
if(e->HitTest(this)){
// 敵にヒットしたら弾を消去
RemoveObject(this);
return;
}
}
sprite.Draw(x, y, angle);
}
------------------------------------------------
>>派生クラスで追加した変数とか関数にアクセスしたい時はどうすれば良いのでしょうか?
派生クラス型のポインタを受け取れば、そのポインタを介して
派生クラスのメンバ関数やメンバにアクセスできると思います。
(private属性ならば、アクセッサを定義すればその関数からアクセス出来ます。)
また、基底クラス型のポインタの戻り値に、dynamic_castをすれば派生型のポインタに変換できます。#include <iostream>
#include <list>
typedef void (*HitCallBack)(void*);
struct Rect {
Rect(){}
Rect(int x, int y, int w, int h) {
this->x = x;
this->y = y;
this->w = w;
this->h = h;
}
int x, y, w, h;
};
class HitManager {
struct Data {
Rect rect; // あたり範囲
HitCallBack callBack; // 当たった時に呼び出される関数
void* param; // 関数のパラメータ
};
std::list<Data> dataLeft_;
std::list<Data> dataRight_;
public :
void Clear() {
dataLeft_.clear();
dataRight_.clear();
}
void AddLeft(const Rect& rect, HitCallBack callBack, void* param) {
Data data;
data.rect = rect;
data.callBack = callBack;
data.param = param;
dataLeft_.push_back(data);
}
void AddRight(const Rect& rect, HitCallBack callBack, void* param) {
Data data;
data.rect = rect;
data.callBack = callBack;
data.param = param;
dataRight_.push_back(data);
}
void Check() {
std::list<Data>::iterator it1 = dataLeft_.begin();
for (; it1 != dataLeft_.end(); ++it1) {
std::list<Data>::iterator it2 = dataRight_.begin();
for (; it2 != dataRight_.end(); ++it2) {
// あたり判定
const Rect& rect1 = it1->rect;
const Rect& rect2 = it2->rect;
if ((((rect1.x - rect2.x - rect2.w) & (rect2.x - rect1.x - rect1.w) & (rect1.y - rect2.y - rect2.h) & (rect2.y - rect1.y - rect1.h)) < 0)) {
// 当たっていた場合コールバック呼び出し
it1->callBack(it1->param);
it2->callBack(it2->param);
}
}
}
}
};
class Kitai {
public :
virtual ~Kitai(){}
virtual void Run(HitManager*) = 0;
};
class Player : public Kitai {
// 当たった時の処理を記述
void HitCallBack_() {
std::cout << "プレイヤーあたった" << std::endl;
}
static void HitCallBack(void* p) {
((Player*)p)->HitCallBack_();
}
public :
void Run(HitManager* hitManager) {
// あたり判定クラスに登録
hitManager->AddLeft(Rect(0, 0, 10, 10), HitCallBack, this);
}
};
class Boss : public Kitai {
// 当たった時の処理を記述
void HitCallBack_() {
std::cout << "ボスあたった" << std::endl;
}
static void HitCallBack(void* p) {
((Boss*)p)->HitCallBack_();
}
public :
void Run(HitManager* hitManager) {
// あたり判定クラスに登録
hitManager->AddRight(Rect(0, 0, 10, 10), HitCallBack, this);
}
};
int main() {
Player player;
Boss boss;
HitManager hit;
// 各オブジェクトの処理を実行
player.Run(&hit);
boss.Run(&hit);
// あたり判定チェック
hit.Check();
} class Object
{
private:
int x;
public:
int GetX(){ return x; }
void SetX(int a){ x = a; }
};
このように、一つのメンバ変数に対し、(必要であればですが)セッターおよびゲッターを用意するだけで、struct Rect {
Rect(){}
Rect(int x, int y, int w, int h) {
this->x = x;
this->y = y;
this->w = w;
this->h = h;
}
int x, y, w, h;
};
これって後ろで定義しているint x, y, w, h;が>>選択画面からゲーム画面に飛ぶ時に選択画面を開放すると >>次に選択画面をインスタンスする時に前回どこを選択していたのかわからないと言う問題も出ますし・・ 消さなければいいんですよ。 というより、そういったシーケンス遷移は木構造を思い出してください。 階層が深くなっても、根元から辿って行く方法です。 ---------------------------------- タイトル ┃ ┏┻┓ 選 終 択 了 画 面 ┣┳┓ 初続終 めきわ かかり ららか ┃┃ら ┃┃┃ オコエ |ンン プテデ ニィィ ンニン グュグ | ---------------------------------- 例えば、"選択画面クラス"には下層の"ゲームプレイクラス"を呼び出して実行するようにするのです。 (ここではOP、Continue、Endingの3種類) で、"ゲームプレイクラス"の処理が終了した時、(主人公が死んだりなど) "ゲームプレイクラス"をデストラクトし、また"選択画面"クラスに戻ってくるようにします。 要するに、デストラクトするのは末端からです。現在のシーケンス状態以外のクラスは存在しない(全てデストラクトしている)
class Boss {
private:
public:
void Run(void);
};
class World {
private:
Boss Boss;
public:
int i;
};
World World;
void Boss::Run(void){
World.i=0;
}実際にやってみると包含であって内部クラスではないのでclass World {
private:
public:
void run(void);
};
void World::run(void){
}これはコンパイルが通るんですがclass World {
private:
public:
void Run(void);
};
void World::Run(void){
}runをRunに変えるとclass A
{
public:
static A* instance;
A(){ instace = this; }
}
こうやって、静的なパブリックメンバとして、自身のポインタを用意し、>と言う事はWorld.hでBoss.hやPlayer.hをインクルードしないと
>WorldクラスのメンバにBoss*型やPlayer*型のポインタを宣言できないんじゃないのかなって事です
いいえ
------------------ World.h ----------------------
class Player;
class Boss;
class World {
private :
Player* player_;
Boss* boss_;
};
------------------ World.cpp ----------------------
#include "World.h"
#include "Player.h"
#include "Boss.h"
・・・