イントロダクション
前回は、説明が不十分な感じでした。
とりあえずで"bye"と入力すると終了するコンソールアプリを作成しました。
今回は、タイトル表示までやりました。
ただし、クラス単体では動きません。。。
使用するクラス
必要なクラスは以下になります。
・RpgMain
・ViewStatus
・TitleView
RPGMainクラス
RPGMainでゲームの処理をstaticで作成しています。つまりは、メインメソッドから直接呼び出せる形で実装しています。
※RpgMain#view()メソッドは使っておりません。今後の実装を考えて作っておりますので今後修正する可能性が大きいです。
<実行動画>
/**
* ゲームを起動する </br>
* 標準入力の受付、「bye」コマンドでAPを終了する</br>
*
* @param args プログラム引数
*/
public static void main(String[] args) {
// 初期化処理
init();
try {
// ゲーム起動(GameLoop開始)
gameLoop();
} catch(IOException ie) {
ie.printStackTrace();
System.exit(-1);
}
// リソースの解放
terminated();
}
処理内容:メインメソッド
- 初期化 #init
- RpgMainクラスを生成(new)して
- メンバメソッドinitialize()で初期画面の標準入力を受け付ける
- ファイルの読み込み&出力 ※ラムダ式を使用してみました
- 初めの画面管理オブジェクト生成
/**
* このゲームでは初期化は必ず1回なのでstaticをつける</br>
* static -> 静的メソッド=> クラス内にあるがJVMから直で参照される
* ※JVM => Java Virtual Machine
*/
private static void init() {
// mainメソッドと同様にクラスをnewする必要あり
game = new RpgMain();
}
- ゲームループ #gameLoop
- 無限ループ開始
- コマンド実行処理(Mainクラス) ※画面管理オブジェクトのexecuteを実行
- 更新後の画面を表示
/**
* この処理もinit()と同様に必ず1回なのでstaticをつける</br>
*/
private static void gameLoop() throws IOException{
/*
* <ゲームループ>
* 1.入力
* 2.データ更新、
* 3.レンダリング(今回はコマンドなので文字出力)
* 4.「bye」コマンドでゲームを終了する
*/
while(true) {
// 1. 入力を受け取る
String command = game.listenInput();
// command変数がnullになる可能性があるため定数.equals()の形にする
if(TERMINATE_GAME.equals(command)) {
break;
}
// 2-1.処理を実行する(タイトル、その他初期表示する
status = game.execute(command);
}
}
- 終了処理 #terminated
- リソース(クラス)のメモリ解放
/**
* ゲームのリソースを解放する
*/
public static void terminated() {
// クラスの解放を行う
game = null;
System.out.println("This game terminated!, See you next time!!");
}
「static」をつけているのは、Mainメソッドから
直接呼ぶ様にしたかったからです。→メインメソッドとクラスのメンバメソッドを分けると見やすいと思ったため。
ViewStatusクラス
このクラスは、抽象クラスで、今後作成していく表示する画面を実行するためのクラスです。大まかに今回の実装のかなめになるクラスです。
ViewCommandはインターフェースですが、インナークラスとして実装しています。
※正しくは、インターフェース・クラス
このクラスの使い方としては、各画面クラス(TitleViewクラスなど)で以下のメソッドをオーバーライドしてやれば、コマンドに対応した処理を実行する
というような実装を行いました。
/** 画面の表示処理 */
public abstract void views();
/** 使用するコマンド */
public abstract Optional<Map<String, ViewCommand>> createCommands();
/** ViewStatusをセットする */
public abstract void setViewStatus(ViewStatus status);
/**
* 画面のステータスクラスを示す。<BR>
* コマンドの実行は内部インターフェースViewCommand<BR>
* の実装クラスが行う<BR>
* 【使い方】
* 1. このクラスを継承して全ての抽象メソッドを実装します。
* 2. コンストラクタでcreateViewStr()を呼び出して画面(文字列)を
* デザインしたファイルを指定してください読み込んで表示します。
*
* @author takunoji
*/
public abstract class ViewStatus {
/** 自クラスを保持する */
protected ViewStatus status;
/** コマンド管理 */
protected Map<String, ViewCommand> commands;
/** 表示する画面(文字列) */
protected List<String> viewStrList;
/**
* インナークラスのインターフェース
* @author takunoji
*/
public interface ViewCommand {
/** コマンドの実行
* Nullが返却されるときは画面の遷移なし
*/
public abstract ViewStatus execute();
}
/**
* コンストラクタ<BR>
* 使用するコマンドをMapに設定する
*/
public ViewStatus() {
Optional<Map<String, ViewCommand>> optCommands = createCommands();
// 取得した結果がNullの場合はからのMapを返す
commands = optCommands.isPresent() ? optCommands.get() : new HashMap<String, ViewCommand>();
}
/**
* コマンド実行処理<BR>
* RpgMainクラスで標準入力を受けてコマンドマップに<BR>
* 登録されているViewCommandのexecuteを実行する<BR>
* Mapに登録されていない時は何も実行しない
* @param input
* @return
*/
public ViewStatus execute(String input) {
Optional<ViewCommand> command = Optional.ofNullable(commands.get(input));
// commandがからの場合は何もしない
command.ifPresent(cmd -> setViewStatus(cmd.execute()));
return this;
}
/**
* ファイルを読み込み画面を描画する(文字列)
* @param filePath
*/
protected void createViewStr(String filePath) {
Path path = Paths.get(filePath);
List<String> lines = null;
try {
lines = Files.readAllLines(path);
} catch (IOException e) {
e.printStackTrace();
}
lines.stream().forEach(System.out::println);
// viewの文字列を保存します
this.setViewStr(lines);
}
/** 作成、ファイルから読み込んだView文字を保存する */
public void setViewStr(List<String> viewStrList) {
this.viewStrList = viewStrList;
}
/** 作成、ファイルから読み込んだView文字を取得する */
public List<String> getViewStr() {
return this.viewStrList;
}
/** 画面の表示処理 */
public abstract void views();
/** 使用するコマンド */
public abstract Optional<Map<String, ViewCommand>> createCommands();
/** ViewStatusをセットする */
public abstract void setViewStatus(ViewStatus status);
}
TitleViewクラスについて
そして、初めの画面管理オブジェクトのTitleViewは「ViewStatus」という抽象クラスを実装しています。
抽象クラスについてはのちに説明いたしますが、インターフェースとクラスの中間にいるクラスです。
具体的に、抽象メソッドと実装するメソッド両方を持っております。
インターフェースと違いimplemensではなく「extends」(継承)する必要があります。
さらにちょっと厄介なインナークラスにインターフェースを作成しています。
ViewStatus以外で、使用することのないインターフェースなのでそうしたのですが、普通は外部に作成します。
クラスが増えない様に、インナークラスにしたのですがやっぱり微妙でした、なんかのタイミングで修正しようと思います。
インポートするときはxxxxViewStatus.ViewCommandの様にやります。
まとめ
以下のクラスを使用しました。それぞれの役目を割り当てています。
- RpgMain: ゲームを動かす。メインメソッドを起動するためのクラス。
- ViewStatus: 抽象クラス、このクラスを継承したクラスはすべて、画面状態を示すクラスとして使用する。
- TitleView: タイトルを表示するステータス(状態)を表現するクラス。
ここまでで、とりあえずは実装をどんどん拡張する準備が整った形です。
具体的には、「ViewStaus」クラスを継承して新しいクラスを作成するということです。
現状では、TitleViewクラスのみですが、これを増やしていけば一つのアプリが作れるかもしれません。
とりあえず、今回はここまでにします。でわでわ。。。
次回は、ミニゲーム作成-〜コンソールゲーム〜/
です。
関連ページ
前回
http://zenryokuservice.com/wp/2018/05/20/java-basic-ミニゲーム作成-〜コンソールゲーム〜/
インターフェースについて
http://zenryokuservice.com/wp/2018/06/01/java-basic-インターフェース・抽象クラスの作り方/
ページ一覧
http://zenryokuservice.com/wp/2018/09/29/mapping-of-java-bassic~java-basicの記事一覧~/