HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-02-21
1 <p><a>#Руководства</a></p>
1 <p><a>#Руководства</a></p>
2 <ul><li>24 июл 2019</li>
2 <ul><li>24 июл 2019</li>
3 <li>0</li>
3 <li>0</li>
4 </ul><p>Создание шутеров позволяет научиться многим вещам, которые пригодятся в разработке игр других жанров.</p>
4 </ul><p>Создание шутеров позволяет научиться многим вещам, которые пригодятся в разработке игр других жанров.</p>
5 <p> vlada_maestro / shutterstock</p>
5 <p> vlada_maestro / shutterstock</p>
6 <p>Пишет о программировании, в свободное время создаёт игры. Мечтает открыть свою студию и выпускать ламповые RPG.</p>
6 <p>Пишет о программировании, в свободное время создаёт игры. Мечтает открыть свою студию и выпускать ламповые RPG.</p>
7 <p>На примере шутеров можно научиться нескольким очень полезным приемам работы с Unity: инстанцированию префабов, созданию логики для NPC, изменению здоровья персонажей и так далее.</p>
7 <p>На примере шутеров можно научиться нескольким очень полезным приемам работы с Unity: инстанцированию префабов, созданию логики для NPC, изменению здоровья персонажей и так далее.</p>
8 <p>Перед чтением статьи рекомендуем ознакомиться с другими нашими материалами о базовых навыках работы с Unity, которые пригодятся для создания шутера:</p>
8 <p>Перед чтением статьи рекомендуем ознакомиться с другими нашими материалами о базовых навыках работы с Unity, которые пригодятся для создания шутера:</p>
9 <ul><li><a>Как создать игру в Unity</a>.</li>
9 <ul><li><a>Как создать игру в Unity</a>.</li>
10 <li><a>Создание анимации в Unity</a>.</li>
10 <li><a>Создание анимации в Unity</a>.</li>
11 </ul><p>Шутер (от англ.<strong>Shoot</strong> - стрелять) - это игра, в которой игрок должен стрелять из какого-либо оружия (пистолет, лук, лазерная винтовка), чтобы побеждать врагов. Добавления этой механики в игру достаточно, чтобы ее можно было назвать шутером.</p>
11 </ul><p>Шутер (от англ.<strong>Shoot</strong> - стрелять) - это игра, в которой игрок должен стрелять из какого-либо оружия (пистолет, лук, лазерная винтовка), чтобы побеждать врагов. Добавления этой механики в игру достаточно, чтобы ее можно было назвать шутером.</p>
12 <p>Кроме стрельбы, также можно реализовать и другие возможности:</p>
12 <p>Кроме стрельбы, также можно реализовать и другие возможности:</p>
13 <ul><li>управление транспортом;</li>
13 <ul><li>управление транспортом;</li>
14 <li>прокачку персонажа;</li>
14 <li>прокачку персонажа;</li>
15 <li>торговлю;</li>
15 <li>торговлю;</li>
16 <li>исследование мира;</li>
16 <li>исследование мира;</li>
17 <li>крафтинг (создание игровых предметов).</li>
17 <li>крафтинг (создание игровых предметов).</li>
18 </ul><p>При этом не важно, в каком сеттинге находится игра (фэнтези, фантастика, Средневековье) и сколько в ней измерений (два или три), - она все равно будет считаться шутером, если есть возможность стрелять. Поэтому в статье основное внимание уделено именно этой механике.</p>
18 </ul><p>При этом не важно, в каком сеттинге находится игра (фэнтези, фантастика, Средневековье) и сколько в ней измерений (два или три), - она все равно будет считаться шутером, если есть возможность стрелять. Поэтому в статье основное внимание уделено именно этой механике.</p>
19 <p>Есть два основных способа реализовать стрельбу:</p>
19 <p>Есть два основных способа реализовать стрельбу:</p>
20 <ul><li><strong>Префабы</strong>. Добавляет на карту снаряд, которому можно прописать поведение - направление полета, действия при попадании и так далее.</li>
20 <ul><li><strong>Префабы</strong>. Добавляет на карту снаряд, которому можно прописать поведение - направление полета, действия при попадании и так далее.</li>
21 <li><strong>Лучи (Raycast)</strong>. Движок рисует невидимую линию от какой-нибудь точки в заданном направлении и возвращает данные о том, есть ли что-нибудь на пути.</li>
21 <li><strong>Лучи (Raycast)</strong>. Движок рисует невидимую линию от какой-нибудь точки в заданном направлении и возвращает данные о том, есть ли что-нибудь на пути.</li>
22 </ul><p>Каждый стоит разобрать более подробно.</p>
22 </ul><p>Каждый стоит разобрать более подробно.</p>
23 <p>В первую очередь нужно подготовить всё, чтобы персонаж мог стрелять. Начать стоит с создания точки, откуда будет лететь снаряд или направляться луч. Для этого добавьте пустой объект с именем<em>FirePoint</em>и поместите его внутрь персонажа, расположив возле дула его оружия:</p>
23 <p>В первую очередь нужно подготовить всё, чтобы персонаж мог стрелять. Начать стоит с создания точки, откуда будет лететь снаряд или направляться луч. Для этого добавьте пустой объект с именем<em>FirePoint</em>и поместите его внутрь персонажа, расположив возле дула его оружия:</p>
24 <p>Затем нужно написать код, который позволит персонажу вращаться вместе с этой точкой. Создайте скрипт Controller.cs и прикрепите его к персонажу:</p>
24 <p>Затем нужно написать код, который позволит персонажу вращаться вместе с этой точкой. Создайте скрипт Controller.cs и прикрепите его к персонажу:</p>
25 public class Controller : MonoBehaviour { private Rigidbody2D rb; private float speed = 5f; private bool isRightSide = true; void Start() { rb = GetComponent&lt;Rigidbody2D&gt;(); //Получение компонентов } void Update() { //Движение float moveX = Input.GetAxis("Horizontal"); rb.MovePosition(rb.position + Vector2.right * moveX * speed * Time.deltaTime); if ((moveX &gt; 0f &amp;&amp; !isRightSide) || (moveX &lt; 0f &amp;&amp; isRightSide)) //Если персонаж начал двигаться в противоположную сторону { Spin(); } } void Spin() { isRightSide = !isRightSide; transform.Rotate(0f, 180f, 0f); //Вращение персонажа по оси X на 180 градусов } }<p>Теперь персонаж сможет двигаться и вращаться вместе с объектом<em>FirePoint:</em></p>
25 public class Controller : MonoBehaviour { private Rigidbody2D rb; private float speed = 5f; private bool isRightSide = true; void Start() { rb = GetComponent&lt;Rigidbody2D&gt;(); //Получение компонентов } void Update() { //Движение float moveX = Input.GetAxis("Horizontal"); rb.MovePosition(rb.position + Vector2.right * moveX * speed * Time.deltaTime); if ((moveX &gt; 0f &amp;&amp; !isRightSide) || (moveX &lt; 0f &amp;&amp; isRightSide)) //Если персонаж начал двигаться в противоположную сторону { Spin(); } } void Spin() { isRightSide = !isRightSide; transform.Rotate(0f, 180f, 0f); //Вращение персонажа по оси X на 180 градусов } }<p>Теперь персонаж сможет двигаться и вращаться вместе с объектом<em>FirePoint:</em></p>
26 <p>После этого можно приступить к скрипту, который позволит стрелять. Назовите его<em>Shooting.cs</em>, добавьте к персонажу и используйте следующий код:</p>
26 <p>После этого можно приступить к скрипту, который позволит стрелять. Назовите его<em>Shooting.cs</em>, добавьте к персонажу и используйте следующий код:</p>
27 public class Shooting : MonoBehaviour { public GameObject bullet; //Снаряд public Transform firePoint; //Точка, с которой будут отправляться снаряды и лучи public LineRenderer lineRenderer; //Луч void Update() { if (Input.GetKeyDown(KeyCode.RightControl)) //Если игрок нажал на правый Ctrl { //Вызов метода стрельбы снарядами ShootBullet(); //Вызов метода стрельбы лучами StartCoroutine(Shoot()); //Выберите один из них } } }<p>Теперь можно разобрать оба способа стрельбы.</p>
27 public class Shooting : MonoBehaviour { public GameObject bullet; //Снаряд public Transform firePoint; //Точка, с которой будут отправляться снаряды и лучи public LineRenderer lineRenderer; //Луч void Update() { if (Input.GetKeyDown(KeyCode.RightControl)) //Если игрок нажал на правый Ctrl { //Вызов метода стрельбы снарядами ShootBullet(); //Вызов метода стрельбы лучами StartCoroutine(Shoot()); //Выберите один из них } } }<p>Теперь можно разобрать оба способа стрельбы.</p>
28 <p>Для начала нужно создать снаряд. Для этого перетащите на карту спрайт и назовите его<em>Bullet</em>:</p>
28 <p>Для начала нужно создать снаряд. Для этого перетащите на карту спрайт и назовите его<em>Bullet</em>:</p>
29 <p>Добавьте коллайдер с триггером и создайте скрипт<em>Bullet.cs</em>, в котором будут обрабатываться попадания (он будет рассмотрен чуть позже). Сохраните объект в качестве префаба, а потом перетащите его в компонент<em>Shooting.cs</em>. Туда же перетащите<em>FirePoint</em>:</p>
29 <p>Добавьте коллайдер с триггером и создайте скрипт<em>Bullet.cs</em>, в котором будут обрабатываться попадания (он будет рассмотрен чуть позже). Сохраните объект в качестве префаба, а потом перетащите его в компонент<em>Shooting.cs</em>. Туда же перетащите<em>FirePoint</em>:</p>
30 <p>Теперь нужно написать метод, который будет создавать (инстанцировать) новые снаряды на карте:</p>
30 <p>Теперь нужно написать метод, который будет создавать (инстанцировать) новые снаряды на карте:</p>
31 void ShootBullet() { Instantiate(bullet, firePoint.position, firePoint.rotation); }<p>Вот как это выглядит:</p>
31 void ShootBullet() { Instantiate(bullet, firePoint.position, firePoint.rotation); }<p>Вот как это выглядит:</p>
32 <p>Пока снаряд остается на месте. Чтобы это исправить, нужно прописать в <em>Bullet.CS</em>этот код:</p>
32 <p>Пока снаряд остается на месте. Чтобы это исправить, нужно прописать в <em>Bullet.CS</em>этот код:</p>
33 public class Bullet : MonoBehaviour { private Rigidbody2D rb; private float speed = 15f; private int damage = 20; private int life = 0; private int lifeMax = 500; void Start() { rb = GetComponent&lt;Rigidbody2D&gt;(); rb.velocity = transform.right * speed; //Изменение скорости } void Update() { life++; if (life &gt;= lifeMax) { Explode(); //Если снаряд пролетел определенное расстояние и ни с чем не столкнулся, его нужно удалить, чтобы он не расходовал ресурсы } } void OnTriggerEnter2D(Collider2D hitInfo) //Метод, который срабатывает при попадании { Explode(); } void Explode() { Destroy(gameObject); //Уничтожение объекта } }<p>Теперь снаряд будет лететь и уничтожаться при попадании во что-то:</p>
33 public class Bullet : MonoBehaviour { private Rigidbody2D rb; private float speed = 15f; private int damage = 20; private int life = 0; private int lifeMax = 500; void Start() { rb = GetComponent&lt;Rigidbody2D&gt;(); rb.velocity = transform.right * speed; //Изменение скорости } void Update() { life++; if (life &gt;= lifeMax) { Explode(); //Если снаряд пролетел определенное расстояние и ни с чем не столкнулся, его нужно удалить, чтобы он не расходовал ресурсы } } void OnTriggerEnter2D(Collider2D hitInfo) //Метод, который срабатывает при попадании { Explode(); } void Explode() { Destroy(gameObject); //Уничтожение объекта } }<p>Теперь снаряд будет лететь и уничтожаться при попадании во что-то:</p>
34 <p>В первую очередь нужно добавить персонажу объект<em>Effect -&gt; Line</em>.</p>
34 <p>В первую очередь нужно добавить персонажу объект<em>Effect -&gt; Line</em>.</p>
35 <p>Укажите в X - 1, а в Z - 0, а затем поставьте галочку<em>Use World Space</em>. После этого можно изменить толщину линии, поменять цвет, закруглить края и так далее.</p>
35 <p>Укажите в X - 1, а в Z - 0, а затем поставьте галочку<em>Use World Space</em>. После этого можно изменить толщину линии, поменять цвет, закруглить края и так далее.</p>
36 <p>Прикрепите получившуюся линию к скрипту<em>Shooting.cs</em>и добавьте следующий метод:</p>
36 <p>Прикрепите получившуюся линию к скрипту<em>Shooting.cs</em>и добавьте следующий метод:</p>
37 IEnumerator Shoot() //Выполнение методов этого типа можно остановить на определенный срок { RaycastHit2D hitInfo = Physics2D.Raycast(firePoint.position, firePoint.right); //Создание луча if (hitInfo) //Если луч во что-то попал { lineRenderer.SetPosition(0, firePoint.position); //Задание позиции начала линии lineRenderer.SetPosition(1, hitInfo.point); //Конец линии } else //Если попадания не было { lineRenderer.SetPosition(0, firePoint.position); lineRenderer.SetPosition(1, firePoint.position + firePoint.right * 50); } lineRenderer.enabled = true; //Включение отображения линии yield return 0; //Ждать один кадр lineRenderer.enabled = false; //Отключить отображение линии }<p>Вот как это выглядит:</p>
37 IEnumerator Shoot() //Выполнение методов этого типа можно остановить на определенный срок { RaycastHit2D hitInfo = Physics2D.Raycast(firePoint.position, firePoint.right); //Создание луча if (hitInfo) //Если луч во что-то попал { lineRenderer.SetPosition(0, firePoint.position); //Задание позиции начала линии lineRenderer.SetPosition(1, hitInfo.point); //Конец линии } else //Если попадания не было { lineRenderer.SetPosition(0, firePoint.position); lineRenderer.SetPosition(1, firePoint.position + firePoint.right * 50); } lineRenderer.enabled = true; //Включение отображения линии yield return 0; //Ждать один кадр lineRenderer.enabled = false; //Отключить отображение линии }<p>Вот как это выглядит:</p>
38 <p>Теперь, выбрав подходящий способ стрельбы, можно реализовать работу с очками здоровья.</p>
38 <p>Теперь, выбрав подходящий способ стрельбы, можно реализовать работу с очками здоровья.</p>
39 <p>За очки жизни будет отвечать скрипт<em>Health.cs</em> - его нужно добавить всем объектам, которые должны получать повреждения при попадании.</p>
39 <p>За очки жизни будет отвечать скрипт<em>Health.cs</em> - его нужно добавить всем объектам, которые должны получать повреждения при попадании.</p>
40 public class Health : MonoBehaviour { public int hp = 100; public int hpMax = 100; void Update() { if (hp &gt; hpMax) { hp = hpMax; } if (hp &lt;= 0) { Die(); } } public void Hit(int damage) { hp -= damage; } void Die() { Destroy(gameObject); } }<p>Теперь нужно изменить код попадания внутри класса<em>Bullet.cs:</em></p>
40 public class Health : MonoBehaviour { public int hp = 100; public int hpMax = 100; void Update() { if (hp &gt; hpMax) { hp = hpMax; } if (hp &lt;= 0) { Die(); } } public void Hit(int damage) { hp -= damage; } void Die() { Destroy(gameObject); } }<p>Теперь нужно изменить код попадания внутри класса<em>Bullet.cs:</em></p>
41 void OnTriggerEnter2D(Collider2D hitInfo) { Health health = hitInfo.GetComponent&lt;Health&gt;(); if (health) { health.Hit(damage); } Explode(); }<p>Обновленный метод проверяет, есть ли у объекта, в который попал снаряд, компонент<em>Health</em>. Если он существует, то вызывается метод<em>Hit()</em>, который отнимает здоровье.</p>
41 void OnTriggerEnter2D(Collider2D hitInfo) { Health health = hitInfo.GetComponent&lt;Health&gt;(); if (health) { health.Hit(damage); } Explode(); }<p>Обновленный метод проверяет, есть ли у объекта, в который попал снаряд, компонент<em>Health</em>. Если он существует, то вызывается метод<em>Hit()</em>, который отнимает здоровье.</p>
42 <p>Примерно такой же код можно добавить в метод запуска луча.</p>
42 <p>Примерно такой же код можно добавить в метод запуска луча.</p>
43 <p>Чтобы игра не превратилась в стрельбу по неподвижным мишеням, нужно написать скрипт поведения объектов -<em>NPC.cs</em>:</p>
43 <p>Чтобы игра не превратилась в стрельбу по неподвижным мишеням, нужно написать скрипт поведения объектов -<em>NPC.cs</em>:</p>
44 public class NPC : MonoBehaviour { public GameObject bullet; public Transform firePoint; private Rigidbody2D rb; private Animator animator; private float speed = 5f; private bool isRightSide = true; private int timer = 0; private int timerMax = 50; private int action = 0; private int reload = 0; private int reloadMax = 5; void Start() { rb = GetComponent&lt;Rigidbody2D&gt;(); animator = GetComponent&lt;Animator&gt;(); } void Update() { timer++; if (timer &gt;= timerMax) { timer = 0; action++; } float moveX = 0f; switch (action) { case 0: moveX = 1f; break; case 1: moveX = 0f; break; case 2: moveX = -1f; break; case 3: moveX = 0f; break; default: action = 0; break; } rb.MovePosition(rb.position + Vector2.right * moveX * speed * Time.deltaTime); if ((moveX &gt; 0f &amp;&amp; !isRightSide) || (moveX &lt; 0f &amp;&amp; isRightSide)) { Spin(moveX); } //Shooting if (reload &gt;= 1) { reload--; } RaycastHit2D hitInfo = Physics2D.Raycast(firePoint.position, firePoint.right); if (hitInfo) { if (hitInfo.transform.tag == "Player") //Если объекту присвоен тег Player { Shoot(); } } } void Spin(float moveX) { isRightSide = !isRightSide; transform.Rotate(0f, 180f, 0f); } void Shoot() { if (reload &lt;= 0) { Instantiate(bullet, firePoint.position, firePoint.rotation); reload = reloadMax; } } }<p>Так создается примитивная логика поведения персонажа. Он может двигаться или стоять на месте, а если заметит героя (это проверяется с помощью<em>Raycast</em>), то начнет стрелять.</p>
44 public class NPC : MonoBehaviour { public GameObject bullet; public Transform firePoint; private Rigidbody2D rb; private Animator animator; private float speed = 5f; private bool isRightSide = true; private int timer = 0; private int timerMax = 50; private int action = 0; private int reload = 0; private int reloadMax = 5; void Start() { rb = GetComponent&lt;Rigidbody2D&gt;(); animator = GetComponent&lt;Animator&gt;(); } void Update() { timer++; if (timer &gt;= timerMax) { timer = 0; action++; } float moveX = 0f; switch (action) { case 0: moveX = 1f; break; case 1: moveX = 0f; break; case 2: moveX = -1f; break; case 3: moveX = 0f; break; default: action = 0; break; } rb.MovePosition(rb.position + Vector2.right * moveX * speed * Time.deltaTime); if ((moveX &gt; 0f &amp;&amp; !isRightSide) || (moveX &lt; 0f &amp;&amp; isRightSide)) { Spin(moveX); } //Shooting if (reload &gt;= 1) { reload--; } RaycastHit2D hitInfo = Physics2D.Raycast(firePoint.position, firePoint.right); if (hitInfo) { if (hitInfo.transform.tag == "Player") //Если объекту присвоен тег Player { Shoot(); } } } void Spin(float moveX) { isRightSide = !isRightSide; transform.Rotate(0f, 180f, 0f); } void Shoot() { if (reload &lt;= 0) { Instantiate(bullet, firePoint.position, firePoint.rotation); reload = reloadMax; } } }<p>Так создается примитивная логика поведения персонажа. Он может двигаться или стоять на месте, а если заметит героя (это проверяется с помощью<em>Raycast</em>), то начнет стрелять.</p>
45 <p>Это довольно простой шутер, но полученных знаний должно хватить, чтобы разработать что-то более сложное и интересное. Если вы хотите глубже погрузиться в тему разработки игр на Unity, читайте статьи в нашем блоге и записывайтесь на курс "<a>Профессия разработчик игр на Unity</a>".</p>
45 <p>Это довольно простой шутер, но полученных знаний должно хватить, чтобы разработать что-то более сложное и интересное. Если вы хотите глубже погрузиться в тему разработки игр на Unity, читайте статьи в нашем блоге и записывайтесь на курс "<a>Профессия разработчик игр на Unity</a>".</p>
46 <a>Научитесь: Профессия Разработчик игр на Unity с нуля Узнать больше</a>
46 <a>Научитесь: Профессия Разработчик игр на Unity с нуля Узнать больше</a>