ゲームの設計と分割コンパイル (3)について

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

ゲームの設計と分割コンパイル (3)について

#1

投稿記事 by くらいん » 14年前

はじめまして龍神録でSTGをはじめて作り一度 タイトル画面からクリアまでを作ってみたものの
ソース等が汚くなった事と、グローバル関数を使わないほうが良いと書いてあったため、一からプログラムを書き直そうというところです。
ゲームの設計と分割コンパイル (3)まで読み進めていた最中のポインタの箇所で躓いたので質問させていただきます。

プログラムを組みながら少しだけ手を加えながら読み進めていて、ためしに実行してみたところ

error C2660: 'Player_Initialize' : 関数に 2 個の引数を指定できません。

と出ました。
2体プレイヤーを作って並べたいので Initialize に yの値に加えて xの値も設定出来るように引数に追加しましたが サンプルコードと見比べていても xの値を加えているだけでそれ以外同じだと思っています。
なぜエラーになるのか。そしてエラーの対処方法がわからずつまずき中です。

長々と失礼しましたが、エラーの対処法を教えていただければ幸いです。

main.cpp

コード:

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

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
        ChangeWindowMode(TRUE),DxLib_Init(),SetDrawScreen( DX_SCREEN_BACK );

        Player_t Player1, Player2;
        Player_Initialize( &Player1,   0 );// 初期化
        Player_Initialize( &Player2, 200 );// 初期化

        while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 ){

                Keyboard_Update();

                Player_Calc( &Player1 );        // 計算
                Player_Calc( &Player2 );        // 計算

                Player_Graph( Player1 );        // 描画
                Player_Graph( Player2 );        // 描画

        }

        Player_Finalize( Player1 );  // 終了処理
        Player_Finalize( Player2 );  // 終了処理

        DxLib_End();
        return 0;
}
Player.cpp

コード:

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

// 初期化をする
void Player_Initialize( Player_t *Player, int x , int y ){
        Player->Image = LoadGraph("../データ/キャラクター/chara01.png");
        Player->x = x;
		Player->y = y;
}

// 動きを計算する
void Player_Calc( Player_t *Player ){
	if( Keyboard_Get( KEY_INPUT_UP   ) > 0 ){
		Player->y--;
	}
	else if( Keyboard_Get( KEY_INPUT_DOWN ) > 0 ){
		Player->y++;
	}
	else if( Keyboard_Get( KEY_INPUT_LEFT   ) > 0 ){
		Player->x--;
	}
	if( Keyboard_Get( KEY_INPUT_RIGHT   ) > 0 ){
		Player->x++;
	}
}

// 描画する
void Player_Graph( Player_t Player ){
        DrawGraph( Player.x, Player.y, Player.Image, TRUE );
}

// 終了処理をする
void Player_Finalize( Player_t Player ){
        DeleteGraph( Player.Image );
}
Player.h

コード:

#ifndef DEF_PLAYER_H //二重include防止

#define DEF_PLAYER_H

typedef struct{
        int Image;
        int y;
	int x;
} Player_t;


// 初期化をする
void Player_Initialize(Player_t *Player, int y , int x );

// 動きを計算する
void Player_Calc(Player_t *Player );

// 描画する
void Player_Graph( Player_t Player );

// 終了処理をする
void Player_Finalize( Player_t Player );

#endif 
Keyboard.cpp

コード:

#include "DxLib.h"

static int m_Key[256];  // キーの入力状態格納用変数

// キーの入力状態更新
void Keyboard_Update(){
        char tmpKey[256];             // 現在のキーの入力状態を格納する
        GetHitKeyStateAll( tmpKey );  // 全てのキーの入力状態を得る
        for( int i=0; i<256; i++ ){ 
                if( tmpKey[i] != 0 ){ // i番のキーコードに対応するキーが押されていたら
                        m_Key[i]++;   // 加算
                } else {              // 押されていなければ
                        m_Key[i] = 0; // 0にする
                }
        }
}

// KeyCodeのキーの入力状態を取得する
int Keyboard_Get( int KeyCode ){
        return m_Key[ KeyCode ]; // KeyCodeの入力状態を返す
} 
Keyboard.h

コード:

#ifndef DEF_KEYBOARD_H //二重include防止

#define DEF_KEYBOARD_H

// キーの入力状態を更新する
void Keyboard_Update();

// 引数のキーコードのキーの入力状態を返す
int Keyboard_Get( int KeyCode );

#endif 

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: ゲームの設計と分割コンパイル (3)について

#2

投稿記事 by softya(ソフト屋) » 14年前

はじめまして。
関数呼び出しが
Player_Initialize( &Player1, 0 );// 初期化
関数自体は
void Player_Initialize(Player_t *Player, int y , int x );
なので、引数の数があっていないのが原因です。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

くらいん

Re: ゲームの設計と分割コンパイル (3)について

#3

投稿記事 by くらいん » 14年前

早々に返信ありがとうございます。

x軸の位置を メイン.cpp で指定してないのが原因だったのですか。
ためしに 0を加えて実行したところ無事起動しました。
ex)
Player_Initialize( &Player1, 0 ,0 );// 初期化
Player_Initialize( &Player2, 100 ,0 );// 初期化

これからは、サンプルコード等が無く自分自身との戦いになると思いますが、
がんばって脱初心者を目指したいと思います。

アバター
Dixq (管理人)
管理人
記事: 1662
登録日時: 15年前
住所: 北海道札幌市
連絡を取る:

Re: ゲームの設計と分割コンパイル (3)について

#4

投稿記事 by Dixq (管理人) » 14年前

あくまでサンプルなので、main.cpp内で具体的な値を渡していますが、本来であれば、Playerが複数いるのであればPlayerマネージャーが上にいても良いでしょう。
同様に敵が沢山いるならば、敵マネージャーモジュールが敵を管理すれば良いと思います。

というのも、main部はプレイヤーの位置がどこか等知りたくないはずです。
プレイヤーのことはプレイヤーで閉じて行って欲しいので、複数作ったり、作るインスタンスによって値をかえたりしたい時は、プレイヤーマネージャで行うと良いと思います。
つまり処理の伝達順序としては
メイン→プレイヤー管理部→プレイヤー1
            |→プレイヤー2
こうです。
そうすればメインはプレイヤー1や2の具体的な座標情報等知らなくてもよく、そうすれば
今後
・エフェクト
・敵
・ボス
・背景
・弾
・・・・等々大量に扱う要素が増えてもメインが手一杯になって意味不明なコードになることが防げます。

アバター
Dixq (管理人)
管理人
記事: 1662
登録日時: 15年前
住所: 北海道札幌市
連絡を取る:

Re: ゲームの設計と分割コンパイル (3)について

#5

投稿記事 by Dixq (管理人) » 14年前

追加:

言葉だけではなかなか分かりにくいと思いましたので、ゲームプログラミングの館d.6節に説明追加しました。
よろしければご覧ください。
http://dixq.net/g/
(d.6が表示されない時はF5キーを押してください。)

くらいん

追加質問 並びに 感謝コメ

#6

投稿記事 by くらいん » 14年前

 こんばんは、  d.6章 管理部の作り方 の記事を作っていただきありがとうございました。
早速拝見させていただきました。
まだ記事がなかった時、ブログの方のSTGファイルの中身を参考にしていたところ Mgr.cpp があったのでどういう意味のファイルであるか、わからずじまいであやふやの状態で見ていたファイルが その管理部ファイルだったのですね。

これを見てまた見やすいように変更していきたいと思っております。

いきなり話が変わりますが、質問 2点あるのですが、またトピックを書くのが顰蹙だったため
こちらに質問させていただきます。

まず1つめの質問です。

上の解決した後少しずつ改良していたところ、コンパイルするときにエラーが40個ほど出たりでいろいろと悪戦苦闘しながらエラーをなくすことができました。
そうして よし最後のコンパイルだと 思ってコンパイルしているとまた新たに エラーが出てきたのですが、今までとは違いどのように対処すればいいのかわからないため質問させていただきます。


エラーはこれです。

Keyboard.obj : error LNK2005: "struct pad_t pad" (?pad@@3Upad_t@@A) は既に error.obj で定義されています。
Keyboard.obj : error LNK2005: "struct configpad_t configpad" (?configpad@@3Uconfigpad_t@@A) は既に error.obj で定義されています。
main.obj : error LNK2005: "struct pad_t pad" (?pad@@3Upad_t@@A) は既に error.obj で定義されています。
main.obj : error LNK2005: "struct configpad_t configpad" (?configpad@@3Uconfigpad_t@@A) は既に error.obj で定義されています。
Chara.obj : error LNK2005: "struct pad_t pad" (?pad@@3Upad_t@@A) は既に error.obj で定義されています。
Chara.obj : error LNK2005: "struct configpad_t configpad" (?configpad@@3Uconfigpad_t@@A) は既に error.obj で定義されています。
~: fatal error LNK1169: 1 つ以上の複数回定義されているシンボルが見つかりました。


 文章からして 構造体が何回も呼び出されている? と思ったのですが、どのように手をつければいいかがわからず、いろいろと消したり加えたりしたのですがお手上げ状態です。
 ですので、対策やら根本的に構造体の考え方が違うのかも知れませんが、ご指摘・アドバイス・解答の方をよろしくお願いします。



2つ目の質問になりますが、これはいかんせん ファイルがないものですから答えようがないのかなと感じますがご了承ください。

 このプロジェクトを作る前にグローバル関数だけ使わず、自機の移動とボードに写真を載せる所までできました。
そうして実際に自機を動かしてみると、途中でとまりました。
しかしながら ボードに画像を載せていない状態で実行すると動くため、画像にノイズを入れたせいもありましたしそのせいか と思い 画像を変更してみました。 結果またも途中で止まりました。

そこで考えてみたのですが その画像に表示する関数も配置位置も基本的なものを使っていたため、なぜなのかな?と不思議に思い質問させていただきました。

ほんとに長々とすみませんが、以下にソースを張らせていただきます。

main.cpp

コード:

#include "DxLib.h"
#include "GameDefine.h"
#include "Chara.h"
#include "Keyboard.h"
#include "GameBoard.h"
#include "error.h"

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
        ChangeWindowMode(TRUE),DxLib_Init(),SetDrawScreen( DX_SCREEN_BACK );
 
        Player_t Player1, Player2;
        Player_Initialize( &Player1, FIELD_MAX_X*7/16 , FIELD_MAX_Y*3/4 );// 初期化
        Player_Initialize( &Player2, FIELD_MAX_X*9/16 , FIELD_MAX_Y*3/4 );// 初期化
		
		while(ProcessLoop()==0){
			
			Player_Calc( &Player1 );        // 計算
			Player_Calc( &Player2 );        // 計算
			
			Player_Graph( Player1 );        // 描画
			Player_Graph( Player2 );        // 描画
			GameBoard_Graph();
			
			if(CheckStateKey(KEY_INPUT_ESCAPE)==1)break;//エスケープが入力されたらブレイク
			ScreenFlip();//裏画面反映
		}
		
		Player_Finalize( Player1 );  // 終了処理
		Player_Finalize( Player2 );  // 終了処理

		DxLib_End();
		return 0;
}
error.cpp

コード:

#include "DxLib.h"
#include "Keyboard.h"


//ループで必ず行う3大処理
int ProcessLoop(){
    if(ProcessMessage()!=0)return -1;//プロセス処理がエラーなら-1を返す
    if(ClearDrawScreen()!=0)return -1;//画面クリア処理がエラーなら-1を返す
    GetHitKeyStateAll_2();//現在のキー入力処理を行う
	GetHitPadStateAll();  //現在のパッド入力処理を行う
    return 0;
}
error.h

コード:

#ifndef DEF_PLAYER_H //二重include防止
#define DEF_PLAYER_H

#endif 

int ProcessLoop();
Keyboard.cpp

コード:

#include "DxLib.h"
#include "Keyboard.h"
#include "GameDefine.h"

//パッドの初期化フェーズ (Initialize)
void Keyborad_Initialize(){
	configpad.down   =  0;
	configpad.left   =  1;
	configpad.right  =  2;
	configpad.up     =  3;
	configpad.bom    =  4;
	configpad.shot   =  5;
	configpad.slow   = 11;
	configpad.start  = 13;
	configpad.change =  6;
}

// キーの入力状態を更新する
int GetHitKeyStateAll_2(){
    char GetHitKeyStateAll_Key[256];
    GetHitKeyStateAll( GetHitKeyStateAll_Key );
    for(int i=0;i<256;i++){
        if(GetHitKeyStateAll_Key[i]==1) stateKey[i]++;
        else                            stateKey[i]=0;
    }
    return 0;
}

int CheckStateKey(unsigned char Handle){
        return stateKey[Handle];
}

//引数1と引数2のうち大きい方を引数1に代入する
void input_pad_or_key(int *p, int k){
        *p = *p>k ? *p : k;
}
//パッドとキーボードの両方の入力をチェックする関数
void GetHitPadStateAll(){
        int i,PadInput,mul=1;
        PadInput = GetJoypadInputState( DX_INPUT_PAD1 );//パッドの入力状態を取得
        for(i=0;i<16;i++){
                if(PadInput & mul)  pad.key[i]++;
                else                pad.key[i]=0;
                mul*=2;
        }
        input_pad_or_key(&pad.key[configpad.left  ] ,CheckStateKey(KEY_INPUT_LEFT    ));
        input_pad_or_key(&pad.key[configpad.up    ] ,CheckStateKey(KEY_INPUT_UP      ));
        input_pad_or_key(&pad.key[configpad.right ] ,CheckStateKey(KEY_INPUT_RIGHT   ));
        input_pad_or_key(&pad.key[configpad.down  ] ,CheckStateKey(KEY_INPUT_DOWN    ));
        input_pad_or_key(&pad.key[configpad.shot  ] ,CheckStateKey(KEY_INPUT_Z       ));
        input_pad_or_key(&pad.key[configpad.bom   ] ,CheckStateKey(KEY_INPUT_X       ));
        input_pad_or_key(&pad.key[configpad.slow  ] ,CheckStateKey(KEY_INPUT_LSHIFT  ));
        input_pad_or_key(&pad.key[configpad.start ] ,CheckStateKey(KEY_INPUT_ESCAPE  ));
        input_pad_or_key(&pad.key[configpad.change] ,CheckStateKey(KEY_INPUT_LCONTROL));
}
//渡されたパッドキー番号の入力状態を返す。返り値が-1なら不正
int CheckStatePad(unsigned int Handle){
        if(0<=Handle && Handle<PAD_MAX){
                return pad.key[Handle];
        }
        else{
                printfDx("CheckStatePadに渡した値が不正です\n");
                return -1;
        }
}
Keyboard.h

コード:

#ifndef DEF_KEYBOARD_H //二重include防止
 
#define DEF_KEYBOARD_H

#include "GameDefine.h"
static int stateKey[256]; // キーの入力状態格納用変数

#endif

//パッドに関する構造体
typedef struct{
	int key[PAD_MAX];
}pad_t;
pad_t pad;

//コンフィグに関する構造体
typedef struct{
	int left;
	int	up;
	int right;
	int down;
	int shot;
	int bom;
	int slow;
	int start;
	int change;
} configpad_t;
configpad_t configpad;

// 初期化をする
void Keyborad_Initialize(); 


 //現在のキー入力処理を行う
int GetHitKeyStateAll_2();

int CheckStateKey(unsigned char Handle);

void input_pad_or_key(int *p, int k);

void GetHitPadStateAll();

int CheckStatePad(unsigned int Handle);
GameBoard.cpp

コード:

#include "DxLib.h"
#include "GameBoard.h"


void GameBoard_Graph(){
	Image_GameBoard00 = LoadGraph("../データ/GameBoard/GameBoard00.png");
	Image_GameBoard01 = LoadGraph("../データ/GameBoard/GameBoard01.png");
	Image_GameBoard02 = LoadGraph("../データ/GameBoard/GameBoard02.png");
	Image_GameBoard03 = LoadGraph("../データ/GameBoard/GameBoard03.png");
	DrawGraph(   0,  16, Image_GameBoard00, TRUE);
	DrawGraph(   0,   0, Image_GameBoard01, TRUE);
	DrawGraph(   0, 464, Image_GameBoard02, TRUE);
	DrawGraph( 416,   0, Image_GameBoard03, TRUE);
}
GameBoard.h

コード:

#ifndef DEF_KEYBOARD_H //二重include防止

static int Image_GameBoard00;
static int Image_GameBoard01;
static int Image_GameBoard02;
static int Image_GameBoard03;
static int y;
static int x;


#define DEF_KEYBOARD_H
#endif 

void GameBoard_Graph();
Chara.cpp

コード:

#include "DxLib.h"
#include "Keyboard.h"
#include "Chara.h"
#include "GameDefine.h"


// 初期化フェーズ (Initialize)
void Player_Initialize( Player_t *Player, int x , int y ){
        Player->Image = LoadGraph("../データ/キャラクター/chara01.png");
        Player->x = x;
        Player->y = y;
}
 
// 計算フェーズ (Calc)
void Player_Calc( Player_t *Player ){
        int i,sayu_flag=0,joge_flag=0;
        double x,y,mx,my,naname=1;
        double move_x[4]={-4.0,4.0,0,0},move_y[4]={0,0,4.0,-4.0};//{左,右,下,上}のスピード
        int inputpad[4];
        inputpad[0]=CheckStatePad(configpad.left); inputpad[1]=CheckStatePad(configpad.right);
        inputpad[2]=CheckStatePad(configpad.down); inputpad[3]=CheckStatePad(configpad.up);


        if(CheckStatePad(configpad.left)>0)//左キーが押されていたら
			Player->Image+=4*2;//画像を左向きに
        else if(CheckStatePad(configpad.right)>0)//右キーが押されていたら
			Player->Image+=4*1;//画像を右向きに
		
		for(i=0;i<2;i++)//左右分
			if(inputpad[i]>0)//左右どちらかの入力があれば
				sayu_flag=1;//左右入力フラグを立てる
		for(i=2;i<4;i++)//上下分
			if(inputpad[i]>0)//上下どちらかの入力があれば
				joge_flag=1;//上下入力フラグを立てる
		if(sayu_flag==1 && joge_flag==1)//左右、上下両方の入力があれば斜めだと言う事
			naname=sqrt(2.0);//移動スピードを1/ルート2に
		
		for(int i=0;i<4;i++){//4方向分ループ
			if(inputpad[i]>0){//i方向のキーボード、パッドどちらかの入力があれば
				x=Player->x , y=Player->y;//今の座標をとりあえずx,yに格納
				mx=move_x[i];   my=move_y[i];//移動分をmx,myに代入
				if(CheckStatePad(configpad.slow)>0){//低速移動なら
					mx=move_x[i]/3; my=move_y[i]/3;//移動スピードを1/3に
				}
				x+=mx/naname , y+=my/naname;//今の座標と移動分を足す
				if(!(x<10 || x>FIELD_MAX_X-10 || y<5 || y>FIELD_MAX_Y-5)){//計算結果移動可能範囲内なら
					Player->x=x , Player->y=y;//実際に移動させる
				}
			}
		}
}
 
// 描画フェーズ (Graph)
void Player_Graph( Player_t Player ){
        DrawGraph( Player.x, Player.y, Player.Image, TRUE );
}
 
// 終了フェーズ (Finalize)
void Player_Finalize( Player_t Player ){
        DeleteGraph( Player.Image );
}
Chara.h

コード:

#ifndef DEF_PLAYER_H //二重include防止

#define DEF_PLAYER_H
#endif 

//自機に関する構造体
typedef struct{
	int flag;       //フラグ
	int cnt;        //カウンタ
	int power;      //パワー
	int score;      //スコア
	int num;        //残機数
	int mutekicnt;  //無敵状態とカウント
	int shot_mode;  //ショットモード
	int Image;      //画像
	int slow;       //スローかどうか
	int x;          //座標
	int y;          //座標
} Player_t;

 
// 初期化をする
void Player_Initialize(Player_t *Player, int x , int y );
 
// 動きを計算する
void Player_Calc(Player_t *Player );
 
// 描画する
void Player_Graph( Player_t Player );
 
// 終了処理をする
void Player_Finalize( Player_t Player );


P.S. 管理人さんへ 誤字というか書き間違えというか見つけたので一応報告。

2つ目の
↓Player.h (赤字部修正) → ↓Player.cpp (赤字部修正)

を変更です。

beatle
記事: 1281
登録日時: 14年前
住所: 埼玉
連絡を取る:

Re: ゲームの設計と分割コンパイル (3)について

#7

投稿記事 by beatle » 14年前

用語の使い方なんですが,くらいんさんが言いたいのはグローバル関数じゃなくて,恐らくグローバル変数ですね.
グローバル関数というのは普通言いません.C言語なら,普通の関数はグローバルですから.

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: ゲームの設計と分割コンパイル (3)について

#8

投稿記事 by softya(ソフト屋) » 14年前

struct pad_t pad
とか
struct configpad_t configpad
は何処のヘッダにあるんでしょうか?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

くらいん

Re: ゲームの設計と分割コンパイル (3)について

#9

投稿記事 by くらいん » 14年前

beatle さん
あっ そうか グローバル変数だから 値が変わっていけないから、使っちゃいけないことになっているんでしたね。
なんか グローバル関数と変数がすこしごっちゃになっていたみたいです。
ご指摘どうもありがとうございます。 こういうところからきっちりと間違えないようにしていかないと^^;

softya(ソフト屋) さん

構造体って

コード:

 struct _person {           /* _person がタグ名 */
    char name[20];        /* 文字配列型のメンバ name */
    char sex;              /* 文字型メンバ sex */
    int age;               /* 整数型メンバ age */
    double height;         /* 倍精度実数型メンバ height */
    double weight;         /* 倍精度実数型メンバ weight */
};
struct _person p;      /* p という名前の struct _person 型変数を宣言 */

このような書き方と

コード:

 
typedef struct {         /* 構造体の型枠を定義して,同時にそれを型名 person_t として定義する */
    char name[20];
    char sex;
    int age;
    double height; 
    double weight; 
} person_t;

person_t p;             /* 変数名 p の person_t 型変数を宣言 */

のような書き方があると思うんですけど、下の書き方の場合でも


struct _person p; /* p という名前の struct _person 型変数を宣言 */

は必要なのでしょうか?

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: ゲームの設計と分割コンパイル (3)について

#10

投稿記事 by softya(ソフト屋) » 14年前

とりあえずヘッダに

コード:

 struct _person {           /* _person がタグ名 */
    char name[20];        /* 文字配列型のメンバ name */
    char sex;              /* 文字型メンバ sex */
    int age;               /* 整数型メンバ age */
    double height;         /* 倍精度実数型メンバ height */
    double weight;         /* 倍精度実数型メンバ weight */
};
typedef struct {         /* 構造体の型枠を定義して,同時にそれを型名 person_t として定義する */
    char name[20];
    char sex;
    int age;
    double height; 
    double weight; 
} person_t;
は書いても良いです。
上は構造体の宣言で構造体名が_personですね。
typedef structはperson_tと言う型名を宣言しています。

ただ、
struct _person p; /* p という名前の struct _person 型変数を宣言 */

person_t p; /* 変数名 p の person_t 型変数を宣言 */
はpと言う実体を宣言しているのでヘッダに書いてはいけません。
ヘッダに書くなら
extern struct _person p;

extern person_t p;
と書いて下さい。実体はどこかのcppで一箇所だけで行います。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

くらいん

Re: ゲームの設計と分割コンパイル (3)について

#11

投稿記事 by くらいん » 14年前

softya(ソフト屋)さん またもありがとうございます。

書いてあることを読んで ヘッダーファイルに追加してみようと思ったので、
person_t p; を extern person_t p;  このように書け とおっしゃっていたので実際のソースに当てはめてみると

コード:

//パッドに関する構造体
typedef struct{
    int key[PAD_MAX];
}pad_t;
extern pad_t pad;
 
//コンフィグに関する構造体
typedef struct{
    int left;
    int up;
    int right;
    int down;
    int shot;
    int bom;
    int slow;
    int start;
    int change;
} configpad_t;
extern configpad_t configpad;
このように直したところ

Keyboard.obj : error LNK2001: 外部シンボル ""struct configpad_t configpad" (?configpad@@3Uconfigpad_t@@A)" は未解決です。
Chara.obj : error LNK2019: 未解決の外部シンボル "struct configpad_t configpad" (?configpad@@3Uconfigpad_t@@A) が関数 "void __cdecl Player_Initialize(struct Player_t *,int,int)" (?Player_Initialize@@YAXPAUPlayer_t@@HH@Z) で参照されました。
Keyboard.obj : error LNK2001: 外部シンボル ""struct pad_t pad" (?pad@@3Upad_t@@A)" は未解決です。
fatal error LNK1120: 外部参照 2 が未解決です。

こうなったため自分が理解できてないからかとおもい、

コード:

#ifndef DEF_KEYBOARD_H //二重include防止
 
#define DEF_KEYBOARD_H

#include "GameDefine.h"
static int stateKey[256]; // キーの入力状態格納用変数
extern pad_t pad;
extern configpad_t configpad;

#endif


//パッドに関する構造体
typedef struct{
	int key[PAD_MAX];
}pad_t;
pad_t pad;

//コンフィグに関する構造体
typedef struct{
	int left;
	int	up;
	int right;
	int down;
	int shot;
	int bom;
	int slow;
	int start;
	int change;
} configpad_t;
configpad_t configpad;
このようにしてみました。

はい。エラーばっかになりました。

その次にためしたことは、
person_t p; ヘッダに書いてはいけないとあったので
メインソースに書いてみました



以下略

次に試したのは
KeyBoard.cppのはじめに
pad_t pad;
configpad_t configpad;

これを書いてみました。すると

'configpad' の宣言を確認してください。
'pad' の宣言を確認してください。
'configpad_t configpad' : 再定義されました。
'configpad' : 再定義されました。

最後に
Keyboard.obj : error LNK2005: "struct pad_t pad" (?pad@@3Upad_t@@A) は既に error.obj で定義されています。
Keyboard.obj : error LNK2005: "struct configpad_t configpad" (?configpad@@3Uconfigpad_t@@A) は既に error.obj で定義されています。
main.obj : error LNK2005: "struct pad_t pad" (?pad@@3Upad_t@@A) は既に error.obj で定義されています。
main.obj : error LNK2005: "struct configpad_t configpad" (?configpad@@3Uconfigpad_t@@A) は既に error.obj で定義されています。
Chara.obj : error LNK2005: "struct pad_t pad" (?pad@@3Upad_t@@A) は既に error.obj で定義されています。
Chara.obj : error LNK2005: "struct configpad_t configpad" (?configpad@@3Uconfigpad_t@@A) は既に error.obj で定義されています。

あれ・・・ どうすればいいんだろう・・・

というわけで手も足も出ませんでした・・・

くらいん

Re: ゲームの設計と分割コンパイル (3)について

#12

投稿記事 by くらいん » 14年前

くらいん さんが書きました: 最後に
Keyboard.obj : error LNK2005: "struct pad_t pad" (?pad@@3Upad_t@@A) は既に error.obj で定義されています。
Keyboard.obj : error LNK2005: "struct configpad_t configpad" (?configpad@@3Uconfigpad_t@@A) は既に error.obj で定義されています。
main.obj : error LNK2005: "struct pad_t pad" (?pad@@3Upad_t@@A) は既に error.obj で定義されています。
main.obj : error LNK2005: "struct configpad_t configpad" (?configpad@@3Uconfigpad_t@@A) は既に error.obj で定義されています。
Chara.obj : error LNK2005: "struct pad_t pad" (?pad@@3Upad_t@@A) は既に error.obj で定義されています。
Chara.obj : error LNK2005: "struct configpad_t configpad" (?configpad@@3Uconfigpad_t@@A) は既に error.obj で定義されています。

あれ・・・ どうすればいいんだろう・・・

というわけで手も足も出ませんでした・・・
この説明が抜けてたので補足
KeyBoard.cppのはじめに書いた部分に externを加えて実行しました。


extern は他のソースでも使えるようにするための物だったと思うのですが、もはやなにがなんやらさっぱりで・・・

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: ゲームの設計と分割コンパイル (3)について

#13

投稿記事 by softya(ソフト屋) » 14年前

書いた重要なことが読み飛ばされているようですね。
いくつか入門サイトを紹介しますので、良く読んでみて下さい。
大事な事は実体と参照の区別をすること。ヘッダに書くのは参照で実体はcpp側にしか書いてはいけません。

「分割の定石」
http://homepage3.nifty.com/mmgames/c_guide/20-02.html
「ソースファイルとヘッダーファイル」
http://www.kab-studio.biz/Programing/Codian/Cpp/02.html

ついでに変数の寿命とスコープ
「C言語入門前編 - 1章 Cとは 7項.データの記憶クラス -」
http://homepage2.nifty.com/assua/aosite ... o/cb7.html

そうですね。
ヘッダは毎回書くて手間を減らすためだけのものですので、まず変数に関してはヘッダを使わずにエラーが無くなる事に挑戦してみましょう。
構造体宣言と関数プロトタイプだけをヘッダに書いて、変数の実体も変数のextern参照もcppだけに書いてみて下さい。

※ ちなみに変数の実体宣言を「定義」、externや関数プロトタイプを「宣言」と呼びます。これが正式用語ですね。
違いはメモリ上に実体があるか、それの参照にすぎないかの違いです。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

くらいん

Re: ゲームの設計と分割コンパイル (3)について

#14

投稿記事 by くらいん » 14年前

softya(ソフト屋) さん 毎度ありがとうございます。
自分の長文を読み直してみると、日本語がおかしいことがありお恥ずかしい限りです。

まず、リンク先のページを読ませていただき、extern 宣言をして ほかのソースファイルでその定義を書き込めばよいということがわかったため 早速実行してみました。

コード:

//パッドに関する構造体
typedef struct{
    int key[PAD_MAX];
}pad_t;
extern pad_t pad;
 
//コンフィグに関する構造体
typedef struct{
    int left;
    int up;
    int right;
    int down;
    int shot;
    int bom;
    int slow;
    int start;
    int change;
} configpad_t;
extern configpad_t configpad;
そうしてKeyboard.cppにも

はじめの方に
pad_t pad;
configpad_t configpad;
を書き込んだところ無事デバックが完了しました。

起動してみるとあら重いと思いながらも無事動きました。
なんどもいろいろな方にお世話になり、いろいろとよい勉強になりました。
この場を借りてお礼申し上げます

とりあえず自機キャラの管理部も作りましたので、
後は敵や弾でも同じようにやればいいと思うのでなんとかがんばっていきたいと思っております。

ではまた機会がありましたら、よろしくお願いします。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: ゲームの設計と分割コンパイル (3)について

#15

投稿記事 by softya(ソフト屋) » 14年前

externは解決してよかったです。
ただ、プログラム的に問題が幾つか見受けられるので書いておきます。 [追記]今まで混乱するの保留にしてました。
・error.cppのファイル名とProcessLoopは全く名前の関連性が見受けられないのでよくないファイル分割です。
・多重インクルード防止で#ifndef~#endifの外側になにか書かれています。意味が無いので全部#ifndef~#endifの間に書いて下さい。
・Player_InitializeがあるのにGameBoard_Initializeがない。GameBoard_Graphでロードするのは一番やってはいけないことです。
・staticな変数をヘッダに書いてますね。staticな変数はcppに書くものです。
以上です。
わからないことがあれば聞いて下さいね。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

くらいん

Re: ゲームの設計と分割コンパイル (3)について

#16

投稿記事 by くらいん » 14年前

・error.cppのファイル名とProcessLoopは全く名前の関連性が見受けられないのでよくないファイル分割です。
→ これについてはerror.cppをなくして main.cpp に書き加えて処理いたしました。

・多重インクルード防止で#ifndef~#endifの外側になにか書かれています。意味が無いので全部#ifndef~#endifの間に書いて下さい。
→  d.6章 管理部の作り方の記事を見ていたときに気がついたためこれについても書き直しました。
ただ、Gameboard.h の部分だけ中に入れると 定義されていませんと表示されるためとりあえず 外に書いたままにしてあります。

'GameBoard_Initialize': 識別子が見つかりませんでした
'GameBoard_Graph': 識別子が見つかりませんでした

コード:

#define DEF_KEYBOARD_H

void GameBoard_Initialize();
void GameBoard_Graph();
この部分が灰色になってるからエラーになっているのかな?と考えています・・・

・Player_InitializeがあるのにGameBoard_Initializeがない。GameBoard_Graphでロードするのは一番やってはいけないことです。
→描画フェーズの時にロードしてボードに張ればよいのかと思っていたので、今までそうしていました。
だから重かったのかな?
そのため対策としまして、
初期フェーズに ロードし
描画フェーズに 描画するように変更いたしました。

・staticな変数をヘッダに書いてますね。staticな変数はcppに書くものです。
→ はい。 直しました。

何から何までありがとうございます。今のところは、実行して自機を動かすと消えるため、
描画フェーズがわるいか 計算フェーズがわるいかのどちらかなので
がんばって見ます。 たぶんポインタの理解があやふやだからとは思っているのですが・・・

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: ゲームの設計と分割コンパイル (3)について

#17

投稿記事 by softya(ソフト屋) » 14年前

くらいん さんが書きました:・多重インクルード防止で#ifndef~#endifの外側になにか書かれています。意味が無いので全部#ifndef~#endifの間に書いて下さい。
→  d.6章 管理部の作り方の記事を見ていたときに気がついたためこれについても書き直しました。
ただ、Gameboard.h の部分だけ中に入れると 定義されていませんと表示されるためとりあえず 外に書いたままにしてあります。
GameBoard.hで
#ifndef DEF_KEYBOARD_H //二重include防止
で名前が間違っているの原因です。
くらいん さんが書きました:何から何までありがとうございます。今のところは、実行して自機を動かすと消えるため、
描画フェーズがわるいか 計算フェーズがわるいかのどちらかなので
がんばって見ます。 たぶんポインタの理解があやふやだからとは思っているのですが・・・
これが怪しいです。
Player->Image+=4*2;//画像を左向きに
Player->Imageに入っているのは画像ハンドル番号なので足したり引いたりしてはいけません。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

くらいん

Re: ゲームの設計と分割コンパイル (3)について

#18

投稿記事 by くらいん » 14年前

ありがとうございます。
順調にがんばっております。といいたいところなのですが、
自機の移動のところで躓いたままでなのです・・・
softya(ソフト屋) さんが書きました:
くらいん さんが書きました:・多重インクルード防止で#ifndef~#endifの外側になにか書かれています。意味が無いので全部#ifndef~#endifの間に書いて下さい。
→  d.6章 管理部の作り方の記事を見ていたときに気がついたためこれについても書き直しました。
ただ、Gameboard.h の部分だけ中に入れると 定義されていませんと表示されるためとりあえず 外に書いたままにしてあります。
GameBoard.hで
#ifndef DEF_KEYBOARD_H //二重include防止
で名前が間違っているの原因です。
はい 直しましたら うまくできました。
多分 新しくヘッダーファイルを作ったときにめんどくさがって、コピペをしたからだと思います。
気をつけます><
softya(ソフト屋) さんが書きました: これが怪しいです。
Player->Image+=4*2;//画像を左向きに
Player->Imageに入っているのは画像ハンドル番号なので足したり引いたりしてはいけません。
はいハンドル番号 アドレス? 番地? いろいろな言い方があるのだと思いますが、
文字列だったと思うので足してもだめだということだと思います。
そのとおりで はじめはPlayer.Image だったのですが引数が*Playerだったのでだめなのかなと思い
アローをつけました。

とりあえず 少し分割してみたのですがやはり無理だったので、ソースを書かせていただきます。
参考にしたのは、
http://dixq.net/g/d_06.html 「管理部の作り方 (似た要素のまとめ方) 」
http://dixq.net/rp/9.html 「キャラクタ移動制御」

Chara.cpp

コード:

 
#include "DxLib.h"
#include "Keyboard.h"
#include "Chara.h"
#include "GameDefine.h"

// 初期化フェーズ (Initialize)
void Player_Initialize( Player_t *Player , int x , int y , int img ){
        Player->Image = img;
        Player->x     = x;
        Player->y     = y;
}
 
// 計算フェーズ (Calc)


void Player_Calc( Player_t Player ){
        Player.cnt++;
        Player.Image=(Player.cnt%24)/6;
		if(CheckStatePad(configpad.left)>0)//左キーが押されていたら
			Player.Image+=4*2;//画像を左向きに
        else if(CheckStatePad(configpad.right)>0)//右キーが押されていたら
			Player.Image+=4*1;//画像を右向きに
}


void Player_Move( Player_t *Player ){
        int i,sayu_flag=0,joge_flag=0;
        double x,y,mx,my,naname=1;
        double move_x[4]={-4.0,4.0,0,0},move_y[4]={0,0,4.0,-4.0};//{左,右,下,上}のスピード
        int inputpad[4];
        inputpad[0]=CheckStatePad(configpad.left); inputpad[1]=CheckStatePad(configpad.right);
        inputpad[2]=CheckStatePad(configpad.down); inputpad[3]=CheckStatePad(configpad.up);
		for(i=0;i<2;i++)//左右分
			if(inputpad[i]>0)//左右どちらかの入力があれば
				sayu_flag=1;//左右入力フラグを立てる
		for(i=2;i<4;i++)//上下分
			if(inputpad[i]>0)//上下どちらかの入力があれば
				joge_flag=1;//上下入力フラグを立てる
		if(sayu_flag==1 && joge_flag==1)//左右、上下両方の入力があれば斜めだと言う事
			naname=sqrt(2.0);//移動スピードを1/ルート2に
		for(int i=0;i<4;i++){//4方向分ループ
			if(inputpad[i]>0){//i方向のキーボード、パッドどちらかの入力があれば
				x=Player->x , y=Player->y;//今の座標をとりあえずx,yに格納
				mx=move_x[i];   my=move_y[i];//移動分をmx,myに代入
				if(CheckStatePad(configpad.slow)>0){//低速移動なら
					mx=move_x[i]/3; my=move_y[i]/3;//移動スピードを1/3に
				}
				x+=mx/naname , y+=my/naname;//今の座標と移動分を足す
				if(!(x<10 || x>FIELD_MAX_X-10 || y<5 || y>FIELD_MAX_Y-5)){//計算結果移動可能範囲内なら
					Player->x=x , Player->y=y;//実際に移動させる
				}
			}
		}		
}
 
// 描画フェーズ (Graph)
void Player_Graph( Player_t Player ){
        DrawGraph( Player.x, Player.y, Player.Image, TRUE );
}
 
// 終了フェーズ (Finalize)
void Player_Finalize( Player_t Player ){
	DeleteGraph( Player.Image );
}
 
Chara.h

コード:

#ifndef DEF_CHARA_H //二重include防止
#define DEF_CHARA_H


//自機に関する構造体
typedef struct{
	int flag;       //フラグ
	int cnt;        //カウンタ
	int power;      //パワー
	int score;      //スコア
	int num;        //残機数
	int mutekicnt;  //無敵状態とカウント
	int shot_mode;  //ショットモード
	int Image;      //画像
	int slow;       //スローかどうか
	int x;          //座標
	int y;          //座標
} Player_t;
extern Player_t Player;
 
// 初期化をする
void Player_Initialize( Player_t *Player , int x , int y , int img );

// 動きを計算する
void Player_Graphmove( Player_t Player);

void Player_Calc( Player_t Player );

void Player_Move( Player_t *Player );
 
// 描画する
void Player_Graph( Player_t Player );
 
// 終了処理をする
void Player_Finalize( Player_t Player );

#endif 
Chara_Mgr.cpp

コード:

#include "DxLib.h"
#include "Keyboard.h"
#include "Chara.h"

static const int NUM = 2;        //プレイヤーの数

static Player_t m_Player[NUM];   //プレイヤーの実体
static int m_ImgPlayer;          //プレイヤーの画像ハンドル

// 初期化をする
void PlayerMgr_Initialize(){
    m_ImgPlayer = LoadGraph("../データ/キャラクター/chara01.png");

    Player_Initialize( &m_Player[0], FIELD_MAX_X*7/16 , FIELD_MAX_Y*3/4 , m_ImgPlayer );// 初期化
    Player_Initialize( &m_Player[1], FIELD_MAX_X*9/16 , FIELD_MAX_Y*3/4 , m_ImgPlayer );// 初期化
}

// 動きを計算する
void PlayerMgr_Calc(){
    for( int i=0; i<NUM; i++ ){
		Player_Calc( &m_Player[i] );//更新
		Player_Move( &m_Player[i] );//更新
    }
}

// 描画する
void PlayerMgr_Graph(){
    for( int i=0; i<NUM; i++ ){
        Player_Graph(m_Player[i]);//描画
    }
}

// 終了処理をする
void PlayerMgr_Finalize(){
    for( int i=0; i<NUM; i++ ){
        Player_Finalize(m_Player[i]);//終了処理
    }

    DeleteGraph( m_ImgPlayer );    //画像を解放
}
Chara_Mgr.h

コード:

#ifndef DEF_CHARA_MGR_H //二重include防止
#define DEF_CHARA_MGR_H

// 初期化をする
void PlayerMgr_Initialize();

// 動きを計算する
void PlayerMgr_Calc();

// 描画する
void PlayerMgr_Graph();

// 終了処理をする
void PlayerMgr_Finalize();

#endif
です。 main.cppは省略しています。あとキーボード系統も・・・ あったほうがやっぱいいのかなとは思いますが、長いのもどうかと思ったので・・・

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: ゲームの設計と分割コンパイル (3)について

#19

投稿記事 by softya(ソフト屋) » 14年前

くらいん さんが書きました:はいハンドル番号 アドレス? 番地? いろいろな言い方があるのだと思いますが、
文字列だったと思うので足してもだめだということだと思います。
そのとおりで はじめはPlayer.Image だったのですが引数が*Playerだったのでだめ
完全に勘違いされていますが、Player.Imageあるいは、Player->Imageは画像ハンドル(番号)が格納されている必要があります。
この値はLoadGraphの戻り値のままである必要がありますのでPlayer.Image+=4*2;などの計算をおこなってはいけません。
それと「アドレス? 番地?」ではありません。文字列でもありません。数値データです。

[追記]
すごく簡単なプログラムでLoadGraph()やLoadDivGraph()したハンドルの扱いを色々試されたほうが良いと思います。
左右移動だけならすごくシンプルなプログラムになるので問題点も分かりやすいでしょう。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
Dixq (管理人)
管理人
記事: 1662
登録日時: 15年前
住所: 北海道札幌市
連絡を取る:

Re: ゲームの設計と分割コンパイル (3)について

#20

投稿記事 by Dixq (管理人) » 14年前

あまりにコードが長くなる時は、プロジェクトをまるごと.zipにしてアップした方が見やすいのではないでしょうか。
(その時.sdf等は消して下さい)

->Imageに格納されているのは、softyaさんが仰るようにアドレス等ではありません。
館でも公式HPでもデータハンドルだと書いてあると思います。

セルフサービスの食堂に行ったと思って下さい。
くらいんさんはラーメンを注文し、「食券番号10番でお待ち下さい」と言われたとします。
そして待ってる間にラーメン大が食べたくなったとしても、勝手に自分の番号を9番にして取りに行ったりできませんよね。
9番はかつ丼かもしれないし、定食かもしれない。全く10番と関連性は無いし、勝手に自分の番号を変えてもいけません。

それと同じです。
あくまでも識別番号です。

くらいん

Re: ゲームの設計と分割コンパイル (3)について

#21

投稿記事 by くらいん » 14年前

softya(ソフト屋) 
いつもありがとうございます。
なにかごちゃごちゃになっているみたいです・・・

 前に変数とは一般に整数などをいれておく「いれもの」だという話をしましたが、いれものに入れられた情報は、メモリ上のどこかに置かれるはずです。メモリが実際にどのように作られているかは、特別な人以外知らないわけですが、イメージだけは持っていましょう。それは、情報をいれておく箱の列のようなものです。そして、メモリの個々の箱には場所を表す番地がついているのです。これをアドレスといいます。このアドレスを格納する変数をポインタというのです。
http://www.asahi-net.or.jp/~yf8k-kbys/newcpp20.html

このようなところだけを覚えていたのでなんか変なことになっていたのだと思います。


[追記]
すごく簡単なプログラムでLoadGraph()やLoadDivGraph()したハンドルの扱いを色々試されたほうが良いと思います。
左右移動だけならすごくシンプルなプログラムになるので問題点も分かりやすいでしょう。

簡単なプログラムを作ってやってみたのですが、とりあえず4方向だけ動くように そうすると作成はできるのですが、応用?みたいなモジュールやらポインタが出てきていろいろと対処ができなくなってしまいます><


Dixq (管理人) さん
あまりにコードが長くなる時は、プロジェクトをまるごと.zipにしてアップした方が見やすいのではないでしょうか。
(その時.sdf等は消して下さい)

了解しました。 ソリュージョンファイルは必要なのかがわからないため今回は、ソースファイルと画像データをrar形式で圧縮してみました。


->Imageに格納されているのは、softyaさんが仰るようにアドレス等ではありません。
館でも公式HPでもデータハンドルだと書いてあると思います。

はい。

セルフサービスの食堂に行ったと思って下さい。
くらいんさんはラーメンを注文し、「食券番号10番でお待ち下さい」と言われたとします。
そして待ってる間にラーメン大が食べたくなったとしても、勝手に自分の番号を9番にして取りに行ったりできませんよね。
9番はかつ丼かもしれないし、定食かもしれない。全く10番と関連性は無いし、勝手に自分の番号を変えてもいけません。
それと同じです。
あくまでも識別番号です。

わかりやすい説明ありがとうございます。
理解はできました・・・
ですが、モジュール作成はポインタが出てきてから挫折しそうな感じがあります。

とりあえず 少し移動計算がややこしかったので簡素化してみました。
別にソースは長くないのですが、迷惑だと思ったため今回はソースのみ圧縮してあります。
ご教授いただける方はお手数ですが解凍の方をおねがいします。

すみません このサイトにアップロードできるのかも知りませんが、やり方をぱっと見た限り見つけられなかったので、他のサイトのアップローダーをお借りしました。

アップロードサイトは迷ったので ファイルストレージにしました。

http://firestorage.jp/download/3b46b4f7 ... ecd25ffb03

くらいん

Re: ゲームの設計と分割コンパイル (3)について

#22

投稿記事 by くらいん » 14年前

すみません 追記です。

敵が1体表示されるようなエネミーファイルを作ったのですが、
エラーはおきなかったのですが、敵が表示されません。

カウンタ、描画、計算 どれかがおかしいと思うのですが
足りないものがわからないので質問させていただきます。

尚いまはコンパイル途中で自機のほうがエラーになる状態ですので、
実行ファイルをつくって確かめることができませんことをご了承ください。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: ゲームの設計と分割コンパイル (3)について

#23

投稿記事 by softya(ソフト屋) » 14年前

ご自分の文章と他の人の書いた文章が混じって読み辛いのでメールのように>で引用を意味させるかquatetタグで引用しているところを明確にお願いします。
それとC言語の文法を逸脱しますがC++の文法である参照を使うと混乱が防げるかも知れません。DXライブラリは元々C++の文法が使えます。

こんな風に書きます。

コード:

// 動きを計算する
void Player_Calc( Player_t &Player );

// 計算フェーズ (Calc)
// 動きを計算する
void Player_Calc( Player_t &Player ){
	if( Key[ KEY_INPUT_RIGHT ] >= 1 ){ // 右キーが押されていたら
		Player.x++;                       // 右へ移動
	}
	if( Key[ KEY_INPUT_DOWN  ] >= 1 ){ // 下キーが押されていたら
		Player.y++;                       // 下へ移動
	}
	if( Key[ KEY_INPUT_LEFT  ] >= 1 ){ // 左キーが押されていたら
		Player.x--;                       // 左へ移動
	}
	if( Key[ KEY_INPUT_UP    ] >= 1 ){ // 上キーが押されていたら
		Player.y--;                       // 上へ移動
	}
}

//呼び出し
PlayerMgr_Calc(m_Player[i]);//更新
メリットはポインタと違って書き方を変えなくて良い点ですね。
ソースコードについては後ほど。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: ゲームの設計と分割コンパイル (3)について

#24

投稿記事 by softya(ソフト屋) » 14年前

ソースを見ましたが今までの話の内容が理解されていないとしか思えません。
enemy[0].img =LoadGraph( "../データ/キャラクター/enemy01.png" ); ← 画像ハンドルを入れています。
enemy.img=enemy.muki*3+(enemy.cnt%18)/6; ← mukiとcntで計算した数値データを入れています。画像ハンドルとは無関係です。

それとLoadGraphで読み込んだ画像は1つだけのハンドルです。複数の画像で構成されている画像はLoadDivGraph()で読み込まなければいけませんし画像ハンドルも配列にする必要があります。
まず、imgと言う名前を止めてhandleと名前にしてみましょう。勘違いが減ると思います。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
Dixq (管理人)
管理人
記事: 1662
登録日時: 15年前
住所: 北海道札幌市
連絡を取る:

Re: ゲームの設計と分割コンパイル (3)について

#25

投稿記事 by Dixq (管理人) » 14年前

ファイル添付は当サイトに登録していただければ行えます。
画面右下に登録画面へのリンクがあります。
よければご登録下さい。

くらいん
記事: 11
登録日時: 14年前

Re: ゲームの設計と分割コンパイル (3)について

#26

投稿記事 by くらいん » 14年前

Dixq (管理人) さんが書きました:ファイル添付は当サイトに登録していただければ行えます。
画面右下に登録画面へのリンクがあります。
よければご登録下さい。
はい。登録させていただきました。
ためしにソースを添付してみます。
softya(ソフト屋) さんが書きました:ソースを見ましたが今までの話の内容が理解されていないとしか思えません。
enemy[0].img =LoadGraph( "../データ/キャラクター/enemy01.png" ); ← 画像ハンドルを入れています。
enemy.img=enemy.muki*3+(enemy.cnt%18)/6; ← mukiとcntで計算した数値データを入れています。画像ハンドルとは無関係です。

はい。なんというかすみません。 わからなくなり、HPのサンプルを写しているだけになっていて何も考えていなかったようです。  
http://www.play21.jp/board/formz.cgi?ac ... &rln=57472
このページを見ながら考えてみたところ自分の写真は1枚絵のため分割の必要性がないため、
enemy.img=enemy.muki*3+(enemy.cnt%18)/6;
この部分は必要ないと考えました。

softya(ソフト屋) さんが書きました:それとLoadGraphで読み込んだ画像は1つだけのハンドルです。複数の画像で構成されている画像はLoadDivGraph()で読み込まなければいけませんし画像ハンドルも配列にする必要があります。
まず、imgと言う名前を止めてhandleと名前にしてみましょう。勘違いが減ると思います。

ハンドルにしてみようかとおもったのですが、
GameBoardではハンドル名を Image_GameBoard00
自機は最初ハンドル名を Image_Chara00 にしていたので
それに習って統一したほうが言いかと思い
Image_Enemy01 このようにハンドル名をおいてみました。

softya(ソフト屋) さんが書きました:ご自分の文章と他の人の書いた文章が混じって読み辛いのでメールのように>で引用を意味させるかquatetタグで引用しているところを明確にお願いします。

はい、今回はこのようにしてみました。

softya(ソフト屋) さんが書きました: それとC言語の文法を逸脱しますがC++の文法である参照を使うと混乱が防げるかも知れません。DXライブラリは元々C++の文法が使えます。

こんな風に書きます。

コード:

// 動きを計算する
void Player_Calc( Player_t &Player );

// 計算フェーズ (Calc)
// 動きを計算する
void Player_Calc( Player_t &Player ){
	if( Key[ KEY_INPUT_RIGHT ] >= 1 ){ // 右キーが押されていたら
		Player.x++;                       // 右へ移動
	}
	if( Key[ KEY_INPUT_DOWN  ] >= 1 ){ // 下キーが押されていたら
		Player.y++;                       // 下へ移動
	}
	if( Key[ KEY_INPUT_LEFT  ] >= 1 ){ // 左キーが押されていたら
		Player.x--;                       // 左へ移動
	}
	if( Key[ KEY_INPUT_UP    ] >= 1 ){ // 上キーが押されていたら
		Player.y--;                       // 上へ移動
	}
}

//呼び出し
PlayerMgr_Calc(m_Player[i]);//更新
メリットはポインタと違って書き方を変えなくて良い点ですね。
ソースコードについては後ほど。
はい 参照についてはよく知らなかったのでとりあえず調べてみたりソフト屋さんのソースを見て書き換えたりしてみたのですが、やはりエラーになるため どうすればいいのかなあと。
もうポインタも参照もよくわからなくなってきました(泣
とりあえず調べてみたところ
アローを使わないでいけることやポインタよりは易化している?実引数と仮引数について
などがかいてありました。
あまり理解していないようなのでまた後で調べようと思っています。
どんどん自信がなくなってきているので、いままでのソースを見ていたのですが、
よくわからない箇所があったので質問です。
static Player_t m_Player[NUM]; //プレイヤーの実体
static int m_ImgPlayer; //プレイヤーの画像ハンドル

この意味がわからなくて質問させていただきました
Player_t Player でプレイヤーの実体をあらわしたと思ってたのですが、ここでなぜまたプレイヤーの実体を宣言しているのか。
Player->Image = img; //画像ハンドルの格納
ここらへんで画像ハンドルは決めたのではないかな?と思っていたのですが何でだろう・・・と。

とういうことで詳しいことは上に書いてあるとして簡単に言いますと質問点は、
①自機についてエラーになるため どうすればよいか

②敵が出ない。とりあえず描画は直してみたのだが、まだ出ない (ハンドル情報がまだ間違っている?)

③static Player_t m_Player[NUM]; //プレイヤーの実体
static int m_ImgPlayer; //プレイヤーの画像ハンドル
この意味がわからない

この3点です。なおエラーコードは下記に記載。そしてソースは添付


エラーコード
ゲーム\chara_mgr.cpp(20): error C2660: 'PlayerMgr_Calc' : 関数に 1 個の引数を指定できません。
ゲーム\chara_mgr.cpp(27): error C2660: 'PlayerMgr_Graph' : 関数に 1 個の引数を指定できません。
画像
添付ファイル
圧縮用.rar
(346.96 KiB) ダウンロード数: 167 回

くらいん
記事: 11
登録日時: 14年前

Re: ゲームの設計と分割コンパイル (3)について

#27

投稿記事 by くらいん » 14年前

画像が貼れてなかった?ので再投稿 こんどはプレビューで画像も確認。
画像

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: ゲームの設計と分割コンパイル (3)について

#28

投稿記事 by softya(ソフト屋) » 14年前

①自機についてエラーになるため どうすればよいか

コード:

// 動きを計算する
void PlayerMgr_Calc(){
    for( int i=0; i<NUM; i++ ){
		PlayerMgr_Calc(m_Player[i]);//更新
    }
}
なぜか自分自身を呼び出しています。
void PlayerMgr_Calc(){

PlayerMgr_Calc(m_Player);
は同じ名前ですよね?
Player_Calcを呼び出したかったんだと思いますがミスをしたのは名前の付け方が混乱を生む原因なのかも知れません。
PlayerMgr_Calcを、もっとわかり易い名前に変更されてはどうでしょうか?

残りは後で。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
Dixq (管理人)
管理人
記事: 1662
登録日時: 15年前
住所: 北海道札幌市
連絡を取る:

Re: ゲームの設計と分割コンパイル (3)について

#29

投稿記事 by Dixq (管理人) » 14年前

ファイル添付を提案したのは、回答者にソースを見やすく提供する意味があったので、VC++のプロジェクトファイルがあるなら、一緒に添付した方が良いと思います。
現在のコードはモジュール名がファイル名になっていないので、関数がどこに存在するのか予測できないです。

・Playerモジュールなら、Playerと、
PlayerMgrモジュールならPlayerMgrとファイル名を付ける方が良いと思います。

・関数名が小文字で始まったり大文字で始まったりしています。

・graph_developは何を意味するのかメソッド名から予測しにくいです。

・モジュール内の変数はInitialize関数内で初期化すべきです。

まだ全て見られていませんが以上が少し気になりました。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: ゲームの設計と分割コンパイル (3)について

#30

投稿記事 by softya(ソフト屋) » 14年前

②敵が出ない。とりあえず描画は直してみたのだが、まだ出ない (ハンドル情報がまだ間違っている?)
原因ですが、Enemy_Initialize()は最初の1回しか呼ばれないのでstage_countが100になることはありません。なので敵は発生しません。

さて、あと気になるところを羅列します。
・static enemy_t enemy[ENEMY_MAX] = {0};として初期化して欲しい。今のままだと初期化せずに使っています。
・通過ポイントを確認したかったら、DrawFormatStringやPrintDXなど表示機能を使ってif文の通過点でちゃんと分岐しているかとか呼ばれるタイミングを確認すること。
「簡単RPG講座 番外編。 デバッグ入門 • C言語交流フォーラム ~ mixC++ ~」 ← 私の書いたデバッグ技法入門です。
http://dixq.net/forum/blog.php?u=114&b=982&c=2
・static int stage_count;はEnemy.cppだけで有効な変数なので、誰もカウントアップしてません。なので100になることは永久にありません。
③static Player_t m_Player[NUM]; //プレイヤーの実体
static int m_ImgPlayer; //プレイヤーの画像ハンドル
この意味がわからない

この意味がわからなくて質問させていただきました
Player_t Player でプレイヤーの実体をあらわしたと思ってたのですが、ここでなぜまたプレイヤーの実体を宣言しているのか。
Player->Image = img; //画像ハンドルの格納
ここらへんで画像ハンドルは決めたのではないかな?と思っていたのですが何でだろう・・・と。
extern Player_t Player;はありますが、Player_t Player;は何処にもありません。なのでextern Player_t Player;の実体は現在ありません。
関連してstatic Player_t m_Player[NUM];はファイル内スコープですのでChara_Mng.cpp外部から参照もできませんが引数で渡しているので問題ないと思われます。
なのでextern Player_t Player;は不要ではないでしょうか?

static int m_ImgPlayer;は上に書いた通りファイル内スコープです。
引数でPlayer_Initializeに渡してPlayer.Image に代入しているので、これで問題ないと思います。
ただし、

コード:

void Player_Finalize( Player_t Player ){
	DeleteGraph( Player.Image );
}
でハンドルをDeleteGraphするのは

コード:

void PlayerMgr_Finalize(){
    for( int i=0; i<NUM; i++ ){
        Player_Finalize(m_Player[i]);//終了処理
    }

    DeleteGraph( m_ImgPlayer );    //画像を解放
}
で同じハンドルを何度もDeleteGraphすることになるのでPlayer_FinalizeでDeleteGraphせずに
DeleteGraph( m_ImgPlayer ); //画像を解放
だけで済ますべきです。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

くらいん
記事: 11
登録日時: 14年前

Re: ゲームの設計と分割コンパイル (3)について

#31

投稿記事 by くらいん » 14年前

いつもお世話になっております。
またご指南をいただけたのでコメ返信。
softya(ソフト屋) さんが書きました:Player_Calcを呼び出したかったんだと思いますがミスをしたのは名前の付け方が混乱を生む原因なのかも知れません。
PlayerMgr_Calcを、もっとわかり易い名前に変更されてはどうでしょうか?
考えて見たのですが、ぱっと見て意味がわかるような名前が思いつかなかったので今回はこのままで行かせてください;
softya(ソフト屋) さんが書きました:Enemy_Initialize()は最初の1回しか呼ばれないのでstage_countが100になることはありません。なので敵は発生しません。
敵の情報登録を計算フェーズに移行し、初期フェーズには画像のロードだけにしました。
softya(ソフト屋) さんが書きました:static enemy_t enemy[ENEMY_MAX] = {0};として初期化して欲しい。今のままだと初期化せずに使っています。
enemy.cpp の初期フェーズに  static enemy_t enemy[ENEMY_MAX] = {0};  を加えました。
softya(ソフト屋) さんが書きました:extern Player_t Player;はありますが、Player_t Player;は何処にもありません。なのでextern Player_t Player;の実体は現在ありません。
関連してstatic Player_t m_Player[NUM];はファイル内スコープですのでChara_Mng.cpp外部から参照もできませんが引数で渡しているので問題ないと思われます。
なのでextern Player_t Player;は不要ではないでしょうか?
extern宣言は実体を呼ばずに形だけを伝達するプロトタイプ宣言だったと思うので必要ないと
自分も理解できましたので、消してみました。
softya(ソフト屋) さんが書きました:で同じハンドルを何度もDeleteGraphすることになるのでPlayer_FinalizeでDeleteGraphせずに
DeleteGraph( m_ImgPlayer ); //画像を解放
だけで済ますべきです。
はい。館のHPの管理部の作り方の記事に付属していたプロジェクトファイルのPlayer.cppに

// 終了処理をする
void Player_Finalize( Player_t Player ){
DeleteGraph( Player.Image );
}

このように書かれていたので何も考えず写していました。><
ですから、 DeleteGraph( Player.Image ); この部分を消しました。
softya(ソフト屋) さんが書きました:static int stage_count;はEnemy.cppだけで有効な変数なので、誰もカウントアップしてません。なので100になることは永久にありません。
はい。もし100カウントになったら敵情報登録を少し分割しもし100カウントになったらの部分をmain.cppに書き
カウントアップも移動しました。
softya(ソフト屋) さんが書きました:通過ポイントを確認したかったら、DrawFormatStringやPrintDXなど表示機能を使ってif文の通過点でちゃんと分岐しているかとか呼ばれるタイミングを確認すること。
「簡単RPG講座 番外編。 デバッグ入門 • C言語交流フォーラム ~ mixC++ ~」 ← 私の書いたデバッグ技法入門です。
blog.php?u=114&b=982&c=2
printfを使ってやってみたのですが、
dxライブラリの公式サイトに書かれている
... : 書式付き文字列に付随する引数 がprintfでいいのかわからなかったのでとりあえずおいてみた所
if前・
if中・
のようになり出力できてないようなので諦め、
ブレークポイントの方を使わせていただくと、敵情報登録でブレークポイント設定するときちんとデバック停止になるが、計算フェーズまで進ませると、カウントが停止せずにずっと進んだままになったらめ、諦めました。

ついでにメインソースに敵の画像の読み込み書き込みをそのままコピーして座標だけ変更したところ表示されたため、計算フェーズがおかしいのかなと思っています。
Dixq (管理人) さんが書きました:ファイル添付を提案したのは、回答者にソースを見やすく提供する意味があったので、VC++のプロジェクトファイルがあるなら、一緒に添付した方が良いと思います。
現在のコードはモジュール名がファイル名になっていないので、関数がどこに存在するのか予測できないです。
了解しました。モジュール名がわからないのですが多分自分で作るフォルダ名のことかな と勝手に理解。

Dixq (管理人) さんが書きました: Playerモジュールなら、Playerと、PlayerMgrモジュールならPlayerMgrとファイル名を付ける方が良いと思います。
上記の繰り返しになってしまいますが、分からないので割愛。
フォルダ名のことであれば、今はCharaフォルダの中にChara.cpp Chara.h Chara_Mgr.cpp Chara.h が混在しております。
Dixq (管理人) さんが書きました: 関数名が小文字で始まったり大文字で始まったりしています。
できるだけそろえてみました。 ヘッダーファイルに書いた関数名は対応させて全て大文字に直しました。
Dixq (管理人) さんが書きました: graph_developは何を意味するのかメソッド名から予測しにくいです。
はい、名前を変えようと思っていましたが、少しmain.cppの書き方を変えたら

DrawFormatString(0,0,GetColor(255,255,255),"%d",stage_count); //カウンタ表示

とmain.cppに書くことになってしまったため まあそれでもいいかな と。
Dixq (管理人) さんが書きました: モジュール内の変数はInitialize関数内で初期化すべきです。
またまた繰り返しになりますが、すみませんモジュールが分かりません・・・><


指摘された箇所はできる限りなおし、main.cppをswich文に変えてみました。 少し見やすくなった気がします。
そうしてカウンタも表示されるようになりめでたしめでたしとなりたいのです。

しかしながら、自機と敵の表示ができません>< 尚エラーは特にないのです
ソフト屋さんのデバック方法がわかれば少しは進展するのかもしれませんが・・・

ほぼ100%計算フェーズに間違えがあると考えております。

フォルダの階層分け
http://photozou.jp/photo/photo_only/2017877/121833206
前回の画像が直リンだと添付できますが、定期的にアドレスが変わるようなのですみません。
添付ファイル
圧縮用.rar
(349.8 KiB) ダウンロード数: 162 回

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: ゲームの設計と分割コンパイル (3)について

#32

投稿記事 by softya(ソフト屋) » 14年前

ソースはまだ見てませんが、返信で怪しい所だけ書きます。
enemy.cpp の初期フェーズに  static enemy_t enemy[ENEMY_MAX] = {0};  を加えました。
変数のスコープを紹介したと思うのですが、関数内や{}内に定義した変数はブロック外では見えません。
なのでもし初期フェーズでstatic enemy_t enemy[ENEMY_MAX] = {0};していると他の関数から見えるenemyは別の変数ということになります。
はい。もし100カウントになったら敵情報登録を少し分割しもし100カウントになったらの部分をmain.cppに書き
カウントアップも移動しました。
これも怪しいのでソースを見てコメントします。
printfを使ってやってみたのですが、
dxライブラリの公式サイトに書かれている
... : 書式付き文字列に付随する引数 がprintfでいいのかわからなかったのでとりあえずおいてみた所
if前・
if中・
のようになり出力できてないようなので諦め、
ブレークポイントの方を使わせていただくと、敵情報登録でブレークポイント設定するときちんとデバック停止になるが、計算フェーズまで進ませると、カウントが停止せずにずっと進んだままになったらめ、諦めました。
printfと言うことは、コンソールウィンドウを使われたのでしょうか?
ソースにこの処理を埋め込んだままアップロードしてくださいね。
これもソースを見てみます。
了解しました。モジュール名がわからないのですが多分自分で作るフォルダ名のことかな と勝手に理解。
これは関数名の共通部分(モジュール名)とファイル名が一致しないと言うことです。
モジュールとはある機能単位のまとまりを言います。PlayerMngで始まるならPlayerMgrというモジュールと言うことですね。
PlayerMgr系の関数があるならファイル名もPlayerMgrになっていないとファイル名と関数が一致しないので探しづらいと言うことです。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: ゲームの設計と分割コンパイル (3)について

#33

投稿記事 by softya(ソフト屋) » 14年前

main.cpp

コード:

				 if(stage_count == 100 ){
					 void Enemy_register(); //敵の情報登録
				 }
void Enemy_register();は関数のプロタイプ宣言なので関数呼び出しはされません。
これはブレークポイントをEnemy_register側に設定してみると分かります。

そういえば、Enemy_registerとEnemy_moveは_の後が小文字ですね。

Enemy.cpp

コード:

enemy_t enemy[ENEMY_MAX];

//初期フェーズ
//敵の情報登録
void Enemy_Initialize(){
	Image_Enemy01 =LoadGraph( "../データ/キャラクター/enemy01.png" );
	static enemy_t eneny[ENEMY_MAX] = {0};
}
最初のenemy配列とEnemy_Initialize内のenemy配列は別メモリ空間を持つ配列で関連が一切ありません。
最初のものをstatic enemy_t eneny[ENEMY_MAX] = {0};にしてEnemy_Initialize関数からは削除して下さい。

ブレークポイントなどの使うべきポイント
Enemy.cpp

コード:

void Enemy_register(){
	enemy[0].cnt      = 0;
	enemy[0].muki     = 1;
	enemy[0].flag     = 1;
	enemy[0].bltime   = 150;
	enemy[0].hp       = 1000;
	enemy[0].hp_max   = enemy[0].hp;
	enemy[0].pattern  = 0;
	enemy[0].x        = FIELD_MAX_X/2;
	enemy[0].y        = 100;
}	← ここに貼れば発生することが確認できます。ついでに変数のウオッチで変数の値も確認しましょう。

//敵の行動制御
void Enemy_move(){
	int i;	← ここに貼れば関数が呼び出されていることが確認できます。
	for(i=0;i<ENEMY_MAX;i++){
		if(enemy[i].flag==1){//その敵のフラグがオンになってたら
			Enemy_pattern0(i);	
			enemy[i].cnt++;			← ここに貼れば発生して動作している事が確認できます。
			//敵が画面から外れたら消す
			if(enemy[i].x<-50 || FIELD_MAX_X+50<enemy[i].x || enemy[i].y<-50 || FIELD_MAX_Y+50<enemy[i].y){
				enemy[i].flag=0;
			}
		}
	}
}

//描画フェーズ
void Enemy_Graph(){
	int i;	← ここに貼れば関数が呼び出されていることが確認できます。
	for(i=0;i<ENEMY_MAX;i++){
		if(enemy[i].flag==1){
			DrawGraph( enemy[i].x+FIELD_MIN_X , enemy[i].y+FIELD_MIN_Y , Image_Enemy01, TRUE );		← ここに貼れば描画されていることが確認できます。
		}
	}
}
Playerが描画されないのはmain.cppで

コード:

			 PlayerMgr_Finalize();    //プレイヤー管理モジュールの終了処理
			 ScreenFlip();            //裏画面反映
で毎フレーム画像を破棄しているのが原因です。これもブレークポイントをちゃんと使えば確認できます。

[追記]ブレークポイントの練習に、直す前にブレークポイントで動作をまず確認してみて下さい。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

くらいん
記事: 11
登録日時: 14年前

Re: ゲームの設計と分割コンパイル (3)について

#34

投稿記事 by くらいん » 14年前

返信が遅くなって申し訳ありません。
ご指摘くださった点を直しましたところ無事、敵が上から動いてきて、自機も思うように動くようになりました。
あとは今まで作ったソースを参照しながら、自弾や敵弾など作っていこうと思っております。

ただ1点だけご指摘くださった所なのですが、直すとエラーになるのでご報告。
softya(ソフト屋) さんが書きました: Enemy.cpp

コード:

enemy_t enemy[ENEMY_MAX];

//初期フェーズ
//敵の情報登録
void Enemy_Initialize(){
	Image_Enemy01 =LoadGraph( "../データ/キャラクター/enemy01.png" );
	static enemy_t eneny[ENEMY_MAX] = {0};
}
最初のenemy配列とEnemy_Initialize内のenemy配列は別メモリ空間を持つ配列で関連が一切ありません。
最初のものをstatic enemy_t eneny[ENEMY_MAX] = {0};にしてEnemy_Initialize関数からは削除して下さい。
この部分なのですが、おっしゃっていただいたとおりにしますと
外部シンボルが~
外部参照が・・・

等が出てきてビルドできず 上のソースのままビルドすると実行ファイルが作られました。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: ゲームの設計と分割コンパイル (3)について

#35

投稿記事 by softya(ソフト屋) » 14年前

エラーが出るから諦めるのではなく、理由を理解しましよう。
extern enemy_t enemy[ENEMY_MAX];
があるからエラーになるのです。外部公開が不要ならstaticでエラーが出ないはずです。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

くらいん
記事: 11
登録日時: 14年前

Re: ゲームの設計と分割コンパイル (3)について

#36

投稿記事 by くらいん » 14年前

softya(ソフト屋) さんが書きました:エラーが出るから諦めるのではなく、理由を理解しましよう。
extern enemy_t enemy[ENEMY_MAX];
があるからエラーになるのです。外部公開が不要ならstaticでエラーが出ないはずです。
プロトタイプ宣言があったため実体を表示することが必要であり、static ではコンパイルが不可能であったということですね。
そのため、 extern部分 を無くして enemy_t eneny[ENEMY_MAX] = {0}; インクルードの下に書き込んだところコンパイルできました。

次に、自機の弾についてプログラミングしています。
自機の弾の描画座標をもし Player.x+20 の位置に表示しようとしたとき
Player.xを外部参照しなければならないと思ったので はじめに extern Player_t Player; を書いたのですが、
座標が20のまま?だったのできちんと参照されていないということがわかり、他の外部参照が思いつかなかったため、質問させていただきます。
Player.xを外部参照するにはどのような方法があるのでしょうか?

Player.xをextern宣言するのかな?等考えては見たのですが・・・
添付ファイル
圧縮用.rar
(344.84 KiB) ダウンロード数: 147 回

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: ゲームの設計と分割コンパイル (3)について

#37

投稿記事 by softya(ソフト屋) » 14年前

まだ、少し勘違いされています。
こう書いてみて下さい。エラーにならないはずですよ。

コード:

static int Image_Enemy01;
static enemy_t enemy[ENEMY_MAX] = {0};

//初期フェーズ
//敵の情報登録
void Enemy_Initialize(){
	Image_Enemy01 =LoadGraph( "../data/img/chara/enemy01.png" );
}
くらいん さんが書きました:自機の弾の描画座標をもし Player.x+20 の位置に表示しようとしたとき
Player.xを外部参照しなければならないと思ったので はじめに extern Player_t Player; を書いたのですが、
座標が20のまま?だったのできちんと参照されていないということがわかり、他の外部参照が思いつかなかったため、質問させていただきます。
Player.xを外部参照するにはどのような方法があるのでしょうか?

Player.xをextern宣言するのかな?等考えては見たのですが・・・
extern Player_t Player;で合っています。
もし参照できないなら別の問題です。
[補足]ただ、むやみなextern公開は避けたほうがバグが減るので出来れば関数の引数で渡して下さい。


あとChara.hとPlayer.h他が同じ内容なのでちゃんと不要なものは削除して下さい。
さらに
#ifndef DEF_CHARA_H //二重include防止
がちゃんとファイル名に合わせて変えていないので役目を果たしていません。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

くらいん
記事: 11
登録日時: 14年前

Re: ゲームの設計と分割コンパイル (3)について

#38

投稿記事 by くらいん » 14年前

softya(ソフト屋) さんが書きました: extern Player_t Player;で合っています。
もし参照できないなら別の問題です。
この部分だけ少し気になったので質問です。
他の部分はまだ訂正していません><

コード:

//通常ショット登録
void Normal_Shot_Pattern(){
	int k;
	for(int i=0;i<Player_Shot0num[Player.power<200?0:1];i++){
		if((k=Search_Player_Shot())!=-1){
			Player_Shot[k].flag=1;
			Player_Shot[k].cnt=0;
			Player_Shot[k].angle=-PI/2;
			Player_Shot[k].spd=20;
			Player_Shot[k].x=Player.x+Player_Shot0pos_x[i];
			Player_Shot[k].y=Player.y+Player_Shot0pos_y[i];
			Player_Shot[k].power=23;
			Player_Shot[k].knd=0;
		}
	}
}
この部分の
Player.x Player.y をそれぞれ200に変更して実行してみたところ多分 (200,200) 真ん中付近で弾が無事発射されるのがみれるのですが、やはり Player.x この部分はおかしくないのでしょうか?

自機の描画のところを見ると Player.x になっているため その点はあっていると思うのですが・・・

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: ゲームの設計と分割コンパイル (3)について

#39

投稿記事 by softya(ソフト屋) » 14年前

くらいん さんが書きました:この部分の
Player.x Player.y をそれぞれ200に変更して実行してみたところ多分 (200,200) 真ん中付近で弾が無事発射されるのがみれるのですが、やはり Player.x この部分はおかしくないのでしょうか?
何処がどの様におかしいと思われるのでしょうか?
私にはおかしなところがあるように見えませんが。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

くらいん
記事: 11
登録日時: 14年前

Re: ゲームの設計と分割コンパイル (3)について

#40

投稿記事 by くらいん » 14年前

softya(ソフト屋) さんが書きました:
くらいん さんが書きました: この部分の Player.x Player.y をそれぞれ200に変更して実行してみたところ多分 (200,200) 真ん中付近で弾が無事発射されるのがみれるのですが、やはり Player.x この部分はおかしくないのでしょうか?
何処がどの様におかしいと思われるのでしょうか?
私にはおかしなところがあるように見えませんが。
おかしいと思った箇所は
初期のPlayer.xは FIELD_MAX_X/2 の値を受け取り、Player_Graph のint xに入るので
自機描画のx座標は FIELD_MAX_X/2 であると考えました。
もし 初期位置のまま弾を発射すると

Player_Shot[k].x=Player.x+Player_Shot0pos_x/3

上記を見習いますと 
Player.x+Player_Shot0pos_x/3
(FIELD_MAX_X/2)+(Player_Shot0pos_x[4]={-10, 10,-30, 30})/3

という風な座標にPlayer_Shot[k].x は なると思いました。

しかし結果は画面左上 多分Player_Shot0pos_x/3 この値が代入されただけ の座標位置に描画されました。

そして Player.xをもし200に変えたならば
200+(Player_Shot0pos_x[4]={-10, 10,-30, 30})/3  がx座標の値になり、実際試したところ そのようになったためうまくいかなかった箇所Player.x がおかしい箇所なのかとおもったため先の質問になりました。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: ゲームの設計と分割コンパイル (3)について

#41

投稿記事 by softya(ソフト屋) » 14年前

意味がわかりました。
ローカル変数(引数)とグローバル変数の意味が分かっていないために起こる混乱です。

コード:

Player_t Player; ← グローバル空間のPlayer。

// 初期化フェーズ (Initialize)
void Player_Initialize( Player_t &Player , int x , int y , int img ){  ← ローカル空間(引数)のPlayer。
        Player.Image = img;
        Player.x     = x;
        Player.y     = y;
}
グローバル空間のPlayerとローカル空間(引数)のPlayerは、まったく別物です。
つまり、一丁目の田中さんと三丁目の田中さんを同じ人扱いしているのと同じだと思って下さい。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

くらいん
記事: 11
登録日時: 14年前

Re: ゲームの設計と分割コンパイル (3)について

#42

投稿記事 by くらいん » 14年前

ローカル空間とグローバル空間は別空間のため値を共有できないということですか・・・

では共有するためにはどうすればよいかと考えて、調べた結果 仮引数実引数や値の参照付近を使うだろうと考えました。 しかしながら、どのように編集すればよいのかわからないため他の方法をとってみました。

そこで考えたのは、Playerの位置座標をとってくればよいのだからそれを格納するint型を作り代入すればよいのではないか と。
実際にこのようにしてみました。 ソース一部抜粋

コード:

			if(!(x<30 || x>FIELD_MAX_X-50 || y<10 || y>FIELD_MAX_Y-25)){//計算結果移動可能範囲内なら
				Player.x=x , Player.y=y;//実際に移動させる
				Player_x = x , Player_y = y; 
			}
上記のように書き、それに加え 
int Player_x;
int Player_y;
を書き
extern宣言でPlayer_Shot.cppに書きました。
するとうまくいきました。しかし問題点が2点。

・移動をしない初期座標の時はx yともに0のため左上に表示される。
(ifを使えば初期座標を変更しうまくいくかも知れません・・・)

・グローバル変数をまた用いてしまうことです。

ですのでこの方法はダメなのかなと考えています・・・

関数の引数で呼び出せることができるようになればこの点も解決できるのかしらとは考えておりますが、いかんせんどのように呼び出すのかを存じませんので お手上げ状態です・・・

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: ゲームの設計と分割コンパイル (3)について

#43

投稿記事 by softya(ソフト屋) » 14年前

プレーヤーが2体?いるみたいですから、どちらにしろ外部変数で渡すのは無理があります。
Player_CalcがEnter_Shot()を引数付きで呼び出せば解決できると思います。
Enter_Shot()は、Slow_Shot_Pattern()やNormal_Shot_Pattern();に引数付きで渡します。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

くらいん
記事: 11
登録日時: 14年前

Re: ゲームの設計と分割コンパイル (3)について

#44

投稿記事 by くらいん » 14年前

softya(ソフト屋) さんが書きました:プレーヤーが2体?いるみたいですから、どちらにしろ外部変数で渡すのは無理があります。
Player_CalcがEnter_Shot()を引数付きで呼び出せば解決できると思います。
Enter_Shot()は、Slow_Shot_Pattern()やNormal_Shot_Pattern();に引数付きで渡します。
ほお そうなのですか。 とりあえず、敵の表示を管理部の作り方を見たときに2体にしたままだったので1体に変更。

そうしてよし引数付きで呼び出そうとし、何を引数とすればよいのかわからず Enter_Shot() をPlayer_Calcが呼び出すときに必要なのは
Player.shot_cnt
Player.x
Player.y
なのかなと考えとりあえず
Player.xだけ試しました
すると 関数呼び出しの引数が多すぎます

なんでかなとごちゃごちゃとプログラムをいじってみましたが一向に消える気配がなかったので、
一時中断し、せっかくだからさっきのPlayer_xを完成させちゃえ という衝動に駆られ、
とりあえずPlayer_xできちんと弾の位置を登録することを完成させました。

そんでもってまたいじくり、気分転換にボードのスコア等の表示を作っていたところ・・・
ここでも Player.num(残機数) Player.bomb等で呼び出さないといけないことに気づいてしまい
避けては通れない道なのかと・・・ そういうことで再び質問しに来たといいますか ヒントを頂きに来ました;;

ですから、質問点は 引数付きで呼び出すとき呼び出し方等はわかっている(基礎の話はわかっている(つもり)?)のですが、

・どの部分を引数にしたらいいかがわからない
・インスウにする部分を( , , ...)のように表示したとき (int ~ , int ~ ,int~ , ...) ~の部分は勝手に名前を決めてもいいのか
・上のことが正しければ そのint ~ の~の部分を今までの Player.shot_cnt 等の代わりに書き換えればよいのか

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: ゲームの設計と分割コンパイル (3)について

#45

投稿記事 by softya(ソフト屋) » 14年前

くらいん さんが書きました:(1)どの部分を引数にしたらいいかがわからない
(2)インスウにする部分を( , , ...)のように表示したとき (int ~ , int ~ ,int~ , ...) ~の部分は勝手に名前を決めてもいいのか
(3)上のことが正しければ そのint ~ の~の部分を今までの Player.shot_cnt 等の代わりに書き換えればよいのか
(1)その関数が必要とする値でその関数が持っていないものを引数とします。
(2)受ける側の関数の引数の名前=仮引数の名前は自由に変えて構いません。
(3)仮引数は変数として扱えます。

たとえば、
Enter_Shot(Player.shot_cnt,Player.x,Player.y);
と呼び出して
Enter_Shot(int shot_cnt,int x,int y);
と宣言されれば
でEnter_Shot関数内でshot_cntとxとyが利用できます。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

くらいん
記事: 11
登録日時: 14年前

Re: ゲームの設計と分割コンパイル (3)について

#46

投稿記事 by くらいん » 14年前

softya(ソフト屋) さんが書きました: (1)その関数が必要とする値でその関数が持っていないものを引数とします。
(2)受ける側の関数の引数の名前=仮引数の名前は自由に変えて構いません。
(3)仮引数は変数として扱えます。

たとえば、
Enter_Shot(Player.shot_cnt,Player.x,Player.y);
と呼び出して
Enter_Shot(int shot_cnt,int x,int y);
と宣言されれば
でEnter_Shot関数内でshot_cntとxとyが利用できます。
はい 実行してみました。 そうしたところ x y 等をEnter_Shot内で使用するとPlayer_Shotの構造体部分のx と認識されるので Player_x のように修正しました。

デバックしてみたところ弾が出なくなったため、ブレークポイントを設定し、ウォッチで見てみたところ数値が引用されていないようなのでそこがおかしいのだとはわかりました。しかし プログラムを見てみたところ ここが違うじゃないか など思いつかなかったのでまたご助言を頂きに来ました。

それとスコアボードの数値引用に関しては まだ 自機がやられたり、スコア設定など何も設定していないままのため、初期値設定が表示されるのですが、
その ボード表示を呼び出す場所をどこに設定しようかと考え 
とりあえず初期値に設定したところ、最初1秒弱で消え (当たり前)
描画フェーズに書くと 表示されない
なぜなのかは main ソースを見ればわかりました。
描画順が
ステージ背景→自機→敵→自機弾→ボード(額縁)→ボードスコア
のように設定したため


すみません 書いてる途中に自分で気がついて自機の描画位置を後ろにしましたところ表示できました。
でもスコアの設定をしたときにまた書き換えないといけないですね・・・


とりあえず今 躓いている箇所は
 EnterShot をプレイヤー計算で呼び出すときはPlayer.x 等に値が表示されるが、EnterShotの定義部で int x値がうまく引用できていないこと
と思っていましたが、ブレークポイントをまた使い実行してみた結果 318 などの値が表示されていました。
ということで

・ なぜ値が表示されているが弾が出ないのか、そしてその解決策をぜひともご教授願いたい

というところです

いつも以上に見づらくてすみません。
添付ファイル
圧縮用.rar
(2.28 MiB) ダウンロード数: 151 回

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: ゲームの設計と分割コンパイル (3)について

#47

投稿記事 by softya(ソフト屋) » 14年前

Player_shot_cntは仮引数ですので書き換えても反映されません。Enter_ShotからPlayer_shot_cntを戻り値で戻して下さい。

コード:

//ショット登録部
void Enter_Shot(int Player_shot_cnt,int Player_x,int Player_y,int Player_power){
	Player_x;
	Player_y;
	Player_power;
	//ショットボタンが押されていたら
	if(Key[ KEY_INPUT_Z  ]>0){
		Player_shot_cnt++;
		if(Player_shot_cnt%4==0){//4カウントに1回
			if(Key[ KEY_INPUT_LSHIFT  ]>0)//低速移動中なら
				Slow_Shot_Pattern(Player_x,Player_y,Player_power);
			else
				Normal_Shot_Pattern(Player_x,Player_y,Player_power);
		}
	}
	else
		Player_shot_cnt=0;
}
それと先頭の
Player_x;
Player_y;
Player_power;
は無意味です。

呼び出し側
Player.shot_cnt = Enter_Shot(Player.shot_cnt,Player.x,Player.y,Player.power);
呼び出される側。ついでですが初心者は、{}を必ず書きましょう。

コード:

//ショット登録部
int Enter_Shot(int Player_shot_cnt,int Player_x,int Player_y,int Player_power){
	//ショットボタンが押されていたら
	if(Key[ KEY_INPUT_Z  ]>0){
		Player_shot_cnt++;
		if(Player_shot_cnt%4==0){//4カウントに1回
			if(Key[ KEY_INPUT_LSHIFT  ]>0) {//低速移動中なら
				Slow_Shot_Pattern(Player_x,Player_y,Player_power);
			} else {
				Normal_Shot_Pattern(Player_x,Player_y,Player_power);
			}
		}
	} else {
		Player_shot_cnt=0;
	}
	return Player_shot_cnt;
}
これで私の方では動きましたが、移動しながらでないとZで発射できないのは狙った仕様でしょうか?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

くらいん
記事: 11
登録日時: 14年前

Re: ゲームの設計と分割コンパイル (3)について

#48

投稿記事 by くらいん » 14年前

返信が遅くなってしまい申し訳ありません。
softya(ソフト屋) さんが書きました: ついでですが初心者は、{}を必ず書きましょう。
if関数の時は
if{
//ここに書き込む
}
のように気がついたところは直しました。
softya(ソフト屋) さんが書きました: 移動しながらでないとZで発射できないのは狙った仕様でしょうか?
移動しながらでないとZで発射できないという点については狙っていなかったのでいつでも発射するように変更しました。

続いて、気分でタイトル作成をしました。
case10 にタイトル画面をはさみ if を使い
返り値が-1のときはcase10に戻り
返り値が 1のときはcase99(STGの初期処理)に移行
返り値が 0のときは終了処理
のようにし、int型の関数を作成しタイトルを作成しました。

プログラムを見直していて ふと疑問になったことがありましたので質問させていただきます。

1つ目

ゲームのタイトル画面の はじめから などの部分にマウスカーソルを置いたときや方向キーでどれにするか選んでいるとき対応した部分が動いたり 大きくなったりすると思うのです。それについて
・タイトル画面の文字は一般的に背景と文字を同じ1枚の画像に書き、
計算でx yの座標の時にオリジナル画像(文字を大きくした画像など)を上書き描画し、
x y が移動したらそのオリジナル画像を消し、移動した座標に対応した画像をまた描画するという繰り返し。
という方針なのか

・文字フォント等の変更でタイトル画面を描画しているのか

どちらなのかな と まあ 今回はそのような面倒ごとはやらないつもりですが・・・


2つ目

最初にグローバル変数をあまり用いらないで作ろうと思っていましたがばりばりexternを使用している・・・
と気がついてしまったので、引数つきの関数で呼び出してextern宣言を少なくしようと考えているのですが、
もし引数を2つの別のソースからとってくる場合 たとえば当たり判定を判別する時に プレイヤーと敵の情報をそれぞれ使わないといけないので このような場合どのようにして書けばいいのか。


3つ目

プレイヤー、敵、・・・のようにフォルダを分ける? CPPをつくり分けているのですが、たとえばエフェクトやあたり判定 などは各対応する部分にそれぞれ書いていくのがいいのか、
またはエフェクトやあたり判定も新規でCPPをつくり、それを呼び出すほうがいいのか。


4つ目
main.cpp等には極力 初期化 計算 描画 終了処理 の4つを書いていき、アイテム登録や敵情報登録 当たり判定など その4つの部分に当てはまらないものは、void型関数で一度まとめてmain関数に書き込むか、どんどんmain関数に書き込んだりするほうがいいのかどうか。


という以上4点が疑問になったところです。わからなくても作れるっぽいのですが、
見づらくなりそうなので質問させていただきました。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: ゲームの設計と分割コンパイル (3)について

#49

投稿記事 by softya(ソフト屋) » 14年前

1つ目

ゲームのタイトル画面の はじめから などの部分にマウスカーソルを置いたときや方向キーでどれにするか選んでいるとき対応した部分が動いたり 大きくなったりすると思うのです。それについて
・タイトル画面の文字は一般的に背景と文字を同じ1枚の画像に書き、
計算でx yの座標の時にオリジナル画像(文字を大きくした画像など)を上書き描画し、
x y が移動したらそのオリジナル画像を消し、移動した座標に対応した画像をまた描画するという繰り返し。
という方針なのか

・文字フォント等の変更でタイトル画面を描画しているのか

どちらなのかな と まあ 今回はそのような面倒ごとはやらないつもりですが・・・
画像なら拡大はDXライブラリの機能にあります。
どちらにしろ毎回画面は書き換えるので前回のフレームのことは気にしなくて良いです。
文字サイズの変更もできます。
どれをやるにも自分で狙った効果が出るかテストして決めていくしかないので、どれをやるかは状況次第です。
2つ目

最初にグローバル変数をあまり用いらないで作ろうと思っていましたがばりばりexternを使用している・・・
と気がついてしまったので、引数つきの関数で呼び出してextern宣言を少なくしようと考えているのですが、
もし引数を2つの別のソースからとってくる場合 たとえば当たり判定を判別する時に プレイヤーと敵の情報をそれぞれ使わないといけないので このような場合どのようにして書けばいいのか。
プレイヤーと敵の情報を取得する関数とあたり判定をする関数の組み合わせでしょうか。
3つ目

プレイヤー、敵、・・・のようにフォルダを分ける? CPPをつくり分けているのですが、たとえばエフェクトやあたり判定 などは各対応する部分にそれぞれ書いていくのがいいのか、
またはエフェクトやあたり判定も新規でCPPをつくり、それを呼び出すほうがいいのか。
エフェクトや当たり判定はそれぞれ新しい別ファイルのほうが良いと思います。
フォルダは別に分けなくて良いですよ。
4つ目
main.cpp等には極力 初期化 計算 描画 終了処理 の4つを書いていき、アイテム登録や敵情報登録 当たり判定など その4つの部分に当てはまらないものは、void型関数で一度まとめてmain関数に書き込むか、どんどんmain関数に書き込んだりするほうがいいのかどうか。
分かりやすくシンプルなmain.cppが良いと思います。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

閉鎖

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