まず、いつも通り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型

絵文字を画像として表示するために、絵文字を読み込んで画像型の一つである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();
	}
}

* 描画結果

Fig. 1: 絵文字の表示

位置の調整

画像の位置は、.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);
	}

次は、メッセージを表示するためのウインドウを表示する関数を作ってみます。
以下のようなウィンドウを表示します。

* 表示例

Fig. 2: メッセージウィンドウの表示

関数を考える

とりあえず、クラスのメンバーを渡すのは後で考えるとして。。。
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側は変更はありません。

* 表示例

Fig. 3: メッセージウィンドウにメッセージを表示

ついでに猫位置、龍位置を変数化

この辺でついでに、猫の位置 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)
{ //ごにょごにょ。。。省略
}

こんな感じで引数を増やして、ポインタ経由で関数に渡してあげるといいです。
構造体やクラスをポインタ経由で渡すのはどうしてだっけ?

  • game-engineer/classes/2022/game-programing-1/second-term/10/11-01-xx.txt
  • 最終更新: 3年前
  • by root