Loading...

Простая миникарта-детектор

Categories:

Приобрели: 1 раз

Авторство:Есть разрешение автора
Free To cart

В некоторых играх, чаще всего шутерах, бывают миникарты без плана местности, а только отображают позицию врага и важных точек, например, текущего задания. Проще говоря это нечто вроде детектора и компаса. Такие детекторы, также хорошо подходят для игр с видом сверху, как Alien Shooter и подобные. Данная миникарта по сути является модификаций предыдущих наших проектов, здесь мы поработали над оптимизацией, теперь скрипт работает с пулом иконок, который мы заранее генерируем в редакторе, исходя из того, сколько врагов может быть в определенной области. Добавлены новые функции, например, возможность использовать миникарту не только формы квадрата, но и круглую.

Для начала разберем иерархию Canvas:

Простая миникарта-детектор

В начале у нас идет родитель, который определяет размеры для дочерних окон (им нужно установить соответствующий пресет), окон здесь три. Mask – форма детектора, и основное хранилище пула. InBounds – окно для иконок, которые не выходят за границы миникарты. Hover – это окошко поверх всех остальных, там у нас будет иконка игрока.

Далее, объект Sample это шаблон иконки:

Стоит обратить внимание, что размеры тансформа 1х1, так как финальный размер иконки мы регулируем через класс игровой цели.

Скрипт для шаблона иконок:

using UnityEngine;
using UnityEngine.UI;
using System.Collections;

public class MinimapBlip : MonoBehaviour {

	public Transform target;
	public bool inBounds;
	public float iconSize = 1; // локальный размер
	public Image image; // родительский Image

	public void MyUpdate(float zoom)
	{
		Vector2 position = Minimap.Instance.TransformPosition(target.position);
		if(inBounds) position = Minimap.Instance.MoveInside(position);
		image.rectTransform.localScale = new Vector3(zoom * iconSize, zoom * iconSize, 1);
		image.rectTransform.localEulerAngles = Minimap.Instance.TransformRotation(target.eulerAngles);
		image.rectTransform.localPosition = position;
	}
}

Вручную здесь мы добавляем только Image шаблона.

Иконка игрока статична, но определяющая по сути:

using UnityEngine;
using UnityEngine.UI;
using System.Collections;

public class MinimapPlayer : MonoBehaviour {

	public Transform target; // моделька игрока (та часть которая вращается, голова или камера)
	public float iconSize = 1; // локальный размер
	public Image image; // родительский Image

	public void MyUpdate(float zoom)
	{
		image.rectTransform.localScale = new Vector3(zoom * iconSize, zoom * iconSize, 1);
	}
}

Поэтому для нее у нас вот такой упрощенный вариант, самое важное тут target, на его основе делаются все расчеты.

Для моделек врагов или объектов задания:

using UnityEngine;
using System.Collections;

public class MinimapTarget : MonoBehaviour {

	public bool inBounds;
	public float iconSize = 1; // локальный размер
	public Sprite iconSprite;
	public Color iconColor = Color.red;
	private GameObject blip;

	void Start()
	{
		// создание новой иконки данного объекта на миникарте
		blip = Minimap.Instance.AddObject(this.transform, inBounds, iconSprite, iconColor, iconSize);
	}

	void OnDisable() // если объект не активен (или уничтожен), отключаем иконку на миникарты
	{
		if(blip) blip.SetActive(false);
	}

	void OnEnable() // если объект активирован, делаем тоже самое на миникарте
	{
		if(blip) blip.SetActive(true);
	}
}

На старте, добавляется конка, но если вы используете пул врагов, то стоит сделать публичную функцию для повторного добавления иконки.

Далее, главный скрипт, который мы вешаем на родителя миникарты:

using UnityEngine;
using UnityEngine.UI;
using System.Collections;

[RequireComponent(typeof(RectTransform))]

public class Minimap : MonoBehaviour {

	private enum MapMode {Circle, Square};
	[SerializeField] private MapMode mapForm; // опция для формы маски, круглая или квадратная
	[SerializeField] private MinimapPlayer player; // иконка персонажа
	[SerializeField] private float zoom = 10; // зум миникаты и всех иконок на ней
	[SerializeField] private MinimapBlip sample; // шаблон объекта для миникарты
	[SerializeField] private RectTransform inBoundsRect; // рамка для объектов, которые должны быть внутри окна карты
	[SerializeField] private RectTransform maskRect; // трансформ маски, где будут находиться пул
	[SerializeField] private MinimapBlip[] pool; // пул иконок миникарты

	private Vector2 XRot, YRot;
	private float rotateValue;
	private RectTransform parent;
	private static Minimap _instance;

	void Awake()
	{
		parent = GetComponent<RectTransform>();
		_instance = this;
	}

	public static Minimap Instance
	{
		get{ return _instance; }
	}
	
	public Vector2 TransformPosition(Vector3 position)
	{
		Vector3 offset = position - player.target.position;
		Vector2 pos = offset.x * XRot;
		pos += offset.z * YRot;
		pos *= zoom;
		return pos;
	}

	public Vector3 TransformRotation(Vector3 rotation)
	{
		return new Vector3(0, 0, rotateValue - rotation.y);
	}

	public Vector2 MoveInside(Vector2 point)
	{
		if(mapForm == MapMode.Square)
		{
			point = Vector2.Max(point, parent.rect.min);
			point = Vector2.Min(point, parent.rect.max);
		}
		else
		{
			Vector2 pos = point.normalized * (parent.sizeDelta.x / 2f);

			float max = Mathf.Max(point.x, pos.x);
			float min = Mathf.Min(point.x, pos.x);

			if(point.x < 0) point.x = max; else point.x = min;

			max = Mathf.Max(point.y, pos.y);
			min = Mathf.Min(point.y, pos.y);

			if(point.y < 0) point.y = max; else point.y = min;
		}

		return point;
	}

	public GameObject AddObject(Transform target, bool inBounds, Sprite icon, Color color, float size)
	{
		for(int i = 0; i < pool.Length; i++)
		{
			if(!pool[i].isActiveAndEnabled)
			{
				if(inBounds) pool[i].image.rectTransform.SetParent(inBoundsRect);
				else pool[i].image.rectTransform.SetParent(maskRect);
				pool[i].inBounds = inBounds;
				pool[i].target = target;
				pool[i].iconSize = size;
				pool[i].image.sprite = icon;
				pool[i].image.color = color;
				pool[i].gameObject.SetActive(true);
				return pool[i].gameObject;
			}
		}

		return null;
	}

	void LateUpdate()
	{
		XRot = new Vector2(player.target.right.x, -player.target.right.z);
		YRot = new Vector2(-player.target.forward.x, player.target.forward.z);
		rotateValue = player.target.eulerAngles.y;
		player.image.rectTransform.localEulerAngles = Vector3.zero;

		for(int i = 0; i < pool.Length; i++)
		{
			if(pool[i].isActiveAndEnabled) pool[i].MyUpdate(zoom);
		}

		player.MyUpdate(zoom);
	}

	#if UNITY_EDITOR
	[SerializeField] private int poolSize = 10;
	public void Create()
	{
		for(int i = 0; i < pool.Length; i++)
		{
			if(pool[i] != null) DestroyImmediate(pool[i].gameObject);
		}

		sample.gameObject.SetActive(false);
		pool = new MinimapBlip[poolSize];
		for(int i = 0; i < pool.Length; i++)
		{
			pool[i] = Instantiate(sample, maskRect) as MinimapBlip;
			pool[i].transform.localScale = Vector3.one;
			pool[i].transform.name = "Target-" + i;
		}
	}
	#endif
}

Все основные расчеты делаются тут.

Дополнительно, добавим кнопку в инспектор редактора:

#if UNITY_EDITOR
using System.Collections;
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(Minimap))]
public class MinimapEditor : Editor {

	public override void OnInspectorGUI()
	{
		DrawDefaultInspector();
		Minimap t = (Minimap)target;
		if(GUILayout.Button("Create Pool")) t.Create();
	}
}
#endif

Publication author

offline 1 month

Tolia

244
flagРоссия.
Comments: 13Publics: 10383Registration: 10-04-2017

Leave a Reply

Authorization
*
*

2 + 8 =


Registration
*
*
*
A password has not been entered
*
Укажите промокод одного из партнеров нашего сайта и получите подарок.

1 + 5 =


Password generation

9 + 9 =