画像貼り付けたりなんだり~動くまで

自機と敵キャラに画像をはっつけよう。
ゲームのデータはMainでgameData gDatとして宣言してある。

Listing. 1: 画像の登録
Main.cpp
	gameData gDat;
	gDat.gState = GAME_STATE::TITLE;
        //Fontアセットの登録
	FontAsset::Register(U"font", FontMethod::MSDF, 48, Typeface::Bold);
        //Textureアセットの登録
	TextureAsset::Register(U"PLAYER", U"プレイヤー画像の場所 ****.pngなど");
	TextureAsset::Register(U"ENEMY", U"敵画像の場所とファイル名 フォルダは//でつないで書く");

後はこれを使って、自機と敵のパラメータと画像を初期化していく。
ここから後は、関数のプロトタイプ宣言の書き方や追加は省略するので、関数が追加されたら、確実に自分でプロトタイプ宣言を書こう
初期化は初期化関数void InitGameData(gameData& _dat);を作成して、その中でそれぞれ初期化していくすなわち
gameDataには、ゲームで使うすべてのデータが入っているはずなので、そいつを渡しといて、内部で個別に初期化する

Listing. 2: 初期化関数void InitGameData(gameData& _dat);の作成
gameSequence.cpp
void InitGameData(gameData& _dat)
{
    InitPlayer(_dat.Player);
    InitEnemy(_dat.Enemy);
}

キャラクターのサイズ等は、gameSequence.hに定数で作ってあげよう。
ついでに当たり判定枠のサイズとか、弾とか自分の速さとか使いそうなのを追加しておくので、何を表しているかチェックしといて。

gameSequence.h
//ファイルの冒頭の方
const int ENEMY_CHR_SIZE{ 48 }; //敵キャラの画像サイズ(縦横同じ)
const int ENEMY_RECT_SIZE{ 48};//敵キャラの当たり判定枠の大きさ(縦横同じ)
const double ENEMY_MOVE_SPEED{ ENEMY_CHR_SIZE / 4.0 }; //敵キャラの移動速度
const int ENEMY_BULLET_SIZE{ 15 }; //敵キャラの弾のサイズ
const double ENEMY_BULLET_SPEED{ 250 }; //敵キャラの弾の速さ
 
 
const int PLAYER_CHR_SIZE{ 48 };//自機の画像サイズ(縦横同じ)
const double PLAYER_RECT_SIZE{ 48 };//自機の当たり判定枠の大きさ(縦横同じ)
const double PLAYER_MOVE_SPEED{ PLAYER_CHR_SIZE * 4.5 };//自機の移動速度
const int PLAYER_BULLET_SIZE{ 15 };//自機の弾のサイズ
const double PLAYER_BULLET_SPEED{ 250 };//自機の弾の速さ

キャラクタの表示サイズとかスピードとかを変更したい場合はこの辺をいじればいいし、この辺の定数を使っておけば一気に書き換えができる。

そんで、個別にInitPlayerInitEnemyを作ろう

Listing. 3: InitPlayerとInitEnemyの追加
gameSequence.cpp
void InitPlayer(gameChar& _player)
{
	Vec2 chrMargin{0, 座標をセンターから下の方にいい感じで下げる値を入れる};
	_player.pos = Scene::Center() + chrMargin;
	_player.speed = 自機のスピード;
	_player.tex = 自機のテクスチャアセット
	_player.rect = 左上点は、自機の位置から自機サイズの1/2引いたやつ、幅と高さはキャラサイズ;
	_player.moveDir = ゼロベクトルで初期化;
	_player.isAlive = 真にしておく;
}
 
void InitEnemy(gameChar& _enemy)
{
	_enemy.pos = シーンの真ん中からちょい上ぐらいに配置;
	_enemy.speed = 敵のスピード;
	_enemy.tex = 敵の画像(TextureAssetで登録したやつ);
	_enemy.rect =敵機の座標と、敵機の大きさで初期化(中心じゃな、矩形の左上の座標をしていするんだよ);
	_enemy.moveDir = 右向きのベクトルをとりあえず設定
	_enemy.isAlive = 真(生きてる)
}

ここまでできたらいったん表示してみよう。

Listing. 4: 初めての画像表示
gameSequence.cpp
void DrawPlay(gameData& _dat)
{
	Scene::SetBackground(Palette::Black);
	Vec2 strMargin{ 0, Scene::Height() / 2 - FontAsset(U"font").height() };
	FontAsset(U"font")(U"PLAY_SCENE").drawAt(Scene::Center() - strMargin);
 
	_dat.Player.tex.resized(自キャラサイズ).drawAt(_datのプレイヤー位置);
	_dat.Enemy.tex.resized(敵キャラサイズ).drawAt(_datの敵位置);
 
}

実行例

Fig. 1: キャラクタへの画像貼り付けサンプル

何回も書くのが面倒になりそうなので、作業用に関数を3つほど追加します。

  1. void SetCharaRect(gameChar& _gamechar, SizeF _size);
    • gameChar型の当たり判定用四角形を設定する関数
    • サイズを指定すると、現在のposを中心にSizeF _size{幅、高さ}の四角形を割り当てる
  2. void DrawPlayer(gameChar& _player);
    • プレイヤーの画像を描画
  3. void DrawEnemy(gameChar& _enemy);
    • 敵の画像を描画

例によってgameSequence.hへのプロトタイプ宣言の追加は、省略しやんす。

Listing. 5: 関数を3つ追加
gameSequence.cpp
void SetCharaRect(gameChar& _gamechar, SizeF _size)
{
        //四角形の中心がわかっていて、それを基準に左上の点を求めるための補正値
	Vec2 adjustVal = (_pos.x, _pos.y)が、四角形の真ん中だから???
        //左上の点を算出
        Vec2 topLeft = _gamechar.pos - adjustVal;
	_gamechar.rect = { topLeft, _size.x, _size.y };
}
 
void DrawPlayer(gameChar& _player)
{
    if (_player.isAlive) {
	_player.tex.resized(PLAYER_CHR_SIZE).drawAt(_player.pos);
	//_player.rect.drawFrame(1, 1, Palette::Red);
    }
}
 
void DrawEnemy(gameChar& _enemy)
{
	if (_enemy.isAlive) {
		_enemy.tex.resized(ENEMY_CHR_SIZE).drawAt(_enemy.pos);
		//_enemy.rect.drawFrame(1, 1, Palette::Red);
	}
}

作ったら呼ぶ。
InitPlayer、 InitEnemy、DrawPlayを以下のように、変更する。
Init

gameSequence.cpp
void InitPlayer(gameChar& _player)
{
省略
	//_player.rect = { ごにょごにょ};
	SetCharaRect(_player, SizeF{ PLAYER_RECT_SIZE, PLAYER_RECT_SIZE});
	_player.moveDir = { 0, 0 };
	_player.isAlive = true;
}
 
void InitEnemy(gameChar& _enemy)
{
省略
	//_enemy.rect = { ごにょごにょ};
	SetCharaRect(_enemy, SizeF{ ENEMY_RECT_SIZE, ENEMY_RECT_SIZE });
	_enemy.moveDir = { 1, 0 };
	_enemy.isAlive = true;
}
 
void DrawPlay(gameData& _dat)
{
	Scene::SetBackground(Palette::Black);
	Vec2 strMargin{ 0, Scene::Height() / 2 - FontAsset(U"font").height() };
	FontAsset(U"font")(U"PLAY_SCENE").drawAt(Scene::Center() - strMargin);
 
	DrawPlayer(_dat.Player);
	DrawEnemy(_dat.Enemy);
	//_dat.Player.tex.resized(PLAYER_CHR_SIZE).drawAt(_dat.Player.pos);
	//_dat.Enemy.tex.resized(ENEMY_CHR_SIZE).drawAt(_dat.Enemy.pos);
 
}

ついでにデバッグしやすいように、Rectの描画を追加してみよう。
これはおまけで、DrawPlayerとDrawEnemyにもう書いてあるので、コメントを外してみよう。
キャラサイズをちょうど囲む矩形が描画されるはずである。
うまくいかない人は、SetCharaRectの中のRectの設定を見直そう

実行結果

Fig. 2: Rectの描画

現在、方向はdirection、ゲームに登場するキャラクタはgameChar型で制御するプログラムになっている。
ここで、自機を左右に動かす処理を追加してみよう。

enum direction
{
	UP, LEFT, DOWN, RIGHT, NONE
};
 
struct gameChar
{
	Vec2 pos;     //位置
 
	bool isAlive; //生死
	Texture tex;  //画像
	double speed; //移動速度
	RectF rect;   //バウンディングボックス
	Vec2 moveDir; //方向ベクトル
};

自機の、位置、移動方向、移動速度はすべてgameChar型に保存されているので、値さえ自分の目的通りに設定されていれば、

    //自機が gameChar mycharで、宣言され適切に初期化、更新されているとして
    mychar.pos = mychar.pos + mychar.speed * Scene::DeltaTime() * mychar.moveDir;

で更新できる。
従ってやらなければならないことは、

  1. キーボードからwasdやアローキーで移動方向の入力を受け取る
  2. 入力方向を方向ベクトルに直しmychar.moveDirにセット
  3. 現在の位置を、現在の位置とスピード、移動方向のベクトル、フレーム経過時間、を使って更新する
  4. 更新された位置のキャラクタを描画する

である。

  • キーボードからwasdやアローキーで移動方向の入力を受け取る

これは、実はすでに作ってあるものを使える。スライドパズルかコスコマンのgetDirectionをコピペしてこよう。そのままでOKである。
一応貼っとく

gemSequence.cpp
direction GetDirection()
{
	if ((KeyUp | KeyW).pressed())
	{
		return UP;
	}
	else if ((KeyLeft | KeyA).pressed())
	{
		return LEFT;
	}
	else if ((KeyDown | KeyS).pressed())
	{
		return DOWN;
	}
	else if ((KeyRight | KeyD).pressed())
	{
		return RIGHT;
	}
	else
		return NONE;
}

これを使って

  1. キーボードからwasdやアローキーで移動方向の入力を受け取る
    • GetDirection()を、Playerを更新するUpdatePlayer関数とかを作ってその中で実行
    • UpdatePlayと混じらんようにね。(名前の付け方失敗した)
  2. 入力方向を方向ベクトルに直しmychar.moveDirにセット
    • UpdatePlayer内でGetDirectionで取得した方向をもとに方向ベクトルを生成
  3. 現在の位置を、現在の位置とスピード、移動方向のベクトル、フレーム経過時間、を使って更新する
    • UpdatePlayer内で、計算
  4. UpdatePlayerをUpdatePlay内で実行(ややこしや。。。)
  5. 更新された位置のキャラクタを描画する
    • DrawPlayerをDrawPlayで実行

上の処理をするものを、UpdatePlayerとUpdaePlayが以下の様になるように作ってみよう。

Listing. 6: UpdatePlayerとUpdatePlayの中身
gameSequence.cpp
void UpdatePlay(gameData& _dat)
{
	UpdatePlayer(_dat);
	//if (MouseL.down())
	//{
	//	_dat.gState = GAME_STATE::CLEAR;
	//}
}
 
void DrawPlay(gameData& _dat)
{
	Scene::SetBackground(Palette::Black);
	Vec2 strMargin{ 0, Scene::Height() / 2 - FontAsset(U"font").height() };
	FontAsset(U"font")(U"PLAY_SCENE").drawAt(Scene::Center() - strMargin);
 
	DrawPlayer(_dat.Player);
	DrawEnemy(_dat.Enemy);
}

ヒントとして(ほとんど答え)UpdatePlayerは大体以下のような流れになります。
今回は、スペースインベーダーオマージュなので、自機は左右にしか動きません。したがって...

gameSequence.cpp
void UpdatePlayer(gameData& _dat)
{
	direction d = GetDirection();
 
	switch (d)
	{
	case LEFT:
		_dat.Player.moveDir に左に移動するための単位ベクトルセット;
		break;
	case RIGHT:
		_dat.Player.moveDirに右に移動するための単位ベクトルをセット;
		break;
	default:
		return;
	}
	//これも関数化しちゃった方がすっきりするかもねぇ
	_dat.Player.posを更新(上の解説通りに)
        //更新した自機位置に、当たり判定用の四角形をセット
	SetCharaRect(_dat.Player, SizeF{ PLAYER_RECT_SIZE,PLAYER_RECT_SIZE });
}

実行例

省略するが、実行してキーボード操作で左右に動けばOK
速度などを変えて、パラメータの意味を確認しよう

その3 弾 へ

  • game-engineer/classes/2023/something-else/summertime-special-cource/cosmichooligun-2.txt
  • 最終更新: 2年前
  • by root