キャラクターの当たり判定について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
匿名希望

キャラクターの当たり判定について

#1

投稿記事 by 匿名希望 » 14年前

はじめまして。
こちらには初めて投稿させていただきます。

今、学校の課題でマリオ系の2Dのスクロールアクションゲームを作っています。
その中で、プレイヤーのキャラクターと敵キャラクターが衝突したら、敵キャラクターの生存フラグをFALSEに変更するという仕様があります。

当たり判定まではできたのですが、衝突したときに生存フラグを変更するところだけがうまくいかなくて困っています。

いろいろ試行錯誤してみたのですが、なかなかうまくいかないのでこちらで質問させていただきました。
ポインタの示しているアドレスがおかしい気がしていますが、どこがおかしいのでしょうか?
よろしくお願いいたします。

メイン
Player player;
EnemyManager enemyManager;
HitManager hitManager;

while (1) {
  player.Update();
  enemyManager.Update();
  hitManager.CheckHitPlayerVsEnemy(player, enemyManager)
}

// 主人公
class Player {
public:
  void Update() { キー操作や描画処理 }
}

// 敵キャラのクラス
class Enemy {
private:
  bool isAlive;
public:
  void SetIsAlive(bool alive) { isAlive = alive; }
}

// 敵キャラを扱うクラス
class EnemyManager {
private:
  std::list<Enemy> enemies;
public:
  void Update() { 敵キャラを生成してlistに入れたり、描画処理 }
  std::list<Enemy> GetEnemies() { return enemies; }
}

// 当たり判定を行うクラス
class HitManager {
public:
  void CheckHitPlayerVsEnemy(Player player, EnemyManager enemyManager);
}

void HitManager::CheckHitPlayerVsEnemy(Player player, EnemyManager enemyManager) {
  std::list<Enemy> enemies = enemyManager.GetEnemies();

  for (std::list<Enemy>::iterator it = enemies.begin();it != enemies.end();it++) {
    Enemy* enemy = &*it;

    if (主人公と敵キャラが当たっている) {
      enemy.SetIsAlive(FALSE); // ここでenemyの生存フラグがFALSEになってほしいのに、変更されない
    }
  }
}

匿名希望

Re: キャラクターの当たり判定について

#2

投稿記事 by 匿名希望 » 14年前

見にくかったのでコード表記?にしました。

コード:

//メイン
Player player;
EnemyManager enemyManager;
HitManager hitManager;

while (1) {
  player.Update();
  enemyManager.Update();
  hitManager.CheckHitPlayerVsEnemy(player, enemyManager)
}

// 主人公
class Player {
public:
  void Update() { キー操作や描画処理 }
}

// 敵キャラのクラス
class Enemy {
private:
  bool isAlive;
public:
  void SetIsAlive(bool alive) { isAlive = alive; }
}

// 敵キャラを扱うクラス
class EnemyManager {
private:
  std::list<Enemy> enemies;
public:
  void Update() { 敵キャラを生成してlistに入れたり、描画処理 }
  std::list<Enemy> GetEnemies() { return enemies; }
}

// 当たり判定を行うクラス
class HitManager {
public:
  void CheckHitPlayerVsEnemy(Player player, EnemyManager enemyManager);
}

void HitManager::CheckHitPlayerVsEnemy(Player player, EnemyManager enemyManager) {
  std::list<Enemy> enemies = enemyManager.GetEnemies();

  for (std::list<Enemy>::iterator it = enemies.begin();it != enemies.end();it++) {
    Enemy* enemy = &*it;

    if (主人公と敵キャラが当たっている) {
      enemy.SetIsAlive(FALSE); // ここでenemyの生存フラグがFALSEになってほしいのに、変更されない
    }
  }
}

アバター
a5ua
記事: 199
登録日時: 14年前

Re: キャラクターの当たり判定について

#3

投稿記事 by a5ua » 14年前

オブジェクトを関数に渡して、状態を変更してもらうには、
ポインタもしくは参照を渡す必要があります。

メンバ関数の戻り値としてクラス外部にメンバー変数を公開する場合も、
受け取り先で、状態を変更す場合は、参照(or ポインタ)として返す必要があります。

最小限の修正としては、以下のようになるでしょうか。

コード:

//メイン
Player player;
EnemyManager enemyManager;
HitManager hitManager;
 
while (1) {
  player.Update();
  enemyManager.Update();
  hitManager.CheckHitPlayerVsEnemy(player, enemyManager)
}
 
// 主人公
class Player {
public:
  void Update() { キー操作や描画処理 }
}
 
// 敵キャラのクラス
class Enemy {
private:
  bool isAlive;
public:
  void SetIsAlive(bool alive) { isAlive = alive; }
}
 
// 敵キャラを扱うクラス
class EnemyManager {
private:
  std::list<Enemy> enemies;
public:
  void Update() { 敵キャラを生成してlistに入れたり、描画処理 }
  
  /* コピーではなく参照を返す */
  std::list<Enemy> &GetEnemies() { return enemies; }
}
 
// 当たり判定を行うクラス
class HitManager {
public:
	/* コピーではなく参照を渡す */
  void CheckHitPlayerVsEnemy(Player &player, EnemyManager &enemyManager);
}

/* コピーではなく参照を渡す */
void HitManager::CheckHitPlayerVsEnemy(Player &player, EnemyManager &enemyManager) {
	/* コピーではなく参照を受取る */
  std::list<Enemy> &enemies = enemyManager.GetEnemies();
 
  for (std::list<Enemy>::iterator it = enemies.begin();it != enemies.end();it++) {
    Enemy* enemy = &*it;
 
    if (主人公と敵キャラが当たっている) {
      enemy.SetIsAlive(FALSE); // ここでenemyの生存フラグがFALSEになってほしいのに、変更されない
    }
  }
}

匿名希望

Re: キャラクターの当たり判定について

#4

投稿記事 by 匿名希望 » 14年前

a5uaさん

ご回答ありがとうございます。
受け渡し部分を変更したところ動くようになりました。

コード:

std::list<Enemy>* EnemyManager::GetEnemies() {
  return &enemies;
}
というのは一度試していて、「なんでポインタ返してるのにコピーになるんだ?」と思っていました。
HitManager::CheckHitPlayerVsEnemy() の引数のPlayerとEnemyManagerもポインタにするというのが抜けていたようです。
勉強になります。

>状態を変更す場合は、参照(or ポインタ)として返す必要があります。

参照 または ポインタ をということですが、「こういう場合は参照をかえしたほうが良い」「こういう場合はポインタをかえしたほうが良い」という判断がまだ明確にできていないです。
今回のようなプログラムではどちらを使っても動作は変わらないですし。
参照とポインタの使い分けの基準ってあるのでしょうか?

よろしくお願いいたします。

アバター
a5ua
記事: 199
登録日時: 14年前

Re: キャラクターの当たり判定について

#5

投稿記事 by a5ua » 14年前

参照は以下の特徴を持っています。機能限定版のポインタとも言えます。
・間接演算子(*←これ)で参照はがしをする必要がない
・NULLにはなれない
・再代入ができない

したがって、参照自体を書き換える必要がなく、NULL(何も指していない状態)が不要ならば、参照を使えば良いです。

ほとんどの場合、参照で事足りると思うので、基本的に参照を使うようにしておき、
参照では実現できないケースのときのみ、ポインタを使うようにすれば良いと思います。

hidden

Re: キャラクターの当たり判定について

#6

投稿記事 by hidden » 14年前

参照とポインタの使い分けですが、私は関数の引数として使った時の違いを基準にしています。
int型の変数を 値渡し、参照、ポインタで引数を受け取る三種類の関数を例にするとそれぞれ

コード:

void func1(int);
void func2(int&);
void func3(int*)

int main()
{
	int a=1;

	func1(a);     // 値渡し
	func2(a);     // 参照渡し
	func3(&a);   // ポインタ渡し
…
…
…
}
と書くことになります。このとき、呼び出し側からは a を渡すか &a を渡すかの違いですが
func2は参照渡しなので a の内容を変更することができます。
よってfunc2に渡される a の値は1ですがfunc3に渡される a はfunc2で変更されている可能性があります。

この、参照渡しによる呼び出し側から気がつき辛い値の変更が嫌なので私は
参照は値を変更しない且つコピーするには大きな構造体などを扱う場合に使う(constをつける)
ポインタは値を変更する場合に使う というようにして
変数の値の変動に注意して利用するようにしています。

閉鎖

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