Как сделать звуки шагов? [FPS]

Грамотное звуковое оформление в игре, может вытянуть всю атмосферу. Простенький менеджер звуков мы уже разбирали, ищите его где-то тут неподалеку. На этот раз затронем тему озвучки шагов персонажа. Проект ориентирован на игру от первого лица, когда ног персонажа не видно, то есть, звук не привязан к анимации. Впрочем, есть отдельный класс, который отвечает за воспроизведение звуков, поэтому при желании можно сделать привязку к анимации ног, что применимо и для двухмерной игрушки. В нашем же случаи, мы рассмотрим настройку интервала шагов в вручную. Кроме того, в комплекте идет и расширенный скрипт управления, так как он работает в связке со скриптом звуков.


Для работы нам понадобится условная моделька персонажа. Сделать ее достаточно просто. На пустой объект вешаем капсульный коллайдер, затем удочеряем камеру, поднимаем на уровень головы и всё.

Далее, в проекте должна быть папка Resources. В ней у нас будут хранится звуки. Мы будем использовать загрузку массивов на старте сцены, потому как в процессе разработки, звуки могут меняться, можем добавлять новые или же удалять ненужные. И чтобы каждый раз не мучится с ручным заполнением массива, лучше делать это через скрипт в таких случаях.



Создаем класс, который отвечает за загрузку и воспроизведение:

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(AudioSource))]

public class Footsteps : MonoBehaviour {

	public enum StepsOn {Beton, Wood, Metal, Ground};

	// mainFolder - родительская папка в Resources
	// betonFolder и т.д. дочерние
	private string mainFolder = "Footsteps", betonFolder = "Beton", woodFolder = "Wood", metalFolder = "Metal", groundFolder = "Ground";
	private AudioClip[] Beton, Wood, Metal, Ground;
	private AudioSource source;
	private AudioClip clip;

	void Start()
	{
		source = GetComponent<AudioSource>();
		source.playOnAwake = false;
		source.mute = false;
		source.loop = false;
		LoadSounds();
	}

	void LoadSounds()
	{
		Beton = Resources.LoadAll<AudioClip>(mainFolder + "/" + betonFolder);
		Wood = Resources.LoadAll<AudioClip>(mainFolder + "/" + woodFolder);
		Metal = Resources.LoadAll<AudioClip>(mainFolder + "/" + metalFolder);
		Ground = Resources.LoadAll<AudioClip>(mainFolder + "/" + groundFolder);
	}

	public void PlayStep(StepsOn stepsOn, float volume)
	{
		switch(stepsOn)
		{
		case StepsOn.Beton:
			clip = Beton[Random.Range(0, Beton.Length)];
			break;
		case StepsOn.Wood:
			clip = Wood[Random.Range(0, Wood.Length)];
			break;
		case StepsOn.Metal:
			clip = Metal[Random.Range(0, Metal.Length)];
			break;
		case StepsOn.Ground:
			clip = Ground[Random.Range(0, Ground.Length)];
			break;
		}

		source.PlayOneShot(clip, volume);
	}
}

Здесь есть меню, для выбора определенных звуков. Меню можно конечно расширить, если это нужно.

Создаем скрипт управления:

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(Footsteps))]

public class PlayerControl : MonoBehaviour {

	[Header("General")]
	public float speed = 1.5f; // скорость движения
	public float acceleration = 100f; // ускорение
	public float stepTimer = 0.8f; // интервал шагов во время ходьбы (секунды)

	[Header("Head / Camera")]
	public Transform head;
	public float sensitivity = 5f; // чувствительность мыши
	public float headMinY = -40f; // ограничение угла для головы
	public float headMaxY = 40f;

	[Header("Jump")]
	public KeyCode jumpButton = KeyCode.Space; // клавиша для прыжка
	public float jumpForce = 10; // сила прыжка
	public float jumpDistance = 1.2f; // расстояние от центра объекта, до поверхности

	[Header("Run")]
	public KeyCode runButton = KeyCode.LeftShift; // клавиша для бега
	public float addForce = 5; // добавить к скорости
	public float stepTimerRun = 0.35f; // интервал шагов во время бега (секунды)

	private Vector3 direction;
	private int layerMask;
	private Rigidbody body;
	private float rotY, curT, h, v, curSpeed, curStepTimer;
	private string tagName;
	private bool fall;

	private Footsteps foot;
	
	void Start () 
	{
		foot = GetComponent<Footsteps>();
		body = GetComponent<Rigidbody>();
		body.freezeRotation = true;
		layerMask = 1 << gameObject.layer | 1 << 2;
		layerMask = ~layerMask;
	}
	
	void FixedUpdate()
	{
		body.AddForce(direction.normalized * curSpeed * acceleration * body.mass);
		
		// Ограничение скорости, иначе объект будет постоянно ускоряться
		if(Mathf.Abs(body.velocity.x) > curSpeed)
		{
			body.velocity = new Vector3(Mathf.Sign(body.velocity.x) * curSpeed, body.velocity.y, body.velocity.z);
		}
		if(Mathf.Abs(body.velocity.z) > curSpeed)
		{
			body.velocity = new Vector3(body.velocity.x, body.velocity.y, Mathf.Sign(body.velocity.z) * curSpeed);
		}
	}

	bool GetJump() // проверяем, есть ли коллайдер под ногами
	{
		RaycastHit hit;
		Ray ray = new Ray(transform.position, Vector3.down);
		if(Physics.Raycast(ray, out hit, jumpDistance, layerMask))
		{
			tagName = hit.transform.tag; // берем тег поверхности
			return true;
		}

		tagName = string.Empty;
		return false;
	}

	void Update () 
	{
		curSpeed = speed;
		curStepTimer = stepTimer;

		h = Input.GetAxis("Horizontal");
		v = Input.GetAxis("Vertical");

		// управление головой (камерой)
		float rotX = head.localEulerAngles.y + Input.GetAxis("Mouse X") * sensitivity;
		rotY += Input.GetAxis("Mouse Y") * sensitivity;
		rotY = Mathf.Clamp (rotY, headMinY, headMaxY);
		head.localEulerAngles = new Vector3(-rotY, rotX, 0);

		// вектор направления движения
		direction = new Vector3(h, 0, v);
		direction = head.TransformDirection(direction);
		direction = new Vector3(direction.x, 0, direction.z);

		Debug.DrawRay(transform.position, Vector3.down * jumpDistance, Color.red); // подсветка, для визуальной настройки jumpDistance

		if(Input.GetKey(runButton)) // если удерживать клавишу бега
		{
			curSpeed = speed + addForce;
			curStepTimer = stepTimerRun;
		}

		if(GetJump())
		{
			if(Input.GetKeyDown(jumpButton))
			{
				body.velocity = new Vector2(0, jumpForce);
			}

			Steps();
			Falling();
		}
		else
		{
			curT = 0;
			fall = true;
		}
	}

	void Falling() // падение на что-нибудь
	{
		if(fall)
		{
			fall = false;
			curT = 0;
			GetStep();
		}
	}

	void Steps()
	{
		// округляем текущее значение скорости по оси X и Z, до сотых
		// чтобы исключить те, которые близкие к нулю, например: 0.000001805331f
		// в противном случаи, функция будет срабатывать, даже если персонаж не движется
		float velocityZ = RoundTo(Mathf.Abs(body.velocity.z), 100);
		float velocityX = RoundTo(Mathf.Abs(body.velocity.x), 100);

		if(velocityZ > 0 && Mathf.Abs(v) > 0 || velocityX > 0 && Mathf.Abs(h) > 0) // если персонаж движется
		{
			curT += Time.deltaTime;

			if(curT > curStepTimer)
			{
				curT = 0;
				GetStep();
			}
		}
		else
		{
			curT = 1000;
		}
	}

	void GetStep() // фильтр по тегу
	{
		switch(tagName)
		{
		case "GameController":
			foot.PlayStep(Footsteps.StepsOn.Beton, 1);
			break;
		case "Finish":
			foot.PlayStep(Footsteps.StepsOn.Ground, 1);
			break;
		}
	}

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

Цепляем его на модельку, которую мы сделали ранее. Автоматически будут добавлены все необходимые компоненты. Не забываем указать камеру в качестве головы нашего персонажа, так как вращаться мышкой будет именно этот объект. И само собой, так как у нас фильтр по тегам, то значит если в игре, например, металлическая площадка, то и тег ей ставим соответствующий.

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

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

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

Офлайн
Пожалуйста отправте бесплатную ссылку этого проекта!!!!! https://www.assetstore.unity3d.com/en/#!/content/25579 ПРОСТО УМОЛЯЮ!!!!!!!!!!!!!
Офлайн
bruner 13 декабря 2016
Это все прекрасно, но вопрос: Как это привинтить к штатному FPS от первого лица в Юньке 5.4?
Ну или хотя бы к более продвинутому контроллеру, который мог бы бегать, прыгать и приседать?
Офлайн
Light 14 декабря 2016
bruner, просто выдернуть нужные функции и прикрутить куда угодно.
Офлайн
Папка Resurces недоступна для импорта. Что делать?
Офлайн
Light 13 ноября 2017
Andrei Smalianko, проверять наличие ошибок.
Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.
  • Яндекс.Метрика