Реализовать мини-карту для игры
Условие
Реализуйте мини-карту в углу экрана.
Требования
- Отображение области вокруг игрока
- Иконки для игрока, врагов, целей
- Вращение карты вместе с игроком или статичная ориентация
- Масштабирование карты
- Fog of war (опционально)
Реализация
- Отдельная камера сверху
- Render Texture
- UI Image для отображения
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Решение: Система мини-карты для игры
Эта задача требует создания интерактивной мини-карты с отслеживанием игрока, врагов и целей, с поддержкой различных режимов ориентации. Расскажу о полной архитектуре.
Архитектура системы
Основные компоненты:
- MinimapCamera — отдельная камера для мини-карты
- MinimapRenderer — управление RenderTexture
- MinimapMarker — маркеры на мини-карте
- MinimapController — основной контроллер
- FogOfWar — система скрытия области (опционально)
MinimapCamera - Камера мини-карты
public class MinimapCamera : MonoBehaviour
{
[SerializeField] private Camera minimapCamera;
[SerializeField] private Transform playerTransform;
[SerializeField] private float cameraHeight = 50f;
[SerializeField] private float minimapSize = 100f;
[SerializeField] private MinimapOrientation orientation = MinimapOrientation.Rotated;
[SerializeField] private Color backgroundColor = Color.black;
public enum MinimapOrientation
{
Rotated, // Карта вращается с игроком
Static // Карта ориентирована фиксированно
}
private void Start()
{
if (minimapCamera == null)
minimapCamera = GetComponent<Camera>();
minimapCamera.orthographic = true;
minimapCamera.orthographicSize = minimapSize;
minimapCamera.backgroundColor = backgroundColor;
}
private void LateUpdate()
{
if (playerTransform == null)
return;
// Позиционируем камеру над игроком
Vector3 cameraPos = playerTransform.position + Vector3.up * cameraHeight;
minimapCamera.transform.position = cameraPos;
// Поворот карты в зависимости от режима
if (orientation == MinimapOrientation.Rotated)
{
// Карта вращается следуя направлению игрока
Quaternion targetRotation = Quaternion.Euler(90, playerTransform.eulerAngles.y, 0);
minimapCamera.transform.rotation = targetRotation;
}
else
{
// Карта всегда ориентирована одинаково
minimapCamera.transform.rotation = Quaternion.Euler(90, 0, 0);
}
}
public void SetCameraSize(float size)
{
minimapCamera.orthographicSize = size;
minimapSize = size;
}
public void SetOrientation(MinimapOrientation newOrientation)
{
orientation = newOrientation;
}
}
MinimapMarker - Маркеры
public enum MarkerType
{
Player,
Enemy,
Ally,
Objective,
Item,
Waypoint
}
public class MinimapMarker : MonoBehaviour
{
[SerializeField] private MarkerType markerType = MarkerType.Enemy;
[SerializeField] private Color markerColor = Color.red;
[SerializeField] private float markerSize = 0.5f;
[SerializeField] private bool rotateWithPlayer = false;
[SerializeField] private GameObject markerVisuals;
private Transform targetTransform;
private Image markerImage;
private RectTransform markerRect;
private MinimapController minimapController;
private Vector3 localOffset = Vector3.zero;
public MarkerType Type => markerType;
public Transform TargetTransform => targetTransform;
private void Start()
{
minimapController = FindObjectOfType<MinimapController>();
if (markerVisuals != null)
{
markerImage = markerVisuals.GetComponent<Image>();
markerRect = markerVisuals.GetComponent<RectTransform>();
}
if (markerImage != null)
markerImage.color = markerColor;
}
public void Initialize(Transform target, Color color, MarkerType type, Vector3 offset = default)
{
targetTransform = target;
markerType = type;
markerColor = color;
localOffset = offset;
if (markerImage != null)
markerImage.color = color;
}
public void UpdatePosition(Vector3 playerPosition, Quaternion playerRotation, float scale)
{
if (targetTransform == null || minimapController == null)
return;
// Вычисляем позицию относительно игрока
Vector3 relativePos = targetTransform.position - playerPosition + localOffset;
// Масштабируем позицию
relativePos *= scale;
// Если карта вращается - применяем поворот
if (minimapController.IsMapRotated())
{
relativePos = Quaternion.Inverse(playerRotation) * relativePos;
}
if (markerRect != null)
{
markerRect.anchoredPosition = new Vector2(relativePos.x, relativePos.z);
// Применяем размер маркера
markerRect.sizeDelta = Vector2.one * markerSize * 10f;
}
// Опционально вращаем маркер с игроком
if (rotateWithPlayer && markerVisuals != null)
{
markerVisuals.transform.eulerAngles = new Vector3(0, 0, -playerRotation.eulerAngles.y);
}
}
public void SetVisibility(bool visible)
{
if (markerVisuals != null)
markerVisuals.SetActive(visible);
}
}
MinimapController - Основной контроллер
public class MinimapController : MonoBehaviour
{
[SerializeField] private RawImage minimapDisplay;
[SerializeField] private RenderTexture minimapTexture;
[SerializeField] private MinimapCamera minimapCameraComponent;
[SerializeField] private Transform playerTransform;
[SerializeField] private Canvas minimapCanvas;
[SerializeField] private float minimapScale = 1f;
[SerializeField] private float updateRate = 0.016f; // ~60 FPS
[SerializeField] private bool enableFogOfWar = false;
[SerializeField] private MinimapCamera.MinimapOrientation orientation = MinimapCamera.MinimapOrientation.Rotated;
private Dictionary<Transform, MinimapMarker> markerMap = new Dictionary<Transform, MinimapMarker>();
private List<MinimapMarker> allMarkers = new List<MinimapMarker>();
private float updateTimer = 0f;
private FogOfWarSystem fogOfWar;
public event System.Action<MinimapMarker> OnMarkerAdded;
public event System.Action<MinimapMarker> OnMarkerRemoved;
private void Start()
{
if (minimapTexture == null)
{
minimapTexture = new RenderTexture(512, 512, 16);
minimapTexture.name = "MinimapTexture";
}
if (minimapDisplay != null)
minimapDisplay.texture = minimapTexture;
if (minimapCameraComponent != null)
minimapCameraComponent.minimapCamera.targetTexture = minimapTexture;
if (enableFogOfWar)
{
fogOfWar = gameObject.AddComponent<FogOfWarSystem>();
}
CollectExistingMarkers();
}
private void LateUpdate()
{
updateTimer += Time.deltaTime;
if (updateTimer >= updateRate)
{
UpdateMinimapMarkers();
updateTimer = 0f;
}
}
private void CollectExistingMarkers()
{
MinimapMarker[] markers = FindObjectsOfType<MinimapMarker>();
foreach (var marker in markers)
{
RegisterMarker(marker);
}
}
private void UpdateMinimapMarkers()
{
if (playerTransform == null)
return;
foreach (var marker in allMarkers)
{
if (marker == null || marker.TargetTransform == null)
continue;
marker.UpdatePosition(
playerTransform.position,
playerTransform.rotation,
minimapScale
);
// Обновляем Fog of War
if (fogOfWar != null)
{
bool isVisible = fogOfWar.IsPositionRevealed(marker.TargetTransform.position);
marker.SetVisibility(isVisible);
}
}
}
public void RegisterMarker(MinimapMarker marker)
{
if (marker.TargetTransform != null && !markerMap.ContainsKey(marker.TargetTransform))
{
allMarkers.Add(marker);
markerMap[marker.TargetTransform] = marker;
OnMarkerAdded?.Invoke(marker);
}
}
public void UnregisterMarker(MinimapMarker marker)
{
if (marker.TargetTransform != null && markerMap.ContainsKey(marker.TargetTransform))
{
allMarkers.Remove(marker);
markerMap.Remove(marker.TargetTransform);
OnMarkerRemoved?.Invoke(marker);
}
}
public void SetMinimapScale(float scale)
{
minimapScale = Mathf.Clamp(scale, 0.1f, 5f);
}
public void SetOrientation(MinimapCamera.MinimapOrientation newOrientation)
{
orientation = newOrientation;
if (minimapCameraComponent != null)
minimapCameraComponent.SetOrientation(newOrientation);
}
public bool IsMapRotated() => orientation == MinimapCamera.MinimapOrientation.Rotated;
public void ToggleFogOfWar(bool enabled)
{
enableFogOfWar = enabled;
}
private void OnDestroy()
{
if (minimapTexture != null)
Destroy(minimapTexture);
}
}
FogOfWar - Система скрытия области
public class FogOfWarSystem : MonoBehaviour
{
[SerializeField] private float revealRadius = 20f;
[SerializeField] private float fogDecay = 5f; // Как быстро исчезает видимость
[SerializeField] private int textureResolution = 256;
private Texture2D fogTexture;
private float[] fogData;
private List<Transform> revealingSources = new List<Transform>();
private void Start()
{
// Инициализируем текстуру тумана
fogTexture = new Texture2D(textureResolution, textureResolution, TextureFormat.R8, false);
fogData = new float[textureResolution * textureResolution];
// Заполняем данные тумана
for (int i = 0; i < fogData.Length; i++)
{
fogData[i] = 0f; // 0 = видно, 1 = скрыто
}
// Находим источники света видимости
revealingSources.AddRange(FindObjectsOfType<Transform>());
}
private void Update()
{
UpdateFogOfWar();
}
private void UpdateFogOfWar()
{
// Обновляем данные тумана
for (int i = 0; i < fogData.Length; i++)
{
fogData[i] = Mathf.Min(fogData[i] + fogDecay * Time.deltaTime, 1f);
}
// Открываем области для источников видимости
foreach (var source in revealingSources)
{
if (source == null) continue;
RevealArea(source.position, revealRadius);
}
// Обновляем текстуру
UpdateFogTexture();
}
private void RevealArea(Vector3 position, float radius)
{
int centerX = Mathf.RoundToInt((position.x / 100f + 0.5f) * textureResolution);
int centerZ = Mathf.RoundToInt((position.z / 100f + 0.5f) * textureResolution);
int radiusInTexels = Mathf.RoundToInt(radius / 100f * textureResolution);
for (int x = centerX - radiusInTexels; x <= centerX + radiusInTexels; x++)
{
for (int z = centerZ - radiusInTexels; z <= centerZ + radiusInTexels; z++)
{
if (x >= 0 && x < textureResolution && z >= 0 && z < textureResolution)
{
float distance = Vector2.Distance(new Vector2(x, z), new Vector2(centerX, centerZ));
if (distance <= radiusInTexels)
{
int index = z * textureResolution + x;
fogData[index] = 0f; // Открываем
}
}
}
}
}
private void UpdateFogTexture()
{
Color[] colors = new Color[fogData.Length];
for (int i = 0; i < fogData.Length; i++)
{
colors[i] = new Color(fogData[i], fogData[i], fogData[i], 1f);
}
fogTexture.SetPixels(colors);
fogTexture.Apply();
}
public bool IsPositionRevealed(Vector3 position)
{
int x = Mathf.RoundToInt((position.x / 100f + 0.5f) * textureResolution);
int z = Mathf.RoundToInt((position.z / 100f + 0.5f) * textureResolution);
if (x >= 0 && x < textureResolution && z >= 0 && z < textureResolution)
{
int index = z * textureResolution + x;
return fogData[index] < 0.5f;
}
return false;
}
}
UI Setup - Настройка интерфейса
public class MinimapUI : MonoBehaviour
{
[SerializeField] private MinimapController minimapController;
[SerializeField] private Button zoomInButton;
[SerializeField] private Button zoomOutButton;
[SerializeField] private Button orientationToggleButton;
[SerializeField] private Slider zoomSlider;
private float currentZoom = 1f;
private void Start()
{
zoomInButton?.onClick.AddListener(() => AdjustZoom(0.2f));
zoomOutButton?.onClick.AddListener(() => AdjustZoom(-0.2f));
orientationToggleButton?.onClick.AddListener(() => ToggleOrientation());
zoomSlider?.onValueChanged.AddListener((value) => SetZoom(value));
}
private void AdjustZoom(float delta)
{
currentZoom = Mathf.Clamp(currentZoom + delta, 0.5f, 3f);
minimapController.SetMinimapScale(currentZoom);
zoomSlider.value = currentZoom;
}
private void SetZoom(float value)
{
currentZoom = value;
minimapController.SetMinimapScale(currentZoom);
}
private void ToggleOrientation()
{
MinimapCamera.MinimapOrientation newOrientation =
minimapController.IsMapRotated()
? MinimapCamera.MinimapOrientation.Static
: MinimapCamera.MinimapOrientation.Rotated;
minimapController.SetOrientation(newOrientation);
}
}
Пример использования
public class EnemyMarkerSpawner : MonoBehaviour
{
[SerializeField] private GameObject enemyMarkerPrefab;
[SerializeField] private Color enemyMarkerColor = Color.red;
[SerializeField] private MinimapController minimapController;
private void Start()
{
Enemy[] enemies = FindObjectsOfType<Enemy>();
foreach (var enemy in enemies)
{
CreateMarkerForEnemy(enemy);
}
}
private void CreateMarkerForEnemy(Enemy enemy)
{
GameObject markerObj = Instantiate(enemyMarkerPrefab);
MinimapMarker marker = markerObj.GetComponent<MinimapMarker>();
marker.Initialize(enemy.transform, enemyMarkerColor, MarkerType.Enemy);
minimapController.RegisterMarker(marker);
}
}
Ключевые особенности
1. RenderTexture — динамическое рендеринг в отдельную текстуру
2. Отдельная камера — orthographic камера смотрящая сверху
3. Маркеры — система маркеров для разных типов объектов
4. Два режима ориентации — вращающаяся и статичная карта
5. Масштабирование — управление зумом карты через слайдер
6. Fog of War — опциональное скрытие неоткрытых областей
7. Производительность — обновление только необходимых компонентов
Расширение возможностей
- Клик на карте — переход к точке
- Иконки для POI — достопримечательности на карте
- Растеризация — рендеринг окружения в карту
- Радиус видимости — отображение зоны видимости игрока
- Маркеры миссии — красные маркеры целей
- Слои карты — переключение между уровнями
Эта система обеспечивает гибкую и производительную мини-карту для любого типа игры.