キャラの表示がうまくいかない

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

キャラの表示がうまくいかない

#1

投稿記事 by cupa » 2年前

今、C言語の館を参考にしてキャラを表示(移動)+アニメーションをやろうとしているのですが、
挙動がおかしいです。

files:
src:
main.cpp
graph.cpp
load.cpp
char.cpp

include:
struct.h
GV.h
function.h
define.h

main.cpp

コード:

#include "../../DxLib/DxLib.h"
#include "../include/GV.h"

int ch_img[12];
ch_t ch;

int WINAPI WinMain(
				_In_ HINSTANCE hInstance,
				_In_opt_ HINSTANCE hPrevInstance,
				_In_ LPSTR lpCmdLine,
				_In_ int nCmdShow
			)
{
	SetGraphMode(800, 600, 32);
	ChangeWindowMode(TRUE);
	SetMainWindowText("東方風弾幕");
	SetDrawScreen(DX_SCREEN_BACK);
	SetAlwaysRunFlag(TRUE);
	if (DxLib_Init() == -1) return -1;
	
	//============================
	// ここから
	//============================
	
	load();
	ch_init();
	
	while(ProcessMessage() == 0 && CheckHitKey(KEY_INPUT_ESCAPE) == 0 && ClearDrawScreen() == 0)
	{
		ch_move();
		graph_main();
		
		ScreenFlip();
	}
	DxLib_End();
	return 0;
}
graph.cpp

コード:

#include "../include/GV.h"

// キャラクターの描画関係
void graph_ch() { 
	DrawGraph(ch.first_x + ch.x, ch.first_y + ch.y, ch_img[ch.img], TRUE);
}

// 描画関係のメイン関数
void graph_main() {
	graph_ch();
}
load.cpp

コード:

#include "../include/GV.h"

void load() {
	LoadDivGraph("../img/char/c01.png", 12, 4, 3, 50, 50, ch_img);
}
char.cpp(一番の問題であろうファイル)

コード:

#include "../include/GV.h"

void ch_init() {
	ch.first_x = (FIELD_WIDTH - 50) / 2;
	ch.first_y = 500;
}

void ch_move() {
	int joge_flag = 0, sayu_flag = 0;	//左右上下の押下フラグ
	int frame_cnt = 0;	//フレームカウント
	double x, y, mx, my, naname = 1;	//x座標y座標、動いたx距離、動いたy距離
	double move_x[4] = {-4.0, 4.0, 0.0, 0.0}, move_y[4] = {0.0, 0.0, -4.0, 4.0}; //{左右上下}の移動スピード
	int input_key[4];	//左右上下のキー格納
	input_key[0] = CheckHitKey(KEY_INPUT_LEFT); input_key[1] = CheckHitKey(KEY_INPUT_RIGHT);	//左右方向のキー入力
	input_key[2] = CheckHitKey(KEY_INPUT_UP); input_key[3] = CheckHitKey(KEY_INPUT_DOWN);	//上下方向のキー入力
	
	//左右キ-が押されていなかったら
	for (int i = 0; i < 2; i++) {	//左右分のループ
		if (input_key[i] == 0) {	//もしi番目(左右)が押されていなかったら
			if (frame_cnt % 15 == 0) {	//もしframe_cntを15で割った余りが0ならば
				if (ch.img == NEUTRAL_CH_IMG_MAX) //もしキャラの画像が3(最大)だったのなら
					ch.img = FIRST_NEUTRAL_CH_IMG;	//元の番号に戻す
				ch.img++;	//キャラ画像をプラスする
			}
		}
	}
	
	if (input_key[0] == 1) {	//左方向キーが押されていたら
		ch.img = FIRST_LT_CH_IMG;	//画像をはじめの左向き画像に
		if (frame_cnt % 15 == 0) {	//もしframe_cntを15で割った余りが0ならば
			if (ch.img == LT_CH_IMG_MAX) //もしキャラの画像が11(最大)だったのなら
				ch.img = FIRST_LT_CH_IMG;	//元の番号に戻す
			ch.img++;	//キャラの画像番号をプラスする
		}
	} else if (input_key[1] == 1) {	//右方向キーが押されていたら
		ch.img = FIRST_RT_CH_IMG;	//左向きの初期値
		if (frame_cnt % 15 == 0) {	//もしframe_cntを15で割った余りが0ならば
			if (ch.img == RT_CH_IMG_MAX) //もしキャラの画像が8(最大)だったのなら
				ch.img = FIRST_RT_CH_IMG;	//元の番号に戻す
			ch.img++;	//キャラの画像番号をプラスする
		}
	}
	
	for (int i = 0; i < 2; i++)	//左右二回分
		if (input_key[i] == 1) 	//左右どちらかの入力があれば 
			sayu_flag = 1;	//左右フラグを1にする
	for (int i = 2; i < 4; i++)	//左右二回分
		if (input_key[i] == 1) 	//左右どちらかの入力があれば 
			joge_flag = 1;	//上下フラグを1にする
	if (sayu_flag == 1 && joge_flag == 1) //どちらも入力があれば
		naname = sqrt(2.0);	//斜めを1√2に
	
	for (int i = 0; i < 4; i++) {	//左右上下の4回分繰り返す
		if (input_key[i] == 1) {	//i番目のキーの入力があれば
			x = ch.x, y = ch.y;	//現在の座標を取り敢えず代入
			mx = move_x[i], my = move_y[i];	//i番目の移動分をmx,myに代入
			x += mx / naname, y += my / naname;	//現在の座標と移動分を足す
			if (x > 0 || x < FIELD_WIDTH ||	//x,yが可動範囲内なら
				y > 0 || y < FIELD_HEIGHT) {
				ch.x = x, ch.y = y;	//実際に動かす
			}
		}
	}
}
struct.h

コード:

typedef struct {
	double first_x, first_y;
	double x, y;	// 座標
	int img;		// 画像
}ch_t;
function.h

コード:

// load.cpp
	// データのロード
	extern void load();

// graph.cpp
	// 描画のメイン
	extern void graph_main();
	
// char.cpp
	// キャラの移動制御
	extern void ch_init();
	extern void ch_move();
define.h

コード:

// 画面サイズ
#define WINDOW_WIDTH	(800)
#define WINDOW_HEIGHT	(600)

// ゲーム画面のサイズ(フィールド)(仮)
#define FIELD_WIDTH		(600)
#define FIELD_HEIGHT	(600)

// ◯方向時の最初の画像番号
#define FIRST_LT_CH_IMG			(8)	// 8~11
#define FIRST_RT_CH_IMG			(4)	// 4~7
#define FIRST_NEUTRAL_CH_IMG	(0)	// 0~3

// ◯方向時の最大の画像番号
#define LT_CH_IMG_MAX		(11)
#define RT_CH_IMG_MAX		(7)
#define NEUTRAL_CH_IMG_MAX	(3)
GV.h

コード:

#include <math.h>
#include "../../DxLib/DxLib.h"
#include "struct.h"
#include "function.h"
#include "define.h"

// 画像用変数宣言区域
extern int ch_img[12];

// 構造体変数宣言区域
extern ch_t ch;
起こる現象としては、起動して何もキー入力をしないと普通に前向き時のアニメーションをするのですが、
一回でも方向キーを押すと、ニュートラル状態でも正常に動きません。
方向キーを押している間は表示されるのですが、離すと消えてしまいます。
また、フィールドの当たり判定ができていません・・・
細かいバグはちゃんと表示されていからじゃないとわからないと思います。

丸投げのようになってすみません、お願いします

cupa
記事: 117
登録日時: 3年前

Re: キャラの表示がうまくいかない

#2

投稿記事 by cupa » 2年前

後もう一ついい忘れていました、C言語の館ではインクルードガードしてないと思うのですが、
したほうが良いのでしょうか?

cupa
記事: 117
登録日時: 3年前

Re: キャラの表示がうまくいかない

#3

投稿記事 by cupa » 2年前

コメントアウトは、左右とコピーして上下を作ったりしているので上下なのに左右って書いてあったりします・・・

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

Re: キャラの表示がうまくいかない

#4

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

とりあえず、左右に動かすと、フィールドからはみ出す件:

現在のキャラの x, y 座標は、ch.first_x + ch.x, ch.first_y + ch.y とわざわざ計算しないといけないようになっていますが、その意図は ? ch.first_x, ch.first_y は、今後、使用されるのでしょうか ?
ch.first_x + ch.x, ch.first_y + ch.y ではなく、 ch.x, ch.y とmx, my の演算結果を FIELD_WIDTH, FIELD_HEIGHT と比較していますよ。
VTuber:
東上☆海美☆(とうじょう・うみみ)
http://atassyu.php.xdomain.jp/vtuber/index.html
レスがついていないものを優先して、レスするみみ。時々、見当外れなレスしみみ。

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

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: キャラの表示がうまくいかない

#5

投稿記事 by みけCAT » 2年前

cupa さんが書きました:
2年前
起こる現象としては、起動して何もキー入力をしないと普通に前向き時のアニメーションをするのですが、
一回でも方向キーを押すと、ニュートラル状態でも正常に動きません。
方向キーを押している間は表示されるのですが、離すと消えてしまいます。
今のプログラムには、
・左右キーを押した後離した時に、ch.imgをニュートラル用の値に戻す処理が無いので、ch.imgがどんどん増えていく
・左右キーが押されている場合、毎フレームch.imgを特定の値にセットするので、アニメーションにならない
という問題がありますね。

コード:

	//左右キ-が押されていなかったら
	for (int i = 0; i < 2; i++) {	//左右分のループ
		if (input_key[i] == 0) {	//もしi番目(左右)が押されていなかったら
			if (frame_cnt % 15 == 0) {	//もしframe_cntを15で割った余りが0ならば
				if (ch.img == NEUTRAL_CH_IMG_MAX) //もしキャラの画像が3(最大)だったのなら
					ch.img = FIRST_NEUTRAL_CH_IMG;	//元の番号に戻す
				ch.img++;	//キャラ画像をプラスする
			}
		}
	}
	
	if (input_key[0] == 1) {	//左方向キーが押されていたら
		ch.img = FIRST_LT_CH_IMG;	//画像をはじめの左向き画像に
		if (frame_cnt % 15 == 0) {	//もしframe_cntを15で割った余りが0ならば
			if (ch.img == LT_CH_IMG_MAX) //もしキャラの画像が11(最大)だったのなら
				ch.img = FIRST_LT_CH_IMG;	//元の番号に戻す
			ch.img++;	//キャラの画像番号をプラスする
		}
	} else if (input_key[1] == 1) {	//右方向キーが押されていたら
		ch.img = FIRST_RT_CH_IMG;	//左向きの初期値
		if (frame_cnt % 15 == 0) {	//もしframe_cntを15で割った余りが0ならば
			if (ch.img == RT_CH_IMG_MAX) //もしキャラの画像が8(最大)だったのなら
				ch.img = FIRST_RT_CH_IMG;	//元の番号に戻す
			ch.img++;	//キャラの画像番号をプラスする
		}
	}
という部分のかわりに、こうするといいかもしれません。
(動作確認していないので、バグやtypoなどあるかもしれません)

コード:

	// 左右キーの入力の状況によって、アニメーションに使う画像の範囲を決める
	int firstImg, lastImg;
	if (input_key[0] == 1) {	//左方向キーが押されていたら
		firstImg = FIRST_LT_CH_IMG;
		lastImg = LT_CH_IMG_MAX;
	} else if (input_key[1] == 1) {	//右方向キーが押されていたら
		firstImg = FIRST_RT_CH_IMG;
		lastImg = RT_CH_IMG_MAX;
	} else {	//左右キ-が押されていなかったら
		firstImg = FIRST_NEUTRAL_CH_IMG;
		lastImg = NEUTRAL_CH_IMG_MAX;
	}

	// アニメーションをする
	if (ch.img < firstImg || lastImg < ch.img) {	//前の状態が異なる範囲だったら
		//今の範囲の最初の画像にする
		ch.img = firstImg;
	}
	if (frame_cnt % 15 == 0) {	//もしframe_cntを15で割った余りが0ならば (frame_cntは0で初期化してその後変更されないので、絶対0のはずだけど)
		if (ch.img == lastImg) //もしキャラの画像が最大だったのなら
			ch.img = firstImg;	//元の番号に戻す
		ch.img++;	//キャラの画像番号をプラスする
	}
cupa さんが書きました:
2年前
また、フィールドの当たり判定ができていません・・・
x は double 型で、FIELD_WIDTH は0より大きい数値なので、式

コード:

x > 0 || x < FIELD_WIDTH
は常に真になります。よって、

コード:

			if (x > 0 || x < FIELD_WIDTH ||	//x,yが可動範囲内なら
				y > 0 || y < FIELD_HEIGHT) {
という部分で使われている条件式は常に真になります。
ここで用いている4個の比較の、OR (||) ではなくAND (&&) をとるようにするとよさそうです。
cupa さんが書きました:
2年前
コメントアウトは、左右とコピーして上下を作ったりしているので上下なのに左右って書いてあったりします・・・
コメントアウトではなく、ただのコメントですね。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

cupa
記事: 117
登録日時: 3年前

Re: キャラの表示がうまくいかない

#6

投稿記事 by cupa » 2年前

まず、みけCATさんに言われたものをやってみました。
無事、アニメーションしました。が、たまにバグ?でアニメーションしなくなります。(はじめの画像の0, 3, 8番目のアニメーションしかしない)

また、OR(||)をAND(&&)にしたのですが、動けなくなりました(プレイヤーの周りに壁があるような感じです)

原因は、あたっしゅさんも言ってたところだと思うのですが・・・

よろしくおねがいします

cupa
記事: 117
登録日時: 3年前

Re: キャラの表示がうまくいかない

#7

投稿記事 by cupa » 2年前

後もう一つ、frame_cntを毎フレーム0で初期化しているので、宣言だけにしたのですが、そこは直ったのですがwarningが出ます。修正したほうがいいでしょうか?
また、この関数内でこの関数で使う変数を宣言・(初期化)しちゃっているのはよくないでしょうか?
C言語の館が毎フレーム呼ばれる関数内で宣言・(初期化)していたので・・・

cupa
記事: 117
登録日時: 3年前

Re: キャラの表示がうまくいかない

#8

投稿記事 by cupa » 2年前

よくみたら、frame_cnt++を関数の最後で呼んでいたので結局毎フレームアニメーションしてました・・・
frame_cnt++を変数の宣言・初期化のすぐ下に持ってきたのですが、今度はアニメーションしなくなりました・・・

元に戻しても、バグで、動くときと動かないときがあります・・・

cupa
記事: 117
登録日時: 3年前

Re: キャラの表示がうまくいかない

#9

投稿記事 by cupa » 2年前

自分のスペックの問題かもしれないですが、たまにカクつきます・・・

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: キャラの表示がうまくいかない

#10

投稿記事 by みけCAT » 2年前

cupa さんが書きました:
2年前
後もう一つ、frame_cntを毎フレーム0で初期化しているので、宣言だけにしたのですが、そこは直ったのですがwarningが出ます。修正したほうがいいでしょうか?
初期化されていない自動変数の値を使うと未定義動作になるので、やってはいけません。
(C++のクラスの中には何も書かなくても初期化されるものもありますが、intなどのプリミティブは初期化されません)
cupa さんが書きました:
2年前
また、この関数内でこの関数で使う変数を宣言・(初期化)しちゃっているのはよくないでしょうか?
C言語の館が毎フレーム呼ばれる関数内で宣言・(初期化)していたので・・・
ものによります。
この関数の中でしか使わず、関数の実行が1回終わったらデータを捨てていい変数は、グローバル変数にせず関数内で宣言するべきです。
また、グローバル変数を使いたい時に、使いたいグローバル変数と同じ名前の変数を関数内で宣言すると、その宣言によりグローバル変数が隠されて使えなくなってしまうので、いけません。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

cupa
記事: 117
登録日時: 3年前

Re: キャラの表示がうまくいかない

#11

投稿記事 by cupa » 2年前

細かいところまで教えていただきありがとうございます。

返信

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