ページ 1 / 1
フェードイン・フェードアウト処理
Posted: 2013年3月17日(日) 23:09
by クールアイス
こんばんは。
現在c++とDXライブラリでゲームを作成しています。
その中で「フェードイン・フェードアウト」処理を行うために、以下のコードを作ってみました。
コード:
//--------------------------------------------------
// フェードイン
//--------------------------------------------------
bool Fade::FadeIn(int fade_speed, int r, int g, int b)
{
static int bright = 0;
// 描画輝度をセット
SetDrawBright(bright, bright, bright);
// 画面を覆う四角形を描画
DrawBox(0, 0, 640, 480, GetColor(r, g, b), true);
DrawFormatString(40, 40, GetColor(0, 0, 255), "%d", bright);
// 輝度が255を超えたら
if(bright > 255)
{
// 画面の輝度を戻しておく
SetDrawBright(255, 255, 255);
// 真を返す
return true;
}
else
{
// 輝度を加算
bright += fade_speed;
}
return false;
}
//--------------------------------------------------
// フェードアウト
//--------------------------------------------------
bool Fade::FadeOut(int fade_speed, int r, int g, int b)
{
static int bright = 255;
// 描画輝度をセット
SetDrawBright(bright, bright, bright);
// 画面を覆う四角形を描画
DrawBox(0, 0, 640, 480, GetColor(r, g, b), true);
DrawFormatString(40, 40, GetColor(0, 0, 255), "%d", bright);
// 輝度が0を下回った
if(bright <= 0)
{
// 画面の輝度を戻しておく
SetDrawBright(255, 255, 255);
// 真を返す
return true;
}
else
{
// 輝度を減算
bright -= fade_speed;
}
return false;
}
しかし、1回目の処理は成功するのですが、2回目からは[bright]の初期化を行うべき場所が分からないため、前回の処理終了時のままの数値で描画されてしまいます。
このコードの場合、[bright]はどこで初期化すれば正しい動きをするのでしょうか。
また、コード自体に誤りがあればご指摘くださると助かります。
Re: フェードイン・フェードアウト処理
Posted: 2013年3月17日(日) 23:24
by beatle
よく考えればもっといい設計があるかもしれませんが、取り敢えずの解決策として、
briteをstatic変数にするのではなくて、Fadeクラスのメンバ変数とするのはどうでしょうか。
フェードアウト処理(またはフェードイン処理)を開始するたびにFadeクラスをインスタンス化するのです。
Re: フェードイン・フェードアウト処理
Posted: 2013年3月17日(日) 23:33
by クールアイス
素早い返信をありがとうございます。
<<briteをstatic変数にするのではなくて、Fadeクラスのメンバ変数とするのはどうでしょうか。
ということは、
コード:
class Fade
{
public:
Fade() {}
~Fade() {}
static bool FadeIn(int fade_speed, int r = 255, int g = 255, int b = 255);
static bool FadeOut(int fade_speed, int r = 255, int g = 255, int b = 255);
private:
int bright;
};
として、FadeIn でもFadeOut でも使い回す、ということであっていますか?
<<フェードアウト処理(またはフェードイン処理)を開始するたびにFadeクラスをインスタンス化するのです。
まだまだクラスに関しての知識が足りないため、「インスタンス化」する、というのが良くわかっていません。申し訳ないです。
Re: フェードイン・フェードアウト処理
Posted: 2013年3月17日(日) 23:36
by beatle
メンバ変数にする理解はそれで合っています。
インスタンス化とは平たく言えば変数定義です。こんな感じ。
コード:
Fade fade; // Fadeクラスをインスタンス化する(インスタンス名はfade)
ここで、Fadeを「クラス」といい、fadeを「インスタンス」と言います。
Re: フェードイン・フェードアウト処理
Posted: 2013年3月17日(日) 23:39
by beatle
あ、ちなみに、FadeIn, FadeOut関数をstaticにしてしまうと、メンバ変数をいじれませんので、
staticではない通常のメンバ関数として定義しましょう。
Re: フェードイン・フェードアウト処理
Posted: 2013年3月17日(日) 23:46
by softya(ソフト屋)
beatleさんの案にプラスして、FADEIN/FADEOUTのコントールクラスを作成するのなら、
・SetFadeIn()
・SetFadeOut()
・Update()
・Draw()
というメンバ関数に分けたらどうでしょうか?
初期化はSetFadeIn()/SetFadeOut()で設定するので何時初期化するかの問題はなくなると思います。
Re: フェードイン・フェードアウト処理
Posted: 2013年3月17日(日) 23:58
by クールアイス
>>beatleさん ありがとうございます。
Fadeクラスのstaticを無くして書き直してみました。
しかし、今度は「フェードイン終了」→「フェードアウト開始」のあたりをがたがたと繰り返してしまっているようです。
数字を見る限りでは[bright]が256→254→256...となっているようなので、関数の使い方の間違いなのでしょうか?
Fade.h
コード:
//--------------------------------------------------
// フェードイン・アウトクラス
//--------------------------------------------------
class Fade
{
public:
Fade() : bright(0) {}
~Fade() {}
bool FadeIn(int fade_speed, int r = 255, int g = 255, int b = 255);
bool FadeOut(int fade_speed, int r = 255, int g = 255, int b = 255);
private:
int bright;
};
Fade.cpp
コード:
//--------------------------------------------------
// フェードイン
//--------------------------------------------------
bool Fade::FadeIn(int fade_speed, int r, int g, int b)
{
// 描画輝度をセット
SetDrawBright(bright, bright, bright);
// 画面を覆う四角形を描画
DrawBox(0, 0, 640, 480, GetColor(r, g, b), true);
DrawFormatString(40, 40, GetColor(0, 0, 255), "%d", bright);
// 輝度が255を超えたら
if(bright > 255)
{
// 画面の輝度を戻しておく
SetDrawBright(255, 255, 255);
// 真を返す
return true;
}
else
{
// 輝度を加算
bright += fade_speed;
}
return false;
}
//--------------------------------------------------
// フェードアウト
//--------------------------------------------------
bool Fade::FadeOut(int fade_speed, int r, int g, int b)
{
// 描画輝度をセット
SetDrawBright(bright, bright, bright);
// 画面を覆う四角形を描画
DrawBox(0, 0, 640, 480, GetColor(r, g, b), true);
DrawFormatString(40, 40, GetColor(0, 0, 255), "%d", bright);
// 輝度が0を下回った
if(bright <= 0)
{
// 画面の輝度を戻しておく
SetDrawBright(255, 255, 255);
// 真を返す
return true;
}
else
{
// 輝度を減算
bright -= fade_speed;
}
return false;
}
インスタンス化させている場所
GameMain.h
実際に使っている場所
GameMain.cpp
コード:
switch(now_scene)
{
case eFirstInitialize:
FirstInitialize();
// フェードインが終わったら
if(fade.FadeIn(2))
{
// フェードアウトが終わったら
if(fade.FadeOut(2))
{
// タイトルシーンへ
now_scene = eTitle;
}
}
break;
case eTitle:
DrawFormatString(0, 0, GetColor(255, 255, 255), "タイトル");
fade.FadeIn(2);
break;
default:
printfDx("不明なnow_scene");
break;
}
>>softya(ソフト屋)さん ありがとうございます。
>>初期化はSetFadeIn()/SetFadeOut()で設定するので何時初期化するかの問題はなくなると思います。
今回の場合では、[bright]をSetFadeIn()/SetFadeOut()の中で初期化するように作り、それを現在 FadeIn/FadeOutを使っている場所で随時呼び出すということでしょうか。
Re: フェードイン・フェードアウト処理
Posted: 2013年3月18日(月) 00:43
by softya(ソフト屋)
> 今回の場合では、[bright]をSetFadeIn()/SetFadeOut()の中で初期化するように作り、それを現在 FadeIn/FadeOutを使っている場所で随時呼び出すということでしょうか。
そういう事ですね。Updateの戻り値で終了をチェックして、FADEINなどの状態変数を切り替えてください。
Re: フェードイン・フェードアウト処理
Posted: 2013年3月18日(月) 07:43
by beatle
勘違いだったらすみません。この構造、ぱっと見おかしいなと感じました。
コード:
// フェードインが終わったら
if(fade.FadeIn(2))
{
// フェードアウトが終わったら
if(fade.FadeOut(2))
{
// タイトルシーンへ
now_scene = eTitle;
}
}
これだと、bright>255のときにFadeInがtrueになると、FadeOutが実行されbright<=255となります(FadeOutはfalse)。
再度この場所に来ると、今度はbright<=255ですからFadeInが実行されbright>255となり、FadeOutは実行されません。
したがいまして
クールアイス さんが書きました:
しかし、今度は「フェードイン終了」→「フェードアウト開始」のあたりをがたがたと繰り返してしまっているようです。
数字を見る限りでは[bright]が256→254→256...となっているようなので、関数の使い方の間違いなのでしょうか?
ということになります。
Re: フェードイン・フェードアウト処理
Posted: 2013年3月18日(月) 07:46
by beatle
きっと、FadeIn, FadeOutがそれぞれ自分専用のbright変数を持っていた時代の名残なのではないかと思いますが、
新しいFadeクラスは1つのインスタンスにつき1つのbright変数しか持っていませんので、ご注意下さい。
Re: フェードイン・フェードアウト処理
Posted: 2013年3月18日(月) 13:19
by クールアイス
お二方の意見を参考に書き直してみました。
現在のところ概ね希望通りに動いているようですが、あまりスマートに書けていない気もします。
改善点などがあったりおかしなところがあるようでしたらバシバシとご指摘くださると助かります。
Fade.h
コード:
enum FadeState
{
eNone = 0,
eFadeInPlay,
eFadeInEnd,
eFadeOutPlay,
eFadeOutEnd,
};
//--------------------------------------------------
// フェードイン・アウトクラス
//--------------------------------------------------
class Fade
{
public:
Fade() : alpha(0), fade_speed(0), fade_color(0), fade_in_end(false), fade_out_end(false), fade_state(eNone) {}
~Fade() {}
bool GetFadeInEnd() { return fade_in_end; }
bool GetFadeOutEnd() { return fade_out_end; }
void SetFadeIn(int fade_speed, int alpha = 255, int r = 0, int g = 0, int b = 0);
void SetFadeOut(int fade_speed, int alpha = 0, int r = 0, int g = 0, int b = 0);
void Update();
void Draw();
private:
FadeState fade_state;
int alpha;
int fade_speed;
int fade_color;
bool fade_in_end;
bool fade_out_end;
};
Fade.cpp
コード:
void Fade::SetFadeIn(int fade_speed, int alpha, int r, int g, int b)
{
if(fade_state != eFadeInPlay && fade_state != eFadeInEnd)
{
this->fade_speed = fade_speed;
this->alpha = alpha;
this->fade_color = GetColor(r, g, b);
fade_in_end = false;
// フェードイン開始
fade_state = eFadeInPlay;
}
}
void Fade::SetFadeOut(int fade_speed, int alpha, int r, int g, int b)
{
if(fade_state != eFadeOutPlay && fade_state != eFadeOutEnd)
{
this->fade_speed = fade_speed;
this->alpha = alpha;
this->fade_color = GetColor(r, g, b);
fade_out_end = false;
// フェードアウト開始
fade_state = eFadeOutPlay;
}
}
void Fade::Update()
{
switch(fade_state)
{
case eNone:
break;
case eFadeInPlay:
if(alpha >= 0)
{
alpha -= fade_speed;
}
else
{
fade_state = eFadeInEnd;
}
break;
case eFadeInEnd:
fade_in_end = true;
break;
case eFadeOutPlay:
if(alpha <= 255)
{
alpha += fade_speed;
}
else
{
fade_state = eFadeOutEnd;
}
break;
case eFadeOutEnd:
fade_out_end = true;
break;
default:
break;
}
}
void Fade::Draw()
{
// ブレンドモード
SetDrawBlendMode(DX_BLENDMODE_ALPHA, alpha);
// 画面を覆う四角形を描画
DrawBox(0, 0, 640, 480, fade_color, true);
// ブレンドモード終了
SetDrawBlendMode(DX_BLENDMODE_NOBLEND, 0);
}
使用箇所
GameMain.cpp
コード:
switch(now_scene)
{
case eFirstInitialize:
FirstInitialize();
DrawBox(100, 100, 150, 150, GetColor(255, 100, 100), true);
fade.SetFadeOut(2);
if(fade.GetFadeOutEnd())
{
// タイトルシーンへ
now_scene = eTitle;
}
break;
case eTitle:
fade.SetFadeIn(2);
DrawFormatString(0, 0, GetColor(255, 255, 255), "タイトル");
break;
default:
printfDx("不明なnow_scene");
break;
}
Re: フェードイン・フェードアウト処理
Posted: 2013年3月18日(月) 16:19
by softya(ソフト屋)
fade.SetFadeOut(2);とかfade.SetFadeIn(2);が何度も呼び出されているっぽい所が気になる所ですね。最初の一回だけにできませんか?
それとfade.Update()が何処で呼び出されているのか気になります。
Re: フェードイン・フェードアウト処理
Posted: 2013年3月18日(月) 16:35
by クールアイス
<<fade.SetFadeOut(2);とかfade.SetFadeIn(2);が何度も呼び出されているっぽい所が気になる所ですね。最初の一回だけにできませんか?
現在のプログラムではループの度にセットされている、となってしまっているのでしょうか。
コード:
void Fade::SetFadeIn(int fade_speed, int alpha, int r, int g, int b)
{
if(fade_state != eFadeInPlay && fade_state != eFadeInEnd)
{
this->fade_speed = fade_speed;
this->alpha = alpha;
this->fade_color = GetColor(r, g, b);
fade_in_end = false;
// フェードイン開始
fade_state = eFadeInPlay;
}
}
このようにしているので、てっきりセットしたら次の時には条件に引っかからないとばかり思っていました・・・。
Updateなどは下のほうで行っています。
コード:
//--------------------------------------------------
// ゲームの更新
//--------------------------------------------------
void GameMain::Run()
{
// キー入力更新
Input::UpdateKey();
switch(now_scene)
{
case eFirstInitialize:
FirstInitialize();
DrawBox(100, 100, 150, 150, GetColor(255, 100, 100), true);
fade.SetFadeOut(2);
if(fade.GetFadeOutEnd())
{
// タイトルシーンへ
now_scene = eTitle;
}
break;
case eTitle:
fade.SetFadeIn(2);
DrawFormatString(0, 60, GetColor(255, 255, 255), "タイトル");
break;
default:
printfDx("不明なnow_scene");
break;
}
// フェード処理
fade.Update();
fade.Draw();
DrawFormatString(0, 0, GetColor(255, 255, 255), "[In:%d,Out:%d]", fade.GetFadeInEnd(), fade.GetFadeOutEnd());
DebugTitleBarToMessage("[現在のシーン = %d, 現在のステージ番号 = %d]", now_scene, now_stage);
// Escapeキーで終了
if(Input::Key(KEY_INPUT_ESCAPE) == 1) { DxLib_End(); }
}
Re: フェードイン・フェードアウト処理
Posted: 2013年3月18日(月) 16:39
by softya(ソフト屋)
私の言い方が悪かったですね。内部でガードする方式だと分かりづらいのとコードな複雑化してバグを生みやすいので避けたほうが良いという意味です。
それと FirstInitialize();も何度も呼び出していますが大丈夫ですか?
Re: フェードイン・フェードアウト処理
Posted: 2013年3月18日(月) 16:47
by クールアイス
>>私の言い方が悪かったですね。内部でガードする方式だと分かりづらいのとコードな複雑化してバグを生みやすいので避けたほうが良いという意味です。
なるほど・・・。なんでも関数内で完結してしまうと逆に面倒なことになってしまうのですね。
>>それと FirstInitialize();も何度も呼び出していますが大丈夫ですか?
そこをすっかり忘れていました。本来ならば初期化したらすぐ抜けるべきですよね。なんとか改良してみます。
Re: フェードイン・フェードアウト処理
Posted: 2013年3月18日(月) 17:09
by クールアイス
修正したコードです。
とりあえずこれで FirstInitialize が一回だけになるのと、関数を少しだけ変えてみました。一応挙動はちゃんとしてくれています。
Fade.cpp
コード:
//--------------------------------------------------
// フェードインセット
//--------------------------------------------------
void Fade::SetFadeIn(int fade_speed, int alpha, int r, int g, int b)
{
this->fade_speed = fade_speed;
this->alpha = alpha;
this->fade_color = GetColor(r, g, b);
fade_in_end = false;
// フェードイン開始
fade_state = eFadeInPlay;
}
//--------------------------------------------------
// フェードアウトセット
//--------------------------------------------------
void Fade::SetFadeOut(int fade_speed, int alpha, int r, int g, int b)
{
this->fade_speed = fade_speed;
this->alpha = alpha;
this->fade_color = GetColor(r, g, b);
fade_out_end = false;
// フェードアウト開始
fade_state = eFadeOutPlay;
}
//--------------------------------------------------
// 更新
//--------------------------------------------------
void Fade::Update()
{
switch(fade_state)
{
case eNone:
break;
case eFadeInPlay:
if(alpha >= 0)
{
alpha -= fade_speed;
}
else
{
alpha = 0;
fade_state = eFadeInEnd;
}
break;
case eFadeInEnd:
fade_in_end = true;
break;
case eFadeOutPlay:
if(alpha <= 255)
{
alpha += fade_speed;
}
else
{
alpha = 255;
fade_state = eFadeOutEnd;
}
break;
case eFadeOutEnd:
fade_out_end = true;
break;
default:
break;
}
}
//--------------------------------------------------
// 描画
//--------------------------------------------------
void Fade::Draw()
{
// ブレンドモード
SetDrawBlendMode(DX_BLENDMODE_ALPHA, alpha);
// 画面を覆う四角形を描画
DrawBox(0, 0, 640, 480, fade_color, true);
// ブレンドモード終了
SetDrawBlendMode(DX_BLENDMODE_NOBLEND, 0);
DrawFormatString(0, 20, GetColor(255, 255, 255), "[alpha:%d,fade_state:%d]", alpha, fade_state);
}
GameMain.cpp
コード:
//--------------------------------------------------
// ゲームの更新
//--------------------------------------------------
void GameMain::Run()
{
// キー入力更新
Input::UpdateKey();
switch(now_scene)
{
case eFirstInitialize:
FirstInitialize(); // 初回の初期化
now_scene = eFirstLogo; // ロゴシーンへ
break;
case eFirstLogo:
DrawFormatString(0, 60, GetColor(255, 255, 255), "ここに制作者ロゴを表示");
// フェードアウト中でも終了時でもなければ
if(fade.GetFadeState() != eFadeOutPlay && fade.GetFadeState() != eFadeOutEnd)
{
// フェードアウトをセット
fade.SetFadeOut(1);
}
// フェードアウトが終わったら
if(fade.GetFadeOutEnd())
{
now_scene = eTitle; // タイトルシーンへ
}
break;
case eTitle:
// フェードイン中でも終了時でもなければ
if(fade.GetFadeState() != eFadeInPlay && fade.GetFadeState() != eFadeInEnd)
{
// フェードインをセット
fade.SetFadeIn(1);
}
DrawFormatString(0, 60, GetColor(255, 255, 255), "タイトル");
break;
default:
printfDx("不明なnow_scene");
break;
}
// フェード処理
fade.Update();
fade.Draw();
DrawFormatString(0, 0, GetColor(255, 255, 255), "[In:%d,Out:%d]", fade.GetFadeInEnd(), fade.GetFadeOutEnd());
DebugTitleBarToMessage("[現在のシーン = %d, 現在のステージ番号 = %d]", now_scene, now_stage);
// Escapeキーで終了
if(Input::Key(KEY_INPUT_ESCAPE) == 1) { DxLib_End(); }
}
Re: フェードイン・フェードアウト処理
Posted: 2013年3月18日(月) 17:20
by softya(ソフト屋)
うーん複雑ですね。
eFirstInitialize();と同じ時にfade.SetFadeOut(1);しちゃだめですか? 1フレームぐらい大丈夫だと思うんですけどね。
それと fade.SetFadeIn(1);も「now_scene = eTitle; // タイトルシーンへ」と同時ならスッキリしませんか?
あるいはシーン毎のフレーム数を数えて0フレーム目だけ初期化するのも方法です。
Re: フェードイン・フェードアウト処理
Posted: 2013年3月18日(月) 17:50
by クールアイス
中々難しいですね・・・。どうにも頭が固いようです。
これならば少しは見通しが良くなったでしょうか。
Fade.cpp
コード:
switch(now_scene)
{
case eFirstInitialize:
FirstInitialize(); // 初回の初期化
fade.SetFadeIn(1); // フェードインをセット
now_scene = eFirstLogo; // ロゴシーンへ
break;
case eFirstLogo:
// フェードインが終了した
if(fade.GetFadeState() == eFadeInEnd)
{
// フェードアウトをセット
fade.SetFadeOut(1);
}
// フェードアウトが終わった
if(fade.GetFadeState() == eFadeOutEnd)
{
// フェードインをセット
fade.SetFadeIn(1);
// タイトルシーンへ
now_scene = eTitle;
}
DrawFormatString(0, 60, GetColor(255, 255, 255), "ここに制作者ロゴを表示");
break;
case eTitle:
DrawFormatString(0, 60, GetColor(255, 255, 255), "タイトル");
break;
default:
printfDx("不明なnow_scene");
break;
}
Re: フェードイン・フェードアウト処理
Posted: 2013年3月18日(月) 18:33
by softya(ソフト屋)
そうですね。これだとフェードインとフェードアウトの関係がややこしく見えますね。eFirstLogoのステートを分けたほうが良いかもしれません。
それと制作者ロゴでフェードインもアウトもしていない期間は設けなくて良いのでしょうか?
Re: フェードイン・フェードアウト処理
Posted: 2013年3月18日(月) 18:54
by クールアイス
ステートを分けるの意味を間違ってとっていたら申し訳ないです。
以下のようになりました。やはりフェード処理があるとちょっとだけかっこよく見えます(個人的に凄い好きなんです)。
制作者ロゴの件ですが、現在ちょうどいい時間で出現・消滅してくれているので仕様となっています。
コード:
switch(now_scene)
{
case eFirstInitialize:
FirstInitialize(); // 初回の初期化
fade.SetFadeIn(5); // フェードインをセット
now_scene = eFirstLogoFadeIn; // ロゴフェードインシーンへ
break;
case eFirstLogoFadeIn:
// フェードインが終了した
if(fade.GetFadeState() == eFadeInEnd)
{
// フェードアウトをセット
fade.SetFadeOut(5);
// ロゴフェードアウトシーンへ
now_scene = eFirstLogoFadeOut;
}
DrawFormatString(0, 0, GetColor(255, 255, 255), "ここに制作者ロゴを表示");
break;
case eFirstLogoFadeOut:
// フェードアウトが終わった
if(fade.GetFadeState() == eFadeOutEnd)
{
// フェードインをセット
fade.SetFadeIn(5);
// タイトルシーンへ
now_scene = eTitle;
}
DrawFormatString(0, 0, GetColor(255, 255, 255), "ここに制作者ロゴを表示");
break;
case eTitle:
title.Update();
title.Draw();
break;
default:
printfDx("不明なnow_scene");
break;
}
Re: フェードイン・フェードアウト処理
Posted: 2013年3月18日(月) 19:27
by softya(ソフト屋)
これで良いと思います。弱点は、ロゴ表示が2箇所有ることぐらいでしょうか。まぁ、どうしようもないんですが。
Re: フェードイン・フェードアウト処理
Posted: 2013年3月18日(月) 19:53
by クールアイス
詳しくわかりやすい説明をありがとうございました。
ずっと一人で悩んでいましたが、皆さんのおかげで形になってきました。
これからもっと勉強をし、より良いプログラムを組めるように精進していきたいと思います。