Как сделать управление персонажем в unity3d
Перейти к содержимому

Как сделать управление персонажем в unity3d

  • автор:

Unity3d управление персонажем

здравствуйте. подскажите пожалуйста, как сделать управление персонажем от первого лица в unity3d, так чтобы перс двигался туда, куда смотрит камера (ну при нажатии клавиш, естественно). то есть смотрю вверх, нажимаю W — двигаюсь вверх и т. д. нужно создать своеобразный «полет» или «плавание» перса по сцене в любом направлении (ну по сюжету он под водой, поэтому как бы должен плавать). в документации к unity не нашел, на просторах необъятного — тоже.. буду благодарен откликнувшимся

94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
Ответы с готовыми решениями:

Unity3d — Управление персонажем (C#)
Подскажите пожалуйста каким способом можно сделать управление выбранным персонажем (например.

[Unity3D] Управление персонажем на телефоне так называемой каплей(джойстиком)
Добрый день, еще давно видел где то в интернете такой красивый "джойстик" в виде капли.. и недавно.

Управление персонажем
С управлением персонажа разобрался, бегает, поворачивается, но проблемка — не хочет перемещаться.

Управление персонажем
У меня не получается написать плавное передвижение на AWSD для куба Можете написать пример.

Регистрация: 06.08.2019
Сообщений: 5

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
using System.Collection; using UnityEngine; public class Move : MonoBehaviour { //вместо Move у вас будет имя скрипта, так и оставляем public GameObject player; //тут мы оглашаем персонажа (Важно: вместо player везде пишем как называется персонаж public int speed = 5; public int jumpspeed = 20; public int downspeed = 15; void Start() { player = (GameObject)this.gameObject; //это чтобы скрипт действовал на персонажа } void Update() { if (Input.GetKey(KeyCode.W)) { player.transform.position += player.transform.forward * speed * Time.deltaTime; //персонаж двигается вперед } if (Input.GetKey(KeyCode.S)) { player.transform.position -= player.transform.forward * speed * Time.deltaTime; //персонаж двигается назад } if (Input.GetKey(KeyCode.D)) { player.transform.position += player.transform.right * speed * Time.deltaTime; //персонаж двигается вправо } if (Input.GetKey(KeyCode.A)) { player.transform.position -= player.transform.right * speed * Time.deltaTime; //персонаж двигается влево } if (Input.GetKeyDown(KeyCode.Space)) //когда Пробел нажата { player.transform.position += player.transform.up * jumpspeed * Time.deltaTime; // персонаж летит выше } if (Input.GetKey(KeyCode.Ctrl)) //когда Ctrl нажата { player.transform.position -= player.transform.up * downspeed * Time.deltaTime; // персонаж летит ниже } }

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

87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
Помогаю со студенческими работами здесь

Управление персонажем мышкой
Нужно что бы персонаж(шарик), при зажатой мышке на экране "приклеился" к ней по позиции x. Как.

Управление персонажем Unity 2D 5.2
Доброго времени суток. Только начинаю изучать unity и немного уже изучаю C#. Проблема в.

Управление персонажем (шар)
Всем привет, форумчане. Начал делать игру наподобие balance 3d. Остановился на управлении шаром с.

2D rpg управление персонажем и анимация
Добрый день ! . Только начал изучать Unity 2D. Я создал 1 объект "man" . Создал 8 анимаций.

Управление персонажем

Управление персонажем
Не работает ни прыжок, ни просто движение. using System.Collections; using.

Управление персонажем
С управлением персонажа разобрался, бегает, поворачивается, но проблемка — не хочет перемещаться.

Unity3d управление персонажем
здравствуйте. подскажите пожалуйста, как сделать управление персонажем от первого лица в unity3d.

Unity3d — Управление персонажем (C#)
Подскажите пожалуйста каким способом можно сделать управление выбранным персонажем (например.

753 / 600 / 204
Регистрация: 06.08.2015
Сообщений: 2,432

Лучший ответ

Сообщение было отмечено Vergervan как решение

Решение

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class InputController_simple : MonoBehaviour { public Transform _target; // в инспекторе задаём объект движения public float moveSpeed = 0.1f; // скорость движения объекта public float verticalSpeed = 0.05f; // скорость подъема объекта (Space/Ctrl) // Use this for initialization void Start() { } // Update is called once per frame void Update() { // здесь блок перемещения объекта WSAD + Space/Ctrl // не забыть в инпуте назначить Ctrl на отрицательную ось Jump'а . float forwardMove = Input.GetAxis("Vertical") * moveSpeed; float sideMove = Input.GetAxis("Horizontal") * moveSpeed; float verticalMove = Input.GetAxis("Jump") * verticalSpeed; _target.position += _target.forward * forwardMove + _target.right * sideMove + _target.up * verticalMove; } }

Регистрация: 06.08.2019
Сообщений: 5

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
using System.Colections; using UnityEngine; public class move : MonoBehaviour { public int speed = 5; public GameObject player;//здесь ми указываем персонажа как игровой Object; void Start() { player = (GameObject)this.gameObject; //тут присваиваем персонажа к игровому Object или как-то так. } // Ах да вместо player надо ставить имя твоего перса которое записано в Unity; void Update() { if (Input.GetKey(KeyCode.W)) { player.transform.position += player.transform.forward * speed * Time.deltaTime; } if (Input.GetKey(KeyCode.S)) { player.transform.position -= player.transform.forward * speed * Time.deltaTime; } if (Input.GetKey(KeyCode.D)) { player.transform.position += player.transform.right * speed * Time.deltaTime; } if (Input.GetKey(KeyCode.A)) { player.transform.position -= player.transform.right * speed * Time.deltaTime;//персонаж плавно двигается на W,S,D,A; } //всё легко и просто, как борщ(всё как Вы и просили) } }

Добавлено через 7 минут

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
using System.Colections; using UnityEngine; public class move : MonoBehaviour { public int speed = 5; public GameObject player;//здесь ми указываем персонажа как игровой Object; void Start() { player = (GameObject)this.gameObject; //тут присваиваем персонажа к игровому Object или как-то так. } // Ах да вместо player надо ставить имя твоего перса которое записано в Unity; void Update() { if (Input.GetKey(KeyCode.W)) { player.transform.position += player.transform.forward * speed * Time.deltaTime; } if (Input.GetKey(KeyCode.S)) { player.transform.position -= player.transform.forward * speed * Time.deltaTime; } if (Input.GetKey(KeyCode.D)) { player.transform.position += player.transform.right * speed * Time.deltaTime; } if (Input.GetKey(KeyCode.A)) { player.transform.position -= player.transform.right * speed * Time.deltaTime;//персонаж плавно двигается на W,S,D,A; } //всё легко и просто, как борщ(всё как Вы и просили) } }

Управление персонажем с помощью SharedEvents

В данной статье я хочу показать, как можно использовать SharedEvents для управления персонажем от третьего лица, который предлагает стандартный набор ассетов. О SharedEvents я писал в предыдущих статьях (этой и в этой).

Добро пожаловать под кат!

Первое что понадобится это взять проект с реализованным SharedState/SharedEvents и добавить стандартный набор ассетов

Я создал небольшую и очень простую сцену из префабов прототипирования

И запек навигацию на поверхности со стандартными настройками

После этого нужно добавить префаб ThirdPersonCharacter на эту сцену

Далее можно запустить и убедиться что все работает из коробки. После чего можно переходить к настройки использования ранее созданной инфраструктуры SharedState/SharedEvents. Для этого нужно удалить компонент ThirdPersonUserController с объекта персонажа

так как ручное управление, с помощью клавиатуры, не понадобится. Персонаж будет управляться агентов, указывая позицию куда он будет двигаться.

И чтобы это стало возможным нужно добавить и настроить компонент NavMeshAgent на объект персонажа

Теперь нужно создать простой контроллер, который будет управлять персонажем
с мыши AgentMouseController

using UnityEngine; using UnityEngine.AI; using UnityStandardAssets.Characters.ThirdPerson; public class AgentMouseController : MonoBehaviour < public NavMeshAgent agent; public ThirdPersonCharacter character; public Camera cam; void Start() < //Вращение перса будет осуществляться через анимацию agent.updateRotation = false; >void Update() < //Получаем позицию клика на карте if (Input.GetMouseButtonDown(0)) < Ray ray = cam.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit)) < agent.SetDestination(hit.point); >> //Если агент еще не добежал, то обновляем персу направление if(agent.remainingDistance > agent.stoppingDistance) < character.Move(agent.desiredVelocity, false, false); >else //Если добежал, то стопаем его < character.Move(Vector3.zero, false, false); >> > 

И добавить его на объект персонажа, передать ему ссылки на камеру, контроллер персонажа и агента. Это все доступно со сцены

И все. Этого достаточно для управления персонажем путем указания агенту куда двигаться, с помощью мыши (щелчок левой кнопки).

Можно запустить и убедиться что все работает

Интеграция с SharedEvents

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

Компонент будет называться, например MouseHandlerComponent

using UnityEngine; public class MouseHandlerComponent : SharedStateComponent < public Camera cam; #region MonoBehaviour protected override void OnSharedStateChanged(SharedStateChangedEventData newState) < >protected override void OnStart() < if (cam == null) throw new MissingReferenceException("Объект камеры не установлен"); >protected override void OnUpdate() < //Обрабатываем клик левой кнопки мыши if (Input.GetMouseButtonDown(0)) < //Берем точку по которой игрок нажал и отправляем всем компонентам уведомление var hit = GetMouseHit(); Events.PublishAsync("poittogound", new PointOnGroundEventData < Sender = this, Point = hit.point >); > > #endregion private RaycastHit GetMouseHit() < Ray ray = cam.ScreenPointToRay(Input.mousePosition); RaycastHit hit; Physics.Raycast(ray, out hit); return hit; >> 

Данному компоненту нужен класс для отправки данных в уведомлениях. Для таких классов, которые будут содержать только данные для уведомлений можно создать один файл и назвать его DefinedEventsData

И добавить в него один класс, для отправки позиции клика мышью

using UnityEngine; public class PointOnGroundEventData : EventData < public Vector3 Point < get; set; >> 

Следующее что нужно сделать, это добавить компонент, который будет являться оберткой или декоратором, как угодно, для компонента NavMeshAgent. Так как существующие (3th party) компоненты менять я не буду, для интеграции с SharedState/SharedEvents я буду использовать именно декораторы.

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

Данный компонент будет зависеть от компонента NavMeshAgent

using UnityEngine; using UnityEngine.AI; [RequireComponent(typeof(NavMeshAgent))] public class AgentWrapperComponent : SharedStateComponent < private NavMeshAgent agent; #region Monobehaviour protected override void OnSharedStateChanged(SharedStateChangedEventData newState) < >protected override void OnStart() < //Получаем агента agent = GetComponent(); //Вращение перса будет осуществляться через анимацию agent.updateRotation = false; Events.Subscribe("pointtoground", OnPointToGroundGot); > protected override void OnUpdate() < //Передача состояния по позиции агента if (agent.remainingDistance >agent.stoppingDistance) < Events.Publish("agentmoved", new AgentMoveEventData < Sender = this, DesiredVelocity = agent.desiredVelocity >); > else < Events.Publish("agentmoved", new AgentMoveEventData < Sender = this, DesiredVelocity = Vector3.zero >); > > #endregion private void OnPointToGroundGot(PointOnGroundEventData eventData) < //Назначаем агенту новую позицию agent.SetDestination(eventData.Point); >>

Этому компоненту для отправки данных, нужен класс, который нужно добавить в файл DefinedEventsData

public class AgentMoveEventData : EventData < public Vector3 DesiredVelocity < get; set; >>

Этого уже достаточно для того чтобы персонаж двигался. Но он будет это делать без анимации, так как мы не используем ThirdPersonCharater пока еще. И для него также как и для NavMeshAgent нужно создать декоратор CharacterWrapperComponent

Компонент будет слушать уведомления об изменении позиции агента, и двигать персонаж в направлении, полученном из уведомления (события).

using UnityEngine; using UnityStandardAssets.Characters.ThirdPerson; [RequireComponent(typeof(ThirdPersonCharacter))] public class CharacterWrapperComponent : SharedStateComponent < private ThirdPersonCharacter character; #region Monobehaviour protected override void OnSharedStateChanged(SharedStateChangedEventData newState) < >protected override void OnStart() < character = GetComponent(); Events.Subscribe("agentmoved", OnAgentMove); > protected override void OnUpdate() < >#endregion private void OnAgentMove(AgentMoveEventData eventData) < //Двигает персонажа в направлении и запускает анимации character.Move(eventData.DesiredVelocity, false, false); >>

И это все. Осталось добавить эти компоненты на игровой объект персонажа. Нужно создать копию из существующего, удалить старый компонент AgentMouseControl

И добавить новые MouseHandlerComponent, AgentWrapperComponent и CharacterWrapperComponent.

В MouseHandlerComponent нужно передать камеру со сцены, с которой и будет вычисляться позиция клика.

Можно запустить и убедиться что все работает.

Так получилось с помощью SharedEvents управлять персонажем, не имея прямой связи между компонентами, как в первом примере. Это позволит более гибко конфигурировать разные составы компонентов и настраивать взаимодействие между ними.

Асинхронное поведение для SharedEvents

То, как сейчас реализован механизм уведомлений, основано на синхронной передаче сигнала и его обработки. То есть чем больше будет слушателей, тем дольше будет выполняться его обработка. Для того чтобы уйти от этого, нужно реализовать асинхронную обработку уведомлений. Первое что нужно сделать, это добавить асинхронную версию метода Publish

 //Отправка данных data подписчикам на событие eventName асинхронно public async Task PublishAsync(string eventName, T data) where T : EventData < if (_subscribers.ContainsKey(eventName)) < var listOfDelegates = _subscribers[eventName]; var tasks = new List(); foreach (Action callback in listOfDelegates) < tasks.Add(Task.Run(() =>< callback(data); >)); > await Task.WhenAll(tasks); > >

Теперь нужно изменить абстрактный метод OnUpdate в базовом классе SharedStateComponent на асинхронный, чтобы он возвращал задачи, которые были инициированы внутри имплементации этого метода и переименовать его на OnUpdateAsync

protected abstract Task[] OnUpdateAsync();

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

 private Task[] _previosFrameTasks = null; //Завершает предыдущие задачи private async Task CompletePreviousTasks() < if (_previosFrameTasks != null && _previosFrameTasks.Length >0) await Task.WhenAll(_previosFrameTasks); >

Метод Update в базовом классе нужно пометить как async и предварительно проверять выполнение предыдущих задач

async void Update() < await CompletePreviousTasks(); //Для вызова в дочерних классах _previosFrameTasks = OnUpdateAsync(); >

После этих изменений в базовом классе, можно переходить к изменению имплементации старого метода OnUpdate на новый OnUpdateAsync. Первый компонент, где это будет сделано это AgentWrapperComponent. Теперь этот метод ожидает возвращение результата. Этим результатом будет массив задач. Массив потому что в методе могут параллельно запускаться несколько и обрабатывать их будем пачкой.

protected override Task[] OnUpdateAsync() < //Передача состояния по позиции агента if (agent.remainingDistance >agent.stoppingDistance) < return new Task[] < Events.PublishAsync("agentmoved", new AgentMoveEventData < Sender = this, DesiredVelocity = agent.desiredVelocity >) >; > else < return new Task[] < Events.PublishAsync("agentmoved", new AgentMoveEventData < Sender = this, DesiredVelocity = Vector3.zero >) >; > >

Следующий кандидат на изменения метода OnUpdate это MouseHandlerController. Тут принцип тот же

 protected override Task[] OnUpdateAsync() < //Обрабатываем клик левой кнопки мыши if (Input.GetMouseButtonDown(0)) < //Берем точку по которой игрок нажал и отправляем всем компонентам уведомление var hit = GetMouseHit(); return new Task[] < Events.PublishAsync("pointtoground", new PointOnGroundEventData < Sender = this, Point = hit.point >) >; > return null; > 

Во всех остальных имплементациях, где данный метод был пустым, достаточно заменить на

protected override Task[] OnUpdateAsync()

На этом все. Теперь можно запустить, и если в компонентах, которые обрабатывают уведомления асинхронно, не осуществляется доступ к тем компонентам, которые должны обрабатываться в главном потоке, такие как, Transform, например, все будет работать. В противном случае мы получим ошибки в консоли, сообщающие о том, что мы обращаемся к этим компонентам не из главного потока

Чтобы решить эту проблему, нужно создать компонент, который будет обрабатывать код в главном потоке. Создадим отдельную папку для скриптов и назовем ее System, а также добавим в нее скрипт Dispatcher.

Этот компонент будет являться синглтоном и иметь один публичный абстрактный метод, который будет выполнять код в главном потоке. Принцип диспатчера довольно прост. Мы будем передавать ему делегаты, которые должны быть выполнены в главном потоке, он будет их складывать в очередь. И в каждом фрейме, если что-то будет в очереди, выполнять в главном потоке. Этот компонент будет сам себя добавлять на сцену в единственном экземпляре, такой простой и эффективный подход мне оч нравится.

using System; using System.Collections; using System.Collections.Concurrent; using UnityEngine; public class Dispatcher : MonoBehaviour < private static Dispatcher _instance; private volatile bool _queued = false; private ConcurrentQueue_queue = new ConcurrentQueue(); private static readonly object _sync_ = new object(); //Запускает делегат в главном потоке public static void RunOnMainThread(Action action) < _instance._queue.Enqueue(action); lock (_sync_) < _instance._queued = true; >> //Инициализируется единственный инстанс и помечается как неудаляемый (синглтон) [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] private static void Initialize() < if (_instance == null) < _instance = new GameObject("Dispatcher").AddComponent(); DontDestroyOnLoad(_instance.gameObject); > > void Update() < if (_queued) //Выполнение очереди делегатов < while (!_queue.IsEmpty) < if (_queue.TryDequeue(out Action a)) < StartCoroutine(ActionWrapper(a)); >> lock (_sync_) < _queued = false; >> > //Оборачивает делегат в энумератор IEnumerator ActionWrapper(Action a) < a(); yield return null; >>

Следующее, что нужно сделать, это применить диспатчер. Есть 2 места, где это необходимо сделать. 1-е это декоратор персонажа, там мы задаем ему направление. В компоненте CharacterWrapperComponent

private void OnAgentMove(AgentMoveEventData eventData) < Dispatcher.RunOnMainThread(() =>character.Move(eventData.DesiredVelocity, false, false)); >

2-е это декоратор агента, там мы указываем позицию для агента. В компоненте AgentWrapperComponent

private void OnPointToGroundGot(PointOnGroundEventData eventData) < //Назначаем агенту новую позицию Dispatcher.RunOnMainThread(() =>agent.SetDestination(eventData.Point)); >

Теперь ошибок возникать не будет, код будет работать корректно. Можно запустить и убедиться в этом.

Немного рефакторинга

После того как все готово и все работает, можно немного причесать код и сделать его чуть более удобным и простым. Для этого понадобится несколько изменений.

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

Внутри создадим простой дженерик метод расширения, который будет оборачивать любой инстанс в массив

public static class Extensions < //Оборачивает экзмепляр в массив public static T[] WrapToArray(this T source) < return new T[] < source >; > >

Следующее изменение это скрытие прямого использования диспатчера в компонентах. Вместо этого создадим метод в базовом классе SharedStateComponent и будем использовать диспатчер оттуда.

protected void PerformInMainThread(Action action)

И теперь нужно применить эти изменения в нескольких местах. Сначала изменим методы, где мы вручную создаем массивы задач и складываем в них единственный экземпляр
В компоненте AgentWrapperComponent

protected override Task[] OnUpdateAsync() < //Передача состояния по позиции агента if (agent.remainingDistance >agent.stoppingDistance) < return Events.PublishAsync("agentmoved", new AgentMoveEventData < Sender = this, DesiredVelocity = agent.desiredVelocity >) .WrapToArray(); > else < return Events.PublishAsync("agentmoved", new AgentMoveEventData < Sender = this, DesiredVelocity = Vector3.zero >) .WrapToArray(); > >

И в компоненте MouseHandlerComponent

protected override Task[] OnUpdateAsync() < //Обрабатываем клик левой кнопки мыши if (Input.GetMouseButtonDown(0)) < //Берем точку по которой игрок нажал и отправляем всем компонентам уведомление var hit = GetMouseHit(); return Events.PublishAsync("pointtoground", new PointOnGroundEventData < Sender = this, Point = hit.point >) .WrapToArray(); > return null; >

Теперь избавимся от прямого использования диспатчера в компонентах и вместо этого будем вызывать метод PerformInMainThread в базовом классе.

Сначала в компоненте AgentWrapperComponent

private void OnPointToGroundGot(PointOnGroundEventData eventData) < //Назначаем агенту новую позицию PerformInMainThread(() =>agent.SetDestination(eventData.Point)); >

и в компоненте CharacterWrapperComponent

private void OnAgentMove(AgentMoveEventData eventData) < PerformInMainThread(() =>character.Move(eventData.DesiredVelocity, false, false)); >

На этом все. Осталось запустить игру и убедиться, что в ходе рефакторинга ничего не сломалось и все работает корректно.

  • unity3d
  • unity 3d
  • unity туториал
  • unity уроки
  • unity3d уроки
  • unity3d уроки оптимизация

Управление персонажем, с помощью мыши в Unity

Стандартные Asset управления в Unity имею несколько скриптов, перемещение только с помощью клавиатуры и клавиатуры и мыши, но отсутствует скрипт перемещения с использованием мыши («Diablo-style»). Скрипты будет написаны на C#.

image

Для проверки всего, что будет написано, понадобятся сцена и размещенные на ней объекты. Их всего 4, это: Персонаж (я использовал: Standard Assets -> Character Controllers -> 3rd Person Controller), Камера, Объект выполняющий роль Цели, я использовал капсулу, можно использовать просто пустой объект, если вы не хотите видеть куда идет ваш персонаж, и Terrain по которому мы и будем перемещаться. Рассмотрим с начало скрипт Камеры.

public class MouseCamera : MonoBehaviour // Позиция объекта Target
public Transform target;
// Слой(и) которые реагируют на клик
public LayerMask mask;
// Персонаж которым управляем
public MousePerson player;
// Вектор перемещения
private Vector3 direction;
// Информация о луче
RaycastHit hit;

void Update() if (Input.GetMouseButtonUp(0)) // Получаем направление луча
Ray ray = camera.ScreenPointToRay(Input.mousePosition);
// Кидаем луч бесконечной длинны и проверяем пересечение слоев
if (Physics.Raycast(ray, out hit, Mathf.Infinity, mask)) // Проверяем то, что вернулось и перемещаем туда наш Target
target.position = hit.point;

// Сообщаем персонажу о новом «задание»
player.GetTarget(target.position);
>
>
>
>

Скрипт проверяет пересекает ли место клика объект(ы) указанные в слое, перемещает в это место объект цели и «говорит» персонажу двигаться к этому месту.
Скрипт персонажа немного больше, но не сильно сложнее.
using UnityEngine;

public class MousePerson : MonoBehaviour // Персонаж
CharacterController player;
// Радиус в котором персонаж считает что он у цели
public float radiusNoClick = 3f;
// Координаты Target
public Vector3 target = Vector3.zero;
// Вектор перемещения
private Vector3 direction;
// Скорость поворота
public float speedRotation = 10f;
// Скорость передвижения
public float speedMove = 45f;
// Маркер персонажа, на месте или нет
private bool onPlace = true;
// Анимации
private Animation _animation;
// Состояния
enum CharacterState Idle = 0,
Walking = 1,
>
// Состояние
private CharacterState _characterState;

void Start() // Получаем анимации
_animation = GetComponent();
// Получаем персонажа
player = (CharacterController)gameObject.GetComponent(typeof(CharacterController));
>

void Update() if (!onPlace) // Вычисляем вектор перемещения
direction = target — this.transform.position;
// Нормализуем, что бы персонаж смотрел не в землю, а прямо
direction = new Vector3(direction.x, 0, direction.z);
direction.Normalize();

// Поворачиваемся
Quaternion look = Quaternion.LookRotation(direction);
this.transform.rotation = Quaternion.Slerp(this.transform.rotation, look, Time.deltaTime * speedRotation);

// Двигаемся
player.Move(direction * Time.deltaTime * speedMove);

// Персонаж в движении
_characterState = CharacterState.Walking;
>
else
// Персонаж в состоянии «покоя»
_characterState = CharacterState.Idle;

if (_animation) // Включаем нужную анимацию в зависимости от состояния
if (_characterState == CharacterState.Walking)
_animation.Play(«walk», PlayMode.StopAll);
else if (_characterState == CharacterState.Idle)
_animation.Play(«idle», PlayMode.StopAll);
>
>

public void GetTarget(Vector3 target) // Проверим позицию Target
if ((Mathf.Abs(transform.position.x — target.x) >= radiusNoClick) &&
(Mathf.Abs(transform.position.z — target.z) >= radiusNoClick))
this.target = target;
// Скажем что персонаж не на месте
onPlace = false;
>
>

public void OnPlaceTrue() onPlace = true;
>
>

Метод GetTarget() принимает из скрипта MouseCamera позицию цели и проверяется не находится ли он около персонажа. Если все хорошо и цель далеко, персонаж начинает движение в сторону цели, путем вычислений проводящихся в Update(). Метод OnPlaceTrue() служит для указания персонажу, что он на месте. Он используется в небольшом триггере который размещается на объект Цели.

public class TriggerOnTarget : MonoBehaviour

public MousePerson player;

void OnTriggerEnter(Collider onPlace) player.OnPlaceTrue();
>
>

При пересечении зоны триггера, сообщается что персонаж прибыл в место назначения.

image

image

В коллайдере необходимо указать что «он» триггер, установить радиус (зависит от размеров вашего персонажа) и указать в скрипте персонажа, от которого он зависит.
У персонажа в скрипте все настройки оставлены по умолчанию, и ничего дополнительного указывать нет необходимости.

На камере в скрипте необходимо указать объект который выполняет роль цели, игрока которым управляет и слой который будет «ловить» клики, и по которому будет ходить персонаж. Для этой цели я завел отдельный слой Terrain и присвоил его объекту выполняющему роль земли.
Вот и все, пробуйте и экспериментируйте.

Исходники и тестовая сцена тут.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *