おーみんブログ

C#, ASP.NET Core, Unityが大好きです。

Unityの学習まとめとして超簡単なRPGを作ってみた!

はじめに

普段はWebアプリ関連のお仕事をしているのですが、「久しぶりにUnity触りたい!」という気持ちになったので約2週間程度ではありますが集中的にUnityを勉強して超簡単なRPGを作ってみました。 (一応2年半前にも一度Unity熱が上がり、少し勉強していた時期はあるので今回が完全に初見という感じではありません。)

unityroom.com

ゲームの内容

ゲームの内容としては、単純にドラゴンを倒して村を救うぞ!という感じの王道なやつです。 以下に作ったゲームの簡単な解説を記載していきます。

ゲームスタート画面

ゲームスタート!.png

プレイヤーのパワーアップイベント

簡単なオープニングが終わった後、フィールドへプレイヤーが配置されます。 さあ、ボスであるドラゴンを倒しにいくぞ!と言いたいところですが、いきなりボス戦というのも何かなぁ...と思ったので一度村に行って装備を最新にするというイベントを挟む必要があるようにしてみました。(村に行かなくてもドラゴンとは戦えますが確実に負けるようになっています。)

ポツンと存在する村 フィールド.png

村にてパワーアップイベント パワーアップイベント.png

村でのパワーアップイベント後にはHPや攻撃力が増えています。 ※フィールドではランダムでスライムが出てきます。

パワーアップ前

戦闘.png

パワーアップ後

パワーアップ後戦闘.png

ドラゴン戦(ボス戦)

森を駆け巡り、ドラゴン(ボス戦)へ辿り着きます。 ドラゴン戦.png

エンディング

無事に倒したら簡単なエンディングが始まり、ゲーム完了となります。 ゲームクリア.png

実装内容

ここからは簡単に実装を振り返ってみたいと思います。

フィールドでの敵とのエンカウント

エンカウントについては以下の2つの種類があるかと思います。

ランダムエンカウント →フィールドを歩いていたら一定確率で敵と遭遇(止まっているときは遭遇しない)

シンボルエンカウント →敵があらかじめ見えており、戦いたい敵をある程度選べる

今回はファイナルファンタジーのようなエンカウントをイメージしていたので前者のランダムエンカウント方式にしてみました。

ロジックは色々と考えてみたのですが、単純にプレイヤーが動いている かつ ランダムで特定の数字が出るという内容にしてみました。

var playerSpeed = Player.GetComponent<Animator>().GetFloat("Speed");
var RateEncount = Random.Range(0, 1200);
if(playerSpeed > 1.0f && RateEncount == 50)
{
    PlayerFieldLocation = Player.transform.position;  // 位置保存
    Player.GetComponent<PlayerController>().MoveSpeed = 0;  // シーン遷移中(フェードイン中)は動かないようにする。
    fade.FadeIn(2.0f, () =>
    {
        // 戦闘シーンへの遷移処理
    });
}

シーン遷移のトランジション

敵とエンカウントした際は戦闘シーンに移るかと思いますが、ただフェードインするだけ、というのも物足りないので渦を巻くトランジションを入れてみました。 敵とエンカウントすると以下のようにトランジションが出てフェードインします。 シーン遷移.png

こちらも特に凝ったプログラムを書いているわけではなく、tsubaki_t1さんが作成してくれたトランジション用のライブラリとFor youさんが提供してくれているルール画像を利用することですんなりと作成できました。

トランジションを使った綺麗な画面切替・場面転換 - テラシュールブログ

For You | ぽけっとの中のいたち

ターン制バトル

敵とエンカウントした場合はバトルシーンに移り、ターン制のバトルが始まります。 ターン制のロジックについては「ゲージがたまったら攻撃アクションを選択できる」ようにしました。 「ゲージをためる」「攻撃アクションを選択後の動作」についてはそれぞれ以下のようにプログラムを書きました。

ゲージをためる

private void FixedUpdate()
{
    // 省略
    
    // ゲージはUnityEngine.UIのスライダーを用い、一定量ずつ増加します。
    playerSlider.value += 0.004f;

    // ゲージがたまり、まだ戦闘アクションが選択されていなかったらパネル表示をします。
    if(playerSlider.value == 1 && !AttackButtonController.HasPushButton)
    {
        PlayerPanel.SetActive(true);
    }
}

攻撃アクションを選択後の動作

public void PushAttackButton()
{
    HasPushButton = true;
    Panel.SetActive(false);  // 攻撃アクション選択後はパネルを非表示にする。

    var attack = Random.Range(PlayerManager.Attack - 15, PlayerManager.Attack + 15);  // 与えるダメージは攻撃力の±15
    
    // 攻撃内容が決まったゲームオブジェクトと与えるダメージをDictionaryで管理
    // 敵も同様の実装を行う
    BattleManager.TurnOrders.Add(Player, attack);  
}
void Update()
{
    // プレイヤーも敵も攻撃情報がない場合は何もバトル処理を行わない
    if (!TurnOrders.Any()) return;

    // 省略

    // バトルターン順に攻撃を行っていく。
    var turnCharactor = TurnOrders.First();
    if (!IsDuringMotion)
    {
        // 省略

        StartCoroutine(DelayCoroutine(0.4f, () =>
        {
            turnCharactor.Key.GetComponent<Animator>().SetTrigger("Attack");
        }));
    }
}

会話イベント

RPGにかかわらず、ゲームにおいて会話イベントを必要とする場面は多いと思いますが、こちらはFungusというとても便利なアセットがあったのでこちらを利用しました。

Fungus

あくまで例ではありますが、村での会話シーンは以下のように設定しています。 会話イベント.png

基本的にはUI上でセリフを設定したり、選択UIも設定できます。状況によってセリフを変更したい場合はif文も入れ込むことができるのでとても便利です。 もしプログラムを実行したいときはCall Methodを選択することでプログラム側のメソッドを呼び出すことも可能です。

おわりに

すごく久しぶりにUnityを触ってみましたが、やはりUnityを使うとサクッとゲームが作れるのでとても便利ですね! 無料のアセットも充実していたので、アニメーションや村作成についてはすごく助かりました。

今後の展望としては、パーティーを組んだり攻撃以外のアクション(魔法やアイテム使用、レベルアップなど)も入れていきたいです。