====== 画像貼り付けたりなんだり~動くまで ======
自機と敵キャラに画像をはっつけよう。\\
ゲームのデータはMainでgameData gDatとして宣言してある。\\
画像の登録
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には、ゲームで使うすべてのデータが入っているはずなので、そいつを渡しといて、内部で個別に初期化する\\
初期化関数void InitGameData(gameData& _dat);の作成
void InitGameData(gameData& _dat)
{
InitPlayer(_dat.Player);
InitEnemy(_dat.Enemy);
}
キャラクターのサイズ等は、**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 };//自機の弾の速さ
キャラクタの表示サイズとかスピードとかを変更したい場合はこの辺をいじればいいし、この辺の定数を使っておけば一気に書き換えができる。\\
そんで、個別に**InitPlayer**と**InitEnemy**を作ろう\\
InitPlayerとInitEnemyの追加
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 = 真(生きてる)
}
ここまでできたらいったん表示してみよう。\\
初めての画像表示
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の敵位置);
}
=== 実行例 ===
{{game-engineer:classes:2023:something-else:summertime-special-cource:スクリーンショット_2023-08-24_220413.png?300}}
キャラクタへの画像貼り付けサンプル
==== 作業に便利な関数を追加 ====
何回も書くのが面倒になりそうなので、作業用に関数を3つほど追加します。\\
- **void SetCharaRect(gameChar& _gamechar, SizeF _size);**
* gameChar型の当たり判定用四角形を設定する関数
* サイズを指定すると、現在のposを中心にSizeF _size{幅、高さ}の四角形を割り当てる
- **void DrawPlayer(gameChar& _player);**
* プレイヤーの画像を描画
- **void DrawEnemy(gameChar& _enemy);**
* 敵の画像を描画
例によってgameSequence.hへのプロトタイプ宣言の追加は、省略しやんす。
関数を3つ追加
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
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の設定を見直そう\\
=== 実行結果 ===
{{game-engineer:classes:2023:something-else:summertime-special-cource:スクリーンショット_2023-08-24_224210.png?300}}
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;
で更新できる。\\
従ってやらなければならないことは、\\
- キーボードからwasdやアローキーで移動方向の入力を受け取る
- 入力方向を方向ベクトルに直しmychar.moveDirにセット
- 現在の位置を、現在の位置とスピード、移動方向のベクトル、フレーム経過時間、を使って更新する
- 更新された位置のキャラクタを描画する
である。\\
* キーボードからwasdやアローキーで移動方向の入力を受け取る
これは、実はすでに作ってあるものを使える。スライドパズルかコスコマンのgetDirectionをコピペしてこよう。そのままでOKである。\\
一応貼っとく\\
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;
}
これを使って\\
- キーボードからwasdやアローキーで移動方向の入力を受け取る
* GetDirection()を、Playerを更新するUpdatePlayer関数とかを作ってその中で実行
* UpdatePlayと混じらんようにね。(名前の付け方失敗した)
- 入力方向を方向ベクトルに直しmychar.moveDirにセット
* UpdatePlayer内でGetDirectionで取得した方向をもとに方向ベクトルを生成
- 現在の位置を、現在の位置とスピード、移動方向のベクトル、フレーム経過時間、を使って更新する
* UpdatePlayer内で、計算
- UpdatePlayerをUpdatePlay内で実行(ややこしや。。。)
- 更新された位置のキャラクタを描画する
* DrawPlayerをDrawPlayで実行
上の処理をするものを、UpdatePlayerとUpdaePlayが以下の様になるように作ってみよう。\\
UpdatePlayerとUpdatePlayの中身
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は大体以下のような流れになります。\\
今回は、スペースインベーダーオマージュなので、__自機は左右にしか動きません。__したがって...\\
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\\
速度などを変えて、パラメータの意味を確認しよう\\
;#;
[[game-engineer:classes:2023:something-else:summertime-special-cource:cosmichooligun-3|その3 弾 へ]]
;#;