マリオの炎の発射がうまくできません。

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

マリオの炎の発射がうまくできません。

#1

投稿記事 by けいすけ。 » 8年前

こんにちは。いつもお世話になっております。
現在、マリオ風の横スクロールアクションを作成しております。
マリオが、左右に炎を発射できるようにしたいのですが、なかなかうまくいきません。
発射はできるのですが、他えば右に発射したとして、弾は右に発射されるのですが、そのあと左を向くと、弾が左に流れて行ってしまいます。左に撃った場合も同じことが起こります。

コード:

void DrawFire(int key) {
	//発射処理
	if (IsBKeyTrigger(key) == TRUE) {
		//空きを探す
		int i;
		for (i = 0; i < MAX_FIRE; i++) {
			if (g_stagedata.fires[i].living == FALSE) break;
		}
		if (i < MAX_FIRE) {
			g_stagedata.fires[i].living = TRUE;
			if (g_stagedata.hero.turn == FALSE) {
				g_stagedata.fires[i].x = g_stagedata.hero.x + IMG_CHIPSIZE;
			}
			if (g_stagedata.hero.turn == TRUE) {
				g_stagedata.fires[i].x = g_stagedata.hero.x - IMG_CHIPSIZE;
			}
			g_stagedata.fires[i].y = g_stagedata.hero.y;
		}
	}
	//炎の描画と移動
	float mv = 350.0f * g_frametime; //移動量計算
	for (int i = 0; i < MAX_FIRE; i++) {
		if (g_stagedata.fires[i].living == FALSE) continue;
		if (g_stagedata.hero.turn == FALSE) {
			g_stagedata.fires[i].x += mv;
		}
		if (g_stagedata.hero.turn == TRUE) {
			g_stagedata.fires[i].x -= mv;
		}
		//マップ当たり判定
		AtariInfo atari = CheckBlock(g_stagedata.fires[i].x, g_stagedata.fires[i].y,
			g_stagedata.fires[i].x);
		if (atari.DR == TRUE || atari.UR == TRUE) g_stagedata.fires[i].living = FALSE;
		
		DrawGraph((int)(g_stagedata.fires[i].x - g_stagedata.scrollx),
			(int)g_stagedata.fires[i].y,
			g_imghandles.fire, TRUE);
	}

}

hide

Re: マリオの炎の発射がうまくできません。

#2

投稿記事 by hide » 8年前

コード:

    for (int i = 0; i < MAX_FIRE; i++) {
        if (g_stagedata.fires[i].living == FALSE) continue;
        if (g_stagedata.hero.turn == FALSE) {
            g_stagedata.fires[i].x += mv;
        }
        if (g_stagedata.hero.turn == TRUE) {
            g_stagedata.fires[i].x -= mv;
        }
炎の玉の移動がg_stagedata.hero.turnに合わせて変化しているので当然ですね。
やるとしたら

コード:

    for (int i = 0; i < MAX_FIRE; i++) {
        if (g_stagedata.fires[i].living == FALSE) continue;
        if (g_stagedata.fires[i].turn == FALSE) {
            g_stagedata.fires[i].x += mv;
        } else {
            g_stagedata.fires[i].x -= mv;
        }
でしょう。
しかし、そもそも炎の位置の計算をする部分でマリオの向きに依存するバグが発生してしまうような設計自体に問題があります。
何でもかんでもグローバル変数から参照できてしまうが故に不要なものを参照して間違えるのです。
このままグルーバル変数を増やせば、いつか破綻します。

炎の玉の生成のタイミングでどちらの向きに発射されるかの情報を受け取り、変数に持ち、
以降炎の玉の位置の更新時はその情報を使用しましょう。
(発射される向きを取りに行くのではなく、受け取るのが大事です。関数の引数を利用してください。)
また、C言語であればファイルスコープでマリオの情報を隠蔽しましょう。
C++等であればクラスに情報を隠蔽しましょう。

けいすけ。

Re: マリオの炎の発射がうまくできません。

#3

投稿記事 by けいすけ。 » 8年前

 

コード:

for (int i = 0; i < MAX_FIRE; i++) {
  if (g_stagedata.fires[i].living == FALSE) continue;
  if (g_stagedata.fires[i].turn == FALSE) {
       g_stagedata.fires[i].x += mv;
       } else {
        g_stagedata.fires[i].x -= mv;
  }
に変更しましたが、やはり改善できません。
けいすけ。 さんが書きました:炎の玉の生成のタイミングでどちらの向きに発射されるかの情報を受け取り、変数に持ち、以降炎の玉の位置の更新時はその情報を使用しましょう。(発射される向きを取りに行くのではなく、受け取るのが大事です。関数の引数を利用してください。)また、C言語であればファイルスコープでマリオの情報を隠蔽しましょう。C++等であればクラスに情報を隠蔽しましょう。
具体的に」どのようにすればよいですか?

けいすけ。

Re: マリオの炎の発射がうまくできません。

#4

投稿記事 by けいすけ。 » 8年前

コードです。

コード:

<gamemain.h>
#ifndef __GAMEMAIN_H__ 
#define __GAMEMAIN_H__

#include <DxLib.h>
#include "main.h"
#include "enemy.h"

#define IMG_CHIPSIZE 50
#define MAP_WIDTH 500
#define MAP_HEIGHT 12
#define MAXSTAGE 1
#define SCR_WIDTH 16
#define ANIM_RATE 4
#define SCROLL_STAPOS 500
#define JUMP_POWER 750.0f
#define GRAVITY 30.0f
#define JUMP_FORWARD 200.0f
#define MAX_ENEMY 20
#define MAX_FIRE 15

extern char g_mapdata[MAP_HEIGHT][MAP_WIDTH + 1];

enum EnemyType {
	ET_ODEN = 2,
	ET_INDAS = 3,
	ET_RAOU = 4,
	ET_BULLET = 5,
};


struct CharaData {
	float x, y;
	BOOL turn;
	BOOL noground;
	BOOL jumping;
	float jumppower, jumpforward;
	BOOL living;
	EnemyType type;
	int life;
};

struct StageData{ 
	int stagenum;
	int mapwidth;
	int animcounter;
	CharaData hero;
	float scrollx;
	float scrolly;
	CharaData enemies[MAX_ENEMY];
	CharaData fires[MAX_FIRE];
	BOOL g_bkey_prev;
};
extern StageData g_stagedata;

struct AtariInfo {
	BOOL UL, UR, DL, DR, GL, GR;
};

void GameMain();
void InitStage();
void DrawHero(int ac);
void DrawMap();
AtariInfo CheckBlock(float x, float y, float rx);
void DrawFire(int key);
BOOL IsBKeyTrigger(int key);

#endif

けいすけ。

Re: マリオの炎の発射がうまくできません。

#5

投稿記事 by けいすけ。 » 8年前

コードです。

コード:

<gamemain.c++>
#include "gamemain.h"

char g_mapdata[MAP_HEIGHT][MAP_WIDTH + 1];
StageData g_stagedata;

//ステージ初期化
void InitStage() {
	char buf[256];
	sprintf_s(buf, 256, "media\\stage%d.txt", g_stagedata.stagenum + 1);
	int fh = FileRead_open(buf);
	for (int y = 0; y < MAP_HEIGHT; y++) {
		FileRead_gets(g_mapdata[y], 256, fh);
	}
	FileRead_close(fh);
	g_stagedata.mapwidth = (int)strlen(g_mapdata[0]);

	g_stagedata.hero.x = 2 * IMG_CHIPSIZE;
	g_stagedata.hero.y = 7 * IMG_CHIPSIZE;
	g_stagedata.hero.turn = FALSE;

	ZeroMemory(g_stagedata.enemies, sizeof(g_stagedata.enemies));
	ZeroMemory(g_stagedata.fires, sizeof(g_stagedata.fires));
	g_stagedata.scrollx = 0;
}


void GameMain() {
	//アニメーションカウンタ
	g_stagedata.animcounter++;
	g_stagedata.animcounter &= MAXINT;
	int ac = g_stagedata.animcounter / ANIM_RATE;

	DrawMap();
	DrawHero(ac);
	DrawEnemy(ac);

	//ゲームクリア判定
	if (g_stagedata.hero.x >= (g_stagedata.mapwidth - 14) * IMG_CHIPSIZE) {
		g_gamestate = GAME_CLEAR;
		g_timerstart = g_lasttime;	//タイマーセット
	}
}
void DrawHero(int ac) {
	int key = GetJoypadInputState(DX_INPUT_KEY_PAD1);
	DrawFire(key);
	float mv = 200.0f * g_frametime; //移動量計算
	float hx = g_stagedata.hero.x;
	float hy = g_stagedata.hero.y;

	//ジャンプ処理
	if (g_stagedata.hero.jumping == TRUE) {
		mv = 0;	//ジャンプ中は自力で移動させない
		g_stagedata.hero.jumppower -= GRAVITY;
		hy -= (g_stagedata.hero.jumppower * g_frametime);
		hx += (g_stagedata.hero.jumpforward * g_frametime);
		}
	if (key & PAD_INPUT_LEFT) {
		hx -= mv;
		g_stagedata.hero.turn = TRUE;
	}
	if (key & PAD_INPUT_RIGHT) {
		hx += mv;
		g_stagedata.hero.turn = FALSE;
	}
	//マップ当たり判定
	AtariInfo atari = CheckBlock(hx, hy, g_stagedata.hero.x);
	if (g_stagedata.hero.turn == FALSE) {
		if (atari.DR == TRUE || atari.UR == TRUE) {
			hx = g_stagedata.hero.x;
		}
	}
	else {
		if (atari.DL == TRUE || atari.UL == TRUE) {
			hx = g_stagedata.hero.x;
		}
	}
	//接地チェック
	if (atari.GL == TRUE || atari.GR == TRUE) {
		g_stagedata.hero.noground = FALSE;
		g_stagedata.hero.jumping = FALSE;
		g_stagedata.hero.jumppower = 0;
		g_stagedata.hero.jumpforward = 0;
		//ジャンプ後に地面に埋まらないよう補正
		hy = (float)((int)(hy / IMG_CHIPSIZE) * IMG_CHIPSIZE);
	}
	else {
		g_stagedata.hero.noground = TRUE;
		//重力で落下
		g_stagedata.hero.jumping = TRUE;
	}
	//天井チェック
	if (g_stagedata.hero.jumping == TRUE) {
		if (atari.UL == TRUE || atari.UR == TRUE) {
			g_stagedata.hero.jumppower = 0;
			g_stagedata.hero.jumpforward = 0;
		}
	}
	//ジャンプ処理その2
	if (g_stagedata.hero.jumping == FALSE) {
		if (IsAKeyTrigger(key) == TRUE && g_stagedata.hero.noground == FALSE)
		{
			g_stagedata.hero.jumping = TRUE;
			g_stagedata.hero.jumppower = JUMP_POWER;
			if (key & PAD_INPUT_LEFT) {
				g_stagedata.hero.jumpforward = -JUMP_FORWARD;
			}
			if (key & PAD_INPUT_RIGHT) {
				g_stagedata.hero.jumpforward = JUMP_FORWARD;
			}
		}
	}
	//スクロール補正
	if (hx - g_stagedata.scrollx > SCROLL_STAPOS) {
		g_stagedata.scrollx += (hx - g_stagedata.hero.x);
	}
	if (hx < g_stagedata.scrollx) hx = g_stagedata.hero.x;
	g_stagedata.hero.x = hx;
	g_stagedata.hero.y = hy;

	DrawRotaGraph2((int)(g_stagedata.hero.x - g_stagedata.scrollx),
		(int)g_stagedata.hero.y, 0, 0, 1, 0,
		g_imghandles.hero[ac % ANIMFRAME], TRUE, g_stagedata.hero.turn);
	if (g_stagedata.hero.x >= (g_stagedata.mapwidth - 50) * IMG_CHIPSIZE) {
		StopSoundMem(g_sndhandles.bgm);
	}
	if (g_stagedata.hero.x >= (g_stagedata.mapwidth - 47) * IMG_CHIPSIZE) {
		PlaySoundMem(g_sndhandles.boss, DX_PLAYTYPE_LOOP);
	}
}

BOOL _CheckBlockSub(float x, float y) {
	int mx = (int)(x / IMG_CHIPSIZE);
	int my = (int)(y / IMG_CHIPSIZE);
	//マップの範囲外ならFALSE
	if ((mx < 0) || (mx >= g_stagedata.mapwidth) || (my >= MAP_HEIGHT) || (my < 0)) {
		return FALSE;
	}
	if ((mx < 0) || (mx >= g_stagedata.mapwidth) || (my >= MAP_HEIGHT) || (my < 0)) {
		return FALSE;
	}
	if (g_mapdata[my][mx] != '0') return TRUE;
	return FALSE;
}
AtariInfo CheckBlock(float x, float y, float rx) {
	AtariInfo result;
	result.UL = _CheckBlockSub(x, y);
	result.UR = _CheckBlockSub(x + IMG_CHIPSIZE - 1, y);
	result.DL = _CheckBlockSub(x, y + IMG_CHIPSIZE - 1);
	result.DR = _CheckBlockSub(x + IMG_CHIPSIZE - 1, y + IMG_CHIPSIZE - 1);
	result.GL = _CheckBlockSub(rx, y + IMG_CHIPSIZE);
	result.GR = _CheckBlockSub(rx + IMG_CHIPSIZE - 1, y + IMG_CHIPSIZE);
	return result;
}

void DrawMap() {
	int sc = (int)(g_stagedata.scrollx / IMG_CHIPSIZE);
	int shiftx = (int)g_stagedata.scrollx % IMG_CHIPSIZE;
	for (int y = 0; y < MAP_HEIGHT; y++) {
		for (int x = 0; x < SCR_WIDTH + 1; x++) {
			if (x + sc >= g_stagedata.mapwidth) break;
			if (g_mapdata[y][x + sc] == '1') {
				DrawGraph(x * IMG_CHIPSIZE - shiftx, y * IMG_CHIPSIZE, g_imghandles.block, TRUE);
				if (g_mapdata[y][x + sc] == '6') {
					DrawGraph(x * IMG_CHIPSIZE - shiftx, y * IMG_CHIPSIZE, g_imghandles.coin, TRUE);
				}
			}
			//モンスター検出
			if (g_mapdata[y][x + sc] > '1') SetEnemy(x + sc, y);
		}
	}
}
void DrawFire(int key) {
	//発射処理
	if (IsBKeyTrigger(key) == TRUE) {
		//空きを探す
		int i;
		for (i = 0; i < MAX_FIRE; i++) {
			if (g_stagedata.fires[i].living == FALSE) break;
		}
		if (i < MAX_FIRE) {
			g_stagedata.fires[i].living = TRUE;
			if (g_stagedata.hero.turn == FALSE) {
				g_stagedata.fires[i].x = g_stagedata.hero.x + IMG_CHIPSIZE;
			}
			if (g_stagedata.hero.turn == TRUE) {
				g_stagedata.fires[i].x = g_stagedata.hero.x - IMG_CHIPSIZE;
			}
			g_stagedata.fires[i].y = g_stagedata.hero.y;
		}
	}
	//炎の描画と移動
	float mv = 350.0f * g_frametime; //移動量計算
	for (int i = 0; i < MAX_FIRE; i++) {
		if (g_stagedata.fires[i].living == FALSE) continue;
		if (g_stagedata.fires[i].turn == FALSE) {
			g_stagedata.fires[i].x += mv;
		}
		else {
			g_stagedata.fires[i].x -= mv;
		}
		//マップ当たり判定
		AtariInfo atari = CheckBlock(g_stagedata.fires[i].x, g_stagedata.fires[i].y,
			g_stagedata.fires[i].x);
		if (atari.DR == TRUE || atari.UR == TRUE) g_stagedata.fires[i].living = FALSE;
		
		DrawGraph((int)(g_stagedata.fires[i].x - g_stagedata.scrollx),
			(int)g_stagedata.fires[i].y,
			g_imghandles.fire, TRUE);
	}

}
//キートリガー処理
BOOL IsBKeyTrigger(int key) {
	if (key & PAD_INPUT_B) {
		if (g_stagedata.g_bkey_prev == FALSE) {
			g_stagedata.g_bkey_prev = TRUE;
			return TRUE;
		}
	}
	else {
		g_stagedata.g_bkey_prev = FALSE;
	}
	return FALSE;
}

hide

Re: マリオの炎の発射がうまくできません。

#6

投稿記事 by hide » 8年前

変更しましたが、やはり改善できません。
方向性のみ示していますので、そこだけ変えても意味はないです。
炎の玉それぞれに移動方向の変数をもたせて正しく値を与えて正しく使用してください。
そもそも改善できませんとだけ言われてもエスパーじゃないのでそれ以上答えられません。
具体的に」どのようにすればよいですか?
具体的に書いているつもりなのですが、何がわかりませんか?
”具体的に”聞いてもらえないと答えられません。
返信までが短かったですが、ご自身で調べたり考えたりする時間はとったのでしょうか?

けいすけ。

Re: マリオの炎の発射がうまくできません。

#7

投稿記事 by けいすけ。 » 8年前

いろいろ試して、やってみたのですが、なかなかうまくいきません。
下が今の状態なのですが、今度は炎が進みません。
なぜでしょうか。

コード:

void DrawFire(int key) {
	int direction;
	float RIGHT = 0;
	float LEFT = 0;
	//発射
	if (IsBKeyTrigger(key) == TRUE) {
		//空きを探す
		int i;
		for (i = 0; i < MAX_FIRE; i++) {
			if (g_stagedata.fires[i].living == FALSE) break;
		}
		if (i < MAX_FIRE) {
			g_stagedata.fires[i].living = TRUE;
			if (g_stagedata.hero.turn == FALSE) {
				g_stagedata.fires[i].x = g_stagedata.hero.x + IMG_CHIPSIZE;
				direction = RIGHT;
			}
			if (g_stagedata.hero.turn == TRUE) {
				g_stagedata.fires[i].x = g_stagedata.hero.x - IMG_CHIPSIZE;
				direction = LEFT;
			}
			g_stagedata.fires[i].y = g_stagedata.hero.y;
		}
	}
	//炎の描画と移動
	float mv = 350.0f * g_frametime; //移動量計算
	for (int i = 0; i < MAX_FIRE; i++) {
		if (g_stagedata.fires[i].living == FALSE) continue;
		if (direction == RIGHT) {
			g_stagedata.fires[i].x += mv;
		}
		if (direction == LEFT) {
			g_stagedata.fires[i].x -= mv;
		}

			
		//マップ当たり判定
		AtariInfo atari = CheckBlock(g_stagedata.fires[i].x, g_stagedata.fires[i].y,
			g_stagedata.fires[i].x);
		if (atari.DR == TRUE || atari.UR == TRUE) g_stagedata.fires[i].living = FALSE;
		
		DrawGraph((int)(g_stagedata.fires[i].x - g_stagedata.scrollx),
			(int)g_stagedata.fires[i].y,
			g_imghandles.fire, TRUE);
	}

}

たいちう
記事: 418
登録日時: 14年前

Re: マリオの炎の発射がうまくできません。

#8

投稿記事 by たいちう » 8年前

でたらめ書いてもうまくいくはずがないでしょ。

ローカル変数のdirectionは、前回関数が呼ばれた時の値を覚えてないし、
複数の炎は全て同じ向きでよいの?
何より、RIGHTとLEFTが同じ値で、どのように区別するつもりなのですか?

けいすけ。

Re: マリオの炎の発射がうまくできません。

#9

投稿記事 by けいすけ。 » 8年前

たいちう さんが書きました:でたらめ書いてもうまくいくはずがないでしょ。

ローカル変数のdirectionは、前回関数が呼ばれた時の値を覚えてないし、
複数の炎は全て同じ向きでよいの?
何より、RIGHTとLEFTが同じ値で、どのように区別するつもりなのですか?
すみません。何をどうすればいいのか全く分からないのでヒントだけでもいただけませんか。

けいすけ。

Re: マリオの炎の発射がうまくできません。

#10

投稿記事 by けいすけ。 » 8年前

hide さんが書きました:
変更しましたが、やはり改善できません。
方向性のみ示していますので、そこだけ変えても意味はないです。
炎の玉それぞれに移動方向の変数をもたせて正しく値を与えて正しく使用してください。
そもそも改善できませんとだけ言われてもエスパーじゃないのでそれ以上答えられません。
具体的に」どのようにすればよいですか?
具体的に書いているつもりなのですが、何がわかりませんか?
”具体的に”聞いてもらえないと答えられません。
返信までが短かったですが、ご自身で調べたり考えたりする時間はとったのでしょうか?

コード:

void DrawFire(int key) {
	//発射
	
	if (IsBKeyTrigger(key) == TRUE) {
		//空きを探す
		int i;
		for (i = 0; i < MAX_FIRE; i++) {
			if (g_stagedata.fires[i].living == FALSE) break;
		}
		if (i < MAX_FIRE) {
			g_stagedata.fires[i].living = TRUE;
			if (g_stagedata.hero.turn == FALSE) {
				g_stagedata.fires[i].x = g_stagedata.hero.x + IMG_CHIPSIZE;
			}
			if (g_stagedata.hero.turn == TRUE) {
				g_stagedata.fires[i].x = g_stagedata.hero.x - IMG_CHIPSIZE;
			}
			g_stagedata.fires[i].y = g_stagedata.hero.y;
		}
	}
	//炎の描画と移動
	float mv = 350.0f * g_frametime; //移動量計算
	for (int i = 0; i < MAX_FIRE; i++) {
		if (g_stagedata.fires[i].living == FALSE) continue; 
		if (g_stagedata.fires[i].x > g_stagedata.hero.x) {
			g_stagedata.fires[i].x += mv;
		}
		else {
			g_stagedata.fires[i].x -= mv;
		}
    //マップ当たり判定
		AtariInfo atari = CheckBlock(g_stagedata.fires[i].x, g_stagedata.fires[i].y,
			g_stagedata.fires[i].x);
		if (atari.DR == TRUE || atari.UR == TRUE) g_stagedata.fires[i].living = FALSE;

		DrawGraph((int)(g_stagedata.fires[i].x - g_stagedata.scrollx),
			(int)g_stagedata.fires[i].y,
			g_imghandles.fire, TRUE);
	}

と変更したらやっとできました。ありがとうございました。

hide

Re: マリオの炎の発射がうまくできません。

#11

投稿記事 by hide » 8年前

まあ無理やり何とか修正したという感じですね。
マリオより炎が速いために今だけ問題にならないのでしょうけれど、位置が入れ替わるとダメですね。
近いうちにどうにもならなくなって捨てることになるかと思います。
その時になったらまたここで設計や綺麗な書き方の仕方を質問するといいでしょう。

返信

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