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

##覚えゲー

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

【引用元】：[Wikipedia](https://ja.wikipedia.org/wiki/%E8%A6%9A%E3%81%88%E3%82%B2%E3%83%BC)

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

unity1weekに参加した際に開発したゲームもいわゆる覚えゲーです。
下記GIFのように(少し乱れてますが)先が見えないところに突っ込んでいくので
失敗が前提の作りというわけです。
![Mitsu6.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/d4750f77-bdb4-1c14-5ee0-aa8f78d0c585.gif)


【投稿した作品】：[MAGNET MONSTER](https://unityroom.com/games/kento_magnet)

##リトライポイント

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

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

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

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

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

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

<img width="269" alt="Retry.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/43ff579e-d033-ca55-c015-d466f93c5427.png">


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

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

##コード

```C#
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を使うときはインターフェースを使おう](https://qiita.com/Akira_Kido_N/items/7cd18944173cd4cd7229)

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

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

【参考リンク】：[Firstメソッド・Lastメソッドを使って](https://smdn.jp/programming/dotnet-samplecodes/collections/First%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%83%BBLast%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6List%E3%81%AE%E6%9C%80%E5%88%9D%E3%81%AE%E8%A6%81%E7%B4%A0%E3%83%BB%E6%9C%80%E5%BE%8C%E3%81%AE%E8%A6%81%E7%B4%A0%E3%82%92%E5%8F%82%E7%85%A7%E3%81%99%E3%82%8B.csharp.xhtml)

---
2020/05/07　追記

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

```C#
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するってのもありかも
        }
    }
}
```

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

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