【Unity(C#)】覚えゲーのリトライポイント実装


覚えゲー

覚えゲー(おぼえゲー)は、コンピュータゲームの分類や評論用語の一つ。ゲームに現れる規則性・法則性、およびそれに基づく攻略手順(パターン)を覚え、その通りにプレイすることで楽に攻略できるゲームのこと。パターンゲーともいう。

【引用元】:Wikipedia

何度もリトライして敵の配置などを覚えることが
クリアの前提条件となっているゲームのことです。

unity1weekに参加した際に開発したゲームもいわゆる覚えゲーです。
下記GIFのように(少し乱れてますが)先が見えないところに突っ込んでいくので
失敗が前提の作りというわけです。

【投稿した作品】:MAGNET MONSTER

リトライポイント

覚えゲーでよくあるのが、ユーザーのストレスがギリギリ耐えられる場所
復活するポイントを設けることです。

これを私はかっこつけてリトライポイントと呼んでいます。

今回の参加作品は開発者自身がデバッグしていて、
イライラしてくるような難易度設計に仕上がっていたので
リトライポイントは必須でした。

意外とすんなり実装できたので(最適解かどうかはわからない)
メモしておこうと思います。

Prefab

リトライポイントとして必要なのは座標なので
1ステージに複数個、オブジェクトを配置する方法をとります。

今回の実装だとその参照したい座標を持ったオブジェクトに
いろいろと下準備が必要なので毎回行わなくてよいように
Prefab化しておきます。

設定と言ってもやることとしては
Layerの設定、当たり判定の設定(今回はIsTrigger)くらいです。

設定が完了したら任意のリトライポイントに配置します。

コード

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

/// <summary>
/// キャラクターの衝突に応じた処理 キャラクターにアタッチ
/// </summary>
public class CharacterCollisionFunction : MonoBehaviour
{
    private List<Transform> _retryPointList = new List<Transform>();

    private void OnTriggerEnter(Collider other)
    {
        //リトライエリアをリストに追加
        if (other.gameObject.layer == LayerMask.NameToLayer("RetryPoint"))
        {
            _retryPointList.Add(other.gameObject.transform);
        }
    }

    private void OnCollisionEnter(Collision other)
    {
        //敵に接触
        if (other.gameObject.layer == LayerMask.NameToLayer("Enemy"))
        {
            //座標を戻す
            this.gameObject.transform.position = _retryPointList.Last().position;

            //その他、フェードや諸々の設定を元に戻すなどのリトライ処理
            //元に戻したい処理が増えてくるなら、
            //Destroyして、Instantiateするってのもありかも
        }
    }
}

キャラクター側で当たったオブジェクトの判定をさせる構造は
あまりよろしくなくて、下記リンクのように実装する方がすっきりします。

【参考リンク】:GetComponentを使うときはインターフェースを使おう

まあ、それは今回本題から反れるので置いておきます。

Enumerable.Last

LINQの拡張メソッドの一つで、最後の要素を取り出すことができます。
リトライポイントは基本的に最後の通過ポイント以外は使わないので、
リストの最後の要素を取り出すLastは相性が良いです。

【参考リンク】:Firstメソッド・Lastメソッドを使って


2020/05/07 追記

コメントでLinqを使わない方法を教えて頂いたのでメモしときます!
ありがとうございます!

using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// キャラクターの衝突に応じた処理 キャラクターにアタッチ
/// </summary>
public class CharacterCollisionFunction : MonoBehaviour
{
    private Stack<Transform> _retryPointStack = new Stack<Transform>();

    private void OnTriggerEnter(Collider other)
    {
        //リトライエリアをスタックに追加
        if (other.gameObject.layer == LayerMask.NameToLayer("RetryPoint"))
        {
            _retryPointStack.Push(other.gameObject.transform);
        }
    }

    private void OnCollisionEnter(Collision other)
    {
        //敵に接触
        if (other.gameObject.layer == LayerMask.NameToLayer("Enemy"))
        {
            //座標を戻す
            this.gameObject.transform.position = _retryPointStack.Peek().position;

            //その他、フェードや諸々の設定を元に戻すなどのリトライ処理
            //元に戻したい処理が増えてくるなら、
            //Destroyして、Instantiateするってのもありかも
        }
    }
}

最後に

時間がなかったというのを言い訳に
あまり調べることなく思い付きで行ったオレオレ実装です。

もっとスマートなやり方あればコメントください。