yumeの日記: C#学習 12 Console Adventure#3 2
コメントありがとうございます。
前回より:
・テキストを25番まで追加
・[goto n]命令。nページへ自動的に飛ぶ。
>選択肢によって追加されたテキスト、とかを表現する
・インライン命令文的な概念を追加。
>テキストが@で始まるなら、その文字列内に[]でくくられた命令文があるので、それを探して実行する。
・インライン命令[write name n] n番の名前を入力する
・インライン命令[name n] 命令文をn番の名前に置き換える。
・関数の不用意な再帰呼び出しを整頓する。
static void Main()
{
int runningGame = 1;
while(runningGame == 1)
{
runningGame = RunGame();
}
if(runningGame != 1)
{
Console.WriteLine("error code : {0}", runningGame);
}
Console.WriteLine("ゲームを終了します。何かキーを押してください……");
Console.ReadKey();
}
Main関数はRunGame関数を終わるまで呼び出す、エラーが返ってきたらエラーを表示するという関数にした。
ちょっと冗長かもしれない。
RunGame関数でやりたいこと以外のことができたら(タイトル画面とか)、そういうのを実行する場所になるからいいかな。
static int RunGame()
{
const string command = "[command]";
const string jumpStart = "[jump start]";
const string gameEnd = "[game end]";
const string gotoPage = "[goto ";
const string inlineOrder = "@";
int result = 0;
for (int i = 0; i < bodyText.Length; i++)
{
if (bodyText[i].StartsWith(gotoPage)) return GoToPage(i);
else if (bodyText[i].StartsWith(inlineOrder)) result = InlineOrder(ref i);
if (result == 1) continue;
result = bodyText[i] switch
{
command => Command(),
jumpStart => TextJump(ref i),
gameEnd => GameEnd(),
_ => TextRead(i),
};
if (result != 0) break;
}
return result;
}
前回Main関数がやっていたことを引き継いで、InlineOrderとGoToPageを追加
private static int GoToPage(int i)
{
// [goto n]命令文で指定されたnページへ飛ぶ。
int gotoNumLength = bodyText[i].IndexOf("]") - 6;
string gotostr = bodyText[i].Substring(6, gotoNumLength);
int gotoNumber = int.Parse(gotostr);
if(LoadText(gotoNumber) == 0) return 20; //例外エラー20。飛び先が無い。
Console.WriteLine("");
return 1;
}
飛ぶだけ。
static int InlineOrder(ref int i)
{
/*
* @で始まる文字列の場合、@を削除し、行中の命令を探し、実行する。
*/
bodyText[i] = bodyText[i].Remove(0, 1);
if (bodyText[i].Contains("[write name")) return WriteName(ref i);
else if (bodyText[i].Contains("[name")) return ReplaceName(ref i);
else return 30; //例外エラー30。@で始まるのに命令文が無い。
}
static int WriteName(ref int i)
{
/*
* nameListにプレイヤーが名前を登録する。
*/
int oBPoint = bodyText[i].IndexOf("[");
int numLength = bodyText[i].IndexOf("]") - oBPoint;
int nameNum = int.Parse(bodyText[i].Substring(oBPoint +12, numLength -12));
bodyText[i] = bodyText[i].Remove(oBPoint, numLength + 1);
string checkName = "******";
bool checkNameTest = false;
while (!checkNameTest)
{
Console.Write(bodyText[i]);
checkName = Console.ReadLine();
checkNameTest = true;
for (int j = 0; j < forbiddenNameList.Length; j++)
{
if (checkName == forbiddenNameList[j])
{
checkNameTest = false;
break;
}
}
}
nameList[nameNum] = checkName;
return 1;
}
static int ReplaceName(ref int i)
{
/*
* [name n]をnameListから読み出した実際の名前に置き換える。
*/
int oBPoint = bodyText[i].IndexOf("[");
int numLength = bodyText[i].IndexOf("]") - oBPoint;
int nameNum = int.Parse(bodyText[i].Substring(oBPoint +5, numLength -5));
bodyText[i] = bodyText[i].Remove(oBPoint, numLength +1);
bodyText[i] = bodyText[i].Insert(oBPoint, nameList[nameNum]);
return 0;
}
インライン命令。nameListとかは:
static string[] nameList = {"メイラ"};
でクラスの最初に宣言しているが、いまいちグローバル変数的なものが理解できてない気がする。
>staticとか要学習。
今思ったが、一行で名前を二度呼んだりするときどうしよう……。極端なケースだと、インライン命令文が一行に二種類以上出る可能性はある。例えば文字読み上げ速度、みたいな概念を入れたら、ゆっくり話すところはインライン命令で[speed 0.5]ゆ っ く り し ゃ べ る よ[spped 1]とかにすると思うんだけど、これに名前とか入るとかなりややこしいぞ。
直感的には、とにかく命令文が無くなるまで"[]"をチェックし続けて、命令文が見つかるたびに命令文を実行する。最終的に置き換えられたプレーンなテキストをコンソールに書く、とかだろうか。今はチェック前に@を削除するが、チェックを終えたら@を削除するという手順かな。
クラスを作る (スコア:0)
そろそろ、GameMasterみたいなクラスを作ったほうが良いと思う。Main()以外の関数をそちらに移せるはず。
まあ、インスタンスが1つだけでいいのなら、わざわざ別にクラスを用意しなくてもいいし、用意しても変数や関数を全てstaticにしておけば、わざわざインスタンスを作る必要もないのだが、インスタンスを作るようにしておいた方が、初期化とリソースの廃棄が楽。プログラムを終了せずに新しいゲームを開始したいときは、新しいインスタンスを作るだけで済むし、古いインスタンスは参照する変数が無くなった時点でデストラクタやGCが始末してくれる。
まずは、GameMasterクラスに関数や変数を移して、Main()のRunGame()をGameMaster.RunGame()みたいにすれば、大きな変更はいらないはず。それが済めば、staticの勉強を試しながら進められると思う。
Re: (スコア:0)
クラス化するのは、もちろん賛成。
だが、GameMasterみたいな名前をつけると、俗に言う神クラスになってしまう予感しかない。
名前付け大事。