読者です 読者をやめる 読者になる 読者になる

遊べるゲームを作りたい

作りたいです。

Unityで自作ゲームを作る レーダー機能を実装

f:id:hogemarunosuke:20170131011144p:plain

遊び方

WASDキーで移動。スペースキーでジャンプ。マウスで視点移動。
左クリックで射撃。右クリックでロックオンモード。

レーダー機能(ミニマップ)追加しました。
これで理不尽な不意打ちでムキムキすることがなくなるはず!

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

public class Radar : BaseBehaviour
{
    public GameObject enemyIconPrefab;              // レーダー用の敵アイコン
    public float destroyUnusedIconInterval = 5.0f;  // 未使用アイコンの破棄間隔

    private GameObject player;            // プレイヤー
    private GameObject radarViewAngle;              // レーダーの方位を示す画像
    private GameObject freeLookCameraRig;           // StandardAssetsのアレ
    private float detectionRange = 0;               // 敵検出範囲
    private float radarRadius = 0;                  // レーダー範囲(半径)
    private List<GameObject> enemyIconCollection;   // オブジェクトプール用

    // Use this for initialization
    private void Start()
    {
        player = GameObject.FindGameObjectWithTag("Player");
        radarViewAngle = GameObject.Find("RadarViewAngle");
        freeLookCameraRig = GameObject.Find("FreeLookCameraRig");

        enemyIconCollection = new List<GameObject>();

        // レーダー半径を取得
        radarRadius = GetComponent<RectTransform>().sizeDelta.x / 2.0f;

        // 非表示になっているプロットを定期的に破棄する
        StartCoroutine(DestroyUnusedIcon(destroyUnusedIconInterval));
    }

    // Update is called once per frame
    private void Update()
    {
        // 角度更新
        radarViewAngle.transform.rotation =
            Quaternion.Euler(0, 0, -freeLookCameraRig.transform.eulerAngles.y);

        // 全アイコンを非表示
        HideAllIcon();

        // 範囲内の敵アイコンをプロット
        var enemys = GameObject.FindGameObjectsWithTag("Enemy")
            .Where(enemy => Vector3.Distance(player.transform.position, enemy.transform.position) < detectionRange);
        foreach (var enemy in enemys)
        {
            PlotEnemyIcon(enemy.transform.position);
        }
    }

    private void HideAllIcon()
    {
        foreach (var enemyIcon in enemyIconCollection)
        {
            enemyIcon.SetActive(false);
        }
    }

    private GameObject PlotEnemyIcon(Vector3 enemyPos)
    {
        Vector3 plotPos =
            (enemyPos - player.transform.position) * (radarRadius / detectionRange);

        foreach (var oldEnemyIcon in enemyIconCollection)
        {
            if (oldEnemyIcon.activeSelf == false)
            {
                RectTransform oldEnemyIconRect = oldEnemyIcon.GetComponent<RectTransform>();
                oldEnemyIconRect.anchoredPosition = new Vector2(plotPos.x, plotPos.z);
                oldEnemyIcon.SetActive(true);
                return oldEnemyIcon;
            }
        }

        var newEnemyIcon = GameObject.Instantiate(enemyIconPrefab) as GameObject;
        newEnemyIcon.transform.SetParent(transform, false);
        RectTransform newEnemyIconRect = newEnemyIcon.GetComponent<RectTransform>();
        newEnemyIconRect.anchoredPosition = new Vector2(plotPos.x, plotPos.z);
        enemyIconCollection.Add(newEnemyIcon);

        return newEnemyIcon;
    }

    private IEnumerator DestroyUnusedIcon(float timeOut)
    {
        while (true)
        {
            var enemyIconRemoveList = new List<GameObject>();
            foreach (var enemyIcon in enemyIconCollection)
            {
                enemyIconRemoveList.Add(enemyIcon);
            }
            foreach (var enemyIcon in enemyIconRemoveList)
            {
                if (enemyIcon.activeSelf == false)
                {
                    enemyIconCollection.Remove(enemyIcon);
                    Destroy(enemyIcon);
                }
            }

            yield return new WaitForSeconds(timeOut);
        }
    }

    public void SetDetectionRange(float detectionRange)
    {
        this.detectionRange = detectionRange;
    }

    public float GetDetectionRange()
    {
        return this.detectionRange;
    }
}

コードはこんな感じ。今後は臆さずにベタベタ貼っていくことにしました。
GitHub? それオイシイの?(ダメエンジニ(ry

f:id:hogemarunosuke:20170111003314p:plain
https://www.assetstore.unity3d.com/jp/#!/content/60311

レーダー画像には「Modern & Clean GUI」を使用しました。動き自体は、今までの知識で意外とすんなり実装できました。ただ、視野角が実際の表示と異なるので、そこは修正が必要そうです。画像を修正するか、プログラムで動的に線を引くか、悩みどころですが。。。

少しはまったのがUIのプレハブ化と座標変更。「SetParent(transform, false)」でしっかりとCanvasの子オブジェクトにして、「RectTransform」を使用するのがミソでした。

しかし、毎フレームで「すべてのプロットを削除」→「範囲内の全敵をプロット」をしているのでエレガントじゃないですね。場合によっては負荷になりそう。まあ、動いてるからいいや!