Стрельба, перезарядка, разброс пуль [2D]

Скрипт оружия для двухмерного платформера. В наличии два режима стрельбы, префабом или рейкастом, первый вариант подойдет для, например, плазменного оружия, а второй вариант для обычного пистолета или автомата. Есть учет количества патронов и перезарядка, как вручную, так и автоматически, если магазин пустой, а игрок нажимает клавишу выстрела. Кроме этого, мы добавили возможность настроить разброс пуль и добавили настройки скорострельности. Плюс, оружие может выстреливать сразу несколько пуль/рейкастов одновременно. Таким образом, данный скрипт можно настроить как: пистолет, автомат, дробовик и т.п.

Важное примечание! При создании модели персонажа с оружием, по умолчанию, и "лицо" персонажа и ствол пушки, должны быть "смотреть" вправо. Это необходимо для корректного переключения влево/вправо для модели. Саму модель можно ставить в любую часть экрана, главное чтобы на старте сцены, персонаж смотрел в правую сторону.

Для взаимодействия с объектом, куда попадает пуля:

using System.Collections;
using UnityEngine;

public class Weapon2DTarget : MonoBehaviour {

	public void DoAction(float damage)
	{
		// делаем что-нибудь...
	}
}

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

Теперь, сделаем префаб пули:

using System.Collections;
using UnityEngine;

public class Weapon2DBullet : MonoBehaviour {

	[SerializeField] private float damage = 15;

	void Start()
	{
		Destroy(gameObject, 10);
	}

	void OnTriggerEnter2D(Collider2D coll)
	{
		if(!coll.isTrigger)
		{
			Weapon2DTarget target = coll.GetComponent<Weapon2DTarget>();
			if(target != null) target.DoAction(damage);
			Destroy(gameObject);
		}
	}
}

Стрельба, перезарядка, разброс пуль [2D]

На префабе должен быть коллайдер в режиме триггера и риджитбоди с отключенной гравитацией.

И сам скрипт оружия:

using System.Collections;
using UnityEngine;

public class Weapon2D : MonoBehaviour {

	[Header("Настройки префаба:")]
	[SerializeField] private Rigidbody2D bulletPrefab; // если префаба пули нет, то оружие будет стрелять рейкастом
	[SerializeField] private float bulletSpeed = 5; // скорость префаба пули
	[Header("Общие параметры:")]
	[SerializeField] private Transform shootPoint; // точка оружия, откуда должны вылетать пули
	[SerializeField] private float fireRate = 1; // скорострельность
	[SerializeField] [Range(1, 5)] private int shootCount = 1; // выстрелов одновременно, например, дробовик может выстреливать больше одной пули
	[SerializeField] [Range(0.9f, 1f)] private float accuracy = 1; // разброс пуль, 1 = 100% точности
	[SerializeField] private int bulletCount = 15; // объем магазина
	[SerializeField] private float reloadTime = 2.5f; // время перезарядки в секундах
	[Header("Объект вращения:")]
	[SerializeField] private Transform zRotate; // объект вращения, например, само оружие
	[SerializeField] private float minAngle = -40; // ограничение по углам
	[SerializeField] private float maxAngle = 40;
	[Header("Родитель персонажа:")]
	[SerializeField] private Transform flipParent; // родитель для этого оружия (персонаж)
	[Header("Настройки рейкаста:")]
	[SerializeField] private float rayDistance = 100; // длинна луча
	[SerializeField] private LayerMask rayLayerMask; // маска цели
	[SerializeField] private float rayDamage = 15; // урон, наносимый цели
	private int bulletCountInt;
	private float invert, timeout, reloadTimeout;
	private Vector3 mouse, direction;
	private bool reload;

	void Awake()
	{
		bulletCountInt = bulletCount;
	}

	IEnumerator WeaponReload()
	{
		// запуск процесса перезарядки
		reloadTimeout = 0;

		while(true)
		{
			yield return null;

			// процесс перезарядки
			reloadTimeout += Time.deltaTime;

			if(reloadTimeout > reloadTime)
			{
				// перезарядка завершена
				bulletCountInt = bulletCount;
				reload = false;
				break;
			}
		}
	}

	void Flip()
	{
		Vector3 theScale = flipParent.localScale;
		theScale.x *= -1;
		invert *= -1;
		flipParent.localScale = theScale;
	}

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

	void GetReload()
	{
		bulletCountInt = 0;
		reload = true;
		StartCoroutine(WeaponReload());
	}

	void LateUpdate()
	{
		invert = Mathf.Sign(flipParent.localScale.x);

		if(zRotate != null) LookAtMouse();

		if(mouse.x < flipParent.position.x && invert == 1) Flip();
		else if(mouse.x > flipParent.position.x && invert == -1) Flip();

		if(reload) return;

		if(Input.GetMouseButton(1)) // выстрел ПКМ
		{
			if(bulletCountInt <= 0)
			{
				GetReload();
				return;
			}

			timeout += Time.deltaTime;
			if(timeout > fireRate)
			{
				timeout = 0;
				Shoot();
			}
		}
		else
		{
			timeout = Mathf.Infinity;
		}

		if(Input.GetKeyDown(KeyCode.R)) // перезарядка "R"
		{
			if(bulletCountInt != bulletCount) GetReload();
		}
	}

	void BulletShoot()
	{
		Rigidbody2D clone = Instantiate(bulletPrefab, shootPoint.position, Quaternion.identity) as Rigidbody2D;
		clone.velocity = direction * bulletSpeed;
		clone.transform.right = direction;
	}

	void RayShoot()
	{
		RaycastHit2D hit = Physics2D.Raycast(shootPoint.position, direction, rayDistance, rayLayerMask);

		if(hit.transform != null)
		{
			Weapon2DTarget target = hit.transform.GetComponent<Weapon2DTarget>();
			if(target != null)  target.DoAction(rayDamage);
		}
	}

	void Shoot()
	{
		for(int i = 0; i < shootCount; i++)
		{
			direction = (shootPoint.right + (Vector3)(Random.insideUnitCircle * (1f - accuracy))).normalized * invert;
			if(bulletPrefab == null) RayShoot(); else BulletShoot();
		}

		bulletCountInt--;
	}
}

Важно! Скрипт управляет вращением оружия по оси Z, можно указать торс персонажа или только модель оружия. И еще скрипт управляет разворотом персонажа, так как это напрямую зависит от положения курсора. Поэтому если в вашем скрипте персонажа есть функция разворота, ее необходимо отключить.

Скачать демо:
https://www.patreon.com/posts/strelba-razbros-23168875
Тестировалось на: Unity 2017.1.1

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

Офлайн
и ни какого видео((((
Офлайн
SlavaXD 7 октября 2017
зато можно загрузить проект на движок и посмотреть
Офлайн
igor1906 10 октября 2017
А подскажите пожалуйста как сделать гильзы при стрельбе?
Офлайн
Light 10 октября 2017
igor1906, ну это обычные 2д ритжитбоди объекты, думаю лучше всего сделать пул из 10-15 элементов, и прогонять их циклом во время стрельбы.
Офлайн
TeDj 21 октября 2017
Добрый день!
Скажите, пожалуйста, в новой версии 2017.2.0 добавили Tileset. Можно ли его использовать для match3? Просто раньше сетку создавали с помощью скрипта и добавляли элементы префабами. А с новым Tileset'ом , если смотреть оптимистично, дело было бы проще. В инете пока нет такой информации
Офлайн
TEEAAA 2 ноября 2017
а как на андроид?
Офлайн
Light 13 ноября 2017
TeDj, это инструмент для редактора, чтобы быстро "рисовать" игровую карту.
Офлайн
P3LM3N 12 апреля 2018
загрузил демо, но персонаж не стреляет
Офлайн
alowp 21 июля 2018
P3LM3N,
стрельба правой кнопкой мыши. всё работает

Столкнулся с такой проблемой... В скрипте можно выставлять скорострельность. И всё было бы замечательно, но если выставить маленькую скорострельность, то просто кликая на мышку как можно быстрее, ты стреляешь чаще чем выставленная скорострельность...
Офлайн
Light 21 июля 2018
alowp, попробуй добавить проверку, когда ПКМ не нажата.

Заменить:

timeout = Mathf.Infinity;

На:

timeout += Time.deltaTime;
if (timeout > fireRate)
{
    timeout = Mathf.Infinity;
}
Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.
  • Яндекс.Метрика