К списку статей

Основы 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

Атрибуты для настройки 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;
}
Атрибуты в Inspector

Получение и работа с компонентами

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 ? "включена" : "выключена")}");
        }
    }
}
Работа с компонентами F1-F3

Динамическое добавление компонентов

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}");
        }
    }
}
Взаимодействие компонентов HP/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("Целевой объект не найден!");
        }
    }
}