敵クラスから自機クラスの座標を取得する方法

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
FRITIA
記事: 6
登録日時: 3年前

敵クラスから自機クラスの座標を取得する方法

#1

投稿記事 by FRITIA » 3年前

C++の勉強を始めて半年くらいのものです。ポインタはあやふやです。基礎知識であるクラス・抽象クラス・クラス継承などは理解しているつもりです。
本題ですが、自機の座標が正確に取得できなくて困っています。
ControlクラスにPlayerクラスとEnemyクラスのインスタンスを保持し、Controlクラスを継承したPositionクラスで敵や自機の座標を取得して、Positionクラスをシングルトンにしてどこからでも座標を取得できる。という処理を行っていますが、PositionクラスのGetPlayerPosition関数に自機の正確な座標が得られません。Positionクラスで座標を出力してみたところPlayerクラスのコンストラクタで初期化した値が入っていることがわかりました。Enemyクラスで出力したときも同様です。また、Playerクラス・Playerクラスのインスタンスを保持しているControlクラスからは正確に自機の座標を出力できました。
※正確な座標とは、自機が移動した値になっていることです。
以下のサイトと龍神録2を参考にして作成しています。以下のサイトだとControlクラス自体をシングルトンにしています。試しにControlクラスをシングルトンにしたところ正常に動作しました。ですが、設計的にControlクラスをシングルトンにするのは避けたいです(ゲーム画面でControlクラスを動的確保するため)。
https://bituse.info/game/shot/

どなたか解決策をお願いします。

以下ソースコード

main.cpp

コード:

#include "DxLib.h"
#include"Control.h"

int Key[256];

int GetHitKeyStateAll_2(int GetHitKeyStateAll_InputKey[]) {
    char GetHitKeyStateAll_Key[256];
    GetHitKeyStateAll(GetHitKeyStateAll_Key);
    for (int i = 0; i < 256; i++) {
        if (GetHitKeyStateAll_Key[i] == 1) GetHitKeyStateAll_InputKey[i]++;
        else                            GetHitKeyStateAll_InputKey[i] = 0;
    }
    return 0;
}

int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_  HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) {
    ChangeWindowMode(TRUE);//ウィンドウモード
    if (DxLib_Init() == -1 || SetDrawScreen(DX_SCREEN_BACK) != 0) return -1;//初期化と裏画面化

    CONTROL* control = new CONTROL; //コントロールクラスの生成

    while (ProcessMessage() == 0 && ClearDrawScreen() == 0 && GetHitKeyStateAll_2(Key) == 0 && Key[KEY_INPUT_ESCAPE] == 0) {
        //↑メッセージ処理          ↑画面をクリア           ↑入力状態を保存       ↑ESCが押されていない

        control->Update();
        control->Draw();

        ScreenFlip();
    }

    delete control; //コントロールクラスの開放

    DxLib_End();
    return 0;
}
Player.cpp

コード:

#include"Player.h"
#include"DxLib.h"

extern int Key[256];

PLAYER::PLAYER()
{
	LoadDivGraph("../dat/img/char/0.png", 12, 4, 3, 73, 73, m_img);

	m_width = 73;
	m_height = 73;

	m_x = 300;
	m_y = 300;
}

bool PLAYER::Update()
{
	int flag = 0;
	
	if (Key[KEY_INPUT_RIGHT] != 0)
	{
		m_x += 4;
	}
	if (Key[KEY_INPUT_LEFT] != 0)
	{
		m_x -= 4;
	}
	if (Key[KEY_INPUT_DOWN] != 0)
	{
		m_y += 4;
	}
	if (Key[KEY_INPUT_UP] != 0)
	{
		m_y -= 4;
	}

	return true;
}

//プレイヤーの座標をポインタ変数 x1とx2 へ格納(x1とx2は借り引数という)
void PLAYER::PlayerGetPosition(double* x1, double* y2)
{
	*x1 = this->m_x;
	*y2 = this->m_y;
}

void PLAYER::Draw()
{
	//座標から横と縦の幅の半分を引くと座標が中心に移動する
	DrawGraph(m_x - m_width / 2, m_y - m_height / 2, m_img[0], TRUE);
	//Playerの座標を確認する ※正常に動作している。
	DrawFormatString(0, 0, GetColor(255, 255, 255), "自機の座標 X = %f, Y = %f : Playerクラス", this->m_x, this->m_y);
}
Enemy.cpp

コード:

#include"Enemy.h"
#include"Position.h"
#include"DxLib.h"
#include<math.h>

ENEMY::ENEMY()
{
	LoadDivGraph("../dat/img/enemy/0.png", 9, 3, 3, 32, 32, m_img);

	m_width = 32;
	m_height = 32;

	m_x = 300;
	m_y = 100;

	e_cnt = 0;

	enemy_flag = false;
}

void ENEMY::EnemyGetPosition(double* x2, double* y2)
{
	*x2 = this->m_x;
	*y2 = this->m_y;
}

bool ENEMY::Update()
{
	e_cnt++;

	double px, py;

	//プレイヤーの座標を取得 ※現状は取得できない。
	Position::getIns()->GetPlayerPosition(&px, &py);
	//Enemyクラスで取得した座標を確認する
	DrawFormatString(0, 32, GetColor(255, 255, 255), "自機の座標 X = %f, Y = %f : Enemyクラス", px, py);

	if (e_cnt > 60)
	{
		enemy_flag = true;
		
	}
	if (e_cnt > 240)
	{
		m_x = px;
		m_y = py;
	}

	return true;
}

void ENEMY::Draw()
{
	//座標から横と縦の幅の半分を引くと座標が中心に移動する
	if(enemy_flag)
		DrawGraph(m_x - m_width / 2, m_y - m_height / 2, m_img[4], TRUE);

}
Control.cpp

コード:

#include"Control.h"
#include"DxLib.h"

const static double player = 14; //プレイヤーの半径
const static double enemy = 8;   //敵の半径

//それぞれのクラスを生成
CONTROL::CONTROL()
{
	Player = new PLAYER;
	Enemy = new ENEMY;
}

//それぞれのクラスの解放
CONTROL::~CONTROL()
{
	delete Player;
	delete Enemy;
}

void CONTROL::EnemmyCollisionAll()
{
	double px, py, ex, ey;

	//プレイヤーの座標を取得
	Player->PlayerGetPosition(&px, &py);
	//プレイヤーの座標を表示 ※正常に動作している。
	DrawFormatString(0, 96, GetColor(255, 255, 255), "自機の座標 X = %f, Y = %f : Controlクラス", px, py);
	//敵の座標を取得
	Enemy->EnemyGetPosition(&ex, &ey);
	if (CircleCollision(player, enemy, px, ex, py, ey)) //接触したなら
	{
		DrawString(32, 64, "敵と接触しました", GetColor(255, 255, 255)); //接触したと表示
	}
	else
	{
		DrawString(32, 64, "接触していません", GetColor(255, 255, 255)); //接触していないと表示
	}
}

//円形処理  主に弾や弾幕の当たり判定処理
bool CONTROL::CircleCollision(double c1, double c2, double cx1, double cx2, double cy1, double cy2)
{
	double hlength = c1 + c2;
	double xlength = cx1 - cx2;
	double ylength = cy1 - cy2;

	if (hlength * hlength >= xlength * xlength + ylength * ylength) {
		return true;
	}
	else {
		return false;
	}
}

//それぞれのクラスの動作処理
bool CONTROL::Update()
{
	Player->Update();
	Enemy->Update();
	EnemmyCollisionAll();
	return true;
}

//それぞれのクラスの描画処理
void CONTROL::Draw()
{
	Player->Draw();
	Enemy->Draw();
}
Position.cpp

コード:

#include"Position.h"
#include"Player.h"
#include"Enemy.h"
#include<DxLib.h>

Position::Position() : CONTROL()
{
}

void Position::GetPlayerPosition(double* x, double* y)
{
	double px, py;
	//プレイヤーの座標を取得する 
	//※現状プレイヤークラスのコンストラクタで初期化した変数が格納されている。
	//Playerは継承したControlクラスに定義されている。
	Player->PlayerGetPosition(&px, &py);

	*x = px;
	*y = py;

	//Positionクラスで取得した座標を確認する。 ※プレイヤークラスのコンストラクタで初期化した座標が格納されている。
	DrawFormatString(0, 128, GetColor(255, 255, 255), "自機の座標 X = %f, Y = %f : Positionクラス", px, py);
}

void Position::GetEnemyPosition(double* x, double* y)
{

}

アバター
あたっしゅ
記事: 663
登録日時: 13年前
住所: 東京23区
連絡を取る:

Re: 敵クラスから自機クラスの座標を取得する方法

#2

投稿記事 by あたっしゅ » 3年前

>Controlクラスを継承したPositionクラスで敵や自機の座標を取得して、
>Positionクラスをシングルトンにしてどこからでも座標を取得できる。という処理

 クラスが継承されていても、インスタンスが共通になるわけではありません。
 じゃあ、どうしたらいいか。
 Position クラスで、Control クラスのインスタンスを指すようにする

コード:

class Position {
    static Control initCtrl;
    Control*         pCtrl;
                         Position() { pCtrl = initCtrl; }
                         Position(const Position&); // コピーコンストラクタも private に置き、定義しない。
    Position&       operator=(const Position&); // コピー代入演算子も private に置き、定義しない。
                         ~Position() {} // デストラクタを private に置く。

public:
    static Position& getInstance() {
        static Position inst; // private なコンストラクタを呼び出す。

        return inst;
    }

    Control* getCtrl() { return pCtrl; }
    void       setCtrl( Control* ctrl ) { pCtrl = ctrl; }
};
こんなんで、どうでしょう ?

 C++11 以降の時は、

https://ja.wikipedia.org/wiki/Singleton ... 5%E4%BE%8B

を参考にすると吉。

 疲れてるのと、ヘッダーファイル(.h)がないので、回答の正確さよりも、「はやくレスをする」を優先したかきこみになっています。
VTuber:
東上☆海美☆(とうじょう・うみみ)
http://atassyu.php.xdomain.jp/vtuber/index.html
レスがついていないものを優先して、レスするみみ。時々、見当外れなレスしみみ。

中の人:
手提鞄あたッしュ、[MrAtassyu] 手提鞄屋魚有店
http://ameblo.jp/mratassyu/
Pixiv: 666303
Windows, Mac, Linux, Haiku, Raspbery Pi, Jetson Nano, 電子ブロック 持ち。

FRITIA
記事: 6
登録日時: 3年前

Re: 敵クラスから自機クラスの座標を取得する方法

#3

投稿記事 by FRITIA » 3年前

返信が遅くなってすみません。あたっしゅさんありがとうございます。実際にPositionクラスを上記のようにしたところ「" CONTROL" 」から「" CONTROL* "」への適切な変換関数が存在しません。というエラーが発生したので、static CONTROL initCtrlをstatic CONTROL* initCtrlに書き換えたところエラーはなくなりました。あたっしゅさんのコードをを参考に自分で考えてPositionクラスを作成したのですが、Controlクラスでアクセス違反が発生しました。プログラムの意味を完全に理解できていないので修正が難しいです。添削をお願いしてもよろしいでしょうか?今回はヘッダファイルも載せます。また、シングルトンデザインパターンは龍神録さんのを参考にしてそのまま使用しています。最後に理解力が乏しくてすみませんが、「Positionクラスで、Controlクラスのインスタンスを指す」という意味があまり理解できていません。もう少し詳し教えていただけないでしょうか?

要望が多くてすみません。

Singleton.h

コード:

#pragma once

template <typename _T>
class Singleton {

protected:
	Singleton() = default;
	virtual ~Singleton() = default;
	Singleton(const Singleton& r) = default;
	Singleton& operator=(const Singleton& r) = default;

public:
	static _T* getIns() {
		static _T inst;
		return &inst;
	};

};
Position.h

コード:

#pragma once

#include"Singleton.h"
#include"Control.h"

class Position : public Singleton<Position>
{
    static CONTROL* initCtrl;
    CONTROL* pCtrl;
    Position() { pCtrl = initCtrl; }
    ~Position() {}

    friend Singleton<Position>;

public:
    CONTROL* getCtrl() { return pCtrl; }
    void       setCtrl(CONTROL* ctrl) { pCtrl = ctrl; }

    void GetPlayerPosition(double* x, double* y);
    void GetEnemyPosition(double* x, double* y);
};
position.cpp

コード:

#include"Position.h"
#include<DxLib.h>

CONTROL* Position::initCtrl;

void Position::GetPlayerPosition(double* x, double* y)
{
	double px, py;
	getCtrl()->PlayerGetPosition(&px, &py);

	*x = px;
	*y = py;
}

void Position::GetEnemyPosition(double* x, double* y)
{
	double ex, ey;
	getCtrl()->EnemyGetPosition(&ex, &ey);

	*x = ex;
	*y = ey;
}
Control.h

コード:

#pragma once

#include"Player.h"
#include"Enemy.h"

class CONTROL
{
public:
	PLAYER* Player;
	ENEMY* Enemy;
private:
	void EnemmyCollisionAll();
	bool CircleCollision(double c1, double c2, double cx1, double cx2, double cy1, double cy2);
public:
	CONTROL();
	~CONTROL();
public:
	bool Update();
	void Draw();

	void PlayerGetPosition(double* x, double* y);
	void EnemyGetPosition(double* x, double* y);
};
Control.cpp

コード:

#include"Control.h"
#include"Position.h"
#include"DxLib.h"

const static double player = 14; //プレイヤーの半径
const static double enemy = 8;   //敵の半径

//それぞれのクラスを生成
CONTROL::CONTROL()
{
	Player = new PLAYER;
	Enemy = new ENEMY;
}

//それぞれのクラスの解放
CONTROL::~CONTROL()
{
	delete Player;
	delete Enemy;
}

void CONTROL::EnemmyCollisionAll()
{
	double px, py, ex, ey;

	Player->PlayerGetPosition(&px, &py);

	Enemy->EnemyGetPosition(&ex, &ey);

	if (CircleCollision(player, enemy, px, ex, py, ey)) //接触したなら
	{
		DrawString(32, 64, "敵と接触しました", GetColor(255, 255, 255)); //接触したと表示
	}
	else
	{
		DrawString(32, 64, "接触していません", GetColor(255, 255, 255)); //接触していないと表示
	}
}

//円形処理  主に弾や弾幕の当たり判定処理
bool CONTROL::CircleCollision(double c1, double c2, double cx1, double cx2, double cy1, double cy2)
{
	double hlength = c1 + c2;
	double xlength = cx1 - cx2;
	double ylength = cy1 - cy2;

	if (hlength * hlength >= xlength * xlength + ylength * ylength) {
		return true;
	}
	else {
		return false;
	}
}

//自機の座標を取得する関数
void CONTROL::PlayerGetPosition(double* x, double* y)
{
	double tempx, tempy;
	Player->PlayerGetPosition(&tempx, &tempy); //例外がスローされました:読み取りアクセス違反。this が nullptr でした。

	*x = tempx;
	*y = tempy;
}

//敵の座標を取得する関数
void CONTROL::EnemyGetPosition(double* x, double* y)
{
	double tempx, tempy;

	Enemy->EnemyGetPosition(&tempx, &tempy);   //エラーは出ていないけど、ここもアクセス違反が起きていると予想

	*x = tempx;
	*y = tempy;
}

//それぞれのクラスの動作処理
bool CONTROL::Update()
{
	Player->Update();
	Enemy->Update();
	EnemmyCollisionAll();
	return true;
}

//それぞれのクラスの描画処理
void CONTROL::Draw()
{
	Player->Draw();
	Enemy->Draw();
}
Enemy.h

コード:

#pragma once

class ENEMY final
{
private:

	int m_img[9];
	int m_width;
	int m_height;
	double m_x, m_y;

	int e_cnt;

	bool enemy_flag;
public:
	ENEMY();
	bool Update();
	void Draw();

	void EnemyGetPosition(double* x2, double* y2);
};
Enemy.cpp

コード:

#include"Enemy.h"
#include"Position.h"
#include"DxLib.h"
#include<math.h>

ENEMY::ENEMY()
{
	LoadDivGraph("../dat/img/enemy/0.png", 9, 3, 3, 32, 32, m_img);

	m_width = 32;
	m_height = 32;

	m_x = 300;
	m_y = 100;

	e_cnt = 0;

	enemy_flag = false;
}

void ENEMY::EnemyGetPosition(double* x2, double* y2)
{
	*x2 = this->m_x;
	*y2 = this->m_y;
}

bool ENEMY::Update()
{
	e_cnt++;
	double px, py;

	//自機の座標を取得
	Position::getIns()->GetPlayerPosition(&px, &py);

	if (e_cnt > 60)
	{
		enemy_flag = true;
		
	}
	if (e_cnt > 240)
	{
		m_x = px;
		m_y = py;
	}

	return true;
}

void ENEMY::Draw()
{
	//座標から横と縦の幅の半分を引くと座標が中心に移動する
	if(enemy_flag)
		DrawGraph(m_x - m_width / 2, m_y - m_height / 2, m_img[4], TRUE);

}
Player.h

コード:

#pragma once

class PLAYER
{
private:
	int m_img[12];
	int m_width;
	int m_height;
	double m_x, m_y;
public:
	PLAYER();
	bool Update();
	void Draw();

	void PlayerGetPosition(double* x1, double* y1P);
};
Player.cpp

コード:

#include"Player.h"
#include"DxLib.h"

extern int Key[256];

//静的メンバは定義しないとリンカーエラーが起きる(未解決の外部参照)
//double PLAYER::m_x = 0;
//double PLAYER::m_y = 0;

PLAYER::PLAYER()
{
	LoadDivGraph("../dat/img/char/0.png", 12, 4, 3, 73, 73, m_img);
	
	m_width = 73;
	m_height = 73;

	m_x = 300;
	m_y = 300;
}

bool PLAYER::Update()
{
	int flag = 0;
	
	if (Key[KEY_INPUT_RIGHT] != 0)
	{
		m_x += 4;
	}
	if (Key[KEY_INPUT_LEFT] != 0)
	{
		m_x -= 4;
	}
	if (Key[KEY_INPUT_DOWN] != 0)
	{
		m_y += 4;
	}
	if (Key[KEY_INPUT_UP] != 0)
	{
		m_y -= 4;
	}

	return true;
}

//プレイヤーの座標をポインタ変数 x1とx2 へ入れる
void PLAYER::PlayerGetPosition(double* x1, double* y1)
{
	*x1 = this->m_x;
	*y1 = this->m_y;
}

void PLAYER::Draw()
{
	//座標から横と縦の幅の半分を引くと座標が中心に移動する
	DrawGraph(m_x - m_width / 2, m_y - m_height / 2, m_img[0], TRUE);
	//Playerの座標を確認する ※正常に動作している。
	DrawFormatString(0, 0, GetColor(255, 255, 255), "自機の座標 X = %f, Y = %f : Playerクラス", this->m_x, this->m_y);
}
main.cpp

コード:

#include "DxLib.h"
#include"Control.h"

int Key[256];

int GetHitKeyStateAll_2(int GetHitKeyStateAll_InputKey[]) {
    char GetHitKeyStateAll_Key[256];
    GetHitKeyStateAll(GetHitKeyStateAll_Key);
    for (int i = 0; i < 256; i++) {
        if (GetHitKeyStateAll_Key[i] == 1) GetHitKeyStateAll_InputKey[i]++;
        else                            GetHitKeyStateAll_InputKey[i] = 0;
    }
    return 0;
}

int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_  HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) {
    ChangeWindowMode(TRUE);//ウィンドウモード
    if (DxLib_Init() == -1 || SetDrawScreen(DX_SCREEN_BACK) != 0) return -1;//初期化と裏画面化

    CONTROL* control = new CONTROL; //コントロールクラスの生成

    while (ProcessMessage() == 0 && ClearDrawScreen() == 0 && GetHitKeyStateAll_2(Key) == 0 && Key[KEY_INPUT_ESCAPE] == 0) {
        //↑メッセージ処理          ↑画面をクリア           ↑入力状態を保存       ↑ESCが押されていない

        control->Update();
        control->Draw();

        ScreenFlip();
    }

    delete control; //コントロールクラスの開放

    DxLib_End();
    return 0;
}

malch
記事: 4
登録日時: 4年前

Re: 敵クラスから自機クラスの座標を取得する方法

#4

投稿記事 by malch » 3年前

位置情報が欲しいだけなら引数に渡すのはどうですか?

コード:

class ENEMY {
public:
	bool Update(double player_x, double player_y);
};

bool CONTROL::Update()
{
	Player->Update();

	double player_x, player_y;
	Player->PlayerGetPosition(&player_x, &player_y);
	Enemy->Update(player_x, player_y);
	EnemmyCollisionAll();
	return true;
}
あるいはプレイヤーの情報が必要ならプレイヤーのポインタを渡すのはどうですか?

コード:

class PLAYER {
public:
	void PlayerGetPosition(double * x, double * y) const;
};

class ENEMY {
public:
	bool Update(const PLAYER * player);
};
bool ENEMY::Update(const PLAYER * player) {
	dobule px, py;
	player->PlayerGetPosition(&px, &py);
}

bool CONTROL::Update()
{
	Player->Update();
	Enemy->Update(Player);
	EnemmyCollisionAll();
	return true;
}

FRITIA
記事: 6
登録日時: 3年前

Re: 敵クラスから自機クラスの座標を取得する方法

#5

投稿記事 by FRITIA » 3年前

malchさんのコードを参考にPositionクラスを作ってみたところ正常に動作しました。シングルトンにしたPositionクラスを使ってどこからでも敵や自機の座標を取得できるようになりました。同じ考え方で自機の弾の座標、敵の弾の座標を取得する処理を作り問題が発生したらまた質問したいと思います。

プレイヤーのポインタを持つ考えはとても参考になり、勉強になりました。

ありがとうございます。

アバター
usao
記事: 1887
登録日時: 11年前

Re: 敵クラスから自機クラスの座標を取得する方法

#6

投稿記事 by usao » 3年前

> Controlクラスを継承したPositionクラスで敵や自機の座標を取得して、Positionクラスをシングルトンにしてどこからでも座標を取得できる。

そもそもこの「Positionクラス」ってのは一体何のために必要なのだろうか?
というところから考え直した方が良い可能性もありそう.
(全貌を知らないから,なんとなく(?) そのように感じるだけですが)

FRITIA
記事: 6
登録日時: 3年前

Re: 敵クラスから自機クラスの座標を取得する方法

#7

投稿記事 by FRITIA » 3年前

usanoさんのおっしゃる通りシングルトンのPositionクラスを作成して座標を操作するのは設計的に無理でした。なのでEnemyクラスとPlayerクラスそれぞれにgetter()とsetter()を作って座標を受け渡しするやり方に変更しました。安易にシングルトンを使うのは今後控えることにします。

返信

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