Unity 5. Указатель цели

Unity 5. Указатель цели

Как сделать Указатель цели в Unity 5.

Иногда в игре игроку необходимо указать, в каком направлении находится какая-то цель. Этим мы и займемся. В этой статье я хочу показать вам как при помощи скриптов можно настроить в своем проекте отображение указателей целей.
Для начала подготовим сцену, добавим плоскость, стандартного персонажа и 4 куба.

kyb

Персонажем мы будем управлять, а кубы будут служить в качестве целей.

Далее добавим компонент Canvas и создадим новый скрипт с названием IndicatorsManager и с таким содержимым:

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

namespace ScreenIndicators
{

	public class IndicatorsManager : MonoBehaviour
    {

        public Canvas IndicatorsContainer;
        public int IndicatorSize = 100;

        private List _indicators = new List();
        private float _border;
        private float _halfScreenHeight;
        private float _halfScreenWidth;


        public void AddIndicator(Transform target, Indicator indicatorPrefab)
        {
            if (GetIndicatorByTarget(target) != null)
                return;

            var newIndicator = InstantiateIndicator(indicatorPrefab);
            newIndicator.Target = target;
            _indicators.Add(newIndicator);
        }

        public void RemoveIndicator(Transform target)
        {
            var indicator = GetIndicatorByTarget(target);
            if (indicator == null) return;

            _indicators.Remove(indicator);
            Destroy(indicator.gameObject);
        }

	    public bool IsTargetExist(Transform target)
	    {
	        var indicator = GetIndicatorByTarget(target);
	        return indicator != null;
	    }


        private void Start()
        {
            _halfScreenHeight = Screen.height / 2f;
            _halfScreenWidth = Screen.width / 2f;

            if (IndicatorsContainer == null)
            {
                Debug.LogError("Need canvas for Indicators");
                return;
            }

            _border = IndicatorSize / 2f;
        }

        private void LateUpdate()
        {
            var indicatorsToRemove = new List();

            foreach (var indicator in _indicators)
            {
                if (indicator.Target == null)
                {
                    indicatorsToRemove.Add(indicator);
                    continue;
                }

                UpdateIndicator(indicator);
            }

            if (indicatorsToRemove.Count <= 0) return;
            foreach (var indicator in indicatorsToRemove)
            {
                _indicators.Remove(indicator);
                Destroy(indicator.gameObject);
            }
        }

        private Indicator InstantiateIndicator(Indicator indicatorPrefab)
        {
            var indicator = Instantiate(indicatorPrefab, IndicatorsContainer.transform, false);
            var rect = indicator.GetComponent();

            indicator.transform.localScale = Vector3.one;
            rect.sizeDelta = new Vector2(IndicatorSize, IndicatorSize);

            return indicator;
        }

        private void UpdateIndicator(Indicator indicator)
        {
			var screenPoint = Camera.main.WorldToScreenPoint(indicator.Target.localPosition);
            Vector3 newPosition;
			float angle;

            var heading = indicator.Target.position - Camera.main.transform.position;
			var isBehindCamera = Vector3.Dot(Camera.main.transform.forward, heading) < 0; if (screenPoint.x > Screen.width - _border || screenPoint.x < _border || screenPoint.y > Screen.height - _border || screenPoint.y < _border || isBehindCamera) { indicator.IsOnScreen = false; angle = Mathf.Atan2(screenPoint.y - _halfScreenHeight, screenPoint.x - _halfScreenWidth); float x, y; if(screenPoint.x - _halfScreenWidth > 0)
                {
					// right
					x = _halfScreenWidth - _border;
					y = x * Mathf.Tan(angle);
				}
                else
                {
					// left
					x = -_halfScreenWidth + _border;
					y = x * Mathf.Tan(angle);
				}

				if(y > _halfScreenHeight - _border)
                {
					// up
					y = _halfScreenHeight - _border;
					x = y / Mathf.Tan(angle);
				}
				if(y < -_halfScreenHeight + _border) { // down y = -_halfScreenHeight + _border; x = y / Mathf.Tan(angle); } if(isBehindCamera) { x = -x; y = -y; } newPosition = new Vector3(x, y, 0); } else { indicator.IsOnScreen = true; var x = screenPoint.x - _halfScreenWidth; var y = screenPoint.y - _halfScreenHeight; newPosition = new Vector3(x, y, 0); } indicator.transform.localPosition = newPosition; //rotate if ((indicator.IsOnScreen && indicator.RotateOnScreen) || (!indicator.IsOnScreen && indicator.RotateOffScreen)) { angle = isBehindCamera ? Mathf.Atan2(-(screenPoint.y - _halfScreenHeight), -(screenPoint.x - _halfScreenWidth)) : Mathf.Atan2(screenPoint.y - _halfScreenHeight, screenPoint.x - _halfScreenWidth); } else { angle = 90 * Mathf.Deg2Rad; } indicator.transform.localEulerAngles = new Vector3(0, 0, angle * Mathf.Rad2Deg - 90); } private Indicator GetIndicatorByTarget(Transform target) { return _indicators.FirstOrDefault(indicator => indicator.Target == target);
        }

    }

}

Это скрипт мы вешаем на Canvas и в строке Indicators Container указываем этот же Canvas

Canvas

Далее добавим пустышку на сцену и сохраним ее как префаб в папку Prefabs и назовем это префаб Indicator. Создадим еще один скрипт с названием Indicator и содержанием:

using UnityEngine;
using UnityEngine.UI;

namespace ScreenIndicators
{

	public class Indicator : MonoBehaviour
    {

        public Image Image;

        [HideInInspector]
        public Transform Target;
        [HideInInspector]
        public bool IsOnScreen;

        public bool ShowOnScreen;
        public bool RotateOnScreen;
        public Sprite OnScreenSprite;
        public Color OnScreenSpriteColor;

        public bool ShowOffScreen;
        public bool RotateOffScreen;
        public Sprite OffScreenSprite;
        public Color OffScreenSpriteColor;


	    private void Update()
	    {
            if (IsOnScreen)
	        {
                Image.sprite = OnScreenSprite;
	            Image.color = OnScreenSpriteColor;
	            Image.enabled = ShowOnScreen;
	            return;
	        }

	        Image.sprite = OffScreenSprite;
	        Image.color = OffScreenSpriteColor;
	        Image.enabled = ShowOffScreen;
	    }

    }

}

Этот скрипт мы поместим на префаб Indicator и можно указать галочки отображения и поворота указателей, так же цвет и иконки (иконки для указателей можно взять здесь).

Indicator

Ну и последний шаг, создаем еще один скрипт Scene:

using System.Collections.Generic;
using ScreenIndicators;
using UnityEngine;

public class DemoScene : MonoBehaviour
{

    public IndicatorsManager IndicatorsManager;
    public Indicator IndicatorPrefab;
    public Transform Camera;
    public List Targets;


    private void Start()
    {
        AddIndicatorsToAllTargets();
    }

    private void Update()
    {
        Camera.Rotate(Camera.up, 10f * Time.deltaTime);
    }


    // UI buttons click
    public void AddIndicatorsToAllTargets()
    {
        foreach (var target in Targets)
        {
            IndicatorsManager.AddIndicator(target, IndicatorPrefab);
        }
    }

    public void RemoveIndicatorsFromAllTargets()
    {
        foreach (var target in Targets)
        {
            IndicatorsManager.RemoveIndicator(target);
        }
    }

    public void DestroyRandomTarget()
    {
        if (Targets.Count <= 0)
            return;

        var randomTarget = Targets[Random.Range(0, Targets.Count)];
        Targets.Remove(randomTarget);
        Destroy(randomTarget.gameObject);
    }

}

Поместим этот скрипт на персонажа и указываем Canvas, префаб Indicator, Камеру персонажа и 4 куба как цели.

Scene

Теперь можно проверить сцену, запускаем игру и видим, что указатели, показывают, в каком направлении находятся наши кубы.

(Информация и скрипты взяты с этого сайта)

На главную


Оставить Комментарий