画像の読み込み(と、表示)
次に画像を読みこんで、表示してみる。
画像の読み込みと表示は、授業でやったので、Siv3Dのチュートリアルページさえ見ればできるはず!
画像の用意は準備の時にやったので、なんかしらあるはずだよね?
ここでは、僕の作った画像を使ってゆく。
不満な人は自分で書くなりなんなりしてください。(64×64の画像にしてね)
まずソースコード全体で使えるようにMainでアセット化しよう
なんとなく、画像が把握しやすいように、
enum OBJNAME { FLOOR, WALL, LUGG, GOAL, HUMAN, HUMAN_ON_GOAL, LUGG_ON_GOAL };
この名前に合わせておくといい気がする。
void Main()
{
//自由に拡大縮小できるフォント
FontAsset::Register(U"font", FontMethod::MSDF, 30, Typeface::Regular);
TextureAsset::Register(U"PLAYER",U"プレイヤー用画像");
TextureAsset::Register(U"FLOOR", U"床用画像");
TextureAsset::Register(U"LUGG", U"荷物用画像");
TextureAsset::Register(U"WALL", U"壁用画像);
TextureAsset::Register(U"GOAL", U"ゴール用画像");
while (System::Update())
{
switch (gState) {
case GAME_STATE::TITLE:
省略!
}
}
ついでに、マップとか画像表示に必要なグローバルな値とかを追加しておこう
- globals.h
//グローバル変数を定義するためのヘッダ //ステージの最大サイズ(Console版からちょい名前を変更) const int MAX_STAGE_WIDTH = 10; const int MAX_STAGE_HEIGHT = 10; const int CHR_SIZE = 64; //キャラクタタイルの大きさ(SIZExSIZE) 新しく追加 //今回はどうせ正方形だろうということで縦横まとめた //ゲームの状態制御用enum enum GAME_STATE { TITLE, PLAY, CLEAR }; //Mapを表す構造体(コンソール版と同じ) struct Map { int stage_width; int stage_height; int Dat[MAX_STAGE_HEIGHT][MAX_STAGE_WIDTH]; }; //0 通路 //1 壁 //2 荷物 //3 目的地 //4 人 //5 人 on the 目的地 //6 荷物 on the 目的地 //オブジェクト名を便利に使いたいためのenum(Consoleと同じ) enum OBJNAME { FLOOR, WALL, LUGG, GOAL, HUMAN, HUMAN_ON_GOAL, LUGG_ON_GOAL }; //これの代わりにグラフィックが出るからいらない //const string OBJS[] = { " ", "#", "$", ".", "@", "+", "*" }; //方向を表すenum (Consoleと同じ) enum direction { UP, LEFT, DOWN, RIGHT, NONE };
Asset化した画像を表示してみる
次は画像を表示してみるよ。
目標は以下のような感じ
ソースコードを書いていく
ここで1個ソースコードを追加しよう。
ステージ用のソースコードは、ステージのみを書いてそれを、必要なソースコード(たぶんgameSequence.cpp)で読み込んで使う。
(ほんとは、ステージ用のデータファイルを用意したりするのが大正義なのだけど、みんなファイルの読み方知らんでしょ?(2回目))
- stages.h
#include "globals.h" Map maps[] = { { 9, 8, { {1,1,1,7,7,7,7,7,7,7}, {1,3,1,7,7,7,7,7,7,7}, {1,2,1,7,7,7,7,7,7,7}, {1,0,1,7,7,7,7,7,7,7}, {1,0,1,7,7,7,7,7,7,7}, {1,0,1,1,1,1,1,1,1,7}, {1,4,2,0,0,0,0,3,1,7}, {1,1,1,1,1,1,1,1,1,7}, {7,7,7,7,7,7,7,7,7,7}, {7,7,7,7,7,7,7,7,7,7} } } //, //{ // ステージ数増やしたいときはここにMap型の配列の初期化をずらずらと書いていく(作ってる途中なんでとりま1面分だけ) //} };
まだすぐには使わないけど、追加しておこう!
ソースの追加は、ソリューションエクスプローラーから、プロジェクト名を右クリック ⇒ 追加 ⇒ 新規項目 ⇒ ヘッダファイル
画像の読み込みと表示を書いていく
だんだんソースコードがでかくなっていくので、さらにファイルを分割するか迷うけど、このままいく
その代わり、以下のように、
- Mainから呼ばれる関数たち
- gameSequenceの中でのみ呼ばれMainからは呼ばれない関数たち
- Console版から輸入してきた関数たち
に分けて書いていこう(それでもわかりにくくなりそうだけども、とりあえず)
- gameSequence.h
//Mainから呼ぶやつ void UpdateTitle(GAME_STATE& _state); void DrawTitle(); void UpdatePlay(GAME_STATE& _state); void DrawPlay(); void UpdateClear(GAME_STATE& _state); void DrawClear(); //Mainからは呼ばず、gameSequenceのほうでだけ呼ぶやつ void DrawStage(Map& _map); //Console版から持ってきたやつ OBJNAME GetObjectNum(Point _pos, Map& _map);
- gameSequence.cpp
//includeはこの3つとも必要 #include "globals.h" #include "gameSequence.h" #include "stages.h" void UpdateTitle(GAME_STATE& _state) { 省略 } void DrawTitle() { 省略 } void UpdatePlay(GAME_STATE& _state) { if (MouseL.down()) { _state = GAME_STATE::CLEAR; } } //DrawStageを呼びたいけど、Map& _mapが必要なので、とりあえずダミーとしてさっき作ったmaps[0](一個だけあるから、[0]で呼ぶ)を書いておく //まだマップのデータ使ってはないよ。 void DrawPlay() { Scene::SetBackground(Palette::Black); FontAsset(U"font")(U"PLAY_SCENE").drawAt(Scene::Center()); DrawStage(maps[0]); } void UpdateClear(GAME_STATE& _state) { 省略 } void DrawClear() { 省略 } void DrawStage(Map& _map) { Scene::SetBackground(Palette::Darkgrey);//背景の設定はもう説明いらんよね? //FLOOR, WALL, LUGG, GOAL, HUMAN, HUMAN_ON_GOAL, LUGG_ON_GOAL の順に画像をTextureの配列でAssetからコピー //Assetのままでもいいけど配列にしちゃった方が後々使い勝手がいいんだよねぇ Texture ObjImg[7]{ TextureAsset(床の画像Asset名), TextureAsset(?????), TextureAsset(?????), TextureAsset(?????), TextureAsset(?????), TextureAsset(ゴールの上にプレイヤーがいたら何が表示されていればいい?), TextureAsset(ゴールの上に荷物があったら何が表示されていればいい?) //キャラが重なっているときはとりあえずは一番上にいるキャラを描こう }; for (int i = 0; i < 7; i++) { ObjImg[i]をCHR_SIZEにresizedして座標Point(i*CHR_SIZE, 0)あたりにdraw } } //Console版から持ってきたやつ OBJNAME GetObjectNum(Point _pos, Map& _map) { return(_map.Datの(_pos.x, _pos.y)にあるオブジェクト名を返す); //_map.Dat[][]に入ってるintの値を(OBJNAME)でキャストしちゃえばいいよ。 }
ついでにマップを表示できるように改造しよう
コンソール版を作った時に、マップの座標は、Map構造体のDatメンバ(2次元配列)で表されていて。座標の位置にあるオブジェクトは0~7の数値で表されていた(はず?)
(もしかすると0~6だったかもだけど、それ以外の領域を追加しただけなので許してちょんまげマーチ)
//0 通路 //1 壁 //2 荷物 //3 目的地 //4 人 //5 人 on the 目的地 //6 荷物 on the 目的地 //7 その他ステージ領域外
あとは、その値に応じて以下のように、表示用のキャラクタ(文字)としてOBJSの配列を作り、表示したいある座標のオブジェクト番号が2だったら荷物を表す“$”を表示、とかやっていけば、マップらしきものが表示できた。
const string OBJS[] = { " ", "#", "$", ".", "@", "+", "*" }; void DrawStage(Map& _map) { for (int j = 0; j < _map.stage_height; j++) { for (int i = 0; i < _map.stage_width; i++) { int num = GetObjNum(Point{j,i}, _map); cout << OBJS[num]; } cout << endl; } }
グラフィック版にするために、この文字の代わりに、読み込んだ画像
を表示してやればよい。
画像の大きさは、幅x高さ=CHR_SIZExCHR_SIZEなので、あるマップ座標(x, y)の位置のグラフィック版のタイル表示位置は、
Point型で表すと、Point(x*CHR_SIZE, y*CHR_SIZE)になる。
Map表示関数DrawStageの作成
- DrawStage.cpp
void DrawStage(Map& _map) { Scene::SetBackground(Palette::Darkgrey); Texture ObjImg[7]{ TextureAsset(U"FLOOR"), TextureAsset(U"WALL"), TextureAsset(U"LUGG"), TextureAsset(U"GOAL"), TextureAsset(U"PLAYER"), TextureAsset(U"PLAYER"), TextureAsset(U"LUGG"), }; //ここで、ついにこの順番でTexture配列作ったのが役に立つよ //いったんいらなくなるからコメント //for (int i = 0; i < 7; i++) //{ // ObjImg[i].resized(CHR_SIZE).draw(Point{i * CHR_SIZE,0}); //} for (int j = 0; j < _mapのたかさ; j++) { for (int i = 0; i < _mapの幅; i++) { OBJNAME objNum = _mapからGetObjectNumで{ i,j }の位置のオブジェクト番号(名前)をとってくる if (objNum != 7)//7の時は何もないから、スルーしたいの { //ステージになっている座標には、必ず一回床を描いてから、オブジェクトを描こう! ObjImg[FLOOR].draw(i * CHR_SIZE, j * CHR_SIZE); ObjImg[{i,j}位置のオブジェクト名(番号)].draw(描画座標を計算する); } } } }
これで、さっきのキャラクターを並べて表示していたDrawStage関数が、さっきstages.hで作っておいたマップを表示するようになるはず。(割とあっさりできるんだよね笑)
Kennyさん所の画像を使った人はこんな感じに表示される
後々は、アニメーションとかもやりたいから、こっち使うようにしていく。