Javaプログラマ Bronze SE 〜資格試験のテキストを学習〜

イントロダクション

Javaを勉強する目的の1つとして「資格を取る」というのがあると思います。

資格試験の学習をすることになりましたので、折角だから内容に関して、自分なりの解説と、練習問題を作成しました。

プログラムとは

早い話が、アプリケーションを作成するものです。色々と記載がありましたが、早い話がそういうことです。

「電卓アプリの場合」
①電卓アプリを起動する → 電卓画面を作成し表示する
そして入力されたことを一時的に記憶する
②数字の「2」をクリックする → 表示エリアに「2」を表示する
そして入力されたことを一時的に記憶する
③「+」をクリックする → 表示エリアに「2+」を表示する
そして入力されたことを一時的に記憶する
④数字の「3」をクリックする → 表示エリアに「3」を表示する
そして入力されたことを一時的に記憶する
⑤「=」をクリックする → 記憶していた「2+3」を元に演算処理を行う
⑥演算結果の「5」を表示する、一時的に臆していた「2+3」を消す

Javaテクノロジーの特徴

①Write Once, Run Anywhere(一度書けばどこでも動く)
②Java仮想マシン(JVM)による処理
③オブジェクト指向

それぞれに細かい説明がありますが、早い話が、JVMをインストールしたデバイス(PC, スマートフォン, スマートデバイス etc...)ならどのデバイスでも動かすことができるというものです。

少し昔の話ですが、Windowsでないと動かない、Macでないと動かない。。。などのような問題があり、アプリケーションを作る方も大変でしたが、昨今はJavaでどのプラットホームでも動くようになりました。
ちなみに、C / C++(言語)では「クロスプラットホーム」などと言って、同じコードをWindows, Linux, MacなどのOSで動くアプリケーションを作成する手段を持っています。

「クロスコンパイル」という方法でアプリを作成します。

Javaでも同じようなことができます。最近だとGLUONでアンドロイド、iOSのどちらでもアプリケーションが作成できるというテクノロジーがあります。

JavaSE

JavaSEとは、Javaテクノロジーのベースになるもので、演算処理のほとんどをこのテクノロジーで実行します。

まとめると、JavaSEの上にJavaMEやJavaEEが乗っている形になります。
「乗っている」というのは土台に「JavaSEがないと動かない」という意味です。

まぁJavaSEの理解ありきでJavaMEとかJavaEEとかやりましょうということです。

JavaME

携帯電話や、家電、コンピュータ以外のプラットホーム向け開発環境

JavaEE

Webアプリケーションを含む、大規模開発向けの開発環境
※大規模アプリケーション=大手百貨店のECサイト、顧客管理、在庫管理などのシステム

開発環境構築

Javaを使用して開発を行うのには(どの言語でも同じですが)開発環境構築が必要になります。

多くの開発現場で使用されているのがEclipseという開発ツール(IDE)です。

行う事は以下の通りです。

1.アプリケーションなので、インストールして使用します。詳細は下のリンクに記載しました。

開発環境構築~Windows版Eclipseの設定~

2.JDKのインストール、JVMが入っています。その他便利なツールがセットになっています。DerbyというDBも入っています。

3.パス(Path)を通す。これは環境変数のPathにJDKに入っているJVM(Java.exe)へのパスを設定します。こうする事で、パソコンはJAVA_HOMEという環境変数の名前でJVMへアクセスすることができます。

プログラムの実行まで

  1. ソースコードを書く
  2. コンパイル
  3. プログラム実行

上の様な手順で実行します。

小さなプログラムであれば作成する、ファイルも少なくオブジェクト指向での実装をしなくても問題ありませんがチームでの開発時には、オブジェクト指向での実装がやりやすいと思います。

そのために、文法やデータ型の理解で学習を終わらせず、クラスの作成〜継承を学習して簡単なアプリを作成するとより実戦的な学習ができると思います。

下のリンクは、簡単なゲームを作成した時の記事です。クラス別に役割を与え実装しました。設計時にどの様に役割分担するか、決めて置くところがポイントです。

Java テキストRPG(戦闘シーンのみ)を再作成する~LWJGLを参考に作り直す~

Java テキストRPG(戦闘シーンのみ)を再作成する2~設計からやり直す~

業務アプリの内訳〜どんなモノを作るか?〜

業務アプリの内訳

業務アプリは、企業体が業務の遂行に使用するアプリケーションの総称です。大まかに以下のようなモノがあります。

作成するアプリ(システム)

  1. グループウェア(社内SNS)
  2. スケジュール(営業)管理システム
  3. 在庫・販売管理システム
  4. スマホアプリ(Android, iOSなどの携帯やタブレット)

システム概要

グループウェア

社内での、勤怠管理や、勤怠報告(日報)などに加えて、チャットでの相談やスケジュール調整機能などを実装

具体的にはNotesが有名です。

スケジュール管理システム

営業の仕事を助けるため、スケジュール管理と必要な部署へのリンクや地図サービスと連携して位置情報を付随させるなど多岐に渡り機能をつけている。在庫管理システムとの連携も多い

上記のアプリは、ほぼ全ての企業(社員300人以上)が使用しているであろうアププリケーションで、必要になる技術をまとめてみました。

ITエンジニアと言ってもやる事は多種多様です。実態としては、各企業の業務内容に依存するために作業は多種多様になります。

上記のモノは、会社として、多くの企業が使用しているであろうモノです。

「〜であろう」というのは、自分の経験上、自分はJavaエンジニアで、受ける仕事は、大体上記の分類になるという意味です。

珍しいのは、組み込み系(C/C++で多い案件だが、オブジェクト指向を理解しているので、Javaエンジニアの自分にも紹介が来た)とか、画像処理(医療機器アプリ)などがありました。

使用する技術

上記の4.スマホアプリを除いては、以下のような技術を使う事が多かったです。

大まかに、以下のような技術を使用します。

画面(ブラウザ表示)の作成

HTMLとJS(Java Script)

これらの技術を併用して、画面やボタン簡単なアニメーションを作成します。スマホやPCのディスプレイサイズの違いによって、表示レイアウトを変更する、CSSでのレスポンシブデザインなんて手法もあります。この部分は

MVCモデルのV(View)に相当する部分です。

リクエストハンドル

Java, PHP

同様に、これらの技術を使用して、ユーザーからアクセスされたページに遷移させる処理の事です。

MVCモデルのC(コントロール)にあたる部分です。

主に、ログインユーザーかどうか判定し、会員以外は全てアクセス出来ないようにするなど、セキュリティのために使用したり、新規・既存の画面への画面遷移コントロールなどに使用します。

そして、あまり耳にしないかもしれませんが、アプリのログ出力や、セッション(サーバー上で管理するユーザー情報オブジェクト)のチェックを行うフィルター処理やAOPと呼ばれる技術を使用して、サーバーサイドのメソッド呼び出し前後に共通処理を挟み込む事もあります。

しかし、最近ではRESTful実装が流行りで、画面=V(View)側から直接コントローラを呼び出す実装方法が増えてきています。というかほぼこれかも?

ちなみにPHPでもこの実装方法は実現可能です。ワードプレスのライブラリ読み込みでちょいと処理が遅いけど、作りました。以下のリンクがそうです。ヘッダー部分のカテゴリを選択すると対応する記事の一覧を取得します。ちなみにAngularJSも使用しました(笑)

https://zenryokuservice.com/#!/pgbox

モデル

うまい表現が見つからなかったのでウィキから引用します。

アプリケーションデータ、ビジネスルール、ロジック、関数

という表現をしていました。詳細は、コントローラーから呼び出されるサービス処理=ビジネス・ロジック(業務処理)がそうです。コントローラーはリクエストを受け付けるのが役割なので、DBにアクセスしたり、細かい計算をするのはモデル(M)の役目になります。

これらの処理は、フレームワークというものを使用してやると実装する量を格段に減らす事が出来ます。

有名なところでは、以下のようなものがあります。

これらのフレームワークを使用して上記のようなアプリを作成する事が多くあります。古いのだとS2Strutsなんてのもあります。

丁度この頃にAOPという技術も出て来て、よく混乱しました(笑)

まとめ

早い話が、MVCモデルがわかれば大体の業務アプリは、対応が可能になるという事です。

そのために、理解する必要があるのは「オブジェクト指向」です。

XXXを行う処理を作成しで下さいというような問題では、あくまでも各機能の小さな一部の処理のみの話です。

この一部の処理を作れる必要はありますが、アプリケーション設計〜テスト迄の大まかな内容を理解するには、クラス間のやり取り、関連性を構築する技術が必要ようです。

そのために、下のリンクにあるようなフローでJavaを学ぶと効率的だと思い学習フローなんぞ作成しました。

Java Basic 学習フロー

ハローワールドに始まり、クラスの作成、役割分担などオブジェクト指向の考え方と実装方法に関して記載した記事のリンクと補足を記載しています。

シンプルに簡単なコンソールアプリを作成してみるのも良いと思います。

Java ミニゲーム ソース 〜じゃんけんゲーム in Console 〜

でわでわ。。。

Java Basic Class Inheritance ~クラスの継承関係を作る1~

継承したクラスを動かす

クラスの継承関係を作成して、それを実行してクラス継承の意味と使い方を理解していきます。
その第一回目となります。
前回の記事でもアクセス修飾子について記載しています。

動きのイメージとして下のような形です。

前提

以下のクラスを作成します。

  1. Parent
  2. ChildAni
  3. ChildOtoto
  4. MainFamilly

それぞれ以下のようなコードです。
そして、MainFamilyクラスはメインメソッドを持っているクラスです。このクラスを用意することでほかのクラスを修正してもメインメソッドを修正せずに起動できるというわけです。

具体的には以下のような手順で起動確認しながら実装ができるというわけです。

  1. MainFamilly以外のクラスを修正する
  2. MainFamillyクラスのメインメソッドを起動する

Parent

public class Parent {
    /** 家計 */
    public static int kakei = 1000;
    /** 苗字 */
    protected String lastName;
    /** 名前 */
    private String name;
    /** 年齢 */
    private int age;
    /** 趣味・特技 */
    public String favorit;

    /** コンストラクタ */
    public Parent() {
        lastName = "tanaka";
        name = "takao";
        age = 50;
        favorit = "ケツで箸を割る";
    }

    /** 自己紹介 */
    public void say() {
        System.out.println(lastName + "と申します。");
        System.out.println("親です。名前は" + name + "です。年齢は" + age + "です。");
        System.out.println("特技は、「" + favorit + "」です。");
    }

    /** おかしな部分 */
    protected void funny() {
        System.out.println("喜びを真逆の言葉で表現する。");
    }

    /** 買い物 */
    public void buy(int money) {
        kakei -= money;
        System.out.println("残金:" + kakei);
    }
}

ChildAni

public class ChildAni extends Parent {
    /** 年齢 */
    private int age;
    /** 名前 */
    private String name;

    public ChildAni() {
        name = "taro";
        age = 12;
    }

    @Override
    public void say() {
        System.out.println(lastName + "です。");
        System.out.println("兄です。名前は" + name + "です。年齢は" + age + "です。");
        System.out.println("特技は、「" + favorit + "」です。");
    }
}

ChildOtoto

public class ChildOtoto extends Parent {
    /** 年齢 */
    private int age;
    /** 名前 */
    private String name;

    public ChildOtoto() {
        name = "jiro";
        age = 10;
        favorit = "鼻を膨らます";
    }

    @Override
    public void say() {
        System.out.println(lastName + "といいます。");
        System.out.println("弟です。名前は" + name + "です。年齢は" + age + "です。");
        System.out.println("特技は、「" + favorit + "」です。");
    }

    @Override
    public void funny() {
        super.funny();
    }
}

MainFamilly

public class MainFamilly {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        System.out.println("**** Game Start ****");
        Parent parent = new Parent();
        ChildAni ani = new ChildAni();
        ChildOtoto ototo = new ChildOtoto();
        while (true) {
            String input = scan.nextLine();
            if ("parent".equals(input)) {
                parent.say();
            } else if ("ani".equals(input)) {
                ani.say();
            } else if ("ototo".equals(input)) {
                ototo.say();
            } else {
                System.out.println("処理を終了します。");
                break;
            }
        }
    }
}

ミッション1

まずは、MainFamillyクラスを起動してみましょう。
実行結果はどのようなものでしょうか?

ParentクラスChildAniクラスの違いは以下の部分です。

Parentのコンストラクタ

/** コンストラクタ */
public Parent() {
    lastName = "tanaka";
    name = "takao";
    age = 50;
    favorit = "ケツで箸を割る";
}

Parent#say()

/** 自己紹介 */
public void say() {
    System.out.println(lastName + "と申します。");
    System.out.println("親です。名前は" + name + "です。年齢は" + age + "です。");
    System.out.println("特技は、「" + favorit + "」です。");
}

ChildAniのコンストラクタ

public ChildOtoto() {
    name = "jiro";
    age = 10;
    favorit = "鼻を膨らます";
}

ChildAni#say()

@Override
public void say() {
    System.out.println(lastName + "です。");
    System.out.println("兄です。名前は" + name + "です。年齢は" + age + "です。");
    System.out.println("特技は、「" + favorit + "」です。");
}

ポイント

使用しているフィールド変数は全く同じものでしょうか?確認してみてください。アクセス修飾子をみて判断しましょう。
アクセス修飾子は下のような意味があります。

アクセス修飾子

public: どこからでも参照できる
protected: パッケージ内、クラスの継承関係がある場合に参照できる
private: クラスの中でのみ参照できる

まとめ1

アクセス修飾子によって、継承関係を作成したクラスの間で、参照する変数が変わります。
この特性を使用して、変数名が同じでも参照する変数が変えることができます。
「だからなに?」と聞かれそうですが、ここでの言いたいことはクラス固有の変数を持つことができるというところです。

ご先祖を作る

上記の記述では、Parent, ChildAni, ChildOtotoの3クラスを作成しました。ここまでで、クラスの継承に関しては理解できたと思います。注意点としてはアクセス修飾子です。この違いが理解できればオッケーです。

そして次の話に行きます。「ご先祖を作成する」というふうにきさいしましたが、抽象クラスを作成する方向で話を進めます。

抽象クラスとは

こちらの記事に記載されていたのは下のような文言です。

抽象クラスとは、抽象メソッドを 1 つ以上持つクラスです。そのクラスだけでは意味を持たず、サブクラスに継承されることで初めて機能します。

抽象メソッドとは、以下のように記述されるメソッドです。abstract の後に、戻り値の型、メソッド名、引数の型、引数の数のみを定義し、処理内容は実装されません。具体的な処理内容は、抽象クラスを継承するサブクラスで実装されます。

早い話が、下のように通常のクラスに「抽象メソッドを持っている」というところです。

アクセス修飾子 abtract 戻り値の型 メソッド名(引数); 

サンプルコード(ご先祖)

public abstract class Gosenzo {
    /** 秘伝 */
    public String hiden;

    public Gosenzo() {
        hiden = "秘伝のタレ";
    }

    /** 血継限界 */
    protected abstract String kekkeiGenkai();
}

このご先祖クラスを親クラスに継承します。
そうすると、ビルドエラーが出ます。

このビルドエラーは、「抽象メソッドのオーバーライド」を行ていないために出たエラーです。

つまり、抽象メソッドを実装することを強制できるというわけです。逆に言うとGosenzoクラスを継承したクラスは、必ずkekkeiGenkai()メソッドがあるということです。

当然、抽象クラスにはメソッドの中身(処理)を書いていないので、Gosenzoクラスを継承したクラスは、必ずkekkeiGenkai()を持っていることになります。

具体的に、ご先祖を継承したParentクラスの血継限界が「"Hello Japan!"」を標準出力へ出力するものでParent2クラスの血継限界が「"Good Night"」を標準出力へ出力するものだったとします。

そうしたときに、下のようなコードを使用するとGosenzoクラスの中小メソッドを使用することで、kekkeiGenkai()の処理内容を別のものにすることができます。

public static void main(String[] args) {
    Gosenzo g1 = new Parent();
    Gosenzo g2 = new Parent2();
    g1.kekkeiGenkai();
    g2.kekkeiGenkai();
}

これが、抽象クラスの扱い方の一つです。

次回予告

次回は、作成した上記のクラスを修正していろいろと動かしてみます。

でわでわ。。。

関連ページ一覧

Eclipse セットアップ(Mac版)

  1. Java Install Eclipse〜開発ツールのインストール〜
  2. TensorFlow C++環境〜EclipseCDTをインストール〜
  3. Setup OpenGL with JavaJOGLを使う準備 for Eclipse
  4. Eclipse Meven 開発手順〜プロジェクトの作成〜
  5. Java OpenCV 環境セットアップ(on Mac)
  6. Eclipse SceneBuilderを追加する
  7. JavaFX SceneBuilder EclipseSceneBuilder連携~

Java Basic一覧

  1. Java Basic Level 1 〜Hello Java〜
  2. Java Basic Level2 〜Arithmetic Calculate〜
  3. Java Basic Level3 〜About String class〜
  4. Java Basic Level 4〜Boolean〜
  5. Java Basic Level 5〜If Statement〜
  6. Java Basic Summary from Level1 to 5
  7. Java Basic Level 6 〜Traning of If statement〜
  8. Java Basic Level8 〜How to use for statement〜
  9. Java Basic Level 8.5 〜Array〜
  10. Java Basic Level 9〜Training of for statement〜
  11. Java Basic Level 10 〜While statement 〜
  12. Java Basic Swing〜オブジェクト指向〜
  13. Java Basic Swing Level 2〜オブジェクト指向2〜
  14. サンプル実装〜コンソールゲーム〜
  15. Java Basic インターフェース・抽象クラスの作り方
  16. Java Basic クラスとは〜Step2_1〜
  17. Java Basic JUnit 〜テストスイートの作り方〜

Git関連

  1. Java Git clone in Eclipse 〜サンプルの取得〜
  2. Eclipse Gitリポジトリの取得 GitからソースをPullしよう〜
  3. IntelliJ IDEA GitGitリポジトリからクローン〜

JavaFX関連ページ

  1. Eclipse SceneBuilderを追加する
  2. JavaFX SceneBuilder 〜EclipseとSceneBuilder連携~
  3. JavaFX SceneBuilder〜ボタンにメソッドを割り当てるワンポイント〜
  4. Java プロコンゲーム 〜見た目の作成(SceneBuilderの使用)〜

ステップアップ関連ページ一覧

  1. Java 初めてでも大丈夫〜ステップアッププログラミングのススメ〜
  2. ステップアッププログラミング〜Java FxでHelloWorld解説〜
  3. Java StepUpPrograming〜JavaFX で四則計算〜
  4. Java StepUpPrograming〜JavaFXで画面切り替えを作る1〜
  5. Java StepUpPrograming〜JavaFXで画面切り替え2ボタン作成〜
  6. Java StepUpPrograming〜JavaFXで画面切り替え3アクション〜
  7. Java StepUpPrograming〜JavaFXで画面切り替え4Pane切り替え〜
  8. Java StepUpPrograming〜JavaFXで画面切り替え5WebEngine

JavaFX + ND4Jで機械学習準備

  1. JavaFX + ND4J〜数学への挑戦1:ND4Jのインストール〜
  2. JavaFX + ND4J〜数学への挑戦2: 行列の計算〜
  3. Java + ND4J 〜数学への挑戦3: ベクトル(配列)の作成方法〜

オブジェクト指向関連ページ

  1. [オブジェクト指向の概念1〜OracleDocのチュートリアル1〜](https://zenryokuservice.com/wp/2019/10/301. /%e3%82%aa%e3%83%96%e3%82%b8%e3%82%a7%e3%82%af%e3%83%88%e6%8c%87%e5%90%91%e3%81%ae%e6%a6%82%e5%bf%b5-%e3%80%9coracledoc%e3%81%ae%e3%83%81%e3%83%a5%e3%83%bc%e3%83%88%e3%83%aa%e3%82%a2%e3%83%ab%ef%bc%91/)
  2. オブジェクト指向の概念2〜クラスとは〜

Java クラスの継承〜アクセス修飾子〜

クラスの継承

クラス継承の方法は、簡単です。下のようにコードを書けば良いです。

public class Child1 extends Parent { ... } 

継承関係を作ると、子クラスが親クラスの実装を受け継ぎ拡張することができます。

クラスの書き方などは下を参照下さい。

Java Basic Class 〜クラスを作ってクラスを理解する〜

Java Basic Class 〜クラスを使うサンプル〜

ここからが本題です。

アクセス修飾子

継承関係を作成したあとには、フィールド変数や、メソッドを定義します。

これらは「メンバ」と呼ばれインスタンスを生成して使用する事が多いです。インスタンスを生成しない場合はstatic修飾子をつけて使用します。

前置きはさておきにして具体的な使い方などに触れていきます。

変数のスコープprivate

まずは、下の様にクラスの継承関係を作ります。

親クラス

public class Parent {
   protected static int kakei = 1000;
   protected Stirng lastName;
   private String name;
   private int age;
      /** コンストラクタ */
      public Parent() {
         lastName = "test";
         name = "taro";
         age = 50;
      }
   public void say() {
      System.out.println("My name is " + name + " " + lastName + " and " + age + "years old.");
   }
}

子クラス

public class Child1 {
   private String name;
   private String age;
   public Child1 {
      name = "polo";
      age = 15;
   }
   @Overeide
   public void say() {
     System.out.println("My name is " + name + " " + lastName + " and " + age + "years old.");
   }
}

メインメソッド

public static void main(String[] args) {
   Parent p = new Parent();
   p.say();
   Child1 c1 = new Child1();
   c1.say));
}

そして、上の様な処理を実装したら

どんな出力が得られるでしょうか?

注意点としては、アクセス修飾子により、アクセスするフィールド変数が異なるところです。実行して確かめてみてください。

変数のスコープprotected

次は、protectedについて見てみます。

ParentクラスのlastNameに着目してください。2つのクラスの出力は、「test」になったと思います。これは継承関係があるクラス間で共有出来る、スコープが継承関係のあるクラス間で使用出来る様になっているからです。※パッケージ内でも使える様です。

これらのクラスのコードをいじっていろんな出力を確認してみてください。

きっと、クラスの継承とはどんなものか?の答えが出ると思います。

でわでわ。。。

Java Basic1 〜文字列操作(Stringクラス)の扱い

文字列の操作

1.1 文字列処理とは

ズバリ、文字列を切り貼りします

例:1.1 「文字列を切り取る」
「こんにちは世界」という文字列の"ちは"を切り取ると「こんに世界」になります。
コードで書くと下のようなコードになります。Javaのプログラムで実行する時には下のようなコードで実行します。あくまでもサンプルなのでやり方は自分で考えて作れるようにしたいですね。

処理の内容などは、これから学んでいきます。

/**
 * 文字列の操作。
 *
 */
public class StringControl {

    /**
     * 動かしたいメソッドを呼び出してやれば動かせます。
     * 例として「1.1文字列処理とは」のサンプルコードを呼び出しています。
     *
     * @param args
     */
    public static void main(String[] args) {
        StringControl main = new StringControl();
        main.substringTest();
    }

    /**
     * 「1.1.1文字列処理とは」のサンプルコード。
     * 文字列を切り取る処理のサンプル<br/>
     * <br/>
     * <b>「こんにちは世界」という文字列の"ちは"を切り取る</b>
     */
    public void substringTest() {
        // 変数の初期化
        String moji = "こんにちは世界";
        // *******************************************************//
        // * やり方1:Stringクラスのメソッド「substring()」を呼び出す *
        // *******************************************************//
        String rightStr = moji.substring(0, 3);
        String leftStr  = moji.substring(5, 7);
        System.out.println("切り取った結果1は「" + rightStr + leftStr + "」です。");

        // *******************************************************//
        // * やり方2:char型の配列を取り出してやる                   *
        // *******************************************************//
        char[] charMoji = moji.toCharArray();
        // 切り取った結果を格納する
        char[] resArray = new char[charMoji.length];
        int resCount = 0;
        for (int i = 0; i < charMoji.length; i++) {
            if (i != 3 && i != 4) {
                // 表示する文字を設定(セット)する
                resArray[resCount] = charMoji[i];
                resCount++;
            }
        }
        // char型の配列をStringとして生成
        String resString = new String(resArray);
        System.out.println("切り取った結果2は「" + resString + "」です。");
    }

    /**
     * 「1.2.1とは」のサンプルコード。
     * 文字列を調査するサンプル<br/>
     * <table>
     *     <thead>
     *         <tr><th>操作</th></tr></tr>
     *         <th>メソッド定義(シグニチャ)</th></tr>
     *     </thead>
     *     <tbody>
     *         <tr><td>内容が等しいか調べる</td></tr>
     *         <tr><td>public boolean equals(Object o)</td></tr>
     *     </tbody>
     * </table>
     */
    public void checkString() {

        String moji = "test";

        // String#equals()
        if (moji.equals("test")) {
            System.out.println("mojiは\"test\"です。");
        } else {
            System.out.println("mojiは\"test\"ではありません。");
        }

        // String#equalsIgnoreCase()
        if (moji.equalsIgnoreCase("Test")) {
            System.out.println("mojiは\"Test\" or \"Test\"です。");           
        } else {
            System.out.println("mojiは\"Test\" or \"Test\"ではありません。");          
        }

        // String#isEmpty()
        if (moji.isEmpty()) {
            System.out.println("mojiは\"\"です。");
        } else {
            System.out.println("mojiは\"\"ではありません。");
        }
    }
}

1.2 基本的な文字列操作

操作 メソッド定義(シグニチャ)
内容が等しいか調べる public boolean equals(Object o)
大文字小文字を区別せず内容が等しいか調べる public boolean equalsIgnoreCase(String s)
文字列長を調べる public int length()
空文字か調べる public boolean isEmpty()

Sample1 1-1 文字列調査メソッドを利用した例

※クラス全体ではなく、メソッド部分のみを記述します。

使用するメソッド一覧
String#equals()
String#equalsIgnoreCase()
String#isEmpty()

/**
 * 文字列調査(文字列が等しいか?)のサンプル。
 * 「リスト1-1 文字列調査メソッドを利用した例」
 */
public void sample1_1StringEquals() {
    // 文字列を比較した場合
    System.out.println("*** String#equals() Sample1 ****");
    if ("test".equals("tezt")) {
        System.out.println("\"test\"と\"tezt\"は等しいです。");
    } else if ("test".equals("tezt") == false) {
        System.out.println("\"test\"と\"tezt\"は等しくありません。");
    } else {
        // 実際は例外しか返ってこないので意味のないコード
        // 想定外のケースも考慮に入れる
        System.out.println("trueもfalseも帰ってこなかった場合。");
    }
    System.out.println("*** String#equals() Sample2 ****");
    // 変数に入れた文字列を比較した場合
    String str = "test";
    String str2 = "tezt";
    if (str.equals(str2)) {
        System.out.println("\"test\"と\"tezt\"は等しいです。");
    } else {
        System.out.println("\"test\"と\"tezt\"は等しくありません。");
    }
    System.out.println("*** String#equalsIgnoreCase() Sample1 ****");
    if ("test".equalsIgnoreCase("tezt")) {
        System.out.println("\"test\"と\"tezt\"は等しいです。");
    } else if ("test".equalsIgnoreCase("tezt") == false) {
        System.out.println("\"test\"と\"tezt\"は等しくありません。");
    } else if ("test".equalsIgnoreCase("Test")) {
        System.out.println("\"test\"と\"Test\"は等しいです。");
    } else {
        // 実際は例外しか返ってこないので意味のないコード
        // 想定外のケースも考慮に入れる
        System.out.println("trueもfalseも帰ってこなかった場合。");
    }

    System.out.println("*** String#isEmpty() Sample1 ****");
    if ("".isEmpty()) {
        System.out.println("文字列は空です。");
    }
    String tmp = null;

    System.out.println("*** String#isEmpty() Sample2 ****");
    try {
        if (tmp.isEmpty()) {
            System.out.println("文字列はNULLです。");
        }
    } catch (NullPointerException e) {
        System.out.println("実行するとNullPoineterExceptionで落ちる。");
    }
}

Sample1: 文字列の調査メソッドの解説

  1. "test".equals("tezt")は、"test"と"tezt"を比較している2つの文字列が等しいならifの中に処理が進む
  2. "test".equals("tezt") == falseは2つの文字列を比較した時の返り値がfalseの場合ifの中に処理が進む
  3. str.equals(str2)は変数「str」と「str2」を比較した時の返り値がtrueの場合ifの中に処理が進む
  4. "test".equalsIgnoreCase("tezt") == falseは下の内容の処理を行っているので「大文字小文字の区別無し」で文字列の比較した時の返り値がtrueの場合ifの中に処理が進む ※Stringクラスのソースファイル参照
    public boolean equalsIgnoreCase(String anotherString) {
    return (this == anotherString) ? true
            : (anotherString != null)
            && (anotherString.value.length == value.length)
            && regionMatches(true, 0, anotherString, 0, value.length);
    }
  5. "test".equalsIgnoreCase("Test")は"test"と"Test"を比較した時の返り値がtrueの場合ifの中に処理が進む
  6. "".isEmpty()は""が空文字だった場合ifの中に処理が進む
  7. String tmp = null;`は変数`tmpにNULLを代入している、この変数を参照するとNullPointerExceptionで落ちる
  8. try { 何かの処理 } catch ( キャッチする例外 ) { 例外時の処理 }で例外が起きた時の処理を行う。

Sample2 1-2 文字列検索メソッドを利用した例

※クラス全体ではなく、メソッド部分のみを記述します。

使用メソッド一覧
String#contains()
String#endsWith()
String#indexOf()

/**
 * 文字列調査(文字列が等しいか?)のサンプル。
 * 「リスト1-2 文字列検索メソッドを利用した例」
 */
public void sample1_2StringSearch() {
    String str = "0123456789ABCABC";
    System.out.println("*** String#contains() Sample1 ****");
    if (str.contains("012")) {
        System.out.println(str + "は「012」を含んでいます。");
    }
    System.out.println("*** String#contains() Sample1 ****");
    if (str.contains("014")) {
        System.out.println(str + "は「014」を含んでいます。");
    } else {
        System.out.println(str + "は「014」を含んでいません。");
    }

    System.out.println("*** String#endsWith() Sample1 ****");
    if (str.endsWith("ABC")) {
        System.out.println(str + "は「ABC」を末尾にあります。");
    }

    System.out.println("*** String#endsWith() Sample1 ****");
    System.out.println(str + "は「ABC」の文字列が" + str.indexOf("ABC") + "番目に出現します。");
    System.out.println(str + "は「ABC」の文字列が最後に出現するのは" + str.lastIndexOf("ABC") + "番目に出現します。");
}

文字列検索メソッドの解説

  1. str.contains("012")で文字列strに"012"が含まれているか検証しています。含まれているならばtrueを返します。そうでない場合はfalseを返す
  2. str.endsWith("ABC")で文字列strの末尾に"ABC"があるか検証しています。末尾にあればtrue, なければfalseを返す。
  3. str.indexOf("ABC")で文字列str内の"ABC"がはじめに出現する位置(int型)を返します。
  4. str.indexOf("ABC")で文字列str内の"ABC"が最後に出現する位置(int型)を返します。

正規表現を使う

上に記載したもの以外にも、よく使うメソッドを紹介します。

String#matchies()です。

このメソッドは、引数に正規表現を渡して、その正規表現にマッチするならtrue, そうでないならfalseを返します。

正規表現は後々に学習します。とりあえずは下の「数字」とaからz、AからZまでの文字にマッチするケースです。

チェック処理なので、静的メソッドにしています。

public static boolean isNumber(String numberStr) {
    // numberStr0から9のうちどれかに該当するか判定します。(1文字だけ)
    if (numberStr.matches("[0-9]")) {
        return true;
    }
    return false;
}

上の処理は、numberStrが数字1文字で、0から9に該当するかどうかの判定を行なっています。

文字数を複数にする場合は後ろに、「{2,5}」のようにつけると「2桁から5桁の間の0から9の数字」というような意味になります。

Java Basic 〜インターフェースの扱い方2〜

インターフェースの扱い方

java.util.Listやjava.util.Mapのインターフェースを使用することは多いと思います

今回は、自作したインターフェースを使用してみようと思います。

==<今回のやりたいこと>===
自作したインターフェースを使用して、起動するクラスを切り替えて実行しようというものです。

具体的には、インターフェースCommandIFを作成し、以下の2つのクラスに実装、メインメソッドで標準入力を受けて入力した文字列によって2つのクラスのうちどちらを起動するか選択できるようにする。

  1. HelloCommand
  2. ReadyCommand

起動した時の動画です。

インターフェースサンプルを作る

  1. まずはインターフェースクラスを作成します。
    下のように、インターフェースを定義

    public interface CommandIF {
    }
  2. インターフェースに、実装クラスで起動するメソッドを定義します。
    ここで作成するメソッドは、実装(implements)したクラスで必ず定義しなくてはいけないメソッドになります。

    public interface CommandIF {
    /** コマンドを実行する */
    public void execute();
    }

    完成したコードはGithubにあります。

  3. インターフェースを実装する
    今回は下のように2つのクラスに実装(implements)しました。

<メインメソッドで実行する>

public class Lv3_1_RefactorLv2_Main {
    /** コマンドリスト */
    private static  Map<String, CommandIF> cmdMap;
    public static void main(String[] args) {
        // コマンドの用意
        cmdMap = new HashMap<String, CommandIF>();
        cmdMap.put("hello", new HelloCommand());
        cmdMap.put("ready", new ReadyCommand());
        // 標準入力
        Scanner input = new Scanner(System.in);

        while(true) {
            System.out.println("入力してください: ");
            String inStr = input.nextLine();
            if ("bye".equals(inStr)) {
                System.out.println("Good Bye");
                break;
            }

            CommandIF cmd = cmdMap.get(inStr);
            if (cmd != null) {
                cmd.execute();
            } else {
                System.out.println("コマンドが登録されていません。: " + inStr);
            }
        }
    }
}

上のメインメソッドでは、以下のような処理を行っています。

  1. HashMapのインスタンスをMap型の変数に代入
  2. マップのキーに"hello"、値に「HelloCommand」クラスのインスタンス
  3. 同様にキーに"ready"、値に「ReaddyCommand」クラスのインスタンス
  4. 無限ループを開始
  5. 入力を促し、標準入力を受ける
  6. マップから「HelloCommand」か「ReaddyCommand」もしくはnullを取得する(キーに該当するものがないときはNULLが返ってくる)
  7. 「HelloCommand」か「ReaddyCommand」のexecute()メソッドを実行

「HelloCommand」と「ReaddyCommand」クラスには「CommandIF」をimplementsしていますので必ず「execute()」メソッドが存在し、実行することができます。
このように、インターフェースを実装したクラスはそのインターフェース型として使用することができますので、CommandIFを実装(implements)したクラスは全て「execute()」メソッドがあり、CommandIF型の変数に代入、execute()を実行できるというわけです。

余談ですが、このインsターフェースを実装した他のサンプルもありますので、これも紹介します(動画ですが。。。)。

<応用編>

今回はそんなところで。。。

でわでわ。。。

関連ページ一覧

Eclipse セットアップ

  1. Java Install Eclipse〜開発ツールのインストール〜
  2. TensorFlow C++環境〜EclipseCDTをインストール〜
  3. Setup OpenGL with JavaJOGLを使う準備 for Eclipse
  4. Eclipse Meven 開発手順〜プロジェクトの作成〜
  5. Java OpenCV 環境セットアップ(on Mac)
  6. Eclipse SceneBuilderを追加する
  7. JavaFX SceneBuilder EclipseSceneBuilder連携~

Java Basic一覧

  1. Java Basic Level 1 〜Hello Java〜
  2. Java Basic Level2 〜Arithmetic Calculate〜
  3. Java Basic Level3 〜About String class〜
  4. Java Basic Level 4〜Boolean〜
  5. Java Basic Level 5〜If Statement〜
  6. Java Basic Summary from Level1 to 5
  7. Java Basic Level 6 〜Traning of If statement〜
  8. Java Basic Level8 〜How to use for statement〜
  9. Java Basic Level 8.5 〜Array〜
  10. Java Basic Level 9〜Training of for statement〜
  11. Java Basic Level 10 〜While statement 〜
  12. Java Basic Swing〜オブジェクト指向〜
  13. Java Basic Swing Level 2〜オブジェクト指向2〜
  14. サンプル実装〜コンソールゲーム〜
  15. Java Basic インターフェース・抽象クラスの作り方
  16. Java Basic クラスとは〜Step2_1〜
  17. Java Basic JUnit 〜テストスイートの作り方〜

Git関連

  1. Java Git clone in Eclipse 〜サンプルの取得〜
  2. Eclipse Gitリポジトリの取得 GitからソースをPullしよう〜
  3. IntelliJ IDEA GitGitリポジトリからクローン〜

JavaFX関連ページ

  1. Eclipse SceneBuilderを追加する
  2. JavaFX SceneBuilder 〜EclipseとSceneBuilder連携~
  3. JavaFX SceneBuilder〜ボタンにメソッドを割り当てるワンポイント〜
  4. Java プロコンゲーム 〜見た目の作成(SceneBuilderの使用)〜

ステップアップ関連ページ一覧

  1. Java 初めてでも大丈夫〜ステップアッププログラミングのススメ〜
  2. ステップアッププログラミング〜Java FxでHelloWorld解説〜
  3. Java StepUpPrograming〜JavaFX で四則計算〜
  4. Java StepUpPrograming〜JavaFXで画面切り替えを作る1〜
  5. Java StepUpPrograming〜JavaFXで画面切り替え2ボタン作成〜
  6. Java StepUpPrograming〜JavaFXで画面切り替え3アクション〜
  7. Java StepUpPrograming〜JavaFXで画面切り替え4Pane切り替え〜
  8. Java StepUpPrograming〜JavaFXで画面切り替え5WebEngine

JavaFX + ND4Jで機械学習準備

  1. JavaFX + ND4J〜数学への挑戦1:ND4Jのインストール〜
  2. JavaFX + ND4J〜数学への挑戦2: 行列の計算〜
  3. Java + ND4J 〜数学への挑戦3: ベクトル(配列)の作成方法〜

オブジェクト指向関連ページ

  1. [オブジェクト指向の概念1〜OracleDocのチュートリアル1〜](https://zenryokuservice.com/wp/2019/10/301. /%e3%82%aa%e3%83%96%e3%82%b8%e3%82%a7%e3%82%af%e3%83%88%e6%8c%87%e5%90%91%e3%81%ae%e6%a6%82%e5%bf%b5-%e3%80%9coracledoc%e3%81%ae%e3%83%81%e3%83%a5%e3%83%bc%e3%83%88%e3%83%aa%e3%82%a2%e3%83%ab%ef%bc%91/)
  2. オブジェクト指向の概念2〜クラスとは〜

Java テキストRPG(戦闘シーンのみ)を再作成する2~設計からやり直す~

イントロfダクション

前回作成したものは、ちゃんと設計していませんでした。とうかアイディアだけが先走り、収拾がつかなくなった次第です。

今回作成するのもの

TesxtRPGをだんだんレベルアップしていく方向で作成しようと考えています。イメージとしては市価のような感じでレベルアップしていこうと考えています。

Lv1: 戦闘シーンのみ
Lv2: レベルアップ・ステータスの表示を追加
Lv3: タイトル表示~ストーリー(シーン)の表示を追加

  1. タイトル表示
  2. ストーリーを開始(シーン切り替えを実装)
  3. 登場する敵はラスボスのみ
    Lv4: ミニストーリーをRPGにする。
  4. イベント発生から、シーンの切り替えの実装
  5. 装備の変更を実装する
    Lv5: ミニストーリーをRPGにする2
  6. 一つの冒険を実装する、ゴールは隣町の近所に住む魔王を倒す
  7. エンディングを実装する

そして、今回実装するのはLv1です。
ここでは、戦闘シーンに必要な登場人物(モンスターを含む)を実装することに重きを置きます。

成果物

<Eclipseでの実行>

<コマンドプロンプトでの実行>

Lv1:TextRpgゲーム

まずは、戦闘シーンをイメージして、フローチャートを作成します。

<作成したフローチャート>

実際には、テストケースを作成しながらこのフローチャートを作成しました。

大まかに、下のように行う処理のイメージを図にする感じです。

フローチャート

ゲームの開始から、終了までの流れを図にしたものです。

作成するときに考えるのは「どのように、ゲームを進めるか?」です。具体的には以下のように考えました。

フローチャートを作る

<ステップ1>
テキストRPG(コンソール表示)で戦闘シーンをどのように進めるか?
まず、頭に浮かんだのは某有名ゲームソフトです。
戦闘シーンの開始時には「XXXが現れた!」と表示されます。ならばこちらもそのように。。。
そのほか、戦闘シーンのイメージはこのゲームソフトです。
そして、初期表示を行い、入力受付。。。と処理が続きます。

<ステップ2>
入力後どのような動きを行うか?今回は戦闘シーンのみなので例外的に「bye」と入力したらアプリケーションを終了するように実装します。
他は、入力⇒画面の更新(HPが減ったり、モンスターがダメージを受けたり)を行います。

クラス図を作る

実際に作成したクラス図は下のものです。赤くなっているものは今回使用しないもの(クラス)です。

フローチャートと同様にクラス図も作成します。
フローチャートを参考に、登場する人物(クラス)を用意します。
今回のプログラム起動方法としては、LWJGLを参考にしてやりました。つまり、①メインメソッドのクラス(GameMain)から②今回作成するゲームを実行するクラス(TextRpgGameEngin)を呼び出し実行します。
ここでのポイントとしては、②のクラスはThreadクラスを継承しマルチすれっと処理に対応できるようにすることです。

そうすることで、ゲーム(アプリケーション)を起動するスレッドと、例えばDBサーバーを起動するスレッドをマルチスレッドで起動したり、ほかにもアイディアがあればそのような実装ができます。

テストケースから作成する

フローチャートとクラス図からそれぞれのクラスに対するテストケースを作成していきます。

テストケースを実行することを考えると、最終的に呼び出されるクラス、「XXXUtilsクラス」がテストケースを実装するのに楽だろうと思われるので、ここら辺から攻めていきました。

フローチャートでも初めにある『「XXXが現れた!」を表示する』という処理から実装しました。あとはフローチャートの処理順にテストケースを作っていきました。初めからフローチャートの順序か。。。

作成したもの

Githubにアップロード(PUSH)しています。

Lv2の実装前に

現状で、実装するのに調査が必要だったものや、てこずったものに関して記載したいと思います。

JUnitでのコンソール出力

今回作成したゲーム(アプリ)は画面表示と言いながらコンソール出力になります。なので、テスト時には出力内容を確認する必要があり、出力先を変更し確認するために取得する必要がありました。

<解決策>
具体的には下のコードになるのですが、手順としては以下のようになります。

  1. 標準出力の出力先を変更する。
  2. 出力した文字列を取得する。

列挙するとシンプルな感じですが、実装もシンプルでした。
使用したの部品(Java API)配下の者です。

  • org.junit.platform.commons.logging.Logger
  • java.io.ByteArrayOutputStream
  • java.io.PrintStream

上記の部品を使用して、標準出力をPrintStreameに変更、PrintStreamにはByteArrayOutputStreamを渡して作成=出力先がByteArrayOutputStreamになる。

テスト実行時の出力する文言に関しては、org.junit.platform.commons.logging.Loggerへ出力しました。

/** ログ出力 */
private static final Logger LOG = LoggerFactory.getLogger(FirstJankenMainTest.class);
/** 標準出力確認 */
private static final ByteArrayOutputStream console = new ByteArrayOutputStream();

テストクラスの起動ですが、これは@BeforeClassを使用しました。

  1. テストクラスの起動時にテスト対象クラスをインスタンス化
    @BeforeClass
    public static void initClass() {
    target = new ConsoleUtils();
    // 標準出力の出力先を変更する
    System.setOut(new PrintStream(console));
    }
  2. 各テストケースの実行前に出力した文字列をリセット
    /**
    * テストの準備
    */
    @Before
    public void init() {
    // フィールド変数
    console.reset();
    }
    
  3. テストを実行した結果をassertEqualsで比較

    /**
    * ステータス表示のテスト:2桁
    */
    @Test
    public void testPrintBattleStatus1() {
    // プレーヤーは一人
    Player player = new Player("test");
    // 名前の文字数は、全角は4文字、半角8文字まで
    player.setLevel(10);
    player.setHP(20);
    player.setMP(10);
    target.printBattleStatus(player);
    LOG.info(() -> SEP + console.toString());
    
    String expect = "**** test ****\r\n" +
            "  Lv: 10     *\r\n" +
            "  HP: 20     *\r\n" +
            "  MP: 10     *\r\n" +
            "**************" + SEP + SEP;
    assertEquals(expect, console.toString());
    }
    

こんな風に実装しました。

コマンドの取得

Playerクラスで実装したメソッドの中に「たたかう」などのコマンド実行時に起動するメソッドがあります。そして、コンストラクタなど、コマンド実行以外で使用するメソッドも定義していあります。
「はて?どうやってコマンド用のメソッドのみを取得しようか?」と考えたところ「アノテーションがあ~るじゃないですか!」とひらめきました。

早速実装しました。参考サイトはこちらです。

  1. 自作のアノテーションを作成。
    /**
    * 行動を表すアノテーション
    * 。
    * @author 実装者の名前
    */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Command {
    /** 表示順 */
    public int index();
    /** 実行選択肢 */
    public String commandName();
    }
  2. コマンド用のメソッドにアノテーションをつける
    /**
    * 攻撃コマンド。
    *
    * @return 武器の攻撃力
    */
    @Command(index=1, commandName="たたかう")
    public int attack() {
    return mainWepon.getOffence();
    }
    
  3. アノテーションをつけたメソッドを取得。
    ※コンソール出力するめそっどですが、取得した内容を出力しています。

    /**
    * プレーヤーの行動選択肢を一覧表示する。
    *
    * @param player
    * @return コマンドマップ(indexでソート済み)
    */
    public Map<Integer, String> printCommandList(Player player) {
    Method[] mess = player.getClass().getDeclaredMethods();
    // 並び替え機能付きのMAP
    Map<Integer, String> commandMap = new TreeMap<>();
    for(Method mes : mess) {
        Command ano = mes.getAnnotation(Command.class);
        if (ano != null) {
            // マップにインデックス(順番)とコマンド名を登録
            commandMap.put(ano.index(), ano.commandName());
        }
    }
    // 並び替え後に表示
    commandMap.forEach((Integer index, String value) -> {
        System.out.println(index + ": " + value);
    });
    return commandMap;
    }

こんな感じで実装しました。

コンソールのクリア

[こちらの記事にも記載]()しましたが、以下のコードでコマンドの実行がでるようです。

Rumtime.getRuntime().exec(コマンド);

しかし、IOExceptionが出力され。。。調べてみるとWindowsでは下のようなコードで実行するとよいみたいです。

new ProcessBuilder("cmd", "/c", "cls").inheritIO().start().waitFor();

Eclipseで実行してみたところ、文字化けた文字が出力されるだけで、意味なかったのですが、JARファイルを作成し、コマンドプロンプトで実行してみたところ問題なく動きました。

今後は、Lv2の実装を進めていきたいと思います。
そして、今後はマルチスレッド実装がちゃんと動く必要があるので、サンプルを作成、実行してみました。

でわでわ。。。

関連ページ一覧

EclipseセットアップWindows版

Eclipse セットアップ

  1. Java Install Eclipse〜開発ツールのインストール〜
  2. TensorFlow C++環境〜EclipseCDTをインストール〜
  3. Setup OpenGL with JavaJOGLを使う準備 for Eclipse
  4. Eclipse Meven 開発手順〜プロジェクトの作成〜
  5. Java OpenCV 環境セットアップ(on Mac)
  6. Eclipse SceneBuilderを追加する
  7. JavaFX SceneBuilder EclipseSceneBuilder連携~

Java Basic一覧

  1. Java Basic Level 1 〜Hello Java〜

Java テキストRPG(戦闘シーンのみ)を再作成する~LWJGLを参考に作り直す~

事の発端について

前回作成した、テキストRPG(戦闘シーンのみ)をJavaFXへと移植しようかと考えてたのですが、如何せん、絵とか必要になるだろうと思い保留していました。

しかし、ゲームブックの要領で、ストーリーを進めるのであれば、ずいぶんと楽しくプレーできそうだと思いました。

そんなわけで、その方向でプログラムを組みなおします。

ここで肝になるのが「ゲームループ」という言葉と実装内容です。ループ処理をするのはよいのですが、参考にするソースを見てみるとマルチスレッド使ったりしているので、ちょっと不安になった次第です。。。

拡張する事を考える

前回の反省点としては、クラスの構成のみを考えていたので実現性に欠けた作りになってしまった事です。自分に仲間がいて、開発を進められるのであれば話は別ですが。。。

そんなわけでテキストのみで、面白くやる方法を考えました。つまるところはゲームブック仕様です。

ゲームブック仕様

ゲームブック仕様というのは、アドベンチャーゲームの様に選択によりゲームの進み方が変わるというものです。

そして、RPGよろしく、戦闘シーンと武器購入など、ステータスコントロールなどを追加してやりたいと考えています。

そこで、本来は画像処理などを並行して実行するLWJGLを参考にしようと思いました。つまりは、DBなどを並行して動かそうと考えています。

Java Game 作成履歴 〜LWJGL導入から作成まで(今後も更新します)〜

LWJGLのゲームループを解析

以前作成した、LWJGLのプログラムサンプルコードを参考に作成したのですが「GameEngine」と「GameLogic」という二つのクラスを使用してうまく作られていました。

LWJGLの登場人物(クラス)

  • Mainクラス
  • DummyGameクラス
  • GameEnginクラス
  • Windowクラス(今回は無視する)
  • Renderクラス(今回は無視する)

今回作成するゲームは、コンソールゲームなのでWindowとか3Dモデルのレンダリングは行わないので今回は無視します。

上記のクラスの関係性を解析

Mainクラス、リンク先のソースを見てみると以下のクラスを呼び出しています。

  1. IGameLogic
  2. GameEngeine

IGameLogicインターフェースはGameLogicに当たる処理を実装したクラス=DummyGameクラスです。
GameEngineはそのままですね。。。

ポイントは、GameEngineクラスに、DummyGameを渡して、インスタンスを生成しているところです。
つまり、DummyGameをGameEngineクラスで使用することができるということです。

DummyGameをGameEngineに渡してしまえば、あとはGameEngineクラスで自由に処理ができます。

なるほど、GameEngineクラスはいちいち改修してなくてもよさそうです。

ということは。。。

今回の実装するテキストRPG(戦闘シーンのみ)は、

  • メインメソッドを持つMainクラス
  • IGameLogicに相当するインターフェースを実装したTextRpgGameクラス
  • GameEngineクラス

上のようなクラスを使用して実装するのがよいと思いました。

もし、必要があればその時に追加実装しようと考えています。

まとめると

Mainクラスから、TextRpgGameクラスを生成してGameEngineクラスを実行するという事です。

マルチスレッドについて

今まで、記載してないのですが、戦闘シーンのみではちょっと寂しいので拡張するということも考慮に入れてマルチスレッドに対応させています。というかLWJGLがすでに対応しているというか、そうのようにする必要があります。

実際LWJGLでは、ウィンドウに描画する(OpenGLを使用する)処理があるのでマルチスレッドでの実装が必須です。

しかし、今回のマルチスレッドの実装は自前のものになってしまうので、サンプルの作成と起動確認を行ってみました。

メインにするスレッドとサブにするスレッドを「synchronized」修飾子をつけて区別をつける必要がありました。

サンプルコードは以下になります。
<実装コード(Github)>
ThreadMain
Thread1Cls
Thread2Cls

<処理内容>

  1. メインメソッド(ThreadMain)から処理を起動
  2. 主軸(メイン)になるスレッド(Thread1Cls)と付随するスレッド(Thread2Cls)を起動
  3. 主軸(メイン)になるスレッド(Thread1Cls)で以下の処理を行う
    • ”Thread1Cls: 処理を開始します"を表示
    • 入力受付、入力値の表示
    • 3秒待機(出力には「5秒」とあるが実装ミス)
  4. 付随するスレッド(Thread2Cls)で以下の処理を行う
    • 3秒待機(出力には「5秒」とあるが実装ミス)
    • ”Thread2Cls: 処理を開始します"を表示
    • 入力受付、入力値の表示

テキストRPGの記事

  1. Java ミニゲーム ソース付き 〜RPGの戦闘シーンを作るin Console〜
  2. Java ミニゲーム ソース付き 〜RPGの戦闘シーンを作る2 in Console〜
  3. Java ミニゲーム ソース付き 〜ここまでのまとめ〜
  4. Java ミニゲーム ソース付き 〜コンソールアプリからGUIアプリへ〜
    ここでアイディアに詰まる。。。

TextRpg前回作成したもの1

TextRpg前回作成したもの2
文字をゆっくり表示する実装を行ったものです。

参考にしたソース

Mainクラスのソース
public class Main {

    public static void main(String[] args) {
        try {
            boolean vSync = true;
            IGameLogic gameLogic = new DummyGame();
            GameEngine gameEng = new GameEngine("GAME", 600, 480, vSync, gameLogic);
            gameEng.run();
        } catch (Exception excp) {
            excp.printStackTrace();
            System.exit(-1);
        }
    }
}
DummyGameクラスのソース
public class DummyGame implements IGameLogic {

    private static final float MOUSE_SENSITIVITY = 0.2f;

    private final Vector3f cameraInc;

    private final Renderer renderer;

    private final Camera camera;

    private GameItem[] gameItems;

    private Vector3f ambientLight;

    private PointLight pointLight;

    private static final float CAMERA_POS_STEP = 0.05f;

    public DummyGame() {
        renderer = new Renderer();
        camera = new Camera();
        cameraInc = new Vector3f(0.0f, 0.0f, 0.0f);
    }

    @Override
    public void init(Window window) throws Exception {
        renderer.init(window);

        float reflectance = 1f;
        //Mesh mesh = OBJLoader.loadMesh("/models/bunny.obj");
        //Material material = new Material(new Vector3f(0.2f, 0.5f, 0.5f), reflectance);

        Mesh mesh = OBJLoader.loadMesh("/models/cube.obj");
        Texture texture = new Texture("textures/grassblock.png");
        Material material = new Material(texture, reflectance);

        mesh.setMaterial(material);
        GameItem gameItem = new GameItem(mesh);
        gameItem.setScale(0.5f);
        gameItem.setPosition(0, 0, -2);
        gameItems = new GameItem[]{gameItem};

        ambientLight = new Vector3f(0.3f, 0.3f, 0.3f);
        Vector3f lightColour = new Vector3f(1, 1, 1);
        Vector3f lightPosition = new Vector3f(0, 0, 1);
        float lightIntensity = 1.0f;
        pointLight = new PointLight(lightColour, lightPosition, lightIntensity);
        PointLight.Attenuation att = new PointLight.Attenuation(0.0f, 0.0f, 1.0f);
        pointLight.setAttenuation(att);
    }

    @Override
    public void input(Window window, MouseInput mouseInput) {
        cameraInc.set(0, 0, 0);
        if (window.isKeyPressed(GLFW_KEY_W)) {
            cameraInc.z = -1;
        } else if (window.isKeyPressed(GLFW_KEY_S)) {
            cameraInc.z = 1;
        }
        if (window.isKeyPressed(GLFW_KEY_A)) {
            cameraInc.x = -1;
        } else if (window.isKeyPressed(GLFW_KEY_D)) {
            cameraInc.x = 1;
        }
        if (window.isKeyPressed(GLFW_KEY_Z)) {
            cameraInc.y = -1;
        } else if (window.isKeyPressed(GLFW_KEY_X)) {
            cameraInc.y = 1;
        }
        float lightPos = pointLight.getPosition().z;
        if (window.isKeyPressed(GLFW_KEY_N)) {
            this.pointLight.getPosition().z = lightPos + 0.1f;
        } else if (window.isKeyPressed(GLFW_KEY_M)) {
            this.pointLight.getPosition().z = lightPos - 0.1f;
        }
    }

    @Override
    public void update(float interval, MouseInput mouseInput) {
        // Update camera position
        camera.movePosition(cameraInc.x * CAMERA_POS_STEP, cameraInc.y * CAMERA_POS_STEP, cameraInc.z * CAMERA_POS_STEP);

        // Update camera based on mouse            
        if (mouseInput.isRightButtonPressed()) {
            Vector2f rotVec = mouseInput.getDisplVec();
            camera.moveRotation(rotVec.x * MOUSE_SENSITIVITY, rotVec.y * MOUSE_SENSITIVITY, 0);
        }
    }

    @Override
    public void render(Window window) {
        renderer.render(window, camera, gameItems, ambientLight, pointLight);
    }

    @Override
    public void cleanup() {
        renderer.cleanup();
        for (GameItem gameItem : gameItems) {
            gameItem.getMesh().cleanUp();
        }
    }

}

GameEngineクラスのソース

public class GameEngine implements Runnable {

    public static final int TARGET_FPS = 75;

    public static final int TARGET_UPS = 30;

    private final Window window;

    private final Timer timer;

    private final IGameLogic gameLogic;

    private final MouseInput mouseInput;

    public GameEngine(String windowTitle, int width, int height, boolean vSync, IGameLogic gameLogic) throws Exception {
        window = new Window(windowTitle, width, height, vSync);
        mouseInput = new MouseInput();
        this.gameLogic = gameLogic;
        timer = new Timer();
    }

    @Override
    public void run() {
        try {
            init();
            gameLoop();
        } catch (Exception excp) {
            excp.printStackTrace();
        } finally {
            cleanup();
        }
    }

    protected void init() throws Exception {
        window.init();
        timer.init();
        mouseInput.init(window);
        gameLogic.init(window);
    }

    protected void gameLoop() {
        float elapsedTime;
        float accumulator = 0f;
        float interval = 1f / TARGET_UPS;

        boolean running = true;
        while (running && !window.windowShouldClose()) {
            elapsedTime = timer.getElapsedTime();
            accumulator += elapsedTime;

            input();

            while (accumulator >= interval) {
                update(interval);
                accumulator -= interval;
            }

            render();

            if ( !window.isvSync() ) {
                sync();
            }
        }
    }

    protected void cleanup() {
        gameLogic.cleanup();
    }

    private void sync() {
        float loopSlot = 1f / TARGET_FPS;
        double endTime = timer.getLastLoopTime() + loopSlot;
        while (timer.getTime() > endTime) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException ie) {
            }
        }
    }

    protected void input() {
        mouseInput.input(window);
        gameLogic.input(window, mouseInput);
    }

    protected void update(float interval) {
        gameLogic.update(interval, mouseInput);
    }

    protected void render() {
        gameLogic.render(window);
        window.update();
    }
}

関連ページ一覧

EclipseセットアップWindows版

Eclipse セットアップ

  1. Java Install Eclipse〜開発ツールのインストール〜
  2. TensorFlow C++環境〜EclipseにCDTをインストール〜
  3. Setup OpenGL with Java〜JOGLを使う準備 for Eclipse〜
  4. Eclipse Meven 開発手順〜プロジェクトの作成〜
  5. Java OpenCV 環境セットアップ(on Mac)
  6. Eclipse SceneBuilderを追加する
  7. JavaFX SceneBuilder 〜EclipseとSceneBuilder連携~

Java Basic一覧

  1. Java Basic Level 1 〜Hello Java〜
  2. Java Basic Level2 〜Arithmetic Calculate〜
  3. Java Basic Level3 〜About String class〜
  4. Java Basic Level 4〜Boolean〜
  5. Java Basic Level 5〜If Statement〜
  6. Java Basic Summary from Level1 to 5
  7. Java Basic Level 6 〜Traning of If statement〜
  8. Java Basic Level8 〜How to use for statement〜
  9. Java Basic Level 8.5 〜Array〜
  10. Java Basic Level 9〜Training of for statement〜
  11. Java Basic Level 10 〜While statement 〜
  12. Java Basic Swing〜オブジェクト指向〜
  13. Java Basic Swing Level 2〜オブジェクト指向2〜
  14. サンプル実装〜コンソールゲーム〜
  15. Java Basic インターフェース・抽象クラスの作り方
  16. Java Basic クラスとは〜Step2_1〜
  17. Java Basic JUnit 〜テストスイートの作り方〜

Git関連

  1. Java Git clone in Eclipse 〜サンプルの取得〜
  2. Eclipse Gitリポジトリの取得 〜GitからソースをPullしよう〜
  3. IntelliJ IDEA Git〜Gitリポジトリからクローン〜

JavaFX関連ページ

  1. Eclipse SceneBuilderを追加する
  2. JavaFX SceneBuilder 〜EclipseとSceneBuilder連携~
  3. JavaFX SceneBuilder〜ボタンにメソッドを割り当てるワンポイント〜
  4. Java プロコンゲーム 〜見た目の作成(SceneBuilderの使用)〜

ステップアップ関連ページ一覧

  1. Java 初めてでも大丈夫〜ステップアッププログラミングのススメ〜
  2. ステップアッププログラミング〜Java FxでHelloWorld解説〜
  3. Java StepUpPrograming〜JavaFX で四則計算〜
  4. Java StepUpPrograming〜JavaFXで画面切り替えを作る1〜
  5. Java StepUpPrograming〜JavaFXで画面切り替え2ボタン作成〜
  6. Java StepUpPrograming〜JavaFXで画面切り替え3アクション〜
  7. Java StepUpPrograming〜JavaFXで画面切り替え4Pane切り替え〜
  8. Java StepUpPrograming〜JavaFXで画面切り替え5WebEngine

JavaFX + ND4Jで機械学習準備

  1. JavaFX + ND4J〜数学への挑戦1:ND4Jのインストール〜
  2. JavaFX + ND4J〜数学への挑戦2: 行列の計算〜
  3. Java + ND4J 〜数学への挑戦3: ベクトル(配列)の作成方法〜

オブジェクト指向関連ページ

  1. [オブジェクト指向の概念1〜OracleDocのチュートリアル1〜](https://zenryokuservice.com/wp/2019/10/301. /%e3%82%aa%e3%83%96%e3%82%b8%e3%82%a7%e3%82%af%e3%83%88%e6%8c%87%e5%90%91%e3%81%ae%e6%a6%82%e5%bf%b5-%e3%80%9coracledoc%e3%81%ae%e3%83%81%e3%83%a5%e3%83%bc%e3%83%88%e3%83%aa%e3%82%a2%e3%83%ab%ef%bc%91/)
  2. オブジェクト指向の概念2〜クラスとは〜

SDカードの容量が少ない~SanDiskの場合~

事の始まり

SanDiskの64GBを購入しリムーバブルディスクとして使用しようとしたが、使用可能領域が256MBに。。。

おかしいと思い調べてみると、ほかにも似たようなことになっている人がいる模様。
こちらのサイトで調べてみたところそれ用のフォーマッター(アプリを使用する必要があるようだった)

解決策

  1. こちらのサイトからアプリをダウンロード
  2. インストーラを起動
    Formatter Install
  3. SDカードのフォーマット
    Format_SD
  4. 無事にフォーマットでき、カードのデータ量も適切なものが表示されました
    After

関連ページ一覧

Eclipse セットアップ

  1. Java Install Eclipse〜開発ツールのインストール〜
  2. TensorFlow C++環境〜EclipseCDTをインストール〜
  3. Setup OpenGL with JavaJOGLを使う準備 for Eclipse
  4. Eclipse Meven 開発手順〜プロジェクトの作成〜
  5. Java OpenCV 環境セットアップ(on Mac)
  6. Eclipse SceneBuilderを追加する
  7. JavaFX SceneBuilder EclipseSceneBuilder連携~

Java Basic一覧

  1. Java Basic Level 1 〜Hello Java〜
  2. Java Basic Level2 〜Arithmetic Calculate〜
  3. Java Basic Level3 〜About String class〜
  4. Java Basic Level 4〜Boolean〜
  5. Java Basic Level 5〜If Statement〜
  6. Java Basic Summary from Level1 to 5
  7. Java Basic Level 6 〜Traning of If statement〜
  8. Java Basic Level8 〜How to use for statement〜
  9. Java Basic Level 8.5 〜Array〜
  10. Java Basic Level 9〜Training of for statement〜
  11. Java Basic Level 10 〜While statement 〜
  12. Java Basic Swing〜オブジェクト指向〜
  13. Java Basic Swing Level 2〜オブジェクト指向2〜
  14. サンプル実装〜コンソールゲーム〜
  15. Java Basic インターフェース・抽象クラスの作り方
  16. Java Basic クラスとは〜Step2_1〜
  17. Java Basic JUnit 〜テストスイートの作り方〜

Git関連

  1. Java Git clone in Eclipse 〜サンプルの取得〜
  2. Eclipse Gitリポジトリの取得 GitからソースをPullしよう〜
  3. IntelliJ IDEA GitGitリポジトリからクローン〜

JavaFX関連ページ

  1. Eclipse SceneBuilderを追加する
  2. JavaFX SceneBuilder 〜EclipseとSceneBuilder連携~
  3. JavaFX SceneBuilder〜ボタンにメソッドを割り当てるワンポイント〜
  4. Java プロコンゲーム 〜見た目の作成(SceneBuilderの使用)〜

ステップアップ関連ページ一覧

  1. Java 初めてでも大丈夫〜ステップアッププログラミングのススメ〜
  2. ステップアッププログラミング〜Java FxでHelloWorld解説〜
  3. Java StepUpPrograming〜JavaFX で四則計算〜
  4. Java StepUpPrograming〜JavaFXで画面切り替えを作る1〜
  5. Java StepUpPrograming〜JavaFXで画面切り替え2ボタン作成〜
  6. Java StepUpPrograming〜JavaFXで画面切り替え3アクション〜
  7. Java StepUpPrograming〜JavaFXで画面切り替え4Pane切り替え〜
  8. Java StepUpPrograming〜JavaFXで画面切り替え5WebEngine

JavaFX + ND4Jで機械学習準備

  1. JavaFX + ND4J〜数学への挑戦1:ND4Jのインストール〜
  2. JavaFX + ND4J〜数学への挑戦2: 行列の計算〜
  3. Java + ND4J 〜数学への挑戦3: ベクトル(配列)の作成方法〜

オブジェクト指向関連ページ

  1. [オブジェクト指向の概念1〜OracleDocのチュートリアル1〜](https://zenryokuservice.com/wp/2019/10/301. /%e3%82%aa%e3%83%96%e3%82%b8%e3%82%a7%e3%82%af%e3%83%88%e6%8c%87%e5%90%91%e3%81%ae%e6%a6%82%e5%bf%b5-%e3%80%9coracledoc%e3%81%ae%e3%83%81%e3%83%a5%e3%83%bc%e3%83%88%e3%83%aa%e3%82%a2%e3%83%ab%ef%bc%91/)
  2. オブジェクト指向の概念2〜クラスとは〜

ゲーム作成アイディア〜タロット占いを学ぶ〜

ステータス作成に

ゲーム作成(テキストRPG)のために汎用性の高いタロット占いを学びます。本当は、ゲーム作成のために調べてたのですが、調べていたらタロット占いを学んだ方がゲームへの反映もしやすいのでわ?という疑惑と、やってみるかな?という気になったという次第です(笑)

具体的には、数秘術、12星座占いなどと関連付けてやれば、パラメータとして反映しやすいだろうと考えています。

数秘術の使い道

数秘術は、大まかに人名からその人の以下に並べる各番号を取得してその人の運勢を占います。

  • 人格数
  • 運命数
  • ハート数
  • 意思数
    *特性数
  • 欠落数
  • 成熟数

上の「~数」の詳細に関してはこちらに記載しています。
そして、算出した各「数」に対応する番号をセフィトロトの木にある丸と丸をつなぐ「パス」に対応する番号にタロットの大アルカナをあてはめます。
後日イメージを貼り付けます。

タロット占い

タロット占いは、「ある1つの質問にカードが答える」という形で占います。
具体的には、質問者(占われる人)と占い師(タロット占いをする人)がいて質問者の質問をタロットカードをシャッフル、カット、カードを展開する。という手順で占います。

ゲームとの関連性

やはり、大アルカナ(22枚)、小アルカナ(56枚)のカードに対して、数値を与えて使用する方法が一番無難だと思います。
しかし、数値を与えるためには、各カードの内容を理解、もしくは、カテゴリー分けしてやる必要があるので。。。

結局は、占いを学ぶというところに至った次第です。

まずは全体を眺める

大アルカナの一覧

ワンドのスートとコートカード

カップのスートとコートカード

ソードのスートとコートカード

ペンタクルスのスートとコートカード

上の様なカードがあり、大アルカナは大まかな事、小アルカナは具体的な事を示すらしいです。ぶっちゃけて、全部にパラメータを振り分けるのは大変なので、大アルカナにターゲットを絞ります。

テキストRPG

ゲームでは、よくあるゲームよろしくHP、MPに始まり、ちから、すばやさなどをステータスとします。問題は、各大アルカナにどの様なパラメータを振り分けるか?というところです。

ただ今検討中。。。

でわでわ。。。

関連ページ一覧

Eclipse セットアップ

  1. Java Install Eclipse〜開発ツールのインストール〜
  2. TensorFlow C++環境〜EclipseにCDTをインストール〜
  3. Setup OpenGL with Java〜JOGLを使う準備 for Eclipse〜
  4. Eclipse Meven 開発手順〜プロジェクトの作成〜
  5. Java OpenCV 環境セットアップ(on Mac)
  6. Eclipse SceneBuilderを追加する
  7. JavaFX SceneBuilder 〜EclipseとSceneBuilder連携~

Java Basic一覧

  1. Java Basic Level 1 〜Hello Java〜
  2. Java Basic Level2 〜Arithmetic Calculate〜
  3. Java Basic Level3 〜About String class〜
  4. Java Basic Level 4〜Boolean〜
  5. Java Basic Level 5〜If Statement〜
  6. Java Basic Summary from Level1 to 5
  7. Java Basic Level 6 〜Traning of If statement〜
  8. Java Basic Level8 〜How to use for statement〜
  9. Java Basic Level 8.5 〜Array〜
  10. Java Basic Level 9〜Training of for statement〜
  11. Java Basic Level 10 〜While statement 〜
  12. Java Basic Swing〜オブジェクト指向〜
  13. Java Basic Swing Level 2〜オブジェクト指向2〜
  14. サンプル実装〜コンソールゲーム〜
  15. Java Basic インターフェース・抽象クラスの作り方
  16. Java Basic クラスとは〜Step2_1〜
  17. Java Basic JUnit 〜テストスイートの作り方〜

Git関連

  1. Java Git clone in Eclipse 〜サンプルの取得〜
  2. Eclipse Gitリポジトリの取得 〜GitからソースをPullしよう〜
  3. IntelliJ IDEA Git〜Gitリポジトリからクローン〜

## JavaFX関連ページ
1. [Eclipse SceneBuilderを追加する](https://zenryokuservice.com/wp/2018/11/17/eclipse-scenebuilder%e3%82%92%e8%bf%bd%e5%8a%a0%e3%81%99%e3%82%8b/)
1. [JavaFX SceneBuilder 〜EclipseとSceneBuilder連携~](https://zenryokuservice.com/wp/2018/11/17/javafx-scenebuilder-%e3%80%9ceclipse%e3%81%a8scenebuilder%e9%80%a3%e6%90%ba/)
1. [JavaFX SceneBuilder〜ボタンにメソッドを割り当てるワンポイント〜](https://zenryokuservice.com/wp/2019/02/05/javafx-scenebuilder%e3%80%9c%e3%83%9c%e3%82%bf%e3%83%b3%e3%81%ab%e3%83%a1%e3%82%bd%e3%83%83%e3%83%89%e3%82%92%e5%89%b2%e3%82%8a%e5%bd%93%e3%81%a6%e3%82%8b%e3%83%af%e3%83%b3%e3%83%9d%e3%82%a4%e3%83%b3/)
1. [Java プロコンゲーム 〜見た目の作成(SceneBuilderの使用)〜](https://zenryokuservice.com/wp/2020/03/30/java-%e3%83%97%e3%83%ad%e3%82%b3%e3%83%b3%e3%82%b2%e3%83%bc%e3%83%a0-%e3%80%9c%e8%a6%8b%e3%81%9f%e7%9b%ae%e3%81%ae%e4%bd%9c%e6%88%90scenebuilder%e3%81%ae%e4%bd%bf%e7%94%a8%e3%80%9c/)

## ステップアップ関連ページ一覧

  1. Java 初めてでも大丈夫〜ステップアッププログラミングのススメ〜
  2. ステップアッププログラミング〜Java FxでHelloWorld解説〜
  3. Java StepUpPrograming〜JavaFX で四則計算〜
  4. Java StepUpPrograming〜JavaFXで画面切り替えを作る1〜
  5. Java StepUpPrograming〜JavaFXで画面切り替え2ボタン作成〜
  6. Java StepUpPrograming〜JavaFXで画面切り替え3アクション〜
  7. Java StepUpPrograming〜JavaFXで画面切り替え4Pane切り替え〜
  8. Java StepUpPrograming〜JavaFXで画面切り替え5WebEngine

## JavaFX + ND4Jで機械学習準備

  1. JavaFX + ND4J〜数学への挑戦1:ND4Jのインストール〜
  2. JavaFX + ND4J〜数学への挑戦2: 行列の計算〜
  3. Java + ND4J 〜数学への挑戦3: ベクトル(配列)の作成方法〜

## オブジェクト指向関連ページ
1. [オブジェクト指向の概念1〜OracleDocのチュートリアル1〜](https://zenryokuservice.com/wp/2019/10/301. /%e3%82%aa%e3%83%96%e3%82%b8%e3%82%a7%e3%82%af%e3%83%88%e6%8c%87%e5%90%91%e3%81%ae%e6%a6%82%e5%bf%b5-%e3%80%9coracledoc%e3%81%ae%e3%83%81%e3%83%a5%e3%83%bc%e3%83%88%e3%83%aa%e3%82%a2%e3%83%ab%ef%bc%91/)
1. [オブジェクト指向の概念2〜クラスとは〜](https://zenryokuservice.com/wp/2019/10/30/%e3%82%aa%e3%83%96%e3%82%b8%e3%82%a7%e3%82%af%e3%83%88%e6%8c%87%e5%90%91%e3%81%ae%e6%a6%82%e5%bf%b5%e3%80%9c%e3%82%af%e3%83%a9%e3%82%b9%e3%81%a8%e3%81%af%e3%80%9c/)