Перемещение объектов в шутере

Сегодня мы научим нашего персонажа, перетаскивать различные игровые объекты. Всё это дело для игрушки от первого лица, допустим шутера. То есть, нам нужно добиться эффекта, когда можно как бы руками перемещать физические объекты. Такое часто используется в играх типа ужастиков, где нужно решать всякие загадки, перекладывать коробки и палки туда-сюда и всякое такое. Управление сделаем на мышке для удобства, одна кнопка – взять, другая – толкнуть. А колесиком мышки можно будет изменять высоту, например, чтобы немного приподнять объект, без необходимости задирать камеру вверх.


Пара примечаний.

1. Камера персонажа должна быть с тегом MainCamera.
2. Физическим объектам, которые можно перемещать ставим Interpolate.

Перемещение объектов в шутере

Это нужно чтобы движения были менее дерганными.



Теперь, добавляем на сцену:

using UnityEngine;
using System.Collections;

public class DragRigidbody : MonoBehaviour {

	public float force = 50; // сила броска
	public float sensitivity = 25; // регулировка скорости при перемещении объекта
	public float heightValue = 0.35f; // чувствительность при изменении высоты колесиком мышки
	public float heightValueMax = 2; // насколько можно поднять объект, относительно стартовой точки
	public float heightValueMin = 1; // насколько можно опустить объект, относительно стартовой точки
	public float distance = 10; // максимальная дистанция, с которой можно схватить объект
	public float maxMass = 10; // максимальная масса объекта, который можно поднять
	public float stopDistance = 3; // допустимое расстояние от точки назначения и текущего положения объекта, при перемещении (чтобы нельзя было перетащить предмет сквозь графику)

	private Rigidbody body;
	private float mass, curHeight, curForce;
	private Transform clone, local;
	private static bool _get;

	public static bool isDrag
	{
		get{ return _get; }
	}

	void Update()
	{
		if(Input.GetMouseButtonDown(1))
		{
			body = GetRigidbody();
		}
		else if(Input.GetMouseButtonUp(1) && body)
		{
			Clear();
		}
		else if(Input.GetMouseButtonDown(0) && body)
		{
			Rigidbody tmpBody = body;
			Clear();
			tmpBody.velocity = Camera.main.transform.TransformDirection(Vector3.forward) * curForce;
		}

		if(Input.GetAxis("Mouse ScrollWheel") > 0 && body)
		{
			NewHeight(heightValue);
		}
		else if(Input.GetAxis("Mouse ScrollWheel") < 0 && body)
		{
			NewHeight(-heightValue);
		}
	}

	void NewHeight(float value) // изменение высоты
	{
		curHeight += value;
		curHeight = Mathf.Clamp(curHeight, heightValueMin, heightValueMax);
		if(curHeight == heightValueMin || curHeight == heightValueMax) return;
		clone.position += new Vector3(0, value, 0);
	}

	Rigidbody GetRigidbody()
	{
		RaycastHit hit;
		Ray ray = Camera.main.ScreenPointToRay(new Vector2(Screen.width/2, Screen.height/2));
		if(Physics.Raycast(ray, out hit, distance))
		{
			if(hit.rigidbody && !hit.rigidbody.isKinematic && hit.rigidbody.mass <= maxMass)
			{
				mass = hit.rigidbody.mass;
				if(mass < 2) mass = 2; // сила броска, зависит от массы, поэтому проверяем, чтобы минимальная масса не была равна или меньше единицы
				curForce = force / mass;
				hit.rigidbody.useGravity = false;
				hit.rigidbody.freezeRotation = true;
				clone.position = hit.point; // устанавливаем точку, где "подхватили" объект
				return hit.rigidbody;
			}
		}
		return null;
	}

	void SetLocal() // копируем позицию и вращение, оригинала
	{
		if(_get) return;
		local.rotation = body.rotation;
		local.position = body.position;
		_get = true;
	}

	float RoundTo(float f, int to) // округлить до
	{
		return ((int)(f*to))/(float)to;
	}

	void FixedUpdate()
	{
		if(!body) return;

		Vector3 lookAt = Camera.main.transform.position;
		lookAt.y = clone.position.y;
		clone.LookAt(lookAt);
		SetLocal();
		body.velocity = (local.position - body.position) * sensitivity;
		body.rotation = local.rotation;

		float dist = Vector3.Distance(body.position, local.position);
		dist = RoundTo(dist, 100); // округляем до сотых, для исключения погрешностей
		if(dist > stopDistance) // сброс, при попытке протащить предмет сквозь стену или если резко повернуть камеру
		{
			body.velocity = Vector3.zero; // обязательный сброс скорости, иначе объект может улететь в стратосферу
			Clear();
		}
	}

	void CheckVelocity() // проверка скорости, когда мы отпускаем объект во время движения камеры, чтобы его скорость не могла превышать силы броска
	{
		Vector3 velocity = body.velocity.normalized * curForce;
		if(body.velocity.sqrMagnitude > velocity.sqrMagnitude)
		{
			body.velocity = velocity;
		}
	}

	void Clear()
	{
		curHeight = 0;
		_get = false;
		clone.localPosition = Vector3.zero;
		local.localPosition = Vector3.zero;
		if(!body) return;
		CheckVelocity();
		body.useGravity = true;
		body.freezeRotation = false;
		body = null;
	}

	void Start()
	{
		if(!clone) // создание вспомогательных точек
		{
			local = new GameObject().transform;
			clone = new GameObject().transform;
			local.parent = clone;
			clone.parent = Camera.main.transform;
		}

		heightValueMin = -Mathf.Abs(heightValueMin);
		heightValueMax = Mathf.Abs(heightValueMax);

		Clear();
	}
}

Данный скрипт можно повесить куда удобнее, хоть на самого персонажа, хоть на обычный пустой объект. Логика работы в следующем. На старте создаются две точки и удочеряются камерой. При этом одна точка, так же является дочерней к другой. Когда мы берем какой-либо объект, точка-родитель помещается туда, куда попал луч. А дочерняя точка, занимает позицию центра объекта. Далее дело в простых расчетах, объект движется по направлению к дочерней точки, при этом родительская точка, всегда смотрит в камеру. Таким образом, регулируется позиция тела и сохраняется его поворот, относительно камеры.

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

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

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

Офлайн
Atanashi 24 июля 2016
Будет ли скрип по врагам?
Офлайн
Light 24 июля 2016
Atanashi, конкретнее, что интересует?
Офлайн
Atanashi 24 июля 2016
Нужен скрипт для искусственного интеллекта врага. Желательно 4 вида врага. 1. Враги атакующие на месте не передвигаясь. 2. Враги использующие дальний бой, стрельба из оружия. 3. Враги использующие ближний бой, оружие ближнего боя, мечи, топоры. 4. Летающие враги.
И еще нужен скрипт по созданию кат сцен, заставки.
Скрипт по созданию задания. Например: закрытая дверь которую нужно открыть ключом, но ключ находится в другом месте. После того как игрок находит ключ, он подходит к закрытой двери и использует найденный ключ.
Офлайн
Как открыть демо версию файла?
Офлайн
Light 10 февраля 2017
Максим Лукин, с помощью Юнити.
Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.
  • Яндекс.Метрика