amustallホーム

RPGMakerUniteでActionRPGを作ろう(part2)

目次

part1
・playerの攻撃を作成(当たり判定なし)までを解説
part2
・playerの常時ステータス表示windowとステータスバー(Hp,MP)までを解説
part3
・敵にHpとGoldを付与して、敵用のHpバーも作成するまでを解説
part4
・敵への攻撃判定までを実装するまでを解説
part5
・playerの攻撃の種類を増やして複数攻撃を実装までを解説
part6
・敵のAIや表示UIの調整から完成まで!!(最終回)

この講座を最後まで呼んでいただけると出来るゲームの内容(このゲームようなアクションRPGをつくります)
ALL YOU NEED IS GOLD 0

playerのステータスにアクセスしてみましょう

さてRGPMakerUniteでアクションRPGを作ろうpart2を始めましょう!まずここまで読んで頂きありがとうございました! そしてpart1で投稿していたfreemのゲームも無事公開されました!!
ALL YOU NEED IS GOLD 0
上記からダウンロード可能です。30分程度で短いですが、一応アクションRPGを構成する最低限はそろっていると 思います。
もしpart1を読んでない方でこの記事とゲームを作成しようと考えてくださっている方がいたら是非part1から読んでください。
前回でplayerは画像上の攻撃をすることが出来るようになりました。しかしまだ敵もどこにもいないので、 実際に攻撃するはできませんし、そもそも攻撃判定の処理をつくっていません。こちらはpart3で作成していきます。 今回は、この攻撃があたった、敵から攻撃されたの処理を作成するときに必要なステータスを参照する方法から 作成してきましょう。

まず初めに常時表示される簡易的なステータス画面を作成しましょう。内容はHp,Mp,Goldとします。 まずはSceneMapに行ってから、My_SceneMapCanvas子オブジェクトとして PlayerStatusManagerを作成して、その子オブジェクトにtext(textMeshPro)でhp,mp,goldを作成しましょう。 playerStatusManagerにはcontent size filtervartiavl layout groupをつけておきましょう。

photo
photo

さてこれで表示する準備が出来ましたので、スクリプトでplayerのステータスを取得していきましょう。 こちらはDataManagerというソースがあり、こちらを使用してplayerのセーブデータにアクセスして、 GameActorというソースの中で宣言されているplayerのhp,mp等のステータスの値を取得できるようです。 では実際にコードを書いていきましょう。playerのhpやmpを取得する関数はPlayerManagerSctiptに 記載していきます。


//HP取得
public int GetPlayerHp() {
    //Data,anagerはstaticなので直接参照できる
    var party = DataManager.Self().GetGameParty();
    //Actor[0]が操作キャラ
    GameActor = party.Actors[0];
    hp = GameActor.Hp;
    return hp;
}
//MP取得
public int GetPlayerMp() {
    var party = DataManager.Self().GetGameParty();
    GameActor = party.Actors[0];
    mp = GameActor.Mp;
    return mp;
}
//Gold取得
public int GetPlayerGold() {
    var party = DataManager.Self().GetGameParty();
    gold = party.Gold;
    return gold;
}
            

usingをいくつか通さないといけないのでそちらも提示しておきます。


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//mapmanager
using RPGMaker.Codebase.Runtime.Map;
//datamanager
using RPGMaker.Codebase.Runtime.Common;
//gameactor
using RPGMaker.Codebase.Runtime.Battle.Objects;
            

これでplayerのステータスを参照する関数が出来たので、それを表示してあげればいいですね。 My_SceneMapCanvasScriptにスクリプト追加しましょう。


    void Update()
    {
        DisplayPlayerStatus();
    }

    public void DisplayPlayerStatus() {
        playerHp.text = "HP: " + my_PlayerManagerScript.GetPlayerHp().ToString();
        playerMp.text = "MP: " + my_PlayerManagerScript.GetPlayerMp().ToString();
        playergold.text = "GOLD: " + my_PlayerManagerScript.GetPlayerGold().ToString();
    }
            

されこれでゲームを起動すればステータスが表示されるはずです。

photo

プレイヤーのHPとMPをスライドバーでプレイヤーの上に表示しよう

さて、ステータスを表示できましたが、アクションRPGではplayerの上にステータスを表示するバーが 見えていることが多いですよね。数字ではなく視覚的に分かるし、操作しているキャラクターから視線を 動かさなくていいので操作性にもいい影響がありますよね。これを実装していきましょう。 まずはこちらを表示するために新しいcanvasを作成しましょう。名前はMy_PlayerCanvasとしましょう。 設定は以下にしめします。大きさ等はゲーム画面にして自分で見やすい様に調整してください。

photo

canvasが出来たらこの子オブジェクトにHP等を表示するスライダーを作成してください。そして以下の手順を行ってください
1.Sliderの子オブジェクトにHandle Slide Areaがありますが、これはSliderのつまみ部分であり、今回はいらないで消してください。
2.スライドバーの不要な余白となる部分がでるので以下の画像をみて赤で囲った部分の数字に調整してください。

photo
photo

3.上記ができたら、スライドバーの色を分かりやすい色に変えていきましょう。 Sliderの子オブジェクトのBackgroundFillにあるImageコンポーネントColorを変えて調整しましょう。 今回はbackgroundを赤、fillを緑にしてみました。
4.そしてスライドバーの角が丸いのでこれでもいいですが、四角にしましょう。fillとbackgroundの imageのソース画像をnoneにしましょう。

photo

すると以下の画像のようによく見るステータスバーの見た目が完成です。

photo

つくったステータスバーがplayerを追随し、実際のHP等を反映させよう

さてステータスバーが大体出来たので、今後をこれをプレハブにして、playerが生成されるタイミングで playerの上に表示されplayerと一緒に動くようにしていき、最初にやったステータスへのアクセス方法を 利用してステータスバーに反映させていきましょう。
まずはMy_PlayerCanvasScriptというスクリプトを作成して、My_PlayerCanvasにアタッチして、以下の スクリプトを記載してください。


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class My_PlayerCanvasScript : MonoBehaviour
{
    //スライダーオブジェクト
    public Slider hpSlider;
    public Slider mpSlider;

    private Canvas playerCanvas;

    //playerHP関連
    public int playerMaxHp;
    public int playerHp;
    //playerMp関連
    public int playerMaxMp;
    public int playerMp;
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }
    //ステータススライダーの初期化
    public void InitPlayerSlider() {
        playerCanvas = gameObject.GetComponent<Canvas>();
        playerCanvas.worldCamera = Camera.main;
        //hp設定
        hpSlider.maxValue = playerMaxHp;
        hpSlider.value = playerHp;
        //mp設定
        mpSlider.maxValue = playerMaxMp;
        mpSlider.value = playerMp;
    }
    //ステータススライダーの更新
    public void setPlayerSlider() {

        hpSlider.maxValue = playerMaxHp;
        hpSlider.value = playerHp;

        mpSlider.maxValue = playerMaxMp;
        mpSlider.value = playerMp;
    }
}
            

書いている側はゆっくり1行ずつ書いているのですが、完成版を一気に見せられると混乱しますよね。。 解説していきます。まずは冒頭でpublicで2つのSliderを宣言していますが、これはSliderオブジェクトインスペクターからアタッチして参照するために宣言しています。(unityでもっと定番の他のオブジェクト参照方法ですよね)
そしてその下にpublicでHPやMPを宣言していますが、これは次に書いていく新しいスクリプトである My_EventManager側で参照して、My_EventmangaerでMy_PlayerManagerScriptの関数を利用して Playerのステータスを取得してMy_PlayercanvasScriptのhpやmpに代入していきます。 あとは、この取得できたステータスの値をスライダーに反映していきます。
つづいてMaxHp等も取得できるようにmy_PlayerManagerScriptを改変します。そして、今後playerの GameObjectも必要になるので、それを取得するためのScriptを記載しておきます。


public int GetPlayerHp() {
    //Data,anagerはstaticなので直接参照できる
    var party = DataManager.Self().GetGameParty();
    GameActor = party.Actors[0];
    hp = GameActor.Hp;
    return hp;
}
public int GetPlayerMaxHp() {
    var party = DataManager.Self().GetGameParty();
    GameActor = party.Actors[0];
    maxHp = GameActor.Mhp;
    return maxHp;
}
//MP取得
public int GetPlayerMp() {
    var party = DataManager.Self().GetGameParty();
    GameActor = party.Actors[0];
    mp = GameActor.Mp;
    return mp;
}
public int GetPlayerMaxMp() {
    var party = DataManager.Self().GetGameParty();
    GameActor = party.Actors[0];
    maxMp = GameActor.Mmp;
    return maxMp;
}
//Gold取得
public int GetPlayerGold() {
    var party = DataManager.Self().GetGameParty();
    gold = party.Gold;
    return gold;
}
//playerのGameObject参照
public GameObject GetPlayerObject() {
    mainCamera = GameObject.FindGameObjectWithTag("MainCamera");
    player = mainCamera.transform.parent.gameObject;
    return player;
}
            

これでMaxHp等々参照できるようになりました。そして新しいスクリプトであるMy_EventManagerスクリプト を書きます。今回のゲーム作成ではこのMy_EventManagerスクリプトが一番の肝になります。今後もどんどん このスクリプトを改造していきます。


using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class My_EventManager : MonoBehaviour
{
    private My_PlayerManagerScript my_PlayerManagerScript;
    private GameObject player;
    public Canvas my_PlayerCanvas;
    private My_PlayerCanvasScript my_PlayerCanvasScript;

    //playrのオブジェクトをstartで取得するとnullのエラーがでるのでこれを挟む
    //またmap移動したときにplayerも破棄されるので再度initするためのフラグにしている
    private bool first;

    // Start is called before the first frame update
    void Start()
    {
        first = false;
        my_PlayerManagerScript = this.gameObject.GetComponent<My_PlayerManagerScript>();
    }

    // Update is called once per frame
    void Update()
    {
        EventManagerInitProcess();
        if(player != null)
        {
            if(CheckPlayerStatusChange())
            {
                SetPlayerStatus();
            }
        }
    }

    public void EventManagerInitProcess() {
        if (first == false)
        {
            if (Camera.main != null)
            {
                player = my_PlayerManagerScript.GetPlayerObject();
                InitPlayeranvas(player);
                first = true;
            }
        }
        else if (first == true)
        {
            if (player == null)
            {
                Debug.Log("player lost");
                //Mapが切り替わっているので初期化を呼ぶためフラグをfalse
                first = false;
            }
        }
    }

    //playerのcanvasの初期化
    public void InitPlayeranvas(GameObject player) {
        //myPlayerCanvasuのプレハブを生成
        Canvas my_PlayercanvasPrefab = Instantiate(my_PlayerCanvas);
        //生成したプレハブをplayerの子オブジェクトにする
        my_PlayercanvasPrefab.transform.SetParent(player.transform, false);
        my_PlayerCanvasScript = my_PlayercanvasPrefab.GetComponent<My_PlayerCanvasScript>();

        //MaxHp,Hp設定
        my_PlayerCanvasScript.playerMaxHp = my_PlayerManagerScript.GetPlayerMaxHp();
        my_PlayerCanvasScript.playerHp = my_PlayerManagerScript.GetPlayerHp();

        //Mp設定
        my_PlayerCanvasScript.playerMaxMp = my_PlayerManagerScript.GetPlayerMaxMp();
        my_PlayerCanvasScript.playerMp = my_PlayerManagerScript.GetPlayerMp();
        //playerスライダーの初期化
        my_PlayerCanvasScript.InitPlayerSlider();
        Debug.Log("player init!");
    }

    
    // playerのステータスが変更になったタイミングを取得する関数
    public bool CheckPlayerStatusChange() {
        //playerのActor取得
        if (my_PlayerCanvasScript.playerMaxHp != my_PlayerManagerScript.GetPlayerMaxHp() || my_PlayerCanvasScript.playerHp != my_PlayerManagerScript.GetPlayerHp()
            || my_PlayerCanvasScript.playerMaxMp != my_PlayerManagerScript.GetPlayerMaxMp() || my_PlayerCanvasScript.playerMp != my_PlayerManagerScript.GetPlayerMp())
        {
            Debug.Log("change player status");
            return true;
        }
        return false;
    }

    //playerのステータスを再設定する関数
    public void SetPlayerStatus() {
        //MaxHp,Hp設定
        my_PlayerCanvasScript.playerMaxHp = my_PlayerManagerScript.GetPlayerMaxHp();
        my_PlayerCanvasScript.playerHp = my_PlayerManagerScript.GetPlayerHp();

        //Mp設定
        my_PlayerCanvasScript.playerMaxMp = my_PlayerManagerScript.GetPlayerMaxMp();
        my_PlayerCanvasScript.playerMp = my_PlayerManagerScript.GetPlayerMp();
        //取得したステータスを反映
        my_PlayerCanvasScript.setPlayerSlider();
    }
}
            

こちらも長いスクリプトなので解説してきます。まずは一番わけのわからないであろう変数firstの 役割です。このやり方は僕の苦肉の策であり、もっと多分うまいやり方とか、そもそもマップ移動の タイミングを取得するいい関数がソースにあると思いますが、僕は見つけれてないので以下の方法で 無理やり実装しました。この方法ではどうしてもobject生成のタイミングでnullのエラーが数回でて しまいます。ゲーム作成するならエラーは出してはいけませんが、もうこのエラーメッセージはでても 動くしビルドも出来たので、このまま突き進みます。ここはよりいい方法を是非考えてみてください

まずこのスクリプトの中のstartでは参照するスクリプトの取得とfisrtをfalseにしているだけです。
一般的に必要なスクリプトの初期化はこのstart関数で行いますが、ここで全ての初期化をすると うまくいきません。このスクリプトで必要な初期化はEventManagerInitProcess関数で行っています。
この関数ではメインでやっていることはplayerオブジェクトの取得です。前にの述べたとおり、 playerのGameObjectはMainCameraの親オブジェクトなので、それを利用して参照していますが、 この参照のタイミングの調整が難しいです。このスクリプトはMy_SceneManagerにアタッチしていますが、 My_SceneManagerの生成の後にplayerが生成されると、My_SceneManagerの生成時にstart関数が呼ばれたときに 参照先のplayerがないのでnullを返してしまいエラーとなります。
この参照先がありませんエラーは、プログラム初心者は今後も死ぬほど出くわすエラーです。 遥かむかしにヌルポという謎の言葉が2chあたりでよく出現していましたが、それはこのnull pointer refarence (プログラミング言語で違いはありますが)のことらしいです。
UnityのStart関数は初期化に使用する関数なので生成後は1度しか呼ばれないので、参照に1度失敗するともう playerの参照を辞めてしまい、ゲームが起動できなくなります。ですので、EventManagerInitProcessは update関数で呼びます。この時必要なplayerを取得できるまでコールして出来たら初期化処理は 終了したいので、firstがfalseの時にplayerを見つけるまでコールして、見つけたらfalseをtrueにして コールを終了にしています。そしてもう一つポイントがあります、それはUniteではMap移動の時には sceneは移動してません。マップ移動が起こるとマップのプレハブやplayer,Event等は全て破棄されて、 移動先のmapやplayer、EVENTが再度作られる仕様です。このMap移動の時も再度EventManagerProcessは コールされる必要があるので、playerを監視して、playerがnullになったとき(Map移動が起きた時)に firstをfalseにして再度EventManagerProcessをコールして初期化をしているのです。

残りのupdate内の処理はplayerが存在してかつplayerのステータスが変更になったタイミングを監視して、 変更になっていれば、変更後のステータスを取得してステータスバーに反映させています。そうしないと updateで不要にずっとステータスの取得とステータスバーの書き換えがコールされるので処理が重く なってしまいます。

さて、スクリプトの説明は終わったので以下の画像を参照にインスペクターの設定をしましょう。

photo

まずはMy_PlayerCanvasScriptMy_PlayerCanvasにアタッチして上記画像で赤のlineの各sliderを 以下の画像のようにアタッチしたらプレハブ化してください。

photo

そして、sceneをSceneMapにしてMy_SceneMapManagerオブジェクトMy_EventManagerスクリプトをアタッチして 以下の画像を参考にして先ほどプレハブしたMy_PlayerCanvasをインスペクターからアタッチしてください。

photo

上記が完成したらゲームを起動してみてください。ステータスバーはplayerと連携して動くはずです、この時 ステータスバーの位置や大きさが不自然なら調整してちょうどいい値を見つけたら、ゲーム終了して、その値 に再調整してください。

ここで気が付くと思いますが、HPにエラーがあります。MaxHPは237なのにHpは235なのでスライダーのhpバーの 右側が少し赤いですよね。このエラーはソースをみてもよくわかりませんので解消できていません。でも 気になる。。。どうやって解消しましょうか??こういったエラーをつぶすのは苦痛な人もいると思いますが、 僕は結構好きです、プログラムのエラーも出るともちろんストレスですが、まぁどうせでるのでとあきらめています。 そして解消できたときのスッキリ感が好きなんですよね。。余談ですが
話はもどりますが、このエラーは根本的な解消法はわかりませんでした、がしかし後述しますが、Uniteは オープニングを作るときはオープニング用のマップを作成してオープニングイベントを作るのですが、 このオープニングマップで自動実行に設定したイベントでゲームの初期設定をしていきます。なので今回の場合は オープニングで一度キャラクターの全回復イベントをしておけば、ゲームスタートしたあとのMaxHpとHpは 同じ値になって見かけ上はエラーは解消できます。(もしろん根本的な解決ではありません。)

Eventをマップに設定してplayerのHpを実際に増減させて挙動を確認

さてそれではコードから少しはなれてUniteの操作をしていきます。 イベントの操作のやり方です。このUniteの操作のやり方はRPGMakerUniteの公式の初心者講座を 一通りやることを強く強くお勧めします。これをやると大体のイベントの作成やり方やマップの作成の 仕方は一通り分かると思います。
Uniteの操作自体の説明は初心者講座をみた前提で説明していきます。それでは触れるとダメージを受けるイベントを作成しましょう。

photo

上記画像を参考にしながら、MAP001(これが初めからある簡易的なMAPです)のイベントをクリックしてください。

photo

すると上記のようにイベントを設定するためのMAPが出てくるので、イベントを設置したいところをクリックして イベントを設定してください。イベントの設定部分の画像は以下に示します。ダメージの処理は処理の後にウェイト を入れないと、触れた瞬間にplayerのHPは1になってしまうので注意が必要です。

photo
photo
photo

さてこれで準備ができました!!ゲームを開始してみてください。ダメージイベントに接触するとちゃんとHPバーが 減っていきますね。このほかにイベントからダメージ回復MAP移動も作成してみてください。 MAP移動はイベント設置時に右クリックして簡易イベントから簡単に作成できます。これでMAP移動してみても コンソールにplayer initが再度表示されている、つまりマップ移動にも対応できていることがわかると思います。

photo

これでplayerステータスバーは完成ですが、このままですとスライダーがinteractiveになっているため、 ゲームの操作によるマウス操作や矢印キーの操作によりスライダーが反応してしまうので、以下の画像を参考 にしてinteractiveの設定をOFFにしてください。

photo

さて、これでだんだんアクションRPGっぽくなってきましたね、プレイヤーはダメージを負う事ができるように なり、それが自分で作ったHPバーにきちんと反映されています!
さてpart2も大分長くなったのでここで終了です。次回part3で今度は敵を作成していきます。 敵の作成時に各敵それぞれにHPを設定する方法、そしてそれを敵のHPバーに反映させる方法、そして余裕があれば、 そのまま待望の攻撃の当たり判定を作成していきます。part3でここまで作成する予定でしたが、大分ながくなるので part4になるかもしれません。すいません。

それではここまで読んでいいただいて本当にありがとうございました!!誰かの役に少しでもたつことを願って おります!!それではpart3でまた会いましょう!!

ブログ一覧へ戻る