Путь планеты
Готовый проект урока, скачать.
Обычно, в играх планетарного масштаба, помимо самих планет, на экране отображается вспомогательная информация, такая как орбита или информация о самих планетах.
Вывести такую информацию на экран довольно просто. В юнити есть как минимум 3 разных способа:
1) LineRenderer/TrailRenderer компоненты, первый компонент позволяет рисовать линию задавая точки её изгиба, а второй сам рисует линию по координатам, который прошел объект на который этот компонент был присоединён. Минус в том, что такие линии могут быть только трёхмерными, что не подходит для отображения в качестве элемента GUI. Плюс в том, что очень просто рисовать линии таким образом.
2) Graphics позволяет рисовать линию как сетку прямоугольника вручную, минус в том, что надо вычислять позицию каждой вершины прямоугольника изображающего линию. Плюс в том, что этот способ даёт максимальный уровень управления внешним видом результата.
3) GL позволяет обратиться напрямую к возможностям OpenGl и нарисовать линию. Минус в том, что линия всегда будет одной ширины, плюс в том, что это несколько проще в сравнении с Graphics.
Последний вариант разберём более подробно.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
[RequireComponent(typeof(Camera))]
public class DrawPath : MonoBehaviour {
// Цель, чей след рисуем
public Transform target;
// Материал линии
public Material material;
// Длина "хвоста" цели в попугаях
public int trailLength = 5000;
private new Camera camera;
// Список всех прошлых позиций цели
private List<Vector3> vertices = new List<Vector3>();
void Start () {
camera = GetComponent<Camera>();
}
void Update () {
// Сохраняем текущую позицию цели
vertices.Add(target.position);
// Ограничиваем длину "хвоста"
if (vertices.Count > trailLength)
vertices.RemoveAt(0);
}
// Специальная функция для рисования через GL,
// чтобы она сработала надо прикреплять компонент
// к объекту камеры.
void OnPostRender()
{
if (!material)
{
Debug.LogError("Please Assign a material on the inspector");
return;
}
// Это особенность работы OpenGL
// Примите как данность, что для
// изменения матрицы проекции
// её надо сначала сохранить PushMatrix
// командой, и восстановить
// PopMatrix командой
GL.PushMatrix();
// Указываем какой материал должен сработать
material.SetPass(0);
// Устанавливаем матрицу проекции без
// перспективного искажения и получаем
// "плоский" мир для всего, что будет
// нарисовано далее
GL.LoadOrtho();
// Начинаем рисовать линии, это означает
// что надо передать по две точки, а в результате
// будет нарисована одна линия
GL.Begin(GL.LINES);
GL.Color(Color.red);
for (int i = 1; i < vertices.Count; i++)
{
// Мы запомнили мировые координаты точки,
// преобразуем их в экранные.
Vector3 pos = camera.WorldToScreenPoint(vertices[i - 1]);
// И вгоняем их в диапазон от 0 до 1
// т.к. OpenGl принимает экранные координаты
// в таком виде.
pos.x /= Screen.width;
pos.y /= Screen.height;
// И передаём точку в GPU
GL.Vertex3(pos.x, pos.y, 0);
// Выше мы начало линии вычислили и передали,
// ниже вычисляем и передаём конец
pos = camera.WorldToScreenPoint(vertices[i]);
pos.x /= Screen.width;
pos.y /= Screen.height;
GL.Vertex3(pos.x, pos.y, 0);
}
// Следующими инструкциями завершаем рисование
GL.End();
GL.PopMatrix();
}
}
Этот компонент надо прикрепить к той камере, на которой мы хотим видеть рисуемые данные, задать цель слежения и материал линии.
Но что если надо не только нарисовать путь, но и вывести некоторый текст.
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(Camera))]
public class DrawHighlight : MonoBehaviour {
public Transform highlightTarget;
public Material material;
public float marginSize = 0.05f;
public float underlineSize = 0.20f;
public int labelHeight = 20;
private new Camera camera;
void Start () {
camera = GetComponent<Camera>();
}
void OnGUI()
{
// Приводим координаты цели к экранным
Vector3 pos = camera.WorldToScreenPoint(highlightTarget.position);
// Делаем отступ от позиции цели
pos.x += Screen.width * marginSize;
pos.y += Screen.height * marginSize + labelHeight;
// Вот такая особенность у GUI юнити, что экранные
// координаты надо передавать в перевёрнутом виде
pos.y = Screen.height - pos.y;
GUI.Label(
new Rect(pos.x, pos.y, Screen.width * underlineSize, labelHeight)
,"Earth");
}
void OnPostRender()
{
if (!material)
{
Debug.LogError("Please Assign a material on the inspector");
return;
}
GL.PushMatrix();
material.SetPass(0);
GL.LoadOrtho();
GL.Begin(GL.LINES);
GL.Color(Color.red);
// Получаем координаты цели
Vector3 pos = camera.WorldToScreenPoint(highlightTarget.position);
pos.x /= Screen.width;
pos.y /= Screen.height;
// Рисуем диагональную линию от цели
GL.Vertex3(pos.x, pos.y, 0);
GL.Vertex3(pos.x + marginSize, pos.y + marginSize, 0);
// Подрисовываем горизонтальную линию к диагональной
GL.Vertex3(pos.x + marginSize, pos.y + marginSize, 0);
GL.Vertex3(pos.x + marginSize + underlineSize, pos.y + marginSize, 0);
GL.End();
GL.PopMatrix();
}
}
В этом примере мы рисовали текст средствами Unity Legacy GUI, но можно было нарисовать и средствами OpenGL и новой системой GUI. Приведённый выше способ использован только из-за наглядности и простоты.