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

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

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. И перетаскиваем объект в папку с префабами.


В завершении, цепляем на префаб скрипт 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.

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

Внимание! Посетители, находящиеся в группе Гости, не могут скачивать файлы.

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

Офлайн
linar2131 16 января 2019
Новичок в юньке. Снаряд после спавна вылетает немного вверх.
Офлайн
очень помогло, огромное спасибо. И за комменты с помощью тоже
Офлайн
Dordoro 16 мая 2018
Как менять спрайт пули, я поменял спрайт у bullet. А клоны всё равно старые.
Офлайн
mekkoss 6 мая 2018
Извиняюсь, проблему решил.
Офлайн
mekkoss 6 мая 2018
Light, немного решил проблему исправив "Vector3 direction = gunPoint.position - transform.position;" на "Vector3 direction = gunPoint.position;"
Но пули вверх стали боком летать

И там если я отхожу влево то пули опять летят назад как на гифке которую я скинул.
Офлайн
mekkoss 6 мая 2018
Light,
Офлайн
Light 6 мая 2018
mekkoss, ссылка не работает.
Офлайн
mekkoss 6 мая 2018
Light,
https://sun1-2.userapi.com/c834501/u155803513/docs/d14/463d4d50eb10/asdaazz.gif?extra=pJuy1MLOFJJnk7FUJrfZkiVyG-oGtoIJ19Q9Qx_QDADTw2Ey4LGWWHchxiAIWPFejvdMx8M9btRXBZ4LPpI9yQpmrUBoSyl8KaXpyE4pFEJ1ELCVVnbT0pEbXJ1GCf71nLcu95o_E680RHI
Офлайн
Light 5 мая 2018
mekkoss, убрать строку: angle = Mathf.Clamp(angle, minAngle, maxAngle);
Офлайн
mekkoss 5 мая 2018
Light, А как сделать что бы пуля летела по курсору, куда курсор показывает туда и пуля. Что бы можно было стрелять 360.
Офлайн
Light 4 апреля 2018
Sapecorp, чтобы получить скорость игрока, у персонажа должен быть компонент Rigidbody2D, потом к переменной скорости тела персонажа, добавляем скорость яблока:
body.velocity.magnitude + скорость яблока
Офлайн
Sapecorp 4 апреля 2018
Взял за основу ваш скрипт для своего платформера, у меня персонаж не стреляет, а бросает яблоко, ну не важно. Подскажите такой момент, когда персонаж стоит, при броске яблоко летит с заданной скоростью, при броске во время бега яблоко так же вылетает с заданной скоростью, и скорость яблока почти совпадает со скоростью персонажа получается персонаж бежит перед яблоком. Подскажите пожалуйста, каким образом можно вычислить скорость персонажа во время совершения выстрела/броска, чтобы к скорости яблока прибавилась скорость движения персонажа?
Офлайн
Light 31 марта 2018
skywoker, зависит от ИИ врага, как он видит, потом вычисление вектора стрельбы...
Офлайн
skywoker 31 марта 2018
а есть такой же скрипт но для врага, чтоб он автоматически стрелял?
Офлайн
xKalibrx 2 октября 2017
Light,
Вариант, Спасиб
Офлайн
Light 2 октября 2017
xKalibrx, сместить триггер пули за ее спрайт, то есть чтобы вначале спрайт пули влетал в цель, а только потом происходила коллизия.
Офлайн
xKalibrx 2 октября 2017
Light,
Добрый день, пока не выпустили новую версию хотел бы задать вопрос.
При скорости пули около 100 то видимая коллизия происходит до того как пуля долетела до объекта. Я понимаю что всё дело в том что следующий ВИДИМЫЙ для глаза человека шаг при такой скорости пули попал именно уже в объект и удалился, но это выглядит корявенько потому что это срабатывает не всегда) а как повезёт) бывает пуля прям в плотную долетит бывает рядом уничтожится. Чем можно решить этот вопрос?)))
Офлайн
TEEAAA 29 сентября 2017
Light,
Спасибо, буду ждать
Офлайн
Light 28 сентября 2017
TEEAAA, планируется обновленная версия этого скрипта, где будут разные режимы, пистолет, автомат и т.п., с возможностью отображения кол. патронов и перезарядки оружия. Следите за новыми публикациями.
Офлайн
TEEAAA 28 сентября 2017
Как сделать перезарядку в этом же скрипте
Помоги пожалуйста если есть время
Офлайн
Light 21 июня 2017
SleepyAsh, не совсем понятно, что и куда у тебя поворачивается. Кинь ссылку на проект в ЛС, посмотрим...
Офлайн
SleepyAsh 20 июня 2017
У меня проблема, сделал все как описано в комментариях(стрельба в обратную сторону не через мышку) theScale *=1; (т.к. на управлении персонажа стоит такой же разворот и рука просто не поворачивается) И суть проблемы в том, что, пуля всегда летит только в правую сторону. Подскажите пожалуйста люди добрые в чем может быть косяк?
Офлайн
Light 9 июня 2017
Дмитрий Туркин, нужно установить стандартный тег камеры MainCamera.
Офлайн
Camera.main не работает. Пишет: Camera не содержит определения для main
Офлайн
Stason4ikRU 13 мая 2017
Light,
Спасибо
Офлайн
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);
		}
	}
}

Если указать слой объекта или тег, в настройках триггера.
То пуля пролетит сквозь этот коллайдер.
Офлайн
Stason4ikRU 12 мая 2017
Как сделать что бы пуля проходила сквозь например "HidenWall".
Офлайн
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
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();

т.к не нужна "стрельба в обратную сторону через мышку"
Офлайн
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;
		}
	}
}
Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.
  • Яндекс.Метрика