まず、いつも通りsiv3Dのプロジェクトを作ります。(ここまではいいよね?)
それから、背景画像を用意します。
Siv3Dで表示できる画像形式なら、何でもいいよ。
(画像形式に関しては、前期に習ったよね?)
Siv3Dのプロジェクトフォルダ⇒ App ⇒ example ⇒ 画像ファイル(名前は半角アルファベットで何でもいいよ)
今回は、background.png を置いてみました。
とりあえず、テンプレートで作ったSiv3Dのプロジェクトは余計なもんが多すぎるので、痩身させます。
# include <Siv3D.hpp> // OpenSiv3D v0.6.3 void Main() { //背景画像を設定 const Texture backGroundImage{ U"example/background.png" }; // 通常のフォントを作成 | Create a new font const Font font{ 60 }; // 絵文字用フォントを作成 | Create a new emoji font const Font emojiFont{ 60, Typeface::ColorEmoji }; // `font` が絵文字用フォントも使えるようにする | Set emojiFont as a fallback font.addFallback(emojiFont); while (System::Update()) { } }
ついでに背景画像を読み込む設定をします。
こう書いておくだけで画像を呼び出すことができます。
そうなんだ。。。って思うかもしれないですが後でwindowsプログラミングを習うととてもすごいことだってわかります。
//背景画像を設定 const Texture backGroundImage{ U"example/background.png" };
Rectは矩形(くけい:四角形のことです、これからよく出てくるのでタンケイとか読まないように)
矩形も前にやった、いろいろな形を書く命令の一つです。
形状.draw()みたいな感じで大体かけましたよねぇ。
//Rectの宣言(定義) Rect{左上のx座標, 左上のy座標, 幅, 高さ} //上のRectにTexture image_name(画像)を貼り付ける Rect{左上のx座標, 左上のy座標, 幅, 高さ}(image_name) //上のRectにTexture image_name(画像)を貼り付けて、画面に描画 Rect{左上のx座標, 左上のy座標, 幅, 高さ}(image_name).draw()
最終的に、読み込んだ背景画像を描画するには、以下のように書く。
//背景を表示 Rect{ 0,0, wsize.x,wsize.y }(backGroundImage).draw();
windowのサイズをwsizeに定数として宣言しておきます。
//デフォルトのウィンドウサイズを表す変数 Size // メンバにxとyを持つウィンドウなどのサイズを表すのに便利な型 // wsize.x: windowの幅 // wsize.y: windowの高さ const Size wsize{ 800,600 };
んで、背景を表示するには以下のようにします。
簡単よね?
# include <Siv3D.hpp> // OpenSiv3D v0.6.3 void Main() { //背景画像を設定 const Texture backGroundImage{ U"example/background.png" }; // 通常のフォントを作成 | Create a new font const Font font{ 60 }; // 絵文字用フォントを作成 | Create a new emoji font const Font emojiFont{ 60, Typeface::ColorEmoji }; // `font` が絵文字用フォントも使えるようにする | Set emojiFont as a fallback font.addFallback(emojiFont); const Size wsize{ 800,600 }; while (System::Update()) { //背景を表示 Rect{ 0,0, wsize.x,wsize.y }(backGroundImage).draw(); } }
次にキャラ画像を追加します。
自分でpngなどの背景を透過できる画像を探してきたり、作成してもよいです。
今回は簡単にsiv3Dの機能で、「絵文字を画像として表示する」機能を使ってみます。
絵文字ペディアにある絵文字ならどれでも読み込んで使えます。
今回は猫(🐈)とドラゴン(🐉)を使ってみます。
絵文字を画像として表示するために、絵文字を読み込んで画像型の一つであるTexture型で変数を作ります。
// 絵文字からテクスチャを作成 | Create a texture from an emoji const Texture neko{ U"🐈"_emoji }; const Texture dragon{ U"🐉"_emoji };
あとは、そのままTextureのメンバ関数の.draw()を呼んでしまえば画面に表示できます。
# include <Siv3D.hpp> // OpenSiv3D v0.6.3 void Main() { //背景画像を設定 const Texture backGroundImage{ U"example/background.png" }; // 通常のフォントを作成 | Create a new font const Font font{ 60 }; // 絵文字用フォントを作成 | Create a new emoji font const Font emojiFont{ 60, Typeface::ColorEmoji }; // `font` が絵文字用フォントも使えるようにする | Set emojiFont as a fallback font.addFallback(emojiFont); // 絵文字からテクスチャを作成 | Create a texture from an emoji const Texture neko{ U"🐈"_emoji }; const Texture dragon{ U"🐉"_emoji }; const Size wsize{ 800,600 }; while (System::Update()) { //背景を表示 Rect{ 0,0, wsize.x,wsize.y }(backGroundImage).draw(); neko.draw(); dragon.draw(); } }
* 描画結果
画像の位置は、.draw()の引数で指定できます。
const Font emojiFont{ 60, Typeface::ColorEmoji };
途中で、絵文字のサイズを指定していますが、これを60にしたからと言って60x60の矩形に収まるわけではないみたい。。。
とりあえず、試行錯誤で以下のように位置指定してみます。
位置はPoint型で指定できます。
Point neko_position(200, 400);//猫位置 Point dragon_position(400, 400);//龍位置 while (System::Update()) { //背景を表示 Rect{ 0,0, wsize.x,wsize.y }(backGroundImage).draw(); neko.draw(neko_position); dragon.draw(dragon_position); }
とりあえず、クラスのメンバーを渡すのは後で考えるとして。。。
RoundRectによって、角の丸い矩形を作ることができます。
.drawFrameメンバ関数で枠を書くことができます。(便利だよねぇ)
Font& _f引数には、Mainのほうで背景の次の行で宣言しているfontを渡してあげます。
渡すフォントを変えるといろいろな字体で書けるはずです。
https://zenn.dev/reputeless/books/siv3d-documentation/viewer/tutorial-font!Siv3D チュートリアル(14.4フォントの種類参照)
void drawMessageWindow(const Font& _f, const Rect _rect) { const int bound_margine = 3; //周りの余白 const int round_size = 5; //過度の丸さの具合 RoundRect{ _rect.x, _rect.y , _rect.w, _rect.h ,round_size } .draw(Palette::Black) //ウィンドウの内部の色 .drawFrame(2, 0, Palette::White); //枠のフレームの表示と色(枠外の太さ、枠内側の太さ、色) }
こんな関数を作っておいて、Mainで呼びます。
Rectの左上位置、幅と高さは,{Point型, Size型}と並べることでも設定できます。
後々見やすくなると思うので、この形で呼び出してみます。
Point mw_pos{ 50,50 }; Size mw_size{ 250, 280 }; while (System::Update()) { //背景を表示 Rect{ 0,0, wsize.x,wsize.y }(backGroundImage).draw(); drawMessageWindow(font, Rect{ mw_pos, mw_size }); neko.draw(Point(200, 400)); dragon.draw(Point(400,400)); }
これで表示例の様にメッセージウィンドウを表示できます。
あとは、メッセージウィンドウに文字列を書いてみます。
最終的には、関数の引数に、キャラクタを表すクラスのインスタンスを追加して、受け取ったインスタンスのパラメータから、
キャラ名、HPやMPを表示するようにします。
とりあえず、関数内で必要な文字描画の命令を試してみます。
void drawMessageWindow(const Font& _f, const Rect _rect)を以下のように変更します。
void drawMessageWindow(const Font& _f, const Rect _rect) { const int bound_margine = 3; //周りの余白 const int round_size = 5; //過度の丸さの具合 RoundRect{ _rect.x, _rect.y , _rect.w, _rect.h ,round_size } .draw(Palette::Black) .drawFrame(2, 0, Palette::White); std::string tmp = "もじだよ"; //std::stringをSiv3D用にUnicode変換する String cname = Unicode::Widen(tmp);//std::string => Unicode変換 _f(cname).draw(25, _rect.x + 10, _rect.y + 10, Palette::White); //読み込んだフォントで書く! int hp = 300;//intをstring => Unicodeに変換して表示 std::string str_hp = " HP:" + std::to_string(300); _f(Unicode::Widen(str_hp)).draw(25, _rect.x + 10, _rect.y + 40, Palette::White); }
Main側は変更はありません。
* 表示例
この辺でついでに、猫の位置 Point neko_position、龍の位置 Point dragon_positionを宣言して、根琴竜の位置を変数化してしまいます。
こうしとくと、動きを付けるときとかに、あとで変更しやすいよね。
Point neko_position{ 200,400 }; Point dragon_position{ 400,400 }; while (System::Update()) { //背景を表示 Rect{ 0,0, wsize.x,wsize.y }(backGroundImage).draw(); drawMessageWindow(font, Rect{ mw_pos, mw_size }); neko.draw(neko_position); dragon.draw(dragon_position); }
ペットの犬に、「お手!」と言うと必ず「ワン!」と鳴いて手を出す犬がいます。(いるとします)
このように、何か合図やメッセージを送った時に一方通行にならず、お互いに反応が返ってくることを「相互作用的」(インタラクティブ)である。インタラクション(Interaction)する。などと言います。
と言います。
今回は、「キャラクタ(の画像)をクリックしたらメッセージウィンドウが表示される」と言うインタラクションに挑戦してみます。
とは言っても、Siv3Dではとても簡単に実現できて、draw()関数がシステムに返しているオブジェクト(深くは考えなくてもいいです)のメンバ関数である
| 関数名 | 内容 | 戻り値 |
|---|---|---|
| leftClicked() | 左クリックされたか? | bool |
| leftPressed() | 左ボタンが押されているか? | bool |
| leftReleased() | 左ボタンが離された瞬間か? | bool |
| rightClicked() | 左クリックされたか? | bool |
| rightPressed() | 左ボタンが押されているか? | bool |
| rightReleased() | 左ボタンが離された瞬間か? | bool |
で調べられます。具体的には
if(neko.draw(neko_position).leftPressed()) { //左ボタンが押されているときの処理 }
のように書けます。
って訳で、ボタンを押している間メッセージウィンドウを表示してみます。
たぶんできるよね。。。
while (System::Update()) { //背景を表示 Rect{ 0,0, wsize.x,wsize.y }(backGroundImage).draw(); //drawMessageWindow(font, Rect{ mw_pos, mw_size }); if (neko.draw(neko_position).leftPressed()) { //どうすればいい? } if (dragon.draw(dragon_position).leftPressed()) { //どうすればいい? } }
最後に、やっと猫と龍のもとになるゲームキャラクラスを作ってパラメータ設定します。
class cGameChara { private: std::string name_; int HP_; //生命力 int MP_; //魔法力 int ATK_; //攻撃力 Vec2 Position_; //2次元ベクトルでキャラクターの位置を表す public: cGameChara() {};//引数なしコンストラクタ(未完成) メンバイニシャライザを使ってみよう! cGameChara(std::string _name, int _hp, int _mp, int _atk);//引数有コンストラクタ(未完成) //デストラクタも書いてあげよう(中身は空でもOK) void setName(std::string _name) { name_ = _name; } void setHP(int _hp) { HP_ = _hp; } void setMP(int _mp) { MP_ = _mp; } void setATK(int _atk) { ATK_ = _atk; } int getHP() { return(HP_); }; int getMP() { return(MP_); }; int getATK() { return(ATK_); }; void setPosition(Vec2 _position) { Position_ = _position;} Vec2 getPosition() { return(Position_); } std::string getName() { return(name_); } };
こんな感じでキャラクタを表すクラスを作ってあげてください。(コンストラクタなどの実装は任せます)
このクラスのインスタンスとして、nekoとdragonを作って、そのパラメータを設定します。
//猫とドラゴンのインスタンスを生成 cGameChara cNeko("ねこ", 300, 100, 125), cDragon("おりゅう",600,800,500); //cGameChara *pNeko = new cGameChara("ねこ", 300, 100, 125), *pDragon = new cGameChara("おりゅう",600,800,500); //でもいいかなぁ
そんで最後に、メッセージウィンドウ表示関数にその情報を渡してあげます。(どうやったらいい?)
void drawMessageWindow(const Font& _f, const Rect _rect) { //ごにょごにょ。。。省略 }
これを。。。
void drawMessageWindow(const Font& _f, Rect _rect, cGameChara *_mychar) { //ごにょごにょ。。。省略 }
こんな感じで引数を増やして、ポインタ経由で関数に渡してあげるといいです。
構造体やクラスをポインタ経由で渡すのはどうしてだっけ?