2D платформер, стрельба

Стрельба для двухмерной игры, платформера. Пример, прежде всего будет интересен начинающим игроделам, впрочем, не только, так как вариант уже готовый, бери и используй. Код достаточно простой, легко отредактировать если что. Стрельба реализована не через луч, а объектами. То есть, как в обычном платформере, чтобы пули были видимы. Подойдет для игры наподобие, как Contra или для управления какой-нибудь турелью. Реализовано вращение указанного объекта по оси Z, допустим, автомата. И предусмотрено ограничение на вращения. Настраиваются такие параметры как, скорость пули и скорострельность.

2D платформер, стрельба

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

Итак, добавляем на сцену пустой объект, и делаем спрайт оружия дочерним к нему. В дальнейшем, чтобы скрипт работало правильно, вращать мы будем именно дочерний спрайт оружия, но не родителя. Теперь, для спрайта оружия надо еще один пустой дочерний ему объект и пододвинуть его в то место, откуда будут вылетать пули. Важно помнить, что определяющая тут ось Х, проще говоря она должна "вылетать" из дула, в этом же направление будут потом лететь пули.

2D платформер, стрельба

Далее, на родителя модельки нашего оружия вешаем скрипт FireScript2D:

using UnityEngine;
using System.Collections;

public class FireScript2D : MonoBehaviour {

	public float speed = 10; // скорость пули
	public Rigidbody2D bullet; // префаб нашей пули
	public Transform gunPoint; // точка рождения
	public float fireRate = 1; // скорострельность

	public Transform zRotate; // объект для вращения по оси Z

	// ограничение вращения
	public float minAngle = -40;
	public float maxAngle = 40;

	private float curTimeout;
	
	void Start()
	{
	}

	void SetRotation()
	{
		Vector3 mousePosMain = Input.mousePosition;
		mousePosMain.z = Camera.main.transform.position.z; 
		Vector3 lookPos = Camera.main.ScreenToWorldPoint(mousePosMain);
		lookPos = lookPos - transform.position;
		float angle  = Mathf.Atan2(lookPos.y, lookPos.x) * Mathf.Rad2Deg;
		angle = Mathf.Clamp(angle, minAngle, maxAngle);
		zRotate.rotation = Quaternion.AngleAxis(angle, Vector3.forward);
	}
	
	void Update()
	{
		if(Input.GetMouseButton(0))
		{
			Fire();
		}
		else
		{
			curTimeout = 100;
		}

		if(zRotate) SetRotation();
	}

	void Fire()
	{
		curTimeout += Time.deltaTime;
		if(curTimeout > fireRate)
		{
			curTimeout = 0;
			Rigidbody2D clone = Instantiate(bullet, gunPoint.position, Quaternion.identity) as Rigidbody2D;
			clone.velocity = transform.TransformDirection(gunPoint.right * speed);
			clone.transform.right = gunPoint.right;
		}
	}
}

Создадим модельку пули. Там дело на пару кликов. На спрайт пули вешаем компонент Rigidbody 2D и параметр Gravity Scale ставим на ноль, чтобы выключить гравитацию. Добавляем коллайдер и ставим галочку Is Trigger. И перетаскиваем объект в папку с префабами.

2D платформер, стрельба

В завершении, цепляем на префаб скрипт Bullet2D:

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(Rigidbody2D))]

public class Bullet2D : MonoBehaviour {

	void Start()
	{
		// уничтожить объект по истечению указанного времени (секунд), если пуля никуда не попала
		Destroy(gameObject, 20);
	}
	
	void OnTriggerEnter2D(Collider2D coll)
	{
		if(!coll.isTrigger) // чтобы пуля не реагировала на триггер
		{
			switch(coll.tag)
			{
			case "Enemy_1":
				// что-то...
				break;
			case "Enemy_2":
				// что-то еще...
				break;
			}

			Destroy(gameObject);
		}
	}
}

Указываем префаб в переменной скрипта стрельбы и можно тестировать.

Кстати, у основной камеры должен быть стандартный тег MainCamera.

Скачать демо сцену:

У вас нет доступа!

Комментариев 35

Офлайн
Пуля не вылетает. В чем могла бы быть проблема? Unity 5.3
Офлайн
Light 28 декабря 2015
Цитата: Федор Николаев

Пуля не вылетает. В чем могла бы быть проблема? Unity 5.3

При однократном нажатии ПКМ? В демо проект внесены изменения, ошибка исправлена.
Офлайн
Light,
Спасибо,теперь работает.А не подскажете, как реализовать стрельбу в обратную сторону?
Офлайн
Light 28 декабря 2015
Цитата: Федор Николаев
А не подскажете, как реализовать стрельбу в обратную сторону?

Для разворота вправо или влево, нужно сделать отражение спрайта по горизонтали и инвертировать ось вращения.

Например, так:

using UnityEngine;
using System.Collections;

public class FireScript2D : MonoBehaviour {

	public float speed = 10; // скорость пули
	public Rigidbody2D bullet; // префаб нашей пули
	public Transform gunPoint; // точка рождения
	public float fireRate = 1; // скорострельность
	public bool facingRight = true; // направление на старте сцены, вправо?

	public Transform zRotate; // объект для вращения по оси Z

	// ограничение вращения
	public float minAngle = -40;
	public float maxAngle = 40;

	private float curTimeout;
	private int invert;
	
	void Start()
	{
		if(!facingRight) invert = -1; else invert = 1;
	}

	void SetRotation()
	{
		Vector3 mousePosMain = Input.mousePosition;
		mousePosMain.z = Camera.main.transform.position.z; 
		Vector3 lookPos = Camera.main.ScreenToWorldPoint(mousePosMain);
		lookPos = lookPos - transform.position;
		float angle  = Mathf.Atan2(lookPos.y, lookPos.x * invert) * Mathf.Rad2Deg;
		angle = Mathf.Clamp(angle, minAngle, maxAngle);
		zRotate.rotation = Quaternion.AngleAxis(angle, Vector3.forward);
	}
	
	void Update()
	{
		if(Input.GetMouseButton(0))
		{
			Fire();
		}
		else
		{
			curTimeout = 100;
		}

		if(zRotate) SetRotation();

		float h = Input.GetAxis("Horizontal");

		if(h > 0 && !facingRight) Flip(); else if(h < 0 && facingRight) Flip(); 
	}

	void Flip() // отражение по горизонтали
	{
		facingRight = !facingRight;
		Vector3 theScale = transform.localScale;
		theScale.x *= -1;
		invert *= -1;
		transform.localScale = theScale;
	}

	void Fire()
	{
		curTimeout += Time.deltaTime;
		if(curTimeout > fireRate)
		{
			curTimeout = 0;
			Vector3 direction = gunPoint.position - transform.position;
			Rigidbody2D clone = Instantiate(bullet, gunPoint.position, Quaternion.identity) as Rigidbody2D;
			clone.velocity = transform.TransformDirection(direction.normalized * speed);
			clone.transform.right = direction.normalized;
		}
	}
}

Разворот делается с помощью стандартных клавиш управления, AD или стрелки.
Офлайн
Light,
огромнейшее спасибо! Я новичок,вот и докучаю всякими "что" да "как".

P.S. для тех,кто пользуется стандартным скриптом от Unity, в "void Flip" вместо "theScale.x *= -1;" писать "theScale.x *= 1;".
Офлайн
mrSaSoRy 31 мая 2016
Можете подсказать, как реализовать стрельбу в обратную сторону через мышку? т.е. оружие доходит да верхнего предела, и отражается в другую сторону.
Офлайн
Light 1 июня 2016
mrSaSoRy, вот небольшие изменения для скрипта FireScript2D.

using UnityEngine;
using System.Collections;

public class FireScript2D : MonoBehaviour {

	public float speed = 10; // скорость пули
	public Rigidbody2D bullet; // префаб нашей пули
	public Transform gunPoint; // точка рождения
	public float fireRate = 1; // скорострельность
	public bool facingRight = true; // направление на старте сцены, вправо?

	public Transform zRotate; // объект для вращения по оси Z

	// ограничение вращения
	public float minAngle = -40;
	public float maxAngle = 40;

	private float curTimeout, angle;
	private int invert;
	private Vector3 mouse;

	void Start()
	{
		if(!facingRight) invert = -1; else invert = 1;
	}

	void SetRotation()
	{
		Vector3 mousePosMain = Input.mousePosition;
		mousePosMain.z = Camera.main.transform.position.z; 
		mouse = Camera.main.ScreenToWorldPoint(mousePosMain);
		Vector3 lookPos = mouse - transform.position;
		angle  = Mathf.Atan2(lookPos.y, lookPos.x * invert) * Mathf.Rad2Deg;
		angle = Mathf.Clamp(angle, minAngle, maxAngle);
		zRotate.rotation = Quaternion.AngleAxis(angle, Vector3.forward);
	}

	void Update()
	{
		if(Input.GetMouseButton(0))
		{
			Fire();
		}
		else
		{
			curTimeout = 1000;
		}

		if(zRotate) SetRotation();

		if(angle == maxAngle && mouse.x < zRotate.position.x && facingRight) Flip();
		else if(angle == maxAngle && mouse.x > zRotate.position.x && !facingRight) Flip();
	}

	void Flip() // отражение по горизонтали
	{
		facingRight = !facingRight;
		Vector3 theScale = transform.localScale;
		theScale.x *= -1;
		invert *= -1;
		transform.localScale = theScale;
	}

	void Fire()
	{
		curTimeout += Time.deltaTime;
		if(curTimeout > fireRate)
		{
			curTimeout = 0;
			Vector3 direction = gunPoint.position - transform.position;
			Rigidbody2D clone = Instantiate(bullet, gunPoint.position, Quaternion.identity) as Rigidbody2D;
			clone.velocity = transform.TransformDirection(direction.normalized * speed);
			clone.transform.right = direction.normalized;
		}
	}
}

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

Внеси изменения и потестируй. Проведи курсор поверху из одной части экрана в другую.
Офлайн
mrSaSoRy 1 июня 2016
Light,
Большое спасибо, всё супер!!
Офлайн
Katerpiller 9 июня 2016
Здравствуйте. А как можно реализовать персонажа бросающего во врагов снаряды? Например, сюрикены. Там получается что нет оружия, а снаряд летит напрямую из персонажа. Принцип тот же что и с оружием или есть какие-нибудь нюансы? Если можно поподробнее, а то я совсем нуб)))
Офлайн
myxaell 10 июня 2016
а как реализовать вращение снаряда? что добавить или изменить в
void Fire()
{
curTimeout += Time.deltaTime;
if(curTimeout > fireRate)
{
curTimeout = 0;
Vector3 direction = gunPoint.position - transform.position;
Rigidbody2D clone = Instantiate(bullet, gunPoint.position, Quaternion.identity) as Rigidbody2D;
clone.velocity = transform.TransformDirection(direction.normalized * speed);
clone.transform.right = direction.normalized;
}
}
Офлайн
Light 10 июня 2016
Цитата: myxaell
а как реализовать вращение снаряда?

По какой оси? Постоянное или только при "рождении"?

Цитата: Katerpiller
Здравствуйте. А как можно реализовать персонажа бросающего во врагов снаряды? Например, сюрикены. Там получается что нет оружия, а снаряд летит напрямую из персонажа. Принцип тот же что и с оружием или есть какие-нибудь нюансы? Если можно поподробнее, а то я совсем нуб)))

Принцип тот же. Нужна точка, откуда должен вылететь снаряд и направление полета. Нюанс в том, что тут надо момент выстрела определять из аниматора, т. е. когда происходят соответствующие движения.
Офлайн
myxaell 10 июня 2016
Light, по оси Z. Можно ли оба способа описать, если не сложно? Разница в AddForce при "рождении", и обычный transform.rotation при постоянном, я так понял.
Офлайн
Light 10 июня 2016
myxaell, чтобы добавить при рождении рандомный поворот по Z, после строки:

clone.transform.right = direction.normalized;

Пишем:

clone.transform.rotation = Quaternion.Euler(0, 0, Random.Range(0, 360f));

Если нужно постоянное вращение, тогда пишем в том же месте:

clone.angularDrag = 0;
clone.angularVelocity = 100; // скорость вращения
Офлайн
Katerpiller 10 июня 2016
Light, да. Опишите пожалуйста поподробнее как все это делается.

Я думаю по иксу наверное Надо чтобы вперед бросал и все. Вверх и влево вроде не надо)))
Офлайн
Light 10 июня 2016
Katerpiller, это уже индивидуальная тема, скриншоты проекта и подробное описание того, что нужно, пиши мне в ЛС.
Офлайн
Katerpiller 11 июня 2016
Light, лучше сделай урок типа вот этого вот. Народу вроде как тоже интересно.
Офлайн
myxaell 13 июня 2016
Light,
Спасибо за отзывчивость и за интересные, и полезные уроки по си шарпу и юнити.
Офлайн
Light 13 июня 2016
Katerpiller, да там писать то и нечего, изменить нужно только пару моментов, обращайся в ЛС со скринами, посмотрим что да как.
Офлайн
dead 26 августа 2016
Спасибо за урок. Как сделать разброс у пуль?
Офлайн
в скрипте какой то баг мой перс стреляет только вправо!! а когда поварачиваюсь и стреляю влево он не отражается и вылетает
Офлайн
Light 15 февраля 2017
Давуд Ахмедов, кинь ссылку на архив с файлами проекта, посмотрим.
Офлайн
я исправил работает нормально все но количество выстрелов почему то ограниченно когда он стреляет стреляет стреляет потом выходит такая ошибка
Офлайн
Light 15 февраля 2017
Давуд Ахмедов, в скрипте таких ограничений нет.
Офлайн
Light,
сделайте теперь урок по созданию стреляющего врага было бы круто и многим помогли бы smile
Офлайн
Stason4ikRU 11 мая 2017
У меня проблема при повороте влево когда я опускаю курсор оружие поднимает ствол ,когда поднимаю курсор ствол вниз. Как исправить ?



P.S Unity 5.6
Офлайн
Light 12 мая 2017
Stason4ikRU, попробуй эту версию:

using UnityEngine;
using System.Collections;

public class FireScript2D : MonoBehaviour {

	public float speed = 10; // скорость пули
	public Rigidbody2D bullet; // префаб нашей пули
	public Transform gunPoint; // точка рождения
	public float fireRate = 1; // скорострельность
	public bool facingRight = true; // направление на старте сцены, вправо?

	public Transform zRotate; // объект для вращения по оси Z

	// ограничение вращения
	public float minAngle = -40;
	public float maxAngle = 40;

	private float curTimeout, angle;
	private int invert;
	private Vector3 mouse;

	void Start()
	{
		if(!facingRight) invert = -1; else invert = 1;
	}

	void SetRotation()
	{
		Vector3 mousePosMain = Input.mousePosition;
		mousePosMain.z = Camera.main.transform.position.z; 
		mouse = Camera.main.ScreenToWorldPoint(mousePosMain);
		Vector3 lookPos = mouse - transform.position;
		angle  = Mathf.Atan2(lookPos.y, lookPos.x * invert) * Mathf.Rad2Deg;
		angle = Mathf.Clamp(angle * invert, minAngle, maxAngle);
		zRotate.rotation = Quaternion.AngleAxis(angle, Vector3.forward);
	}

	void Update()
	{
		if(Input.GetMouseButton(0))
		{
			Fire();
		}
		else
		{
			curTimeout = 1000;
		}

		if(zRotate) SetRotation();

		if(mouse.x < zRotate.position.x && facingRight) Flip();
		else if(mouse.x > zRotate.position.x && !facingRight) Flip();
	}

	void Flip() // отражение по горизонтали
	{
		facingRight = !facingRight;
		Vector3 theScale = transform.localScale;
		theScale.x *= -1;
		invert *= -1;
		transform.localScale = theScale;
	}

	void Fire()
	{
		curTimeout += Time.deltaTime;
		if(curTimeout > fireRate)
		{
			curTimeout = 0;
			Vector3 direction = gunPoint.position - transform.position;
			Rigidbody2D clone = Instantiate(bullet, gunPoint.position, Quaternion.identity) as Rigidbody2D;
			clone.velocity = transform.TransformDirection(direction.normalized * speed);
			clone.transform.right = direction.normalized;
		}
	}
}
Офлайн
Stason4ikRU 12 мая 2017
Light,Спасибо,работает.Но вернул void Update()
{
if (Input.GetMouseButton(0))
{
Fire();
}
else
{
curTimeout = 1000;
}

if (zRotate) SetRotation();

float h = Input.GetAxis("Horizontal");

if (h > 0 && !facingRight) Flip(); else if (h < 0 && facingR();

т.к не нужна "стрельба в обратную сторону через мышку"
Офлайн
Stason4ikRU 12 мая 2017
Light,
Спасибо,работает. Поменял: if(mouse.x < zRotate.position.x && facingRight) Flip();
else if(mouse.x > zRotate.position.x && !facingRight) Flip();
На:
float h = Input.GetAxis("Horizontal");

if(h > 0 && !facingRight) Flip(); else if(h < 0 && facingRight) Flip();
т.к не мне не нужно:
"...оружие доходит да верхнего предела, и отражается в другую сторону."
Офлайн
Stason4ikRU 12 мая 2017
Как сделать что бы пуля проходила сквозь например "HidenWall".
Офлайн
Light 12 мая 2017
Stason4ikRU, делать проверку объекта по маске или тегу.

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(Rigidbody2D))]

public class Bullet2D : MonoBehaviour {

	[Header("Настройки триггера")]
	[SerializeField] private string[] tagList;
	[SerializeField] private LayerMask layerMask;

	void Start()
	{
		// уничтожить объект по истечению указанного времени (секунд), если пуля никуда не попала
		Destroy(gameObject, 20);
	}

	bool CheckObject(GameObject obj)
	{
		if(((1 << obj.layer) & layerMask) != 0)
		{
			return true;
		}

		foreach(string t in tagList)
		{
			if(obj.tag == t) return true;
		}

		return false;
	}
	
	void OnTriggerEnter2D(Collider2D coll)
	{
		if(!coll.isTrigger && !CheckObject(coll.gameObject))
		{
			switch(coll.tag)
			{
			case "Enemy_1":
				// что-то...
				break;
			case "Enemy_2":
				// что-то еще...
				break;
			}

			Destroy(gameObject);
		}
	}
}

Если указать слой объекта или тег, в настройках триггера.
То пуля пролетит сквозь этот коллайдер.
Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.
  • Дешевый хостинг
  • Яндекс.Метрика