Сегодняшний обзор расскажет о том, как игрок взаимодействует с окружающей средой с помощью raycasting.
Я закончил GLI Framework для курса GLI. Цель состояла в том, чтобы закончить некоторые основные механики в игре Sci-Fi Duck Hunt. Мне нужно было реализовать навигационную сетку, ИИ, поведение игрока, HUD, игровой звук, условия победы, условия проигрыша, два бонусных задания, включая взрывную бочку и здоровье барьера.
Цель игры — не дать врагам добраться до верхней двери, чтобы сбежать. Поэтому враги должны путешествовать от начальной точки к конечной. В то же время игрок находится в сторожевой башне, стреляя из винтовки, чтобы остановить их.
Сегодняшний обзор расскажет о том, как игрок взаимодействует с окружающей средой и Raycasting.
Поведение игрока
У игрока есть два простых действия: движение и стрельба. Также к плееру прикреплены три скрипта: один скрипт для контроллера FPS, скрипт запуска и скрипт менеджера плеера.
- Перемещение ограничено внутри сторожевой башни и контролируется WASD.
- Стрельба привязана к левой кнопке мыши и полуогню. Это означает, что оружие срабатывает только при нажатии и сбрасывается при отпускании. С небольшим похолоданием.
Скрипт запуска
Сценарий стрельбы стреляет лучом в центр экрана, где находится прицельная сетка.
_ray = _camera.ViewportPointToRay(new Vector3(0.5f, 0.5f));
Сейчас я использую новую систему ввода, и в этом скрипте я включаю карту игрока и вызываю выполненное событие. Мой метод, прикрепленный к событию, проверяет, может ли игрок стрелять из своего оружия. Если это так, бросьте луч из центра, включив дульную вспышку, воспроизведите звук, снова отключите проигрыватель от стрельбы до отпускания, а затем мы вызовем другой метод, который обрабатывает действия выпущенного луча.
Вспышка дула — это актив с Filebase.com, а звук — бесплатный источник, который я нашел в Интернете.
Все, что я сделал для дульной вспышки, это прикрепил систему частиц к стволу.
public class Fire : MonoBehaviour { private Ray _ray; private ActionMaps _inputs; private AudioSource _gunFire; [SerializeField] private AudioClip _barrier; [SerializeField] private GameObject _muzzleFlash; private bool _canFire = true; [SerializeField] private Camera _camera; private PlayerManager _playerManager; [SerializeField] private LayerMask _mask; private float _timer = 0.5f; private IDamage _damagable; private void Awake() { _playerManager = GetComponent<PlayerManager>(); _gunFire = GetComponent<AudioSource>(); _inputs = new ActionMaps(); } private void OnEnable() { _inputs.Player.Enable(); _inputs.Player.Fire.performed += Fire_performed; _inputs.Player.Fire.canceled += Fire_canceled; _muzzleFlash.SetActive(false); } private void Fire_canceled(InputAction.CallbackContext obj) { _canFire = true; } private void Fire_performed(InputAction.CallbackContext obj) { if (_canFire) { _ray = _camera.ViewportPointToRay(new Vector3(0.5f, 0.5f)); _muzzleFlash.SetActive(true); _gunFire.Play(); _canFire = false; _timer += Time.time; Firing(); } } }
Метод стрельбы
В этом методе запускается сопрограмма охлаждения, чтобы отключить эффект частиц для вспышки дульного выстрела.
IEnumerator FiringCoolDownRoutine() { yield return new WaitForSeconds(0.2f); _muzzleFlash.SetActive(false); }
Затем метод проверяет, попадает ли raycast на объект с правильной маской слоя. (ИИ, Барьеры, Взрывчатка)
если это так, у меня есть три оператора if-else, которые сравнивают тег объектов и определяют результат.
- ИИ (враг) Мы вызываем менеджера возрождения и уменьшаем общее количество врагов, появляющихся в данный момент.
- Позвоните менеджеру-игроку и добавьте 60 очков к счету игрока.
- Затем уведомите сценарий ИИ выстрела, чтобы запустить метод смерти.
StartCoroutine(FiringCoolDownRoutine()); if (Physics.Raycast(_ray, out RaycastHit _hit, Mathf.Infinity,_mask)) { if (_hit.transform.CompareTag("Enemy")) { _hit.transform.gameObject.TryGetComponent<AI>(out AI ai); SpawnManager._instance._spawnCount--; _playerManager.PlayerScore(60); ai.Death(); }
- Компонент барьера хранится в переменной, и я вызываю метод повреждения (интерфейс) и проигрываю звуковой сигнал, когда снаряд попадает в барьер.
else if (_hit.transform.CompareTag("Barriers")) { _damagable = _hit.transform.gameObject.GetComponent<Barrier>(); _damagable.Damage(5); AudioSource.PlayClipAtPoint(_barrier, new Vector3(0, 11, 32), 500); }
- Наконец, что касается взрывных бочек, всякий раз, когда луч попадает в бочку, мы сохраняем взрывной компонент в переменной и уведомляем скрипт, чтобы зажечь бочку и взорваться.
else { Explosive_Barrel barrel = _hit.transform.gameObject.GetComponentInParent<Explosive_Barrel>(); barrel._isIgnited = true; Debug.Log("BOOM"); }
Скрипт менеджера игроков
Скрипт обрабатывает счет игрока. Вероятно, я мог бы написать сценарий для одиночной игры, в котором учитывались бы и стрельба, и счет, но вместо этого я пошел по этому пути.
public class PlayerManager : MonoBehaviour { [SerializeField] public int Score { get; private set; } private void Awake() { Cursor.visible = false; } public void PlayerScore(int value) { Score += value; UIManager._instance.PlayerScore(Score); } }
В заключение, вот краткий обзор того, как я настраивал сценарии игроков и использовал raycasting в проекте для воспроизведения снайперской винтовки. Опять же, движение персонажа контролируется контроллером персонажа FPS. В следующей статье я могу углубиться в начальные и финальные сцены или рассказать о разрушаемых барьерах.