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); } } }
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; } }
using UnityEngine; public class PointOnGroundEventData : EventData { public Vector3 Point { get; set; } }
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<NavMeshAgent>(); //Вращение перса будет осуществляться через анимацию agent.updateRotation = false; Events.Subscribe<PointOnGroundEventData>("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); } }
public class AgentMoveEventData : EventData { public Vector3 DesiredVelocity { get; set; } }
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<ThirdPersonCharacter>(); Events.Subscribe<AgentMoveEventData>("agentmoved", OnAgentMove); } protected override void OnUpdate() { } #endregion private void OnAgentMove(AgentMoveEventData eventData) { //Двигает персонажа в направлении и запускает анимации character.Move(eventData.DesiredVelocity, false, false); } }
//Отправка данных data подписчикам на событие eventName асинхронно public async Task PublishAsync<T>(string eventName, T data) where T : EventData { if (_subscribers.ContainsKey(eventName)) { var listOfDelegates = _subscribers[eventName]; var tasks = new List<Task>(); foreach (Action<T> callback in listOfDelegates) { tasks.Add(Task.Run(() => { callback(data); })); } await Task.WhenAll(tasks); } }
protected abstract Task[] OnUpdateAsync();
private Task[] _previosFrameTasks = null; //Завершает предыдущие задачи private async Task CompletePreviousTasks() { if (_previosFrameTasks != null && _previosFrameTasks.Length > 0) await Task.WhenAll(_previosFrameTasks); }
async void Update() { await CompletePreviousTasks(); //Для вызова в дочерних классах _previosFrameTasks = OnUpdateAsync(); }
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 }) }; } }
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() { return null; }
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<Action> _queue = new ConcurrentQueue<Action>(); 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<Dispatcher>(); 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; } }
private void OnAgentMove(AgentMoveEventData eventData) { Dispatcher.RunOnMainThread(() => character.Move(eventData.DesiredVelocity, false, false)); }
private void OnPointToGroundGot(PointOnGroundEventData eventData) { //Назначаем агенту новую позицию Dispatcher.RunOnMainThread(() => agent.SetDestination(eventData.Point)); }
public static class Extensions { //Оборачивает экзмепляр в массив public static T[] WrapToArray<T>(this T source) { return new T[] { source }; } }
protected void PerformInMainThread(Action action) { Dispatcher.RunOnMainThread(action); }
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(); } }
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; }
private void OnPointToGroundGot(PointOnGroundEventData eventData) { //Назначаем агенту новую позицию PerformInMainThread(() => agent.SetDestination(eventData.Point)); }
private void OnAgentMove(AgentMoveEventData eventData) { PerformInMainThread(() => character.Move(eventData.DesiredVelocity, false, false)); }
Source: https://habr.com/ru/post/439194/