====== 画像貼り付けたりなんだり~動くまで ====== 自機と敵キャラに画像をはっつけよう。\\ ゲームのデータは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 弾 へ]] ;#;