RPGでたまに見かける戦闘ログのようなものを作りたいのですが...

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
いろは
記事: 11
登録日時: 8年前

RPGでたまに見かける戦闘ログのようなものを作りたいのですが...

#1

投稿記事 by いろは » 8年前

件名通りで、RPGでたまに見かける戦闘ログやシステムログのようなものを作ってみようかと思い、DXライブラリで作ってみているのですがなかなか上手くいきません。
理想としては、下から最新のログを表示させていき上の方は古いログという感じです。また、ゲーム本体では十字キーでスクロールできるように考えていますので50行分のログデータを保持させています。
イメージとしては「elona」というゲームのログウィンドウに近い感じです。

下のコードは最低限の機能をピックアップした、数字のキー入力に対するログの発生とスペースキーによるログの表示量の変更です。

どう直せばよいかわからなくなってしまったので、間違っている点の指摘や、またそれを直していただければ嬉しいです。
環境はwindows8.1でvisual studio 2015です。

コード:

#include "DxLib.h"
#include <string>
#include <utility>

//ログの発生管理クラス
class CLog {
	std::string LogMessage;			//現在保持している全てのログ
	bool LogFlag;					//ログの表示する量を変更するフラグ
	
	CLog() {
		LogFlag = false;
	}
public:
	~CLog() {
		std::string().swap(LogMessage);		//解放
	}
	static CLog* Inst() {
		static CLog Inst;
		return &Inst;
	}

	//ログ追加
	void AddLogMessage(const char* str) {
		static int cnt = 0;			//現在のカウントしている行数
		LogMessage = std::string(str) + '\n' + LogMessage;	//ログ追加
		//50行超えれば1行分削除
		if (cnt < 50)
			cnt++;
		else
			LogMessage.erase(LogMessage.end() - LogMessage.rfind('\n'), LogMessage.end());	//削除
	}
	void ChangeLogMode() {		//ログの表示するモードの変更
		if (LogFlag)
			LogFlag = false;
		else
			LogFlag = true;
	}
	//ログの内容の表示
	void DrawLog() {
		std::string StringBuf;		//表示する用の文字列
		size_t Position = 0;		//現在の文字の位置
		size_t OldPosition;			//前回の文字の位置
		if (!LogFlag) {
			for (int i = 0; i < 6; ++i) {		//6行分を確保
				if (LogMessage.size() < Position)		//探索する文字の位置がサイズを超えたときブレイク
					break;
				OldPosition = Position;
				Position = LogMessage.find('\n', Position+1);
				StringBuf = LogMessage.substr(OldPosition, Position) + StringBuf;		//文字列の追加
			}
		}
		else {
			for (int i = 0; i < 15; ++i) {		//15行分を確保
				if (LogMessage.size() < Position)		//探索する文字の位置がサイズを超えたときブレイク
					break;
				OldPosition = Position;
				Position = LogMessage.find('\n', Position+1);
				StringBuf = LogMessage.substr(OldPosition, Position) + StringBuf;		//文字列の追加
			}
		}
		DrawString(100, 50, StringBuf.c_str(), GetColor(255, 255, 255));
		std::string().swap(StringBuf);
	}
};

//キー入力管理クラス
class CKey {
	char Key[256];					//現在の入力
	char KeyBuf[256];				//前回の入力

	CKey() {}
public:
	~CKey(){}

	static CKey* Inst() {
		static CKey Inst;
		return &Inst;
	}

	//キー入力の更新
	bool KeyGet() {	
		std::swap(Key, KeyBuf);				//スワップ
		if (GetHitKeyStateAll(Key) == -1)
			return false;
		return true;
	}
	//キーを入力した瞬間
	bool KeyTrigger(int KeyCode) {
		if (Key[KeyCode] == 1 && KeyBuf[KeyCode] == 0)
			return true;
		return false;
	}
};
//キーの入力処理
bool KeyCheck(){
	CKey* Key = CKey::Inst();
	CLog* Log = CLog::Inst();

	//ログの追加
	if (Key->KeyTrigger(KEY_INPUT_0))
		Log->AddLogMessage("0キーを押した");
	if (Key->KeyTrigger(KEY_INPUT_1))
		Log->AddLogMessage("1キーを押した");
	if (Key->KeyTrigger(KEY_INPUT_2))
		Log->AddLogMessage("2キーを押した");
	if (Key->KeyTrigger(KEY_INPUT_3))
		Log->AddLogMessage("3キーを押した");
	if (Key->KeyTrigger(KEY_INPUT_4))
		Log->AddLogMessage("4キーを押した");
	if (Key->KeyTrigger(KEY_INPUT_5))
		Log->AddLogMessage("5キーを押した");
	if (Key->KeyTrigger(KEY_INPUT_6))
		Log->AddLogMessage("6キーを押した");
	if (Key->KeyTrigger(KEY_INPUT_7))
		Log->AddLogMessage("7キーを押した");
	if (Key->KeyTrigger(KEY_INPUT_8))
		Log->AddLogMessage("8キーを押した");
	if (Key->KeyTrigger(KEY_INPUT_9))
		Log->AddLogMessage("9キーを押した");

	if (Key->KeyTrigger(KEY_INPUT_SPACE))		//スぺースキーでモード変更
		Log->ChangeLogMode();
	if (Key->KeyTrigger(KEY_INPUT_ESCAPE))		//エスケープキーで終了
		return false;

	return true;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {

	ChangeWindowMode(TRUE);
	if (DxLib_Init() == -1)return -1;
	SetDrawScreen(DX_SCREEN_BACK);

	//メインループ
	while (ScreenFlip() == 0 && ProcessMessage() == 0 && ClearDrawScreen() == 0 && CKey::Inst()->KeyGet()) {
		if (!KeyCheck())		//キーでのログの変更処理
			break;
		CLog::Inst()->DrawLog();		//ログの表示
	}

	DxLib_End();

	return 0;
}

moba
記事: 82
登録日時: 9年前

Re: RPGでたまに見かける戦闘ログのようなものを作りたいのですが...

#2

投稿記事 by moba » 8年前

質問文からでは、どううまくいかなかったのか分からないです。

いくつか気づいた点だけ挙げていきますね。


* logFlagという名前では何のフラグか分かりません
wiiChangeSizeなど、名前を変えるのがいいかもしれません。

* 変数の命名規則を見直した方が良さそうです
ローカル変数が小文字で始まると、一瞬型名かと思ってしまいます。

* staitcなローカル変数をメンバ変数のように使うのは避けたほうがいいです
ただのメンバ変数にしておきましょう。

* ログは環状バッファで蓄えるのが良さそうです
少なくとも、配列やコレクションクラスを使えば、一回分のメッセージを取り出すのが楽になります。ここを修正すればかなりスッキリして、不具合を見つけやすいのではないでしょうか、

配列と添え字を用意し、添え字の進み方を工夫すれば、
環状バッファのように振る舞えます。

_index += 1;
_index %= maxSize;

具体的な数を入れてみれば、0, 1, 2, 0, 1, 2... などと数値が循環していくのが分かると思います。
ログの配列のサイズは固定で、添え字を循環させるのが単純でオススメです。


[追記]
クラスが整理されたら、ミスが無くなるのかと思い上記のように回答させていただきました。バグを直すよりも、綺麗なコードを書けるようになるのが先決の課題かなと。


* ログクラスはシングルトンではない方がいいかもしれません
Game Programming Patternsという本に詳しく書いてあります。(シングルトン、サンドボックス、サーヴィスロケータの章)。この手の話(アーキテクチャ)に興味があればオススメです。
ログを使うクラスの基底クラスに、ログの参照を返す関数を仕込むのがいいかなーと思います。その関数からグローバルな変数にアクセスするとしても、そのコードは一箇所だけ絞れます。


(追記や誤字の修正で何度も編集しました)
最後に編集したユーザー moba on 2017年2月21日(火) 18:03 [ 編集 1 回目 ]

いろは
記事: 11
登録日時: 8年前

Re: RPGでたまに見かける戦闘ログのようなものを作りたいのですが...

#3

投稿記事 by いろは » 8年前

アドバイスありがとうございます。
無事、理想通りに動かすことができました。
もう少しゲーム全体のクラスを見直した方がいいですね....

閉鎖

“C言語何でも質問掲示板” へ戻る