Основы Unity и C#
Архитектура Unity
Unity построен на компонентной архитектуре. Вместо сложных иерархий классов Unity использует простую концепцию: GameObject + Components.
📦 GameObject
Пустой контейнеH для компонентов. Сам по себе ничего не делает - это просто "коробка" для деталей.
⚙️ Component
Добавляет функциональность: Transform (позиция), Rigidbody (физика), ваши скрипты (логика).
MonoBehaviour - основа всех скриптов
MonoBehaviour - это базовый класс, который позволяет вашим скриптам:
- Быть прикрепленными к GameObject
- Участвовать в жизненном цикле Unity
- Получать автоматические вызовы методов от Unity
- Показывать public поля в Inspector
using UnityEngine;
// Все пользовательские скрипты наследуются от MonoBehaviour
public class MyScript : MonoBehaviour
{
// Этот класс теперь может:
// 1. Быть добавлен к GameObject как компонент
// 2. Использовать методы Unity (Start, Update, etc.)
// 3. Обращаться к transform, gameObject
}
Жизненный цикл Unity
Unity автоматически вызывает специальные методы в определенном порядке:
Фаза инициализации
using UnityEngine;
public class LifecycleDemo : MonoBehaviour
{
void Awake()
{
// Вызывается ПЕРВЫМ для каждого объекта
// Используется для инициализации внутренних переменных
Debug.Log("Awake: Объект создан");
}
void Start()
{
// Вызывается один раз перед первым Update()
// Используется для настройки связей между объектами
Debug.Log("Start: Готов к работе");
}
void OnEnable()
{
// Вызывается при включении объект в инспекторе
Debug.Log("OnEnable: включен");
}
}
Фаза обновления (игровой цикл)
using UnityEngine;
public class UpdateDemo : MonoBehaviour
{
void Update()
{
// Вызывается каждый кадр (обычно 60-120 раз в секунду)
// Используется для игровой логики, обработки ввода
Debug.Log($"Update: Кадр");
}
void FixedUpdate()
{
// Вызывается с фиксированной частотой (обычно 50 раз в секунду)
// Используется для физических расчетов
Debug.Log($"FixedUpdate: Кадр");
}
}
Фаза уничтожения
using UnityEngine;
public class DestructionDemo : MonoBehaviour
{
void OnDestroy()
{
// Вызывается перед уничтожением объекта
// Используется для очистки ресурсов
Debug.Log("OnDestroy: Объект уничтожается");
}
void OnDisable()
{
// Вызывается при отключении объект в инспекторе
Debug.Log("OnDisable: включен");
}
}
🔧 Компоненты и Инспектор
Как Unity связывает код с интерфейсом
Unity использует сериализацию для автоматического отображения полей C# в Inspector.
using UnityEngine;
public class SerializationRules : MonoBehaviour
{
// ✅ Будут показаны в Inspector:
public int publicInt = 10;
public float publicFloat = 5.5f;
public string publicString = "Hello";
public GameObject publicGameObject;
[SerializeField] // - Так выглядит атрибут
private int serializedPrivate = 20;
// ❌ НЕ будут показаны в Inspector:
private int privateInt = 30;
public static int staticVariable = 100;
}
Атрибуты для настройки Inspector
using UnityEngine;
public class InspectorCustomization : MonoBehaviour
{
[Header("Настройки движения")] // - Так выглядит атрибут
public float moveSpeed = 5f;
public float jumpForce = 10f;
[Space(10)] // Добавляет пустое место
[Header("Настройки здоровья")]
[Range(0, 100)] // Слайдер от 0 до 100
public int health = 100;
[Tooltip("Эта переменная управляет скоростью атаки")]
public float attackSpeed = 1f;
}
Получение и работа с компонентами
using UnityEngine;
public class ComponentAccess : MonoBehaviour
{
void Update()
{
// Пример 1: Прямой доступ к Rigidbody через GetComponent (кнопка 1)
if (Input.GetKeyDown(KeyCode.F1))
{
Rigidbody rb = GetComponent();
rb.AddForce(Vector3.up * 300f); // Подкидываем объект вверх
Debug.Log("Объект подброшен!");
}
// Пример 2: Безопасный доступ к Renderer через TryGetComponent (кнопка 2)
if (Input.GetKeyDown(KeyCode.F2))
{
if (TryGetComponent(out Renderer renderer))
{
renderer.material.color = Color.red; // Меняем цвет на красный
Debug.Log("Цвет изменен на красный!");
}
}
// Пример 3: Прямой доступ к Collider (кнопка 3)
if (Input.GetKeyDown(KeyCode.F3))
{
Collider col = GetComponent();
col.enabled = !col.enabled; // Включаем/выключаем коллизию
Debug.Log($"Коллизия: {(col.enabled ? "включена" : "выключена")}");
}
}
}
Динамическое добавление компонентов
using UnityEngine;
using System.Collections;
using static System.Threading.Tasks.Task;
public class DynamicComponents : MonoBehaviour
{
async void Start()
{
Debug.Log("=== Добавление компонентов ===");
// Добавляем Rigidbody
Rigidbody newRb = gameObject.AddComponent();
newRb.mass = 2f;
Debug.Log("Rigidbody добавлен");
await Delay(1500); // остановит все приложение на 1.5 секунды
// Добавляем BoxCollider
BoxCollider newCollider = gameObject.AddComponent();
newCollider.size = new Vector3(1, 1, 1);
Debug.Log("BoxCollider добавлен");
await Delay(1500); // остановит все приложение на 1.5 секунды
// Удаляем Rigidbody
Destroy(newRb);
Debug.Log("Rigidbody удален");
}
}
Взаимодействие между компонентами
using UnityEngine;
// Компонент который хранит здоровье
public class Health : MonoBehaviour
{
public int currentHealth = 100;
public int maxHealth = 100;
public void TakeDamage(int damage)
{
currentHealth -= damage;
Debug.Log($"Получен урон: {damage}. Здоровье: {currentHealth}/{maxHealth}");
if (currentHealth <= 0)
{
Debug.Log("Объект уничтожен!");
Destroy(gameObject);
}
}
}
// Компонент который может атаковать
public class Weapon : MonoBehaviour
{
public Health targetHealth; // Нужно перетащить созданное здоровье сюда в Inspector-е
public int damage = 25;
void Update()
{
if (Input.GetKeyDown(KeyCode.F) && targetHealth != null)
{
targetHealth.TakeDamage(damage);
Debug.Log($"Атака! Нанесен урон: {damage}");
}
}
}
🎮 Обработка ввода с Input
Unity предоставляет класс Input для обработки всех видов пользовательского ввода. Вся обработка ввода должна происходить в методах Update().
Методы обработки клавиатуры
using UnityEngine;
public class KeyboardInput : MonoBehaviour
{
void Update()
{
// Проверка нажатия (срабатывает один раз)
if (Input.GetKeyDown(KeyCode.Space))
{
Debug.Log("Пробел НАЖАТ");
}
// Проверка удержания (срабатывает каждый кадр)
if (Input.GetKey(KeyCode.W))
{
Debug.Log("W УДЕРЖИВАЕТСЯ");
transform.Translate(Vector3.forward * 5f * Time.deltaTime);
}
// Проверка отпускания (срабатывает один раз)
if (Input.GetKeyUp(KeyCode.LeftShift))
{
Debug.Log("Shift ОТПУЩЕН");
}
}
}
Системы осей Unity
Unity предоставляет предустановленные оси для упрощения обработки ввода:
using UnityEngine;
public class AxisInput : MonoBehaviour
{
void Update()
{
// Горизонтальная ось: A/Стрелка влево = -1, D/Стрелка вправо = 1
float horizontal = Input.GetAxis("Horizontal");
// Вертикальная ось: S/Стрелка вниз = -1, W/Стрелка вверх = 1
float vertical = Input.GetAxis("Vertical");
Debug.Log($"Horizontal: {horizontal:F2}, Vertical: {vertical:F2}");
// GetAxisRaw возвращает только -1, 0, или 1 (без инерции)
float rawHorizontal = Input.GetAxisRaw("Horizontal");
// Создание вектора движения
Vector3 movement = new Vector3(horizontal, 0, vertical);
// Проверка на наличие ввода
if (movement.magnitude > 0.1f)
{
transform.Translate(movement * 5f * Time.deltaTime);
}
}
}
Обработка ввода мыши
using UnityEngine;
public class MouseInput : MonoBehaviour
{
void Update()
{
// Левая кнопка мыши (кнопка 0)
if (Input.GetMouseButtonDown(0))
{
Debug.Log("Левая кнопка мыши НАЖАТА");
}
// Позиция мыши
Vector3 mousePosition = Input.mousePosition;
// Движение мыши
float mouseX = Input.GetAxis("Mouse X");
float mouseY = Input.GetAxis("Mouse Y");
// Колесо мыши
float scroll = Input.GetAxis("Mouse ScrollWheel");
if (Mathf.Abs(scroll) > 0.01f)
{
Debug.Log($"Колесо мыши: {scroll:F2}");
}
}
}
💡 Поддержка геймпадов: Unity поддерживает геймпады через те же оси и кнопки. Оси "Horizontal" и "Vertical" автоматически работают с левым стиком геймпада. Настройки можно посмотреть в Edit → Project Settings → Input Manager.
🐛 Отладка с Debug.Log
Debug.Log - это основной инструмент для отладки в Unity. Все сообщения выводятся в Console Window (Window → General → Console).
Базовые методы Debug
using UnityEngine;
public class DebugBasics : MonoBehaviour
{
public int playerHealth = 100;
public float playerSpeed = 5.5f;
public string playerName = "Hero";
void Start()
{
// Обычное сообщение (белый цвет в консоли)
Debug.Log("Игра началась!");
// Предупреждение (желтый цвет)
Debug.LogWarning("Внимание: низкий уровень здоровья!");
// Ошибка (красный цвет)
Debug.LogError("Критическая ошибка: игрок не найден!");
// Вывод переменных
Debug.Log("Здоровье игрока: " + playerHealth);
// Современное форматирование
Debug.Log($"Игрок {playerName} имеет {playerHealth} HP и скорость {playerSpeed}");
}
void Update()
{
// Условный вывод
if (Input.GetKeyDown(KeyCode.F1))
{
Debug.Log($"Текущая позиция: {transform.position}");
Debug.Log($"Текущий кадр: {Time.frameCount}");
}
}
}
Отладка с контекстом
using UnityEngine;
public class ContextualDebug : MonoBehaviour
{
void Start()
{
// Обычное сообщение
Debug.Log("Простое сообщение");
// Сообщение с привязкой к объекту (показывает этот объект в иерархии)
Debug.Log("Сообщение с контекстом объекта", this);
// Поиск другого объекта
GameObject target = GameObject.Find("Target");
if (target != null)
{
Debug.Log("Найден целевой объект", target);
}
else
{
Debug.LogError("Целевой объект не найден!");
}
}
}