Управление 2D танком (вид сверху)

Управление танком для двухмерной игрушки, с видом сверху. Делать будем с использованием компонента Rigidbody2D. Движение танка с клавиатуры, а башня будет следить за курсором. Присутствует отдельная регулировка скорости передвижения и скорости вращения танка, плюс, вращение башни тоже настраивается отдельно. Присутствует возможность стрельбы и учета урона, время перезарядки орудия настраивается. Для снаряда есть несколько настроек: скорость, наносимый урон, фильтр по слою объекта или по тегам. Что расширяет возможности настройки каждого конкретного снаряда под определенные нужды.


Важный момент по спрайтам.
"Лицо" тела танка и его башни, должны быть ориентированы по оси Х.

Управление 2D танком (вид сверху)

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


Тоже самое относится и к дочерней точки, откуда вылетают снаряды.

Кстати говоря, "лицо" снаряда так же должно смотреть вправо.

Управление танком:

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(HPObject))]

public class Tank2DControl : MonoBehaviour {

	[Header("Скорость движения и вращения:")]
	[SerializeField] private float tankSpeed;
	[SerializeField] private float tankRotationSpeed;
	[Header("Оружие:")]
	[SerializeField] private float reloadTime;
	[SerializeField] private Tank2DShell tankShell;
	[SerializeField] private Transform tankShellPoint; // откуда вылетают снаряды
	[Header("Башня танка:")]
	[SerializeField] private Transform turret;
	[SerializeField] private float turretSpeed;

	private Rigidbody2D body;
	private HPObject HP;
	private float shotTime = Mathf.Infinity;
	private bool canShot;

	void Awake()
	{
		HP = GetComponent<HPObject>();
		body = GetComponent<Rigidbody2D>();
		body.gravityScale = 0;
	}

	void FixedUpdate()
	{
		float v = Input.GetAxis("Vertical");
		float h = Input.GetAxis("Horizontal");
		body.AddForce(transform.right * tankSpeed * v, ForceMode2D.Impulse);
		body.AddTorque(tankRotationSpeed * h * -Mathf.Sign(v), ForceMode2D.Impulse);
	}

	Quaternion TurretRotation()
	{
		Vector3 mouse = Input.mousePosition;
		mouse.z = Camera.main.transform.position.z;
		Vector3 direction = Camera.main.ScreenToWorldPoint(mouse) - transform.position;
		float angle  = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
		return Quaternion.AngleAxis(angle, Vector3.forward);
	}

	void CanShot()
	{
		if(canShot) return;
		shotTime += Time.deltaTime;

		if(shotTime > reloadTime)
		{
			shotTime = 0;
			canShot = true;
		}
	}

	void TankShot()
	{
		if(Input.GetMouseButtonDown(0) && canShot)
		{
			canShot = false;
			float angle  = Mathf.Atan2(tankShellPoint.right.y, tankShellPoint.right.x) * Mathf.Rad2Deg;
			Tank2DShell shell = Instantiate(tankShell, tankShellPoint.position, Quaternion.AngleAxis(angle, Vector3.forward)) as Tank2DShell;
			shell.SetDirection(tankShellPoint.right);
		}
	}

	void Update()
	{
		CanShot();
		TankShot();

		turret.rotation = Quaternion.Lerp(turret.rotation, TurretRotation(), turretSpeed * Time.deltaTime);

		if(HP.currentHP <= 0)
		{
			Destroy(gameObject);
		}
	}
}


Здесь можно поэкспериментировать с настройками Rigidbody2D. Однако, поскольку мы не используем гравитацию, то параметры Mass, Linear Drag, Angular Drag - должны быть больше по значению, чем у других объектов. Так как мы управляем всё таки танком, а не воздушным шариком.

Для префаба снаряда, следующий класс:

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(Rigidbody2D))]

public class Tank2DShell : MonoBehaviour {

	[SerializeField] private float speed; // скорость снаряда
	[SerializeField] private float damage; // наносимый урон
	[SerializeField] private string[] tagList; // фильтр по тегам
	[SerializeField] private LayerMask layers; // или по слоям

	public void SetDirection(Vector3 direction)
	{
		Rigidbody2D body = GetComponent<Rigidbody2D>();
		body.gravityScale = 0;
		body.velocity = direction.normalized * speed;
	}

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

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

		return false;
	}

	void OnTriggerEnter2D(Collider2D coll)
	{
		if(!coll.isTrigger && Check(coll.gameObject))
		{
			HPObject HP = coll.GetComponent<HPObject>();

			if(HP != null)
			{
				HP.Adjust(-damage);
			}
		}

		Destroy(gameObject);
	}
}

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

Теперь, здоровье для объектов:

using UnityEngine;
using System.Collections;

public class HPObject : MonoBehaviour {

	[SerializeField] private float _HP = 100;
	[SerializeField] private bool autoDestroy;

	public float currentHP
	{
		get{ return _HP; }
	}

	public void Adjust(float value)
	{
		_HP += value;

		if(autoDestroy && _HP <= 0)
		{
			Destroy(gameObject);
		}
	}
}

Параметр autoDestroy может пригодится для каких-нибудь статичных объектов, типа блоки, камни и всякое такое. В случаи с танком, мы запрашиваем текущее состояние через currentHP. Это удобно, если мы хотим что-то сделать с танком перед его уничтожением, запустить анимацию или еще что.

Скачать демо проект:

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

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

Офлайн
Enemyqish 6 сентября 2016
Здравствуйте, во-первых большое спасибо за столь информативный контент, всегда просматриваю весь код, параллельно изучая для себя что-то новое) Я хотел спросить планируете ли обновление еще каких-то проектов, помимо инвентаря и локализации?
Офлайн
Light 6 сентября 2016
Цитата: Enemyqish
Я хотел спросить планируете ли обновление еще каких-то проектов, помимо инвентаря и локализации?

А что конкретно интересует?
Офлайн
Enemyqish 6 сентября 2016
Light,
Например FPS контроллер, реворк данного проекта пригодится и Вам, в видео-демонстрациях например. А вы не думали делать проекты ориентированы именно на редактор Unity, которые упрощают процесс разработки?
Офлайн
Light 7 сентября 2016
Цитата: Enemyqish
Например FPS контроллер

Ну, в недавних публикациях, например, https://null-code.ru/solution/140-oblast-poiska-predmeta-dlya-fps-tps.html присутствует обновленный контроллер, нежели в более старых статьях. Или интересует что-то определенное?

Цитата: Enemyqish
проекты ориентированы именно на редактор Unity, которые упрощают процесс разработки?

По мелочи, простые инструменты добавляются в проект по мере необходимости, а что-то крупное пока не планируется, хотя и возможно в будущем.
Офлайн
Здравствуйте! Сделал стрельбу очередями (по несколько выстрелов) и снаряды визуально двоятся в полете (выстреливается пять, на сцене присутствует пять, а визуально их десять). Пробовал (навскидку) играться с параметрами Rigidbody2D, но результатов не получил. Что делать с этим безобразием?
Офлайн
Light 24 декабря 2017
Владимир Дебиэн, надо смотреть, кинь в ЛС демку играбельную...
Офлайн
Light,
Вроде отправил.

Light,
Кстати, почему-то при отключении Vsync танк начинает двигаться в несколько раз быстрее...
Офлайн
Light,
Есть результаты? Я ничего нагуглить не могу, хотя люди с такой проблемой сталкивались
Офлайн
Light 27 декабря 2017
Владимир Дебиэн, включить Interpolate у Rigidbody.
Офлайн
Light,
Не помогает. Кстати, увеличил снаряд десятикратно (скейл у префаба) - двоится перестал. Вернул на место, камере сайз поставил 30 - тоже глюка нет.
Офлайн
Как сделать откат ствола очень нужно.
Можно скрипт?
Заранее спасибо
Офлайн
Light 11 июля 2018
P_e_t_a_c_h_e_k, конкретнее, на примере, о чем речь?
Офлайн
Light,
При выстреле бил откат "отдачя" орудия.
Типо стреляешь и ствол откатывается назад.
Заранее спасибо.
Офлайн
Light 12 июля 2018
P_e_t_a_c_h_e_k, это просто анимация выстрела, визуальные плюшки. Тут скриптов не нужно. В юнити есть аниматор для таких дел.
Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.
  • Дешевый хостинг
  • Яндекс.Метрика