ゲーム開発者が偉ぶるブログ

ゲーム開発のビジネスやマネジメントについて日々思うことをあれこれ偉ぶって書き綴ったもの。

Java | 作ったもの① 吉祥寺クエスト

Javaにて最初に作ったものがこちら。
プログラム言語で作ったものとしても初めてになる。

import java.io.IOException;

public class RPG
{
    //定数
    final static String    NAME_PL = "あなた";    //    主人公の名前
    final static String    NAME_EN = "魔王";    //    エネミーの名前
    final static int    TURN_PL = 0;        //    プレイヤーのターン
    final static int    TURN_EN = 1;        //    エネミーのターン

    //変数
    static int    playerLv = 20;        //    プレイヤーのLV
    static int    playerHp = 30;        //    プレイヤーのHP
    static int    playerMp = 30;        //    プレイヤーのMP
    static int    playerPow = 15;        //    プレイヤーの基礎攻撃力
    static int    playerMag = 20;        //    プレイヤーの基礎魔法力
    static int    enemyHp = 100;        //    エネミーのHP
    static int    enemyPow = 3;        //    エネミーの基礎攻撃力
    static int    checkTurn = 0;        //    現在のバトルターン識別
    static String    str = "メッセージ";        //    メッセージ用の文章
    static boolean    loopFlg = true;            //    メインループ用のフラグ

    //    メイン関数
    public static void main(String[] args) throws IOException
    {
        putStartMsg();                        //    冒頭メッセージを表示

        while( loopFlg ) {                    //    メインループ loopFlgがtrueなら実行
            if( checkTurn == TURN_PL ){
                selectCommand();            //    コマンドを求める
                if( enemyHp <= 0){            //    エネミーのHPが0になったらゲームクリア
                    loopFlg = false;
                    putGameClear();
                }else{
                    checkTurn = TURN_EN;    //    エネミーターンに移行
                }
            }else if( checkTurn == TURN_EN ){
                putMsg( "" );
                enemyAttack();                //    エネミーの攻撃
                if( playerHp <= 0 ){        //    プレイヤーのHPが0になったらゲームオーバー
                    loopFlg = false;
                    putGameOver();
                }else{
                    checkTurn = TURN_PL;    //    プレイヤーターンに移行
                }
            }
        }
    }

       //    メッセージ表示用の関数
    public static void putMsg( String str )
    {
        System.out.println( str );
    }

       //    コマンドを求める表示
    public static void selectCommand() throws IOException
    {
        putMsg( "********************************************************" );
        putStatus();    //    ステータス表示
        putMsg( "" );
        putMsg( " ▼どうする?" );
        putMsg( " 1. たたかう  2. かいふく  3. にげる" );
        System.out.print( ">" );
        exeAction();
    }

       //    ステータス表示
    public static void putStatus()
    {
        putMsg( " 【 " + NAME_PL + " LV:" + playerLv + " HP:"
                 + playerHp + " MP:" + playerMp + " | 魔王 HP:" + enemyHp + " 】" );
    }

       //    コマンド入力を判定して実行
    public static void exeAction() throws IOException
    {
        int    key = inputKey();
        switch( key ) {
            case '1':
                putMsg( "" );
                putMsg( "********************************************************" );
                playerAttack();
                break;
            case '2':
                putMsg( "" );
                putMsg( "********************************************************" );
                playerMagic();
                break;
            case '3':
                putMsg( "" );
                putMsg( "********************************************************" );
                playerEscape();
                break;
            default:
                putMsg( " 1 か 2 か 3 のキーを入力してください" );
                selectCommand();
        }
    }

       //    キー入力を受け付ける関数
    public static int inputKey() throws java.io.IOException
    {
        int    key = System.in.read();
        if( key == 10 || key == 13 ){
            return(inputKey());
        }
        return (key);
    }

       //    プレイヤーの「たたかう」
    public static void playerAttack()
    {
        java.util.Random rdm = new java.util.Random();
        int exeAction = rdm.nextInt( 3 );
        if ( exeAction == 0 ){
            int dmg = rdm.nextInt( 10 );
            dmg += playerPow;
            putMsg( " " + NAME_PL + "は" + NAME_EN + "をなぐりつけた!" );
            putMsg( " パコン!" );
            putMsg( " " + NAME_EN + "は " + dmg + " のダメージを受けた!" );
            enemyHp -= dmg;            //    HPが減る処理
            
        } else if( exeAction == 1 ){
            int dmg = rdm.nextInt( 20 ) + playerPow * 2;
            putMsg( " " + NAME_PL + "の会心のいちげき!" );
            putMsg( " すぱぱぱぱーーーん!!" );
            putMsg( " " + NAME_EN + "のズラがはじけ飛ぶ!" );
            putMsg( " " + NAME_EN + "に " + dmg + " のダメージ!" );
            enemyHp -= dmg;            //    HPが減る処理
        } else if( exeAction == 2 ){
            putMsg( " " + NAME_PL + "はtwitterをみている!" );
        }
    }

       //    プレイヤーの「回復魔法」
    public static void playerMagic()
    {
        putMsg( " " + NAME_PL + "は回復魔法を唱えた!" );
        if( playerMp < 10 ){
            putMsg( " ‥MPが足りない!" );
            return;
        }else{
            putMsg( " ぴろぴろぴろりん!" );
            putMsg( " " + NAME_PL + "のHPが " + playerMag + " 回復した!" );
            playerMp -= 8;                //    MPが減る処理
            playerHp += playerMag;        //    HPが回復する処理
            if( playerHp > 30 ){
                playerHp = 30;
            }
        }
    }

       //    プレイヤーの「逃げる」
    public static void playerEscape()
    {
        putMsg( " " + NAME_PL + "は逃げようとした!すたたたたた" );
        putMsg( " ババッ!" );
        putMsg( " " + NAME_EN + "は素早く回り込んだ!逃げられない!" );
    }

       //    エネミーの攻撃
    public static void enemyAttack()
    {
        java.util.Random rdm = new java.util.Random();
        int exeAction = rdm.nextInt( 5 );
        if( exeAction == 0 || exeAction == 1 ){
            int    dmg = rdm.nextInt( 3 ) + enemyPow;
            putMsg( " " + NAME_EN + "の攻撃!" );
            putMsg( " ズババン!" );
            putMsg( " " + NAME_PL + "に " + dmg + " のダメージ!" );
            playerHp -= dmg;        //    HPが減る処理
        }else if( exeAction == 2 ){
            int dmg = rdm.nextInt(10) + enemyPow * 2;
            putMsg( " " + NAME_EN + "の痛恨のいちげき!" );
            putMsg( " ドゴゴゴゴォン!!" );
            putMsg( " " + NAME_PL + "のお腹がたぷたぷ揺れる!" );
            putMsg( " " + NAME_PL + "に " + dmg + " のダメージ!" );
            playerHp -= dmg;        //    HPが減る処理
        }else if( exeAction == 3 ){
            int dmg = rdm.nextInt( 4 ) + 2;
            putMsg( " " + NAME_EN + "はピコ太郎のモノマネをしてきた!" );
            putMsg( " ‥まったく似ていない!" );
            putMsg( " " + NAME_PL + "はMPが " + dmg + " 減った!" );
            playerMp -= dmg;        //    MPが減る処理
                if( playerMp < 0 ){
                    playerMp = 0;
                }
        }else if( exeAction == 4 ){
            putMsg( " " + NAME_EN + "はメールに返信している!" );
        }
    }

    //    ゲーム冒頭のメッセージ
    public static void putStartMsg()
    {
        putMsg( "" );
        putMsg( "     『吉祥寺クエスト』" );
        putMsg( "" );
        putMsg( "********************************************************" );
        putMsg( " " + NAME_PL + "は買い物のために吉祥寺までフラフラやってきた" );
        putMsg( " ‥と、そのときだった" );
        putMsg( " " + NAME_PL + "の目の前に突如、" + NAME_EN + "が立ちふさがった!" );
    }

    //    ゲームクリアのメッセージ
    public static void putGameClear()
    {
        putMsg( " " + NAME_EN + "は力尽き、満足げな表情をして崩れ落ちた" );
        putMsg( "" );
        putMsg( " " + NAME_PL + "は" + NAME_EN + "を倒した!" );
        putMsg( "" );
        putMsg( " これで吉祥寺に平和が戻るだろう" );
        putMsg( "" );
        putMsg( "     おしまい" );
        putMsg( "********************************************************" );
    }

       //    ゲームオーバーのメッセージ
    public static void putGameOver()
    {
        putMsg( "" );
        putMsg( " " + NAME_PL + "は魔王に敗れ力尽きた‥" );
        putMsg( "" );
        putMsg( " その後、吉祥寺は火の海と化したという" );
        putMsg( "" );
        putMsg( "    GAME OVER" );
        putMsg( "********************************************************" );
    }
}

まず関数とwhile、if、switchとキー入力の受け付けを覚えた後に乱数の与え方を覚え、乱数を使って行動パターンを増やして、確率は同じ行動を複数含めることで無理矢理実現している。

プログラム自体が初めてに近い状態から休日5~6日間くらいでできた感じ。
ここまでの習得はかなり楽だと思う。
ウインドウを表示して画像を表示してそれら表示を描き変えて‥となると突然難易度が跳ね上がる。
なぜなら、ウインドウ周りの実装のためにswingといった標準ライブラリを使うことになり、クラスのインスタンスを作成するかコンストラクタを使うかといったあたりのルールの理解が大変だからだ。

ということで作ったのはRPGのバトルっぽいものであり、真面目なメッセージにしてもつまらないと思いおちゃらけてみたが、いかんせん文章回しのセンスの無さが光る。

しかしひとまず基礎の基礎は学べたように思うので、次のステップとしてはこれをちゃんとウインドウに表示させ、敵画像を用意し、メッセージは1文字ずつ表示させつつ文末でキー入力を受け付けるようにして待機させ、攻撃時には効果音を入れたり、瀕死時には文字の色を変えたりできればと思う。

欲を言えばウインドウを揺らしたりもしたいが‥

Java | Tortoise SVNでバージョン管理してみる

バージョン管理を試したくなったのでこちらを導入してみた。

バージョン管理サービスは大きく分けると「集中型」と「分散型」の2種類あるみたいだが、1人でプログラムを勉強する分には集中型で十分と思い、集中型でGUIで操作できて無料の「Tortoise SVN」を選んだ。

「Git」や「GitHub」も使ってみたかったが今回はパス。
一応Gitの理解のために下記ページのスライドをいくつか読んでみた。
大変分かり易くてありがたい。


さて、Tortoise SVNのインストールはこちらを参考にさせていただいた。

 海外のプログラムのダウンロード場所ってなんでこんなに分かりにくいのだろうといつも思うが‥ツール名の最新バージョンがリリースされたというタイトルをクリックするとDLページに飛ぶ。

f:id:game_dev:20170402155231j:plain

32bitと64bitが選べるのでインストールしたい方を選んでDL、そしてページを下にスクロールさせると言語パックが並んでいるのでJapaneseの32bitまたは64bitを選んでDLすればOK。

f:id:game_dev:20170402155232j:plain


この偉ぶるブログの管理人は3DCGアーティストなのだが、こうやってバージョン管理をプライベートでも使えるようになると、ほんのちょっとだけTAに近付いたような気持ちになってテンションも上がるというもの。

今はゲーム開発現場でもプログラマだけでなくアーティストも含めてプロジェクト全体でバージョン管理が導入されるのが当たり前になってきつつあると思うが、PSDとかエクセルとかもバージョン管理して慣れていきたいものだなと思う。

●余談

プログラマとアーティストは作るモノの性質も、スキルの性質も、慣れ親しんできたものも何もかもが違う。つまり異質である。

そのため、プログラマにとって当たり前のことやアーティストにとって当たり前のことが随分と違うので、考え方や文化も異なり、衝突も日常茶飯事だ。

バージョン管理はプログラマには当たり前の文化でアーティストも行うべきと考えている場面によく遭遇するが、実際アーティストにとってバージョン管理は非常に相性が悪い。

‥というのも、アーティストは例えば制作中のモデルを3種類用意してシーンの中で同時に並べて「どれがいいですか?」とディレクターやリードにチョイスしてもらうのが日常であり、「このモデルの顔は1つ前のやつが良いけど、服のデザインは今の方が良い」といったことがよく起こる。
そして、目の前でちょちょいとデータを差し替えたり手を加えたりして「これでどうか?」とOKをもらうという流れになったりする。

しかし、前のバージョンの作業データと今のバージョンの作業データをPhotoshopやMayaやゲームエンジンの同一シーン上で複数並べて見比べてもらう‥というのはバージョン管理と相性が悪い。
つまりファイルを直接コピーしてそれぞれバラバラに編集し、それぞれの良い部分を複雑に統合していかないといけなかったりする。

また、アーティストが扱う2Dや3Dのデータは単純なテキストデータでは無いため、そもそも「マージ」という概念が通じにくいのではと思う。
PhotoshopのこのバージョンのレイヤーAを後のバージョンに追加して代わりにレイヤーBは削除する‥みたいな手作業でのマージは可能だが、そういった作業がバージョン管理ツールで快適になるかというと、それは全く無いのではないか。

アーティストがバージョン管理を活用する世界になるには、AdobeやAutodesk製品に過去バージョンを容易に見比べられて、良いところをチョイスして統合可能‥というような強力なバージョン管理機能が追加されない限り、難しい問題なように思う。

Java | 秀丸でコーディングする際の環境設定

秀丸でのJavaのコーディングも試してみる。

設定は下記サイトを参考にさせて頂いた。
「ファイルタイプ別の設定」ダイアログでJavaを新設すると良い。

 『5分でできる秀丸エディタのゲームプログラミング向け設定


その上で、Java強調表示ライブラリを使ってカスタマイズすると良い。

 『秀まるおのホームページ(サイトー企画)−JavaとJsp

 ライブラリの追加場所はデフォルトならこちら。

C:\Users\ユーザー名\AppData\Roaming\Hidemaruo\Hidemaru\Setting

メニューの「その他>ファイルタイプ別の設定」のダイアログで「デザイン>強調表示」の設定で「読み込み」からDLした「GecchiJava.hilight」を読みこんで、強調が不要なものは表示方法を変えたり(リストをダブルクリックで設定のための小窓が出る)、「デザイン」の設定で文字の色やスタイルを変えたりして好みにカスタムすると良い。

ちなみにこんな感じにしてみた。
プログラマがよく画面に表示している原色カラーリングは目に痛すぎる。

f:id:game_dev:20170402181620j:plain

それから、ブラウザやOffice製品でお馴染の操作方法だが、秀丸でもCtrlを押しながらマウスホイールで書類全体の表示サイズを変えられる。
画面解像度が高いと文字が小さくて見えにくいが、フォントサイズを変えるのではなく表示サイズを変えると良い。

ちなみにCtrl + Fで検索ダイアログを表示できるが、文字列を選択しておいてから(途切れの無い文字列ならダブルクリックで一度に選択できたりする)Ctrl + F、そしてそのままEnterを押すと、ヒットした文字列全てを強調表示してくれて便利。そしてF3とShift + F3で候補を行き来すると良い。

 

Java | GUIアプリ・テキストエディタ系のリンク

GUIアプリ、それも特にテキストエディタ系のチュートリアルやサンプルのリンクを備忘録として貼っておく。

 

●テキスト置換

Swingでテキストエディタをつくる

ここではテキストファイルを読み込んで、FONTを変えたり、テキスト置換ができるシンプルなエディタのサンプルがDLできる。400行ほど。

 

●ドラッグ&ドロップでファイルを受け取る

Java | 学習の工程について(2017年)

現在2017年初頭で、古い記事が沢山散見される中でどんなウェブサイト、書籍を参考にしたかの記録を残しておきたいと思う。

まず最初に行ったチュートリアルが下記サイト。

<ゲームを作ろう!>超初心者のためのプログラミング入門 - 楽しい趣味のプログラミング -

ここで SESSION 1 から SESSION 11 までしっかり手を動かしながら読み進めて「自分で作ってみよう」は飛ばし、SESSION 12 はある程度形になるまでをざっくり追うところまで読んだ。

土日の2日間まるまる使えばプログラミング初心者でも一通り読み進められると思う。
(ただしhtmlなどのスクリプト言語で若干はプログラミングに慣れている前提かも)

こちらが読み終えた頃にはシンプルなテキストアドベンチャーが作れるようになる。

次に観たのがこちらの動画。

第116回まで続いていて本当に素晴らしい。

こちらをおさらい部分は基本的には観るだけで、新しいことが出てきたら試してみる感じで始めたが、ドキュメントの読み方、情報の探し方まで丁寧に解説していて本当にありがたい。

Javaを始めた土日明けの平日夜で動画の「乱数を使ってみよう」のパートまで進めたら、ドラクエの1vs1の戦闘のような流れを自力でさくっと作れるようになっていた。

敵が現れた旨の開始メッセージを表示後「1.たたかう 2.まほう 3.にげる」‥の中から行動を1~3のキー入力で選択して実行し、プレイヤーもエネミーも乱数をswitchのcaseに当てはめて会心の一撃が出たりミスったりさせるようにして、ダメージや魔法での回復量も基礎攻撃力や基礎回復力に乱数の値を乗せる感じで計算して、ゲームオーバーまたはゲームクリアして終了する流れだ。
(ただしこの時点ではまだコマンドプロンプトでしか表示できない状態だったが)

さらに動画を進めていけば、最終的にはFC時代のドラクエのようなものを作れるようになるということだ。

その後、ウインドウを作成して表示、画像を表示、レイアウトを変える‥といった話に進んでいくが、動画ではswingを利用している。完全初心者としては新しくて良さげなJavaFXの方を覚えたいと思いつつも、JavaFXの記事が世の中にまだ十分ない感じがするのでひとまず最後の動画までこのまま進めるのが吉と思う。

ちなみに動画では秀丸Javaを書いて、コマンドプロンプト上でコンパイル>実行させて解説しているが、NetBeand IDEを使っていてしばらくは問題なかった。
最初は「swingで作成したウインドウはどうプレビューするんだ?」と戸惑ったが、メニューの実行>ファイルの実行(Shift + F6)でOK。

しかしその後、ウインドウでキャラ画像を表示するあたりで画像が表示されなかったり、表示されるようになっても3体並んで表示されなくなったりするが、Part.26まで進めると正常に表示されるようになるので、全く同じ記述にしていても画像が表示できない場合はしばらくその状態で我慢しつつ動画の通りになぞっていけばOK。

さて、この動画でパート40あたりまで進めてくると、非常に階層の深いクラスの継承が入ってコンストラクタなど聞き慣れない単語が頻繁に出てくるようになって、ついていけなくなると思う。

そうした場合は、他の入門サイトで書式や単語を調べながら理解を進めると良い。

なかでもオススメはこちら。

クラスなども車に例えて非常に分かり易く解説されており、ひとつひとつの記事がとても丁寧で読みやすい。

Java | 開発環境について

JDKとNet Beansのインストール
こちらを参考にしました。
インストールしたバージョンはJava 8とNetBeans IDE 8.2になります。

SESSION 4 開発環境をつくろう - <ゲームを作ろう!>超初心者のためのプログラミング入門

 

●POIのインストール
エクセルやワードの書類を作成したり編集したりできるということで入れてみたもの。
古い記事しか見つかりませんが、インストールはこちらを参考にしました。

その後、解凍したフォルダを適当な場所に置いて、NetBeansを起動し、メニューのツール>ライブラリ>JAR/フォルダの追加‥でJARファイル群を選べばOK?
ダメっぽい。

プロジェクト作成ごとに追加しないといけない‥?

POI Bookの作成

記事が古いものばかりで分からないのでちょっと後回し‥

 

JavaFXについて

とりあえずメモっておく。

初心者のためのJavaFXプログラミング入門

 

 

Java | 引数も戻り値も無い関数の宣言について

こちらのサイトを参考にJavaの勉強を始めてみた。

<ゲームを作ろう!>超初心者のためのプログラミング入門 - 楽しい趣味のプログラミング -

続くかは分からないが、ひとまず短期間で必要分だけ習得して非常に簡単なゲームを作れるくらいまでは進めておきたい。

とりあえずSESSION 11までは進めた。

が、プログラム自体まともに学習するのは初めてなのでちょっとしたことで躓く。
なので躓いたあたりをメモっておく。

続きを読む