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

ゲームの流れは、今回も同じです。ステージの中に、「よけないとやり直し」という緊張感を足します。
- 迷路のまん中の通路に、左右に行ったり来たりするおばけを1体置く
- おばけに当たると、画面が白く光ってゆれ、スライムちゃんが痛がって止まる
- 1秒たったら、白から暗くなって、ステージの最初からやり直し(集めた星も元に戻る)
作るスクリプトは3つ、前回までのスクリプトを1つ書き足します。
| スクリプト | 役割 | 付ける場所 |
|---|---|---|
| Ghost | 2つの場所の間を、行ったり来たりする | おばけ |
| ScreenShake | 画面を少しの間ゆらす | Main Camera |
| PlayerDamage | おばけに当たったら、演出をしてからやり直す | スライムちゃん(Player) |
| ScreenFlash(#2で作ったもの) | 一瞬だけ白く光らせる処理を足す | Flash |
おばけを置こう

まずは、おばけを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スクリプトを作ろう

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の中で呼ぶ
- Rigidbody 2Dを、その位置へ動かす。物理の計算といっしょに動くので、
GhostにGhostスクリプトを付けたら(Move Offset:4, 0、Move Speed:2のまま)、Prefabsフォルダにドラッグしてプレハブにします。今回はHierarchyのほうも、そのまま置いておきます。
再生すると、おばけが通路を行ったり来たりします。

スライムおばけさん、ちょっとかわいい顔してますね・・・。でも、当たったらダメなんですよね?
当たったときの演出を作ろう


おばけに当たったとき、すぐにやり直しになると、何が起きたのか分かりません。そこで、当たった瞬間に「光る・ゆれる・痛がる」を一度に見せて、「当たった!」をはっきり伝えます。
先に、光らせる処理とゆらす処理を作っておきます。
一瞬だけ白く光らせよう
#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倍)で戻す
- #2で作った、色をだんだん変える処理。少し透ける白(Aが0.7)から、透明な白へ0.3秒(
- 作ってあった処理に、入れる色を変えて使い回すので、書き足すのはこれだけです
画面をゆらそう


画面をゆらすには、カメラを少しずつずらします。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をかけて、カメラをずらす
- 半径1の円の中から、でたらめな点を1つ選ぶ。これに
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にしているのはそのためです)。
当たったらやり直そう


最後に、おばけに当たったときの処理です。光らせて、ゆらして、痛がる絵にしたら、1秒待ってからシーンを読み込み直します。
シーンを読み込み直すと、スライムちゃんの位置も、集めた星も、全部最初の状態に戻ります。「やり直し」のために、何かを元に戻すコードを書かなくてよいのが、この作り方のいいところです。
PlayerDamageを作ろう


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に渡して、同じシーンを読み込み直す
- 今のシーンの名前(Stage1)。これを
当たり判定のイベント(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()を呼んだところでエラーになり、そこで処理が止まってしまいます。
再生して、わざとおばけに当たってみましょう。





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


Titleシーンを開いて再生し、最初から最後まで通して遊びます。わざと1回当たってから、次はよけてクリアしてみましょう。
- まん中の通路で、おばけが左右に行ったり来たりしている
- おばけに当たると、画面が白く光ってゆれ、スライムちゃんが痛がって止まる
- 1秒ぐらいたつと、白から暗くなって、ステージの最初からやり直しになる(星も「のこり 3」に戻る)
- おばけをよけて星を3つ集めると、扉が開く。入るとエンディングへ。「タイトルへ」でタイトルに戻る
おばけが通路の端にいるうちに、反対側を通り抜けるのがコツです。やり直しがあっても、エンディングまでちゃんと遊べることを確かめたら、今回も完成です!
ビルドして遊ぼう


今回も、ビルドして.exeで遊びます。
- Stage1シーンを保存(Ctrl+S)してから、File>Build Profilesを開いてBuild
- 前回と同じ
Build/StarGardenのフォルダを選ぶ(上書きしてOK) - 出来たStarGarden.exeで、おばけをよけて扉から帰れるか遊んでみる
あとがき
今回は、行ったり来たりするおばけと、当たったときのやり直しを作りました。光る・ゆれる・痛がるの3つを同時に出すだけで、「当たった!」がぐっと伝わるようになります。やり直しは、シーンを読み込み直すだけ。ステージを1つのシーンにしておいたおかげで、元に戻す処理を書かずに済みました。
次回は、ライフとゲームオーバーを作ります。何回でもやり直せる今の形から、3回当たったらゲームオーバー、という形にしますよ!



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



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