93.4409
99.5797
21.04.2024
12:06:24

Как организовать список очереди действий персонажа, таких как движение к точке, поиск объекта, рубка дерева

Для организации списка очереди действий персонажа в Unity, вы можете использовать структуру данных, называемую "очередью" (queue) или "списком" (list) в зависимости от ваших потребностей. Вот пример, как это можно сделать:

  1. Создайте класс для действий персонажа:

Сначала создайте класс или скрипт, который будет представлять действие персонажа. Например, это может быть что-то подобное:


public class CharacterAction
{
  public enum ActionType
  {
    MoveTo,
    SearchForObject,
    ChopTree,
    // Другие действия
  }

  public ActionType actionType;
  public Vector3 targetPosition;
  public GameObject targetObject;

  // Конструкторы и другие поля, если необходимо
}

  1. Создайте очередь действий:

Затем создайте очередь (queue) или список (list) действий вашего персонажа в вашем скрипте управления персонажем. Например:


using System.Collections.Generic;
using UnityEngine;

public class CharacterController : MonoBehaviour
{
  private Queue actionQueue = new Queue();

  // Метод для добавления действия в очередь
  public void EnqueueAction(CharacterAction action)
  {
    actionQueue.Enqueue(action);
  }

  // Метод для выполнения следующего действия в очереди
  public void PerformNextAction()
  {
    if (actionQueue.Count > 0)
    {
      CharacterAction nextAction = actionQueue.Dequeue();

      switch (nextAction.actionType)
      {
        case CharacterAction.ActionType.MoveTo:
          MoveTo(nextAction.targetPosition);
          break;
        case CharacterAction.ActionType.SearchForObject:
          SearchForObject(nextAction.targetObject);
          break;
        case CharacterAction.ActionType.ChopTree:
          ChopTree(nextAction.targetObject);
          break;
        // Обработка других действий
      }
    }
  }

  // Методы для выполнения конкретных действий, например, движение, поиск и рубка дерева
  private void MoveTo(Vector3 targetPosition)
  {
    // Реализация движения к цели
  }

  private void SearchForObject(GameObject targetObject)
  {
    // Реализация поиска объекта
  }

  private void ChopTree(GameObject treeObject)
  {
    // Реализация рубки дерева
  }

  // Остальные методы и логика управления персонажем
}

  1. Добавление действий в очередь:

Теперь вы можете добавлять действия в очередь, вызывая метод EnqueueAction . Например:


CharacterAction moveAction = new CharacterAction();
moveAction.actionType = CharacterAction.ActionType.MoveTo;
moveAction.targetPosition = new Vector3(10f, 0f, 10f);
characterController.EnqueueAction(moveAction);

CharacterAction chopAction = new CharacterAction();
chopAction.actionType = CharacterAction.ActionType.ChopTree;
chopAction.targetObject = treeObject;
characterController.EnqueueAction(chopAction);

  1. Выполнение действий из очереди:

Чтобы начать выполнение действий из очереди, вызывайте метод PerformNextAction в вашем цикле обновления (например, в функции Update ).

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

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

Если вы хотите, чтобы действия движения или рубки дерева происходили в функции Update() , вы можете изменить структуру вашего кода следующим образом:

  1. Измените структуру CharacterAction:

    Внесите изменения в структуру CharacterAction , чтобы она могла хранить дополнительную информацию о состоянии выполнения действия и других параметрах, необходимых для его выполнения. Например:

    
    
    public class CharacterAction
    {
      public enum ActionType
      {
        MoveTo,
        SearchForObject,
        ChopTree,
        // Другие действия
      }
    
      public ActionType actionType;
      public Vector3 targetPosition;
      public GameObject targetObject;
    
      public bool isActionCompleted; // Флаг, указывающий, завершено ли действие
    
      // Другие поля и методы, если необходимо
    }
    
    
  2. Измените метод PerformNextAction :

    Метод PerformNextAction будет просто устанавливать флаг isActionCompleted для текущего действия, а фактическое выполнение действия будет происходить в Update() . Вот пример:

    
    
    public void PerformNextAction()
    {
      if (actionQueue.Count > 0)
      {
        CharacterAction nextAction = actionQueue.Peek(); // Получаем следующее действие, но не удаляем его из очереди
    
        switch (nextAction.actionType)
        {
          case CharacterAction.ActionType.MoveTo:
            MoveTo(nextAction.targetPosition);
            break;
          case CharacterAction.ActionType.SearchForObject:
            SearchForObject(nextAction.targetObject);
            break;
          case CharacterAction.ActionType.ChopTree:
            ChopTree(nextAction.targetObject);
            break;
          // Обработка других действий
        }
    
        // Помечаем действие как завершенное, если оно выполнено (например, внутри методов MoveTo, SearchForObject, ChopTree)
        if (nextAction.isActionCompleted)
        {
          actionQueue.Dequeue(); // Удаляем завершенное действие из очереди
        }
      }
    }
    
    
  3. Измените метод Update() :

    В методе Update() вы будете вызывать PerformNextAction() для выполнения следующего действия из очереди.

    
    void Update()
    {
      PerformNextAction();
    }
    
    
  4. Реализуйте выполнение действий в методах MoveTo, SearchForObject и ChopTree:

    В этих методах вы будете выполнять соответствующие действия, а затем устанавливать флаг isActionCompleted в true , когда действие завершено.

    Пример для метода MoveTo :

    
    
    private void MoveTo(Vector3 targetPosition)
    {
      float moveSpeed = 5.0f;
      Vector3 moveDirection = (targetPosition - transform.position).normalized;
      transform.Translate(moveDirection * moveSpeed * Time.deltaTime);
    
      // Проверка, завершено ли действие (например, при достижении цели)
      if (Vector3.Distance(transform.position, targetPosition) < 0.1f)
      {
        isActionCompleted = true;
      }
    }
    
    

    Пример для метода ChopTree :

    
    
    private void ChopTree(GameObject treeObject)
    {
      // Реализация рубки дерева
    
      // Проверка, завершено ли действие (например, после завершения рубки)
      if (treeObject == null)
      {
        isActionCompleted = true;
      }
    }
    
    

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

А как сделать чтобы часть функционала реализовывалось один раз при старте действия, а часть в update

Если вы хотите, чтобы часть функционала выполнялась один раз при старте действия, а часть выполнялась в методе Update() , вам нужно использовать концепцию состояний (state machine) для вашего персонажа. Каждое действие будет иметь свои состояния, и персонаж будет находиться в одном из них.

Вот пример, как это можно реализовать:

  1. Измените структуру CharacterAction:

    Добавьте состояние (state) для каждого действия и флаг, который будет указывать, выполнено ли действие. Например:

    
    public class CharacterAction
    {
      public enum ActionType
      {
        MoveTo,
        SearchForObject,
        ChopTree,
        // Другие действия
      }
    
      public ActionType actionType;
      public Vector3 targetPosition;
      public GameObject targetObject;
      
      public enum ActionState
      {
        NotStarted,
        InProgress,
        Completed
      }
      
      public ActionState currentState = ActionState.NotStarted;
    
      // Другие поля и методы, если необходимо
    }
    
    
  2. Измените метод PerformNextAction :

    Теперь метод PerformNextAction будет управлять состояниями действий и их выполнением:

    
    public void PerformNextAction()
    {
      if (actionQueue.Count > 0)
      {
        CharacterAction nextAction = actionQueue.Peek();
    
        if (nextAction.currentState == CharacterAction.ActionState.NotStarted)
        {
          // Начать выполнение действия
          nextAction.currentState = CharacterAction.ActionState.InProgress;
          StartAction(nextAction);
        }
        else if (nextAction.currentState == CharacterAction.ActionState.InProgress)
        {
          // Продолжить выполнение действия
          ContinueAction(nextAction);
        }
    
        // Проверка, завершено ли действие
        if (nextAction.currentState == CharacterAction.ActionState.Completed)
        {
          actionQueue.Dequeue(); // Удаляем завершенное действие из очереди
        }
      }
    }
    
    
  3. Добавьте методы для управления действиями:

    Создайте методы StartAction и ContinueAction для каждого типа действия, чтобы начать и продолжить выполнение действия соответственно. В методе StartAction вы будете выполнять часть функционала, которая должна быть выполнена только один раз при старте действия. Затем в методе ContinueAction вы будете выполнять часть функционала, которая должна обновляться в Update() .

    
    private void StartAction(CharacterAction action)
    {
      switch (action.actionType)
      {
        case CharacterAction.ActionType.MoveTo:
          // Начать движение к цели
          MoveTo(action.targetPosition);
          break;
        case CharacterAction.ActionType.SearchForObject:
          // Начать поиск объекта
          SearchForObject(action.targetObject);
          break;
        case CharacterAction.ActionType.ChopTree:
          // Начать рубку дерева
          ChopTree(action.targetObject);
          break;
        // Обработка других действий
      }
    }
    
    private void ContinueAction(CharacterAction action)
    {
      switch (action.actionType)
      {
        case CharacterAction.ActionType.MoveTo:
          // Продолжить движение (например, в методе Update())
          MoveTo(action.targetPosition);
          break;
        case CharacterAction.ActionType.SearchForObject:
          // Продолжить поиск объекта (например, в методе Update())
          SearchForObject(action.targetObject);
          break;
        case CharacterAction.ActionType.ChopTree:
          // Продолжить рубку дерева (например, в методе Update())
          ChopTree(action.targetObject);
          break;
        // Обработка других действий
      }
    }
    
    
  4. Реализуйте функционал в методах MoveTo , SearchForObject , ChopTree :

    В этих методах выполните соответствующие действия в зависимости от текущего состояния действия. Например, в методе MoveTo :

    
    private void MoveTo(Vector3 targetPosition)
    {
      if (currentState == CharacterAction.ActionState.InProgress)
      {
        // Выполните код движения (например, в методе Update())
        float moveSpeed = 5.0f;
        Vector3 moveDirection = (targetPosition - transform.position).normalized;
        transform.Translate(moveDirection * moveSpeed * Time.deltaTime);
        
        // Проверка, завершено ли действие
        if (Vector3.Distance(transform.position, targetPosition) < 0.1f)
        {
          currentState = CharacterAction.ActionState.Completed;
        }
      }
    }
    
    

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

Как не допустить ошибку, которая возникает при выполнении действия из очереди с объектом, который был ранее удален. Сейчас реализовано с помощью проверки на пустой объект для действий, которые используют объект, можно ли это реализовать при добавлении в очередь или при разрушении объекта

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

  1. Проверка наличия объекта при добавлении в очередь:

    При добавлении действия в очередь, проверяйте, существует ли целевой объект, и добавляйте действие только если объект существует. Например:

    
    // Метод для добавления действия в очередь
    public void AddAction(CharacterAction action)
    {
      // Проверяем, что целевой объект действия существует
      if (action.targetObject != null)
      {
        actionQueue.Enqueue(action);
      }
      else
      {
        Debug.LogWarning("Попытка добавления действия с отсутствующим объектом в очередь.");
      }
    }
    
    

    Это позволит избежать добавления действий с отсутствующими объектами в очередь.

  2. Обработка удаления объекта из очереди:

    Когда объект уничтожается (например, при срубке дерева), вы можете проверить, содержит ли очередь это действие. Если да, вы можете удалить это действие из очереди. Например:

    
    // Метод для удаления действия из очереди при удалении объекта
    public void RemoveActionOnObjectDestroy(GameObject destroyedObject)
    {
      CharacterAction[] actionsInQueue = actionQueue.ToArray();
    
      foreach (CharacterAction action in actionsInQueue)
      {
        if (action.targetObject == destroyedObject)
        {
          actionQueue.Dequeue();
        }
      }
    }
    
    

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

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

*сделал объект обязательным для всех действий, для перемещения указал объект - terrain

персонаж может брать в руки предметы, такие как бревно или мебель. И в тот момент, когда у него в руках крупный предмет, чтобы были доступны только определенные действия, такие как перемещение к точке, перемещение к объекту и выкинуть предмет, и чтобы не были доступны другие действия. А если в руках предмет и происходит клик по объекту, допустим по дереву, чтобы персонаж подходил к нему, бросал предмет и только потом начинал рубить дерево. как нужно изменить скрипт CharacterController?

Для реализации функциональности, когда персонаж может держать в руках крупные предметы и выполнять определенные действия только в этом режиме, а также для обработки случая, когда клик по объекту вызывает специфическое поведение (например, бросок предмета), вам потребуется внести несколько изменений в ваш скрипт CharacterController .

  1. Добавьте переменную для хранения текущего предмета в руках персонажа:

    В вашем скрипте CharacterController создайте переменную для хранения текущего предмета, который персонаж держит в руках:

    
    public GameObject heldItem; // Текущий предмет, который персонаж держит в руках
    
    
  2. Измените метод для выполнения действий:

    Измените метод PerformCurrentAction так, чтобы он проверял наличие предмета в руках и в зависимости от этого выполнял различные действия. Если предмет есть в руках, то доступны только определенные действия (например, перемещение к точке, перемещение к объекту и выкинуть предмет), иначе доступны все другие действия.

    Вот пример того, как может выглядеть измененный метод:

    
    public void PerformCurrentAction()
    {
      if (actionQueue.Count > 0)
      {
        currentAction = actionQueue.Peek();
    
        if (heldItem != null)
        {
          // Если есть предмет в руках, проверьте доступные действия для этого режима
          if (currentAction.actionType == CharacterAction.ActionType.MoveTo ||
            currentAction.actionType == CharacterAction.ActionType.MoveToObject ||
            currentAction.actionType == CharacterAction.ActionType.DropItem)
          {
            // Выполняйте только указанные действия
            ExecuteAction(currentAction);
          }
          else
          {
            Debug.Log("Нельзя выполнить это действие с предметом в руках.");
          }
        }
        else
        {
          // Если предмета нет в руках, выполните любое доступное действие
          ExecuteAction(currentAction);
        }
      }
    }
    
    private void ExecuteAction(CharacterAction action)
    {
      // Реализуйте выполнение действия здесь
    }
    
    
  3. Измените метод для клика по объекту:

    В методе, который обрабатывает клик по объекту (например, по дереву), добавьте проверку наличия предмета в руках. Если персонаж держит предмет, то сначала выполняйте действие "выкинуть предмет", а затем начинайте выполнение действия, связанного с объектом (например, рубка дерева):

    
    public void OnObjectClick(GameObject clickedObject)
    {
      if (heldItem != null)
      {
        // Если есть предмет в руках, выкиньте его
        ExecuteDropItemAction();
      }
    
      // Затем начните выполнение действия, связанного с объектом (например, рубка дерева)
      ExecuteActionRelatedToObject(clickedObject);
    }
    
    private void ExecuteDropItemAction()
    {
      // Реализуйте выкидывание предмета здесь
    }
    
    private void ExecuteActionRelatedToObject(GameObject targetObject)
    {
      // Реализуйте выполнение действия, связанного с объектом (например, рубка дерева) здесь
    }
    
    

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

28.09.2023 01:23

Лента статей

Наверх