絶対に完成させるゲーム制作#5|おばけをよけよう

迷路のまん中の通路で、スライムちゃんが行ったり来たりするおばけにぶつかる。画面が白く光ってゆれ、スライムちゃんが痛がって止まり、白から暗くなって、ステージの最初の位置からやり直しになる

前回は、星を3つ集めると扉が開くようにして、「集めて、帰る」というゲームの遊びがそろいました。でも、今のステージには邪魔をするものが何もないので、ゆっくり歩けば誰でもクリア出来てしまいます・・・。今回は、迷路の中におばけを出します。おばけに当たったら、ステージの最初からやり直し。当たったときに、画面が光ってゆれる演出も付けますよ!

目次

今回作るもの

今回のゲームの流れ。「タイトル」から「はじめる」で「ステージ」へ、「扉に入る」で「エンディング」へ、「タイトルへ」でタイトルに戻る。流れは前回と同じで、「ステージ」の中におばけと、当たったらやり直しを足す

ゲームの流れは、今回も同じです。ステージの中に、「よけないとやり直し」という緊張感を足します。

  • 迷路のまん中の通路に、左右に行ったり来たりするおばけを1体置く
  • おばけに当たると、画面が白く光ってゆれ、スライムちゃんが痛がって止まる
  • 1秒たったら、白から暗くなって、ステージの最初からやり直し(集めた星も元に戻る)

作るスクリプトは3つ、前回までのスクリプトを1つ書き足します。

スクリプト役割付ける場所
Ghost2つの場所の間を、行ったり来たりするおばけ
ScreenShake画面を少しの間ゆらすMain Camera
PlayerDamageおばけに当たったら、演出をしてからやり直すスライムちゃん(Player)
ScreenFlash(#2で作ったもの)一瞬だけ白く光らせる処理を足すFlash

おばけを置こう

Stage1シーンを開いたエディタ。Hierarchyのいちばん下にGhost。Gameビューで、迷路のまん中の通路の左側におばけがいる。InspectorはGhostで、Positionが-3, 0, 0、Sprite RendererのOrder in Layerが2、Rigidbody 2DのBody TypeがKinematic

まずは、おばけを1体作って、プレハブにします。

  • Sprites/EnemiesのghostをHierarchyにドラッグ&ドロップして、名前をGhostにする
    • Position:(-3, 0, 0)
    • Sprite RendererのOrder in Layer:2(星より手前、スライムちゃん(3)より奥。重なったときも、スライムちゃんが隠れないように)
  • Add ComponentでRigidbody 2Dを付けて、Body Type:Kinematicにする
  • Add ComponentでCircle Collider 2Dを付ける
    • Is Triggerにチェック、Radius:0.4

おばけは、迷路のまん中の通路(左の縦の壁と、右の縦の壁の間)を、左右に行ったり来たりさせます。スライムちゃんは、扉へ行くとちゅうで、この通路を上から下へ通り抜けないといけません。

Kinematicは、「重力やぶつかった力では動かず、スクリプトで動かす」Rigidbody 2Dです。おばけは決まった道を動くだけなので、押されて動いては困ります。それでも動くものにRigidbody 2Dを付けておくと、当たり判定がきちんと計算されます。ColliderとRigidbodyの関係は、3-4 当たり判定とPhysics:ColliderとRigidbodyで紹介しています。

Ghostスクリプトを作ろう

GhostのInspector。Rigidbody 2DのBody TypeがKinematic、Circle Collider 2DのIs Triggerにチェック、Radiusが0.4。GhostのMove Offsetが4, 0、Move Speedが2

ScriptsフォルダにGhostを作ります。

using UnityEngine;

// おばけ:置いた位置と、そこから moveOffset だけずれた位置の間を、行ったり来たりする
public class Ghost : MonoBehaviour
{
    [SerializeField] private Vector2 moveOffset = new Vector2(4f, 0f); // 行き先(置いた位置からのずれ)
    [SerializeField] private float moveSpeed = 2f;                      // 1秒に進むマスの数

    private Rigidbody2D rb;
    private Vector2 startPosition;
    private float timer;

    private void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        startPosition = rb.position;
    }

    private void FixedUpdate()
    {
        timer += Time.fixedDeltaTime;
        float distance = moveOffset.magnitude;
        // 0 → distance → 0 → … と行ったり来たりする数を、0〜1の割合にする
        float rate = Mathf.PingPong(timer * moveSpeed, distance) / distance;
        rb.MovePosition(startPosition + moveOffset * rate);
    }
}
  • moveOffset
    • 置いた位置から、どれだけずれた所まで行くか。(4, 0)なら右に4マス。(0, 3)にすれば上下に動くおばけになる
  • Mathf.PingPong(数, 長さ)
    • 数が増えていくと、0から長さまで上がって、また0へ下がる…をくり返す。行ったり来たりの動きが1行で書けます
  • rb.MovePosition(位置)
    • Rigidbody 2Dを、その位置へ動かす。物理の計算といっしょに動くので、FixedUpdateの中で呼ぶ

時間はTime.time(ゲームを始めてからの時間)を使わずに、timerで自分で数えています。あとで「当たったらシーンを読み込み直す」ようにしたとき、Time.timeだと読み込み直しても時間が続いてしまい、おばけがとちゅうの位置から動き出すためです。

GhostにGhostスクリプトを付けたら(Move Offset:4, 0、Move Speed:2のまま)、Prefabsフォルダにドラッグしてプレハブにします。今回はHierarchyのほうも、そのまま置いておきます。

再生すると、おばけが通路を行ったり来たりします。

迷路のまん中の通路で、おばけが左の壁ぎわから右の壁ぎわまで動いて、また左へ戻る
スライム

おばけさん、ちょっとかわいい顔してますね・・・。でも、当たったらダメなんですよね?

当たったときの演出を作ろう

おばけに当たる瞬間を3コマ並べたもの。スライムちゃんがおばけに重なる → 画面が白く光ってゆれる → 痛がる絵になって止まる

おばけに当たったとき、すぐにやり直しになると、何が起きたのか分かりません。そこで、当たった瞬間に「光る・ゆれる・痛がる」を一度に見せて、「当たった!」をはっきり伝えます。

先に、光らせる処理とゆらす処理を作っておきます。

一瞬だけ白く光らせよう

#2で作ったScreenFlashスクリプトに、Flash()を書き足します。FlashAndLoadRoutineの下(ChangeColorの上)に入れてください。

    // 一瞬だけ白く光らせる(シーンは切り替えない)
    public void Flash()
    {
        StartCoroutine(ChangeColor(new Color(1f, 1f, 1f, 0.7f), new Color(1f, 1f, 1f, 0f), flashSeconds * 2f));
    }
  • ChangeColor(…)
    • #2で作った、色をだんだん変える処理。少し透ける白(Aが0.7)から、透明な白へ0.3秒(flashSecondsの2倍)で戻す
  • 作ってあった処理に、入れる色を変えて使い回すので、書き足すのはこれだけです

画面をゆらそう

Main CameraのInspector。TransformのPositionが0, 0, -10。Screen ShakeのShake Secondsが0.3、Strengthが0.2

画面をゆらすには、カメラを少しずつずらします。ScriptsフォルダにScreenShakeを作ります。

using System.Collections;
using UnityEngine;

// カメラに付けて、画面を少しの間ゆらす
public class ScreenShake : MonoBehaviour
{
    [SerializeField] private float shakeSeconds = 0.3f; // ゆれている時間
    [SerializeField] private float strength = 0.2f;     // ゆれの大きさ(マス)

    private Vector3 basePosition;

    private void Start()
    {
        basePosition = transform.position;
    }

    public void Shake()
    {
        StopAllCoroutines();
        StartCoroutine(ShakeRoutine());
    }

    private IEnumerator ShakeRoutine()
    {
        float time = 0f;
        while (time < shakeSeconds)
        {
            time += Time.deltaTime;
            // だんだん弱くしながら、毎フレームでたらめな方向にずらす
            float power = strength * (1f - time / shakeSeconds);
            Vector2 offset = Random.insideUnitCircle * power;
            transform.position = basePosition + new Vector3(offset.x, offset.y, 0f);
            yield return null;
        }
        transform.position = basePosition;
    }
}
  • Random.insideUnitCircle
    • 半径1の円の中から、でたらめな点を1つ選ぶ。これにpowerをかけて、カメラをずらす
  • 1f - time / shakeSeconds
    • 最初は1、最後は0になる数。ゆれをだんだん弱くして、自然に止まるようにする
  • transform.position = basePosition
    • ゆれ終わったら、元の位置にぴったり戻す

HierarchyのMain CameraにScreenShakeを付けます(Shake Seconds:0.3、Strength:0.2のまま)。

カメラのPositionのZは-10のままにしておきましょう。ゆらすのはXとYだけです。Zまで動かすと、カメラが絵の後ろに回って、何も映らなくなることがあります(上のコードで、ずらす量をnew Vector3(offset.x, offset.y, 0f)とZだけ0にしているのはそのためです)。

当たったらやり直そう

やり直しの流れを3コマ並べたもの。おばけの横でスライムちゃんが痛がっている(左上は「のこり 2」)→ 画面がまっ暗になる → ステージの最初の位置にスライムちゃんが戻り、星も3つに戻って「のこり 3」

最後に、おばけに当たったときの処理です。光らせて、ゆらして、痛がる絵にしたら、1秒待ってからシーンを読み込み直します。

シーンを読み込み直すと、スライムちゃんの位置も、集めた星も、全部最初の状態に戻ります。「やり直し」のために、何かを元に戻すコードを書かなくてよいのが、この作り方のいいところです。

PlayerDamageを作ろう

PlayerのInspectorで、Player Damageを開いたところ。Damage Spriteがslime_damage_22、Screen FlashにFlash、Screen ShakeにMain Camera、Wait Secondsが1

ScriptsフォルダにPlayerDamageを作ります。

using System.Collections;
using UnityEngine;
using UnityEngine.SceneManagement;

// おばけに当たったら、光ってゆれて痛がってから、ステージを最初からやり直す
public class PlayerDamage : MonoBehaviour
{
    [SerializeField] private Sprite damageSprite;     // 痛がる絵
    [SerializeField] private ScreenFlash screenFlash; // 画面を光らせる
    [SerializeField] private ScreenShake screenShake; // 画面をゆらす
    [SerializeField] private float waitSeconds = 1f;  // 痛がってから、やり直すまでの時間

    private bool isHit; // もう当たったか(2回当たらないように)

    private void OnTriggerEnter2D(Collider2D other)
    {
        if (isHit)
        {
            return;
        }
        if (other.GetComponent<Ghost>() == null)
        {
            return;
        }
        StartCoroutine(HitRoutine());
    }

    private IEnumerator HitRoutine()
    {
        isHit = true;
        // 動きとコマ送りを止めて、痛がる絵にする
        GetComponent<PlayerMove>().Stop();
        GetComponent<PlayerFacing>().enabled = false;
        GetComponent<FrameAnimation>().enabled = false;
        GetComponent<SpriteRenderer>().sprite = damageSprite;
        screenFlash.Flash();
        screenShake.Shake();
        yield return new WaitForSeconds(waitSeconds);
        // 今のシーンを読み込み直して、最初からやり直す
        screenFlash.FlashAndLoad(SceneManager.GetActiveScene().name);
    }
}
  • other.GetComponent<Ghost>() == null
    • 重なった相手がおばけでなければ(星や扉なら)、何もしない。#2の扉と同じ見分け方
  • PlayerFacingとFrameAnimationを止める
    • 止めないと、すぐに歩くコマに戻されて、痛がる絵が見えない
  • yield return new WaitForSeconds(waitSeconds)
    • 1秒待つ。すぐにやり直さず、少し止まる時間があることで、当たったことが伝わる
  • SceneManager.GetActiveScene().name
    • 今のシーンの名前(Stage1)。これをFlashAndLoadに渡して、同じシーンを読み込み直す

当たり判定のイベント(OnTriggerEnter2D)は6-10 衝突を検知しよう:当たり判定のイベントとタグ、待つ処理(コルーチン)は6-11 時間を扱う処理:コルーチン・Invoke・一時停止で紹介しています。

HierarchyのPlayerにPlayerDamageを付けて、3つの欄を入れます。

  • Damage Sprite:Sprites/Characters/Slimeのslime_damage_22
  • Screen Flash:HierarchyのCanvasの子のFlashをドラッグ
  • Screen Shake:HierarchyのMain Cameraをドラッグ

当たったのに何も起きないときは、Consoleにエラーが出ていないか見てみましょう。Screen FlashやScreen Shakeの欄が空のままだと、Flash()やShake()を呼んだところでエラーになり、そこで処理が止まってしまいます。

再生して、わざとおばけに当たってみましょう。

おばけの通る道でスライムちゃんが立ち止まっていると、おばけが重なる。画面が白く光ってゆれ、スライムちゃんが痛がる絵になって止まる
スライム

いたたた・・・!でも、ちゃんと光ってゆれるので、当たったのがすぐ分かりますね

完成チェック

タイトルでEnterを押すとステージへ。星を1つ取ってまん中の通路に下りたところで、おばけに当たって光ってゆれ、最初からやり直しになる。2回目はおばけが左にいるうちに通り抜けて、星を3つ集め、開いた扉に入ってエンディングへ。Enterで「タイトルへ」を押すとタイトルに戻る。右下に押したキーが出る

Titleシーンを開いて再生し、最初から最後まで通して遊びます。わざと1回当たってから、次はよけてクリアしてみましょう。

  • まん中の通路で、おばけが左右に行ったり来たりしている
  • おばけに当たると、画面が白く光ってゆれ、スライムちゃんが痛がって止まる
  • 1秒ぐらいたつと、白から暗くなって、ステージの最初からやり直しになる(星も「のこり 3」に戻る)
  • おばけをよけて星を3つ集めると、扉が開く。入るとエンディングへ。「タイトルへ」でタイトルに戻る

おばけが通路の端にいるうちに、反対側を通り抜けるのがコツです。やり直しがあっても、エンディングまでちゃんと遊べることを確かめたら、今回も完成です!

ビルドして遊ぼう

Build ProfilesでWindowsがActive。Scene ListにTitle・Stage1・Endingの3つが入っていて、右下にBuildのボタン

今回も、ビルドして.exeで遊びます。

  • Stage1シーンを保存(Ctrl+S)してから、File>Build Profilesを開いてBuild
  • 前回と同じBuild/StarGardenのフォルダを選ぶ(上書きしてOK)
  • 出来たStarGarden.exeで、おばけをよけて扉から帰れるか遊んでみる

あとがき

今回は、行ったり来たりするおばけと、当たったときのやり直しを作りました。光る・ゆれる・痛がるの3つを同時に出すだけで、「当たった!」がぐっと伝わるようになります。やり直しは、シーンを読み込み直すだけ。ステージを1つのシーンにしておいたおかげで、元に戻す処理を書かずに済みました。

次回は、ライフとゲームオーバーを作ります。何回でもやり直せる今の形から、3回当たったらゲームオーバー、という形にしますよ!

スライム

何回当たっても最初からやり直せるなら、こわくないですね!

フェアリー

次はライフが3つだけになるからね。おばけの動きをよく見て、よけるのに慣れておいてね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次