画像貼り付けたりなんだり~動くまで
自機と敵キャラに画像をはっつけよう。
ゲームのデータはMainでgameData gDatとして宣言してある。
- 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には、ゲームで使うすべてのデータが入っているはずなので、そいつを渡しといて、内部で個別に初期化する
- 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 };//自機の弾の速さ
キャラクタの表示サイズとかスピードとかを変更したい場合はこの辺をいじればいいし、この辺の定数を使っておけば一気に書き換えができる。
そんで、個別に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 = 真(生きてる) }
ここまでできたらいったん表示してみよう。
- 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の敵位置); }
実行例
作業に便利な関数を追加
何回も書くのが面倒になりそうなので、作業用に関数を3つほど追加します。
- void SetCharaRect(gameChar& _gamechar, SizeF _size);
- gameChar型の当たり判定用四角形を設定する関数
- サイズを指定すると、現在のposを中心にSizeF _size{幅、高さ}の四角形を割り当てる
- void DrawPlayer(gameChar& _player);
- プレイヤーの画像を描画
- void DrawEnemy(gameChar& _enemy);
- 敵の画像を描画
例によってgameSequence.hへのプロトタイプ宣言の追加は、省略しやんす。
- 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の設定を見直そう
実行結果
ここで動くところまでやっちゃおう
現在、方向は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である。
一応貼っとく
- 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; }
これを使って
- キーボードからwasdやアローキーで移動方向の入力を受け取る
- GetDirection()を、Playerを更新するUpdatePlayer関数とかを作ってその中で実行
- UpdatePlayと混じらんようにね。(名前の付け方失敗した)
- 入力方向を方向ベクトルに直しmychar.moveDirにセット
- UpdatePlayer内でGetDirectionで取得した方向をもとに方向ベクトルを生成
- 現在の位置を、現在の位置とスピード、移動方向のベクトル、フレーム経過時間、を使って更新する
- UpdatePlayer内で、計算
- UpdatePlayerをUpdatePlay内で実行(ややこしや。。。)
- 更新された位置のキャラクタを描画する
- DrawPlayerをDrawPlayで実行
上の処理をするものを、UpdatePlayerとUpdaePlayが以下の様になるように作ってみよう。
- 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
速度などを変えて、パラメータの意味を確認しよう

