Знакомство

Доброго времени суток, дорогие читатели, меня зовут Илья и я начинающий инди разработчик. Я впервые пишу статью и тем более впервые на тему разработки игр. В ней я хочу поделиться своим опытом создания простенького, но душевного раннера наподобие Динозаврика из Хрома. А называется он:

Сцена меню и визуал

Для разработки проекта я выбрал движок Unity, тк немного был с ним знаком. Я создал однотонный теплый фон, расставил окружающие объекты, это были кактусы, камешки, наш главный герой шериф Генри и конечно же бандит. Добавил облака которые появляются в случайный момент времени и оживил их при помощи простого скрипта.

    [SerializeField] private float _speed = 1f;

    private void Update()
    {
        transform.Translate(_speed * Time.deltaTime * -1, 0, 0);
    }

Теперь они постоянно двигаются справа налево.

На сцене добавил кнопки меню и табличку с рекордом. В итоге первая сцена стала выглядеть вот так:

Механика игры

Наконец приступим к созданию самой игры. Я решил,что наш главный герой, шериф, будет находиться на месте, а все объекты будут двигаться ему навстречу.

За пределами видимости камеры я создал спавнеры объектов, а с другой стороны "корзину" для них, при попадании в триггер "мусорки" - объекты удаляются.

Теперь напишем код, который будет контролировать частоту создания объектов:

    [SerializeField] private GameObject[] _templates;
    [SerializeField] private float _minDelay;
    [SerializeField] private float _maxDelay;
    [SerializeField] private float _devationPositionY = 0;
    [SerializeField] private float _spawnTime;

    private bool _isGameOver;

    private void Start()
    {
        _isGameOver = false;
        StartCoroutine(Spawn());
    }

    private IEnumerator Spawn()
    {
        float minPositionY = transform.position.y - _devationPositionY;
        float maxPositionY = transform.position.y + _devationPositionY;

        while (_isGameOver == false)
        {
            float positionY = Random.Range(minPositionY, maxPositionY);

            _spawnTime = Random.Range(_minDelay, _maxDelay);

            var waitForSeconds = new WaitForSeconds(_spawnTime);

            GameObject gameObject = Instantiate(_templates[Random.Range(0, _templates.Length)],
                new Vector3(transform.position.x, positionY, transform.position.z), Quaternion.identity);

            yield return waitForSeconds;
        }
    }

И после создания заставим их двигаться навстречу главному герою, так будет создаваться впечатление, что игрок бежит сам. Осталось добавить анимацию бега и все готово:

Чтобы игра приобрела тот самый смысл раннера, создадим спавнер, из которого будут появляться бомбы, а нам в свою очередь, нужно будет перепрыгивать через них. И вот мы дошли до управления персонажем. Я уже добавил небольшую подсказку на экран, как управлять игроком, осталось реализовать.

   [SerializeField] private float _jumpForce = 500;

    private Rigidbody2D _rigidbody2D;
    private bool _isGround;

    public event Action AnimationJumpPlayed;

    private void Start()
    {
        _rigidbody2D = GetComponent<Rigidbody2D>();
    }

    private void Update()
    {
        Jump();
    }

    private void Jump()
    {
        if (Input.GetKeyDown(KeyCode.Space) && _isGround)
        {
            AnimationJumpPlayed?.Invoke();
        }
    }

    private void OnCollisionStay2D(Collision2D collision)
    {
        if (collision.collider.TryGetComponent(out Flour flour))
            _isGround = true;
    }

    private void OnCollisionExit2D(Collision2D collision)
    {
        if (collision.collider.TryGetComponent(out Flour flour))
            _isGround = false;
    }

Контролер преодоления бомб находится над объектом: если ты попадаешь в этот тригер, счетчик очков увеличивается на 1, но если у тебя не получилось перепрыгнуть, те игрок соприкасается с бомбой - игра заканчивается..

Для усложнения игры, я добавил метод, который на случайном значении очков дополнительно выбрасывает по одной бомбе,дополнительно к уже созданной. Но это еще не все, чем дальше вы заходите, тем быстрее будут двигаться объекты навстречу вам.

Заключение

В конечном итоге, проект получился играбельным, хоть и не очень оптимизированным, этим я хочу заняться в настоящее время, и поделиться с вами в следующей статье.

Комментарии (9)


  1. Mushhi
    18.05.2024 12:23

    Так держать


  1. Timon96
    18.05.2024 12:23

    Молодец! Теперь у тебя впереди лежит долгий и тернистый путь через долину, пройдя которую ты сильно изменишься. Главное - ни кого не слушай.


  1. MonkeyWatchingYou
    18.05.2024 12:23
    +1

    21:9 поддерживается?


  1. Ksu_Ksu
    18.05.2024 12:23
    +1

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

    Не стесняйтесь. Мои две первые и единственные игры лишили меня необходимость ходить на обычную работу.

    Для оптимизации нужно будет удалять все объекты, выходящие за экран, и использовать более производительные методы.


  1. Gasnopf
    18.05.2024 12:23

    Для первой игры очень даже неплохо, главное продолжать расти, а не стоять на одном месте.


  1. Sindiano
    18.05.2024 12:23
    +4

    Пару советов по поводу оптимизации.

    1. Пореже обращаться напрямую к полю transform.position объектов, особенно в часто вызываемых методах типа Update или в циклах, т.к. вызывается extern метод get_transform_Injected, что ресурсозатратно. Например, если есть возможность, постарайтесь кэшировать вектор позиции, особенно, если его значение не меняется. К примеру, ваш код ниже

    private IEnumerator Spawn()
        {
            float minPositionY = transform.position.y - _devationPositionY;
            float maxPositionY = transform.position.y + _devationPositionY;
    
            while (_isGameOver == false)
            {
                float positionY = Random.Range(minPositionY, maxPositionY);
    
                _spawnTime = Random.Range(_minDelay, _maxDelay);
    
                var waitForSeconds = new WaitForSeconds(_spawnTime);
    
                GameObject gameObject = Instantiate(_templates[Random.Range(0, _templates.Length)],
                    new Vector3(transform.position.x, positionY, transform.position.z), Quaternion.identity);
    
                yield return waitForSeconds;
            }
        }

    можно слегка изменить и получить

    private IEnumerator Spawn()
        {
            var spawnPosition = transform.position;
            float minPositionY = spawnPosition.y - _devationPositionY;
            float maxPositionY = spawnPosition.y + _devationPositionY;
    
            while (_isGameOver == false)
            {
                float positionY = Random.Range(minPositionY, maxPositionY);
    
                _spawnTime = Random.Range(_minDelay, _maxDelay);
    
                var waitForSeconds = new WaitForSeconds(_spawnTime);
    
                GameObject gameObject = Instantiate(_templates[Random.Range(0, _templates.Length)],
                    new Vector3(spawnPosition.x, positionY, spawnPosition.z), Quaternion.identity);
    
                yield return waitForSeconds;
            }
        }

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

    1. Вопреки мнению в комментарии другого пользователя

    Для оптимизации нужно будет удалять все объекты, выходящие за экран...

    много раз создавать и удалять однотипные объекты (например снаряды в шутерах или бомбы и фоновые объекты в вашей игре) методами GameObject.Instantiate и GameObject.Destroy не рекомендуется из-за количества создающегося мусора (особенно, если объект с большим количеством полей) и лишней нагрузки на процессов. Рекомендую посмотреть в сторону Пулов Объектов и вместо того, чтобы постоянно создавать и удалать новые объекты, вы создаете определенное их количество в Пуле при старте и при необходимости достаете их из этого пула, а вместо удаления просто делаете объекты неактивными. Если интересно, гляньте статью https://www.kodeco.com/847-object-pooling-in-unity .


  1. vikta5
    18.05.2024 12:23
    +1

    Молодец, движешься в правильном направлении. Рекомендую в первую очередь добавить пул, чтобы не создавать (и я не вижу здесь уничтожение) постоянно объекты. Это простое действие, но очень важное для оптимизации даже на этом этапе


  1. Zara6502
    18.05.2024 12:23

    за моими плечами мало игр, тем более дошедших до релиза и проблема чаще всего не в отсутствии технических средств, а в том, чтобы саму игру придумать. Делать что-то простое не хочется, а сложное - бесконечный процесс.


    1. Palesandr
      18.05.2024 12:23
      +1

      у меня всего одна игра до релиза дошла.

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