Walking to items

anyone found a solution to walk to an item or person before interacting with them before picking the item up or talking to the character

It’s been a while since i did the RPG course, and im sure someone else has a better solution;
I believe (if i dont forget to link something now) i did this;

using System.Collections;
using System.Collections.Generic;
using GameDevTV.Inventories;
using RPG.Core;
using RPG.Movement;
using UnityEngine;

namespace RPG.Control
{
    [RequireComponent(typeof(Pickup))]
    public class ClickablePickup : MonoBehaviour, IRayCastable, IAction
    {
        [SerializeField] float pickupRange = 1f;

        GameObject player;
        Pickup savedPickup = null;
        Pickup pickup;
        ActionScheduler actionScheduler;

        public bool isItemDroppedByPlayer = false;

        private void Awake()
        {
            pickup = GetComponent<Pickup>();
            player = GameObject.FindWithTag("Player");

            if(player != null)
            {
                actionScheduler = player.GetComponent<ActionScheduler>();
            }
        }

        private void Update()
        {
            if(savedPickup != null && IsInPickupRange())
            {
                savedPickup.PickupItem();
                Cancel();
            }
        }

        public CursorType GetCursorType()
        {
            if (pickup.CanBePickedUp())
            {
                return CursorType.Pickup;
            }
            else
            {
                return CursorType.FullPickup;
            }
        }

        public bool HandleRaycast(PlayerController callingController)
        {
            if (Input.GetMouseButtonDown(1))
            {
                if (player != null)
                {
                    MovePlayerToPickup();
                }

            }
            return true;
        }

        private void MovePlayerToPickup()
        {
            //actionScheduler.StartAction(this);
            if (!IsInPickupRange())
            {
                player.GetComponent<Mover>().MoveTo(transform.position, pickupRange);
                savedPickup = pickup;
            }
            else
            {
                player.GetComponent<Mover>().Cancel();
                pickup.PickupItem();
                savedPickup = null;
            }
        }

        public void Cancel()
        {
            if(player != null)
            {
                player.GetComponent<Mover>().Cancel();
            }
            savedPickup = null;
        }

        public bool IsInPickupRange()
        {
            return Vector3.Distance(transform.position, player.transform.position) < pickupRange;
        }

        private void OnTriggerEnter(Collider other)
        {
            if (other.gameObject.tag == "Player")
            {
                if (isItemDroppedByPlayer == false)
                {
                    pickup.PickupItem();
                    savedPickup = null;
                }
            }
        }
    }
}
1 Like

For the dialogue i did something i feel is weird probably.
I guess i could probably think of a better solution now ive advanced a bit more.
But i basically saved the dialogue and conversant as variables,
that would be used when Update() call the method again when in range.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using RPG.Core;
using Random = UnityEngine.Random;
using RPG.Movement;
// can also fix random with UnityEngine.Random.Range when calling



namespace RPG.Dialogue
{
    public class PlayerConversant : MonoBehaviour, IAction
    {
        [SerializeField] string playerName;
        [SerializeField] float conversationRange = 1f;
        [SerializeField] AudioSource dialogueAudioPlayer = null;

        Dialogue currentDialogue;
        DialogueNode currentNode = null;
        AIConversant currentConversant = null;
        bool isChoosing = false;

        public event Action onConversationUpdated;

        ActionScheduler actionScheduler;
        Mover mover;

        AIConversant savedConversant = null;
        Dialogue savedDialogue = null;


        void Awake()
        {
            actionScheduler = GetComponent<ActionScheduler>();
            mover = GetComponent<Mover>();
        }

        public void Update()
        {
            if(currentConversant == null) { return; }

            if (!IsInConversationRange())
            {
                mover.MoveTo(currentConversant.transform.position, conversationRange);
            }
            else
            {
                mover.Cancel();

                if (savedConversant != null && savedDialogue != null)
                {
                    StartDialogue(savedConversant, savedDialogue);
                    savedConversant = null;
                    savedDialogue = null;
                }
            }
        }

        public void StartDialogue(AIConversant newConversant, Dialogue newDialogue)
        {
            actionScheduler.StartAction(this);
            savedConversant = newConversant;
            savedDialogue = newDialogue;

            currentConversant = newConversant;

            if(!IsInConversationRange()) { return; }

            currentDialogue = newDialogue;
            currentNode = currentDialogue.GetRootNode();
            TriggerEnterAction();
            onConversationUpdated();
        }

        public void Quit()
        {
            currentDialogue = null;
            TriggerExitAction();
            currentNode = null;
            isChoosing = false;
            currentConversant = null;
            onConversationUpdated();
        }

        public bool IsActive()
        {
            return currentDialogue != null;
        }

        public string GetCurrentConversantName()
        {
            if (isChoosing)
            {
                return playerName;
            }
            else
            {
                return currentConversant.GetName();
            }
        }

        public bool IsChoosing()
        {
            return isChoosing;
        }

        public string GetText()
        {
            if(currentNode == null)
            {
            return "";
            }

        return currentNode.GetText(); 
        }

        public IEnumerable<DialogueNode> GetChoices()
        {
            return FilterOnCondition(currentDialogue.GetPlayerChildren(currentNode));
        }

        public void SelectChoice(DialogueNode chosenNode)
        {
            currentNode = chosenNode;
            TriggerEnterAction();
            isChoosing = false;
            Next();
        }

        public void Next()
        {
            int numPlayerResponses = FilterOnCondition(currentDialogue.GetPlayerChildren(currentNode)).Count();

            if(numPlayerResponses > 0)
            {
                isChoosing = true;
                TriggerExitAction();
                onConversationUpdated();
                return;
            }

            DialogueNode[] children = FilterOnCondition(currentDialogue.GetAIChildren(currentNode)).ToArray();
            int randomIndex = Random.Range(0, children.Count());
            TriggerExitAction();
            currentNode = children[randomIndex];
            TriggerEnterAction();
            onConversationUpdated();
        }

        private IEnumerable <DialogueNode> FilterOnCondition(IEnumerable<DialogueNode> inputNode)
        {
            foreach(var node in inputNode)
            {
                if (node.CheckCondition(GetEvaluators()))
                {
                    yield return node;
                }
            }
        }

        private IEnumerable<IPredicateEvaluator> GetEvaluators()
        {
            return GetComponents<IPredicateEvaluator>();
        }

        public bool HasNext()
        {
            return FilterOnCondition(currentDialogue.GetAllChildren(currentNode)).Count() > 0;
        }

        private void TriggerEnterAction()
        {
            if(currentNode != null)
            {
                TriggerAction(currentNode.GetOnEnterAction());

                PlayDialogueAudio();
            }
        }

        private void TriggerExitAction()
        {
            if(currentNode != null)
            {
                TriggerAction(currentNode.GetOnExitAction());
            }
        }

        private void TriggerAction(dialogueTriggerAction action)
        {
            if (action == dialogueTriggerAction.None) return;

            foreach (DialogueTrigger trigger in currentConversant.GetComponents<DialogueTrigger>())
            {
                trigger.Trigger(action);
            }
        }

        public void Cancel()
        {
            mover.Cancel();
            Quit();
        }

        public bool IsInConversationRange()
        {
            return Vector3.Distance(transform.position, currentConversant.transform.position) < conversationRange;
        }
    }
}

1 Like

Both of these scripts look like excellent solutions.

Here’s what I did:

First, I created a new class ItemCollector

public class ItemCollector : MonoBehaviour, IAction
{
}

Then I borrowed some logic from Fighter

public class ItemCollector : MonoBehaviour, IAction
{
     [SerializeField] fload pickupDistance = 3.0f;
     Pickup target; 
     
     void Update()
     {
           if(target==null) return;
           if(Vector3.Distance(transform.position, target.transform.position) < pickupDistance)
           {
                 target.PickupItem;
                 target=null;
           }
     }

     public void CollectItem(Pickup item)
     {
         GetComponent<ActionScheduler>().StartAction(this);
         target=item;
         GetComponent<Mover>().MoveTo(target.transform.position);
     }
    
     public void Cancel()
     {
          target=null;
     }
}

Then the only change needed in the ClickablePickup script is instead of calling PickupItem, getting the ItemCollector component off of the callingController and calling CollectItem()

The Dialogue simply added this functionality to the PlayerConversant, much like you did, I store the target AIConversant and Dialogue.

1 Like

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms