🏠 Главная

Unity 2D Tutorial для новичков: создаём игру «Башенки» (City Defender)

Добро пожаловать в пошаговый Unity 2D tutorial для начинающих! В этом уроке мы создадим простую игру-защиту города — City Defender, где башенки отражают падающие метеориты.

Здесь вы научитесь:

Всё будет сделано максимально просто и понятно — шаг за шагом, без сложных систем и лишнего кода. Вам понадобится только Unity 6.0 LTS или любая версия с поддержкой 2D. Даже если вы впервые открываете Unity — этот урок подойдёт идеально.

Шаг 1. Создаём проект

Запусти Unity Hub и создай новый 2D-проект, выбрав шаблон 2D Core (Built-In Render Pipeline) — это простая и чистая основа для твоей первой 2D-игры-защиты города.

Для этого открой Unity Hub → вкладка ProjectsNew Project. Найди шаблон 2D Core (Built-In Render Pipeline) (он уже включает базовую 2D-графику и физику). В зависимости от версии Unity название может немного отличаться — если этого шаблона нет, смело выбирай 2D (URP/SRP) или обычный 2D.

В поле имени проекта введи CityDefender или любое другое название, затем выбери удобную папку для сохранения. Нажми Create project и немного подожди, пока Unity всё подготовит.

💡 Совет: используй короткое имя проекта (например CityDefender) — это поможет избежать ошибок в путях и сделать структуру проекта аккуратной.

Шаг 2. Добавляем фон города

Сначала скачай изображение фона — силуэт города, который будет служить фоном для нашей сцены.

Превью картинки с городом
Нажми на картинку, чтобы её скачать.

Импортируй скачанную картинку в Unity — можно через Import New Asset... (правый клик в окне Assets) или просто перетащи файл прямо в папку проекта.

Затем добавь изображение города в сцену: перетащи его из Assets в окно Scene. Настрой размер — например, установи Scale X = 0.5 и Scale Y = 0.5, чтобы фон не был слишком большим.

Переименуй объект в City, чтобы потом легко его находить в Hierarchy.

Фон города в сцене Unity
Фон города добавлен в сцену.

Шаг 3. Добавляем метеоры

В игре будет два типа метеоров: маленькие и быстрые — с небольшим уроном, и большие и медленные — с мощным ударом по городу.

Скачай изображения метеоров ниже:

Превью картинки маленького метеора
Нажми на картинку, чтобы скачать маленький метеор.
Превью картинки большого метеора
Нажми на картинку, чтобы скачать большой метеор.

Импортируй скачанные изображения в Unity — для этого кликни правой кнопкой в окне Assets и выбери Import New Asset..., либо просто перетащи файлы прямо в проект.

Шаг 4. Префаб маленького метеора

Добавь изображение маленького метеора в сцену и назови объект Meteor-Small. Настрой размер — например, Scale X = 0.5 и Scale Y = 0.5.

Создай тег Meteor и назначь его объекту маленького метеора.

Установи Order in Layer так, чтобы метеор рисовался перед фоном города (например, если у City стоит 0, то метеору задай 5). Так он не «пропадёт» за фоном.

Добавь компонент Circle Collider 2D (тело для регистрации попаданий) и компонент Rigidbody 2D (чтобы объект участвовал в физике).

В компоненте Rigidbody 2D у метеора установи Gravity Scale = 0 (будем двигать его вручную скриптом) и Interpolate = Interpolate для более плавного движения.

Создадим небольшой скрипт для хранения параметров метеора и его автоудаления (чтобы он не оставался в игре бесконечно, например, если улетит за экран). Добавь новый MonoBehaviour-скрипт и назови его MeteorParam — имя файла и класса должны совпадать.

using UnityEngine;

public class MeteorParam : MonoBehaviour
{
    public float speed;
    public int hp;
    public int damage;

    // Метеор самоуничтожится через 10 секунд,
    // чтобы не висеть в сцене бесконечно.
    void Start()
    {
        Destroy(gameObject, 10f);
    }
}

Прикрепи скрипт MeteorParam к объекту метеора как компонент. В Inspector установи значения: speed = 5, hp = 1, damage = 1.

Затем создай префаб: перетащи объект метеора из Hierarchy в папку Assets.

После того как префаб создан, можешь удалить маленький метеор из сцены — в дальнейшем мы будем создавать экземпляры из префаба.

Префаб маленького метеора с компонентами в Unity
Префаб маленького метеора с добавленными компонентами.

Шаг 5. Префаб большого метеора

Добавь изображение большого метеора в сцену и назови объект Meteor-Big. Настрой размер — например, Scale X = 0.5 и Scale Y = 0.5.

Назначь тег Meteor объекту большого метеора.

Установи Order in Layer = 5, чтобы метеор рисовался перед городом.

Добавь компонент Circle Collider 2D и Rigidbody 2D.

В компоненте Rigidbody 2D установи Gravity Scale = 0 и Interpolate = Interpolate для плавного движения.

Прикрепи скрипт MeteorParam к объекту метеора как компонент. В Inspector задай параметры: speed = 2, hp = 10, damage = 10.

Создай префаб: перетащи объект метеора из Hierarchy в папку Assets. После этого можешь удалить большой метеор из сцены — в дальнейшем он будет создаваться из префаба.

Префаб большого метеора с компонентами в Unity
Префаб большого метеора с добавленными компонентами.

Шаг 6. Спавнер метеоров

Создай в сцене пустой объект (Create Empty), назови его MeteorSpawner и помести над центром экрана (чуть выше видимой области). Внутри него создай ещё два пустых объекта и расположи их соответственно слева и справа: назови leftBound и rightBound. Убедись, что у всех трёх объектов координата Z = 0.

Теперь напишем скрипт, который будет: выбирать случайную позицию по оси X между границами, спавнить маленькие и большие метеоры с разной периодичностью, и задавать им направление полёта к городу (для больших — строго в город, для маленьких — в случайную точку над линией города).

Создай новый скрипт MonoBehaviour и назови его MeteorSpawner:

using UnityEngine;

public class MeteorSpawner : MonoBehaviour
{
    // границы спавна и объект города для наведения
    public Transform leftBound, rightBound, city;

    // префабы метеоров
    public GameObject smallMeteorPrefab, bigMeteorPrefab;

    // периодичность и стартовые задержки
    public float smallRepeat = 1f, bigRepeat = 5f;
    public float startDelaySmall = 1f, startDelayBig = 5f;

    void Start()
    {
        // Unity будет периодически вызывать методы спавна
        InvokeRepeating(nameof(SpawnSmall), startDelaySmall, smallRepeat);
        InvokeRepeating(nameof(SpawnBig),   startDelayBig,   bigRepeat);
    }

    // спавн маленького метеора
    void SpawnSmall()
    {
        // случайный X между левым и правым ограничителем
        float xPos = Random.Range(leftBound.position.x, rightBound.position.x);
        Vector3 pos = new Vector3(xPos, transform.position.y, 0f);

        // создать метеор
        var go = Instantiate(smallMeteorPrefab, pos, Quaternion.identity);

        // цель: случайный X по ширине, Y — уровень города
        float xTarget = Random.Range(leftBound.position.x, rightBound.position.x);
        Vector3 target = new Vector3(xTarget, city.position.y, 0f);

        // нормализуем вектор направления, чтобы длина = 1 (скорость задаём отдельно)
        Vector3 dir = (target - pos).normalized;

        // ссылки на компоненты и задание скорости
        var mp = go.GetComponent<MeteorParam>();
        var rb = go.GetComponent<Rigidbody2D>();
        if (rb != null && mp != null)
            rb.linearVelocity = dir * mp.speed;
    }

    // спавн большого метеора
    void SpawnBig()
    {
        float xPos = Random.Range(leftBound.position.x, rightBound.position.x);
        Vector3 pos = new Vector3(xPos, transform.position.y, 0f);

        var go = Instantiate(bigMeteorPrefab, pos, Quaternion.identity);

        // большие целятся в центр города
        Vector3 target = city.position;
        Vector3 dir = (target - pos).normalized;

        var mp = go.GetComponent<MeteorParam>();
        var rb = go.GetComponent<Rigidbody2D>();
        if (rb != null && mp != null)
            rb.linearVelocity = dir * mp.speed;
    }
}

Прикрепи скрипт MeteorSpawner к объекту MeteorSpawner. В Inspector заполни поля: перетащи leftBound, rightBound и объект City в соответствующие ссылки, а также назначь префабы smallMeteorPrefab и bigMeteorPrefab. Для начала оставь значения по умолчанию: smallRepeat = 1, bigRepeat = 5, startDelaySmall = 1, startDelayBig = 5.

💡 Совет: Если метеоры «рвутся» или дрожат на экране, убедись, что у их Rigidbody2D стоит Interpolate = Interpolate и Gravity Scale = 0 (мы двигаем их вручную через linearVelocity).

Запусти сцену — метеоры должны падать хаотично в сторону города: маленькие — чаще и быстрее, большие — реже и медленнее.

Объект MeteorSpawner с границами спавна и заполненными ссылками в инспекторе Unity
Объект MeteorSpawner с дочерними границами leftBound и rightBound, а также заполненными полями префабов и ссылками в инспекторе.

Шаг 7. Город получает урон

Пока метеоры просто пролетают сквозь город. Теперь добавим регистрацию попаданий и уменьшение здоровья. Для этого используем обработку столкновений через Trigger.

Выбери в сцене объект City и добавь к нему компонент Box Collider 2D. Это не совсем точно повторяет контур города, но нам сейчас важнее простая и наглядная схема, чем абсолютная точность.

Подстрой размеры коллайдера, чтобы он перекрывал нижнюю часть домов, и включи галочку Is Trigger.

Теперь создадим скрипт, который будет следить за здоровьем города и перезапускать сцену, если город разрушен. Создай новый MonoBehaviour-скрипт и назови его CityDefense.

using UnityEngine;
// нужно для перезагрузки сцены
using UnityEngine.SceneManagement;

public class CityDefense : MonoBehaviour
{
    // здоровье города
    public int hp = 30;

    // вызывается, когда другой 2D-коллайдер входит в триггер
    void OnTriggerEnter2D(Collider2D other)
    {
        // если объект не метеор — выходим
        if (!other.CompareTag("Meteor")) return;

        // получаем компонент с параметрами метеора
        MeteorParam mp = other.GetComponent<MeteorParam>();

        // если параметры есть — наносим урон
        if (mp != null)
        {
            int dmg = mp.damage;
            hp -= dmg;
            // уничтожаем метеор после столкновения
            Destroy(other.gameObject);
        }

        // если здоровье закончилось — перезапускаем сцену
        if (hp <= 0)
        {
            hp = 0;
            string currentScene = SceneManager.GetActiveScene().name;
            SceneManager.LoadScene(currentScene);
        }
    }
}

Прикрепи скрипт CityDefense к объекту City. Теперь метеоры будут наносить урон при попадании, а при обнулении здоровья сцена перезапустится.

Объект City с компонентом Box Collider 2D и скриптом CityDefense в Unity
Объект City с добавленным Box Collider 2D и скриптом CityDefense.

Шаг 8. Лазерная башня

Метеоры пока просто падают и разрушают город — пора построить защитные башни! Начнём с лазерной башни. Она будет стрелять быстро и точно по одиночным целям, нанося небольшой урон. Это ваша первая линия обороны, предназначенная для быстрого уничтожения слабых и быстрых метеоров.

Скачай картинку лазерной башни:

Превью картинки лазерной башни
Нажми на картинку, чтобы скачать лазерную башню.

Импортируй изображение башни в Unity. Затем создай объект лазерной башни в сцене — просто перетащи её спрайт из папки Assets в окно Scene. Назови башню, например, Gemeni Analyzer. Отмасштабируй её (например, X = 0.5, Y = 0.5), а в поле Order in Layer поставь 10, чтобы башня отображалась поверх города.

Для эффекта стрельбы будем использовать компонент Line Renderer. Добавь его к объекту башни и настрой цвет линии по желанию. В поле Material выбери Sprites-Default.

Теперь добавим точку, из которой будет выходить лазер. Создай дочерний объект (правый клик на башне → Create Empty) и назови его LaserAnchor. Перемести его на линзу башни и убедись, что его координата Z = 0.

Создай новый MonoBehaviour-скрипт с именем LaserTower и прикрепи его к башне. Вот готовый код:

using System.Collections;
using UnityEngine;

// Требуем, чтобы на объекте обязательно был LineRenderer
[RequireComponent(typeof(LineRenderer))]
public class LaserTower : MonoBehaviour
{
    public Transform laserAnchor;      // Точка, откуда идёт луч
    public int damage = 1;             // Урон за выстрел
    public float shootingDelay = 1f;   // Задержка между выстрелами
    public float maxTargetDist = 7f;   // Радиус атаки (в юнитах)
    public AudioClip laserSound;       //звуковой файл лазера
    public float volume = 0.7f;        // громкость

    private LineRenderer lr;

    void Awake()
    {
        lr = GetComponent<LineRenderer>();

        // Если точка выстрела не указана — используем сам объект башни
        if (laserAnchor == null) laserAnchor = transform;

        // Базовые настройки линии, чтобы луч был видим
        lr.useWorldSpace = true;
        lr.positionCount = 0;
        lr.startWidth = lr.endWidth = 0.06f;
        lr.sortingOrder = 5;
    }

    void Start()
    {
        // Периодический запуск поиска целей и стрельбы
        InvokeRepeating(nameof(TargetSearchAndShoot), 1f, shootingDelay);
    }

    void TargetSearchAndShoot()
    {
        // 1) Находим все метеоры по тегу "Meteor"
        GameObject[] meteors = GameObject.FindGameObjectsWithTag("Meteor");
        if (meteors.Length == 0) return;

        // 2) Выбираем ближайший в пределах радиуса
        GameObject best = null;
        float bestSqr = maxTargetDist * maxTargetDist;
        Vector3 myPos = transform.position;

        foreach (GameObject m in meteors)
        {
            if (m == null) continue;
            float sqr = (m.transform.position - myPos).sqrMagnitude;
            if (sqr <= bestSqr)
            {
                best = m;
                bestSqr = sqr; // нашли ближе — обновляем расстояние
            }
        }

        if (best == null) return;

        // 3) Визуальный выстрел — короткая вспышка линии
        StartCoroutine(FlashLine(laserAnchor.position, best.transform.position));
        // проигрываем звук на позиции камеры
        if (laserSound != null)
        AudioSource.PlayClipAtPoint(laserSound, Camera.main.transform.position, volume);

        // 4) Уменьшаем здоровье метеора через компонент MeteorParam
        var mp = best.GetComponent<MeteorParam>();
        if (mp != null)
        {
            mp.hp -= damage;
            if (mp.hp <= 0)
                Destroy(best);
        }
    }

    // Короткое включение и отключение лазерного луча
    IEnumerator FlashLine(Vector3 start, Vector3 end)
    {
        lr.positionCount = 2;
        lr.SetPosition(0, start);
        lr.SetPosition(1, end);
        yield return new WaitForSeconds(0.05f);
        lr.positionCount = 0;
    }
}

В инспекторе укажи объект LaserAnchor в поле laserAnchor скрипта.

Также можно добавить звук лазера, чтобы сделать выстрел более живым. Скачай файл 🎵 LaserBeep. Импортируй его в Unity (через Assets → Import New Asset...) и укажи этот звук в поле скрипта laserSound.

Создай префаб башни, перетащив объект из сцены в папку Assets. Теперь можно протестировать работу: запусти сцену — башня будет автоматически искать ближайшие метеоры и стрелять в них. После проверки можешь удалить башню из сцены, ведь у тебя уже есть префаб.

Объект лазерной башни с Line Renderer и скриптом LaserTower
Лазерная башня с Line Renderer и скриптом LaserTower.

Шаг 9. Ракета для ракетной башни

У нас уже есть башня против маленьких метеоров. Теперь сделаем башню с большим уроном против крупных целей. Это будет ракетница: стреляет редко, но сильно. Для начала создадим саму ракету — она будет получать цель и с самонаведением лететь к ней.

Скачай картинку ракеты:

Превью картинки ракеты
Нажми на картинку, чтобы скачать ракету.

Импортируй изображение ракеты в Unity. Создай новый объект, перетащив спрайт ракеты из Assets в окно Scene. Назови объект Missile. Отмасштабируй примерно до X = 0.2, Y = 0.2 и выставь Order in Layer на 5.

Добавь компонент Box Collider 2D и включи опцию Is Trigger. Затем добавь Rigidbody 2D и установи:

Для управления ракетой создадим простой скрипт. Ракета летит вперёд относительно себя и плавно поворачивается в сторону цели. Цель ракета будет получать от ракетной башни в момент запуска.

Создай новый MonoBehaviour-скрипт с именем MissileMove и вставь в него этот код:

using UnityEngine;

public class MissileMove : MonoBehaviour
{
    // Цель ракеты (назначается при спавне из башни)
    public GameObject target;

    public float speed = 7f;             // скорость полёта
    public float rotationSpeed = 100f;   // скорость поворота (град/сек)
    public int damage = 100;             // урон при попадании
    public float lifeTime = 5f;          // время самоуничтожения (сек)

    private Rigidbody2D rb;

    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        // страховка: если ракета никуда не попадёт — исчезнет сама
        Destroy(gameObject, lifeTime);
    }

    void Update()
    {
        if (target == null) return; // цель могла быть уничтожена раньше

        // направление на цель
        Vector3 dir = target.transform.position - transform.position;

        // желаемый поворот в сторону цели (в 2D камера смотрит вдоль Z)
        Quaternion targetRot = Quaternion.LookRotation(Vector3.forward, dir);

        // плавно поворачиваем ракету к цели
        transform.rotation = Quaternion.RotateTowards(
            transform.rotation,
            targetRot,
            rotationSpeed * Time.deltaTime
        );
    }

    void FixedUpdate()
    {
        // полёт вперёд по локальной "верхней" оси спрайта (transform.up)
        rb.linearVelocity = transform.up * speed;
    }

    void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.CompareTag("Meteor"))
        {
            var mp = collision.GetComponent<MeteorParam>();
            if (mp != null)
            {
                mp.hp -= damage;
                if (mp.hp <= 0)
                    Destroy(collision.gameObject);
            }

            // уничтожаем ракету после попадания
            Destroy(gameObject);
        }
    }
}

Добавь скрипт MissileMove компонентом на объект Missile. Затем создай префаб ракеты, перетащив объект из сцены в папку Assets. Когда префаб готов — можно удалить ракету из сцены.

Объект ракеты с коллайдером, Rigidbody2D и скриптом MissileMove
Ракета с коллайдером, Rigidbody2D и скриптом MissileMove.

Готово! В следующем шаге сделаем ракетную башню, которая будет запускать такие ракеты по выбранным целям.

Шаг 10. Ракетная башня

Теперь добавим ракетную башню для борьбы с крупными, толстокожими целями. Эта башня стреляет реже, но наносит высокий урон и, самое главное, она приоритетно ищет только те метеоры, которые имеют большой запас прочности (HP).

Скачай картинку башни:

Превью картинки ракетной башни
Нажми на картинку, чтобы скачать башню.

Импортируй картинку в Unity. Создай объект башни, перетащив спрайт в окно Scene, и назови его, например, Kiava Flow. Отмасштабируй примерно до X = 0.5, Y = 0.5, а в Order in Layer поставь 10, чтобы башня рисовалась поверх ракеты.

Создай новый MonoBehaviour-скрипт с именем MissileTower. В этом скрипте башня будет искать «крупные» метеоры (по HP) в радиусе и запускать в них ракету.

using UnityEngine;

public class MissileTower : MonoBehaviour
{
    // Максимальная дистанция обнаружения метеоров
    public float maxTargetDist = 10f;

    // Порог HP, начиная с которого метеор считаем "крупным"
    public int minHpForMissile = 5;

    // Префаб ракеты (создан заранее в Assets)
    public GameObject missilePrefab;

    // Задержка между выстрелами (сек.)
    public float shootingDelay = 1f;

    // Звук запуска ракеты
    public AudioClip missileSound;
    public float volume = 0.7f; // громкость 0..1

    void Start()
    {
        // Периодически вызываем выстрел
        InvokeRepeating(nameof(ShootBig), 1f, shootingDelay);
    }

    void ShootBig()
    {
        // Ищем все метеоры по тегу
        GameObject[] mets = GameObject.FindGameObjectsWithTag("Meteor");

        GameObject best = null;
        float bestSqr = maxTargetDist * maxTargetDist;
        Vector3 p = transform.position;

        foreach (var m in mets)
        {
            if (m == null) continue;
            var mp = m.GetComponent<MeteorParam>();
            if (mp == null) continue;

            // Берём только "крупных" по текущему HP
            if (mp.hp < minHpForMissile) continue;

            float sqr = (m.transform.position - p).sqrMagnitude;
            if (sqr <= bestSqr)
            {
                best = m;
                bestSqr = sqr;
            }
        }

        if (best == null) return;

        // Стартуем ракету из центра башни
        var missile = Instantiate(missilePrefab, transform.position, Quaternion.identity);

        // Назначаем цели ракеты метеор
        missile.GetComponent<MissileMove>().target = best;

        // Проигрываем звук запуска
        if (missileSound != null)
            AudioSource.PlayClipAtPoint(missileSound, Camera.main.transform.position, volume);
    }
}

Добавь скрипт MissileTower компонентом на объект башни.

Скачай звук запуска ракеты: 🎵 Boom. Импортируй его в Unity и укажи в поле скрипта missileSound.

В инспекторе у башни заполни поля: перетащи префаб ракеты в missilePrefab и добавь звук в missileSound. После проверки работы создай префаб башни, перетащив объект из сцены в папку Assets.

Ракетная башня в сцене с прикреплённым скриптом MissileTower
Ракетная башня Kiava Flow с компонентом MissileTower.

Шаг 11. Подставка для башен

Теперь создадим подставки, на которые игрок сможет устанавливать башни. Они служат точками размещения и помогают управлять местом установки.

Скачай картинку подставки:

Превью картинки подставки для башен
Нажми на картинку, чтобы скачать подставку.

Импортируй картинку в Unity и создай из неё игровой объект — просто перетащи спрайт подставки в окно Scene. Назови объект Stand.

Далее добавь новый тег Stand и назначь его подставке (Inspector → Tag → Add Tag... → Stand). Этот тег позже понадобится для распознавания подставок при установке башен кликом мыши.

Чтобы метеоры не сталкивались с подставками, создадим отдельный физический слой:

  1. В инспекторе справа вверху нажми на LayerAdd Layer....
  2. Добавь новый слой с именем Stand.
  3. Назначь этот слой объекту подставки.

Затем открой настройки проекта: Edit → Project Settings → Physics 2D. В разделе Layer Collision Matrix сними галочку пересечения между слоями Default и Stand. 💡 Теперь метеоры будут просто пролетать сквозь подставки, не сталкиваясь с ними.

Чтобы башня точно вставала на нужное место, нужно немного сместить Pivot (опорную точку спрайта):

  1. Открой спрайт подставки в Sprite Editor.
  2. Включи режим Custom Pivot.
  3. Передвинь точку Pivot на место, где должна стоять башня.
  4. Нажми Apply для сохранения.

Установи Order in Layer на 5, чтобы подставка рисовалась под башнями. Добавь компонент Box Collider 2D — он понадобится, чтобы выбирать подставку кликом мыши при установке башен.

Теперь создай префаб подставки: просто перетащи объект из сцены в папку Assets. После этого можно разместить несколько подставок в сцене — например, пять штук, расположив их в ряд.

💡 Если вы размещаете подставку перед городом, убедитесь, что она не перекрывается коллайдером других объектов. Чтобы избежать этого, можно слегка выдвинуть подставку по оси Z — задать небольшое отрицательное значение (например, -0.1). Это выведет её вперёд в слое сцены, не влияя на 2D-отображение.

Подставка для башни с тегом Stand, отдельным слоем и Box Collider 2D
Подставка Stand с собственным тегом, физическим слоем и коллайдером.

Шаг 12. Установка башен и защита города

Теперь осталось самое интересное — защитить город! Сделаем небольшой скрипт, который позволит устанавливать башни на подставки. При клике левой кнопкой мыши будет ставиться лазерная башня, а при правом клике — ракетная. После установки коллайдер подставки автоматически отключится, чтобы на неё нельзя было поставить вторую башню.

Создай новый MonoBehaviour-скрипт с именем TowerSpawner.

using UnityEngine;

public class TowerSpawner : MonoBehaviour
{
    public GameObject laserTower;   // Префаб лазерной башни (ставится ЛКМ)
    public GameObject missileTower; // Префаб ракетной башни (ставится ПКМ)

    void Update()
    {
        // Левая кнопка мыши — ставим лазерную башню
        if (Input.GetMouseButtonDown(0))
            TryPlaceTower(laserTower);

        // Правая кнопка мыши — ставим ракетную башню
        if (Input.GetMouseButtonDown(1))
            TryPlaceTower(missileTower);
    }

    void TryPlaceTower(GameObject towerPrefab)
    {
        // Проверяем, что префаб назначен
        if (towerPrefab == null) return;

        // Получаем позицию клика мыши в мировых координатах
        Vector2 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);

        // Проверяем, попали ли в объект с коллайдером
        RaycastHit2D hit = Physics2D.Raycast(mousePos, Vector2.zero);
        if (hit.collider == null) return;

        // Если это подставка (тег Stand) — ставим башню
        if (hit.collider.CompareTag("Stand"))
        {
            Instantiate(towerPrefab, hit.transform.position, Quaternion.identity);

            // Отключаем коллайдер подставки, чтобы нельзя было поставить вторую башню
            hit.collider.enabled = false;
        }
    }
}

В сцене создай новый пустой объект (его позиция не имеет значения) и добавь на него компонент TowerSpawner.

В инспекторе укажи ссылки на префабы:

Теперь базовая система установки башен готова! Запусти сцену, кликай по подставкам и строй защиту города. Лазеры и ракеты зажгут небо — и начнётся настоящая битва за выживание!

TowerSpawner — объект в сцене с настройками префабов башен
Объект TowerSpawner с привязанными префабами башен.