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

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

Стоит обратить внимание, что размеры тансформа 1х1, так как финальный размер иконки мы регулируем через класс игровой цели.
Скрипт для шаблона иконок:
Вручную здесь мы добавляем только Image шаблона.
Иконка игрока статична, но определяющая по сути:
Поэтому для нее у нас вот такой упрощенный вариант, самое важное тут target, на его основе делаются все расчеты.
Для моделек врагов или объектов задания:
На старте, добавляется конка, но если вы используете пул врагов, то стоит сделать публичную функцию для повторного добавления иконки.
Далее, главный скрипт, который мы вешаем на родителя миникарты:
Все основные расчеты делаются тут.
Дополнительно, добавим кнопку в инспектор редактора:
Скачать демо и скрипты:
Для начала разберем иерархию 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
Скачать демо и скрипты:
Тестировалось на: Unity 2017.1.0
Комментариев 1
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.