Найти в Дзене
KNL Games

Создание и настройка противников в Unity: архитектура MVC

Продолжаем разработку 3D-игры с использованием Модели-Представления-Контроллера (MVC). В этом видео уроке мы добавим в проект интеллектуальных противников — врагов, которые патрулируют территорию, обходят препятствия и взаимодействуют с игроком. MVC — это шаблон проектирования, который разделяет логику приложения на три компонента: Этот сценарий отвечает за состояние врага (позиция, таймеры, здоровье). csharp // EnemyModel.cs
using UnityEngine;
public class EnemyModel
{
public Vector3 CurrentDirection { get; set; }
public float DirectionTimer { get; set; }
public bool IsDead { get; set; } = false;
public float MaxTimeInDirection { get; private set; }
public EnemyModel(float initialMaxTime)
{
MaxTimeInDirection = initialMaxTime;
CurrentDirection = Vector3.forward;
}
public void UpdateTimer(float deltaTime)
{
if (!IsDead)
{
DirectionTimer -= deltaTime;
}
}
public bool IsTimeExpired()
{
Оглавление

Продолжаем разработку 3D-игры с использованием Модели-Представления-Контроллера (MVC). В этом видео уроке мы добавим в проект интеллектуальных противников — врагов, которые патрулируют территорию, обходят препятствия и взаимодействуют с игроком.

🧩 Что такое архитектура MVC в Unity?

MVC — это шаблон проектирования, который разделяет логику приложения на три компонента:

  • Модель (Model) — хранит данные и состояние (например, позиция, здоровье, цель движения).
  • Представление (View) — ответственно за визуализацию и отображение элементов (рендер, анимация, физика.
  • Контроллер (Controller) — управляет логикой, принимает решения и передаёт команды Модели и Представлению.

👾 Создание и настройка противников (MVC)

1. EnemyModel.cs — Модель противника

Этот сценарий отвечает за состояние врага (позиция, таймеры, здоровье).

csharp

// EnemyModel.cs
using UnityEngine;

public class EnemyModel
{
public Vector3 CurrentDirection { get; set; }
public float DirectionTimer { get; set; }
public bool IsDead { get; set; } = false;
public float MaxTimeInDirection { get; private set; }

public EnemyModel(float initialMaxTime)
{
MaxTimeInDirection = initialMaxTime;
CurrentDirection = Vector3.forward;
}

public void UpdateTimer(float deltaTime)
{
if (!IsDead)
{
DirectionTimer -= deltaTime;
}
}

public bool IsTimeExpired()
{
return DirectionTimer <= 0f;
}

public void SetNewDirection(Vector3 newDir)
{
CurrentDirection = newDir.normalized;
DirectionTimer = MaxTimeInDirection;
}
}

2. EnemyView.cs — Представление противника

Здесь находится визуализация и физика: движение, вращение, обход.

csharp

// EnemyView.cs
using UnityEngine;

public class EnemyView : MonoBehaviour
{
[Header("Настройки Движения (View)")]
public float moveSpeed = 3f;
public float rotationSmoothFactor = 0.15f;
public float obstacleCheckDistance = 1.5f;
public LayerMask obstacleLayer;

[Header("Эффекты Смерти")]
public GameObject deathEffectPrefab;

private Rigidbody rb;

void Awake()
{
rb = GetComponent<Rigidbody>();
}

// --- Методы, вызываемые Контроллером ---

public void Move(Vector3 direction, float deltaTime)
{
if (direction.sqrMagnitude < 0.01f) return;

Vector3 movement = direction * moveSpeed * deltaTime;

if (rb != null)
{
rb.MovePosition(rb.position + movement);
}
else
{
transform.position += movement;
}
}

public void RotateToFace(Vector3 direction)
{
Quaternion targetRotation = Quaternion.LookRotation(
new Vector3(direction.x, 0, direction.z));
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSmoothFactor);
}

public bool CheckObstacles(Vector3 direction)
{
RaycastHit hit;

if (Physics.Raycast(transform.position, direction, out hit, obstacleCheckDistance, obstacleLayer))
{
Debug.DrawRay(transform.position, direction * obstacleCheckDistance, Color.red);

if (((1 << hit.collider.gameObject.layer) & obstacleLayer) != 0)
{
return true;
}
}
else
{
Debug.DrawRay(transform.position, direction * obstacleCheckDistance, Color.green);
}
return false;
}

public void ExecuteDeath()
{
if (deathEffectPrefab != null)
{
Instantiate(deathEffectPrefab, transform.position, Quaternion.identity);
}
Destroy(gameObject, 0.5f);
}
}

3. EnemyController.cs — Контроллер противника

Именно здесь происходит управленческое взаимодействие между Моделью и Представлением.

csharp

// EnemyController.cs
using UnityEngine;

[RequireComponent(typeof(EnemyView))]
public class EnemyController : MonoBehaviour
{
private EnemyModel model;
private EnemyView view;

// Параметры, которые должны быть в Inspector (перенесены из старого скрипта)
[Header("Настройки Патрулирования (Контроллер)")]
public float maxTimeInDirection = 5f;

void Awake()
{
view = GetComponent<EnemyView>();
// Инициализируем модель с параметром времени, полученным из Inspector
model =
new EnemyModel(maxTimeInDirection);
}

void Start()
{
ChooseNewState();
}

void Update()
{
if (model.IsDead) return;

// 1. Обновление состояния Модели
model.UpdateTimer(Time.deltaTime);

// 2. Принятие решений (Логика ИИ)
if (model.IsTimeExpired() || view.CheckObstacles(model.CurrentDirection))
{
ChooseNewState();
}

// 3. Применение движения (Controller -> View)
view.Move(model.CurrentDirection, Time.deltaTime);
}

void ChooseNewState()
{
// 1. Выбираем новое направление (Модель)
Vector3 newDirection = GetRandomDirection();
model.SetNewDirection(newDirection);

// 2. Поворачиваемся лицом к новому направлению (View)
view.RotateToFace(model.CurrentDirection);
}

Vector3
GetRandomDirection()
{
float randomX = Random.Range(-1f, 1f);
float randomZ = Random.Range(-1f, 1f);
Vector3 newDir =
new Vector3(randomX, 0f, randomZ).normalized;

if (newDir.sqrMagnitude < 0.01f)
{
return Vector3.forward;
}
return newDir;
}

// Обработка внешних событий (например, взрыва)
private void OnTriggerEnter(Collider other)
{
if (!model.IsDead && other.CompareTag("Explosion"))
{
ApplyDamage();
}
}

public void ApplyDamage()
{
if (!model.IsDead)
{
model.IsDead = true;
// View выполняет визуальное уничтожение
view.ExecuteDeath();
}
}
}

⚙️ Настройка в Unity

📌 Действия в редакторе

  1. Создайте пустой объект на сцене и назовите его Enemy.
  2. Добавьте компонент Rigidbody к объекту врага:Заморозьте поворот по всем осям (X, Y, Z) в Constraints.
  3. Назначьте слой препятствий для объектов, которые враг должен обходить (например, “Wall”).
  4. Настройте тег Enemy для создания объекта взрыва.
  5. Подключите эффект смерти (например, Particle System).

Готовы к следующим урокам? Ещё больше уроков о создании игр на нашем канале! 🎮

Подпишитесь, чтобы не пропустить новые видео о MVC и логике противников!

#Unity #GameDev #MVC #Патрулирование #3DИгра