Search Unity

  1. We are migrating the Unity Forums to Unity Discussions by the end of July. Read our announcement for more information and let us know if you have any questions.
    Dismiss Notice
  2. Dismiss Notice

Question NPC changing sprites to face the player

Discussion in '2D' started by CharlotteJO, May 15, 2024.

  1. CharlotteJO

    CharlotteJO

    Joined:
    Aug 25, 2023
    Posts:
    1
    Hi, I'm currently experiencing an issue with programming NPC's. I want NPC's to change their sprite depending on where the player is positioned (example: if the Player is interacting with them and they're facing right then the NPC faces left). However, while I do know that I'm on the right track, as soon as the Player interacts with the NPC, the NPC changes sprites but then immediately changes back to the idle sprite. If I try interacting with the NPC again, it no longer changes sprites at all and stays at the idle sprite. I've been trying to solve this on my own for way too long so I'm reaching out to see who can help.

    I've linked a video representation of my issue if it helps. Also, if you believe that there's any extra scripts you may need to see, I'll show you the script.

    https://1drv.ms/v/s!Akv6vtVraeC9mRoo--FF7JpJ0GkK?e=h3DL3P

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. /*
    6. public class NPCController : MonoBehaviour, Interactable
    7. {
    8.     [SerializeField] Dialogue dialogue;
    9.    
    10.     public void Interact()
    11.     {
    12.         StartCoroutine (DialogueManager.Instance.ShowDialogue(dialogue));
    13.     }
    14. }
    15. */
    16.  
    17. public class NPCController : MonoBehaviour, Interactable
    18. {
    19.     [SerializeField] Dialogue dialogue;
    20.     [SerializeField] Transform playerTransform; // Assign the player's transform in the inspector
    21.     private Animator animator; // Reference to the Animator component
    22.  
    23.     private void Awake()
    24.     {
    25.         animator = GetComponent<Animator>();
    26.     }
    27.  
    28.     public void Interact()
    29.     {
    30.         Debug.Log("Interact method called");
    31.        
    32.         // Determine the direction to the player
    33.         Vector3 directionToPlayer = playerTransform.position - transform.position;
    34.  
    35.         // Set the appropriate animation based on the player's position
    36.         if (Mathf.Abs(directionToPlayer.x) > Mathf.Abs(directionToPlayer.y))
    37.         {
    38.             // Player is primarily horizontal to NPC
    39.             if (directionToPlayer.x > 0)
    40.             {
    41.                 animator.SetTrigger("FaceRight");
    42.             }
    43.             else if (directionToPlayer.x < 0)
    44.             {
    45.                 animator.SetTrigger("FaceLeft");
    46.             }
    47.         }
    48.         else
    49.         {
    50.             // Player is primarily vertical to NPC
    51.             if (directionToPlayer.y > 0)
    52.             {
    53.                 animator.SetTrigger("FaceUp");
    54.             }
    55.             else if (directionToPlayer.y < 0)
    56.             {
    57.                 animator.SetTrigger("FaceDown");
    58.             }
    59.         }
    60.  
    61.         // Start the dialogue and wait for it to finish before resetting the sprite
    62.         StartCoroutine(ShowDialogueAndReset());
    63.     }
    64.  
    65.     private IEnumerator ShowDialogueAndReset()
    66.     {
    67.         yield return StartCoroutine(DialogueManager.Instance.ShowDialogue(dialogue));
    68.         // Reset the NPC sprite to idle after the dialogue is finished
    69.         animator.SetTrigger("ResetIdle");
    70.     }
    71. }
    72.  
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5.  
    6. public enum GameState { FreeRoam, Dialogue, Battle }
    7. public class GameController : MonoBehaviour
    8. {
    9.     [SerializeField] PlayerMovement playerController;
    10.  
    11.     GameState state;
    12.  
    13.     private void Start()
    14.     {
    15.         DialogueManager.Instance.OnShowDialogue += () =>
    16.         {
    17.             state = GameState.Dialogue;
    18.         };
    19.         DialogueManager.Instance.OnHideDialogue += () =>
    20.         {
    21.             if (state == GameState.Dialogue)
    22.                 state = GameState.FreeRoam;
    23.         };
    24.     }
    25.  
    26.     void Update()
    27.     {
    28.         if (state == GameState.FreeRoam)
    29.         {
    30.             playerController.HandleUpdate();
    31.         }
    32.         else if (state == GameState.Dialogue)
    33.         {
    34.             DialogueManager.Instance.HandleUpdate();
    35.         }
    36.         else if (state == GameState.Battle)
    37.         {
    38.            
    39.         }
    40.     }
    41. }
    42.  
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. [System.Serializable]
    6.  
    7. public class Dialogue
    8. {
    9.     [SerializeField] List<string> lines;
    10.  
    11.     public List<string> Lines
    12.     {
    13.         get { return lines; }
    14.     }
    15. }
    16.  
    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.UI;
    6.  
    7. public class DialogueManager : MonoBehaviour
    8. {
    9.     [SerializeField] GameObject dialogueBox;
    10.     [SerializeField] Text dialogueText;
    11.  
    12.     [SerializeField] int lettersPerSeconds;
    13.  
    14.     public event Action OnShowDialogue;
    15.     public event Action OnHideDialogue;
    16.    
    17.     public static DialogueManager Instance { get; private set; }
    18.  
    19.     private void Awake()
    20.     {
    21.         Instance = this;
    22.     }
    23.  
    24.     Dialogue dialogue;
    25.     int currentLine = 0;
    26.     bool isTyping;
    27.  
    28.     public IEnumerator ShowDialogue(Dialogue dialogue)
    29.     {
    30.         yield return new WaitForEndOfFrame();
    31.         OnShowDialogue?.Invoke();
    32.  
    33.         this.dialogue = dialogue;
    34.         dialogueBox.SetActive(true);
    35.         StartCoroutine(TypeDialogue(dialogue.Lines[0]));
    36.     }
    37.  
    38.     public void HandleUpdate()
    39.     {
    40.         if (Input.GetKeyDown(KeyCode.F) && !isTyping)
    41.         {
    42.             ++currentLine;
    43.             if (currentLine < dialogue.Lines.Count)
    44.             {
    45.                  StartCoroutine(TypeDialogue(dialogue.Lines[currentLine]));
    46.             }
    47.             else
    48.             {
    49.                 dialogueBox.SetActive(false);
    50.                 currentLine = 0;
    51.                 OnHideDialogue?.Invoke();
    52.             }
    53.         }
    54.     }
    55.    
    56.     public IEnumerator TypeDialogue(string line)
    57.     {
    58.         isTyping = true;  
    59.         dialogueText.text = "";
    60.         foreach (var letter in line.ToCharArray())
    61.         {
    62.             dialogueText.text += letter;
    63.             yield return new WaitForSeconds(1f / lettersPerSeconds);
    64.         }
    65.         isTyping = false;
    66.     }
    67. }
    68.  
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    39,412
    This just sounds like a bug you need to track down and fix by debugging.

    Remember, the issue might be in code or it might be in the Animator. Test the Animator first and PROVE it works as you expect when properties are manually changed at runtime without the script interfering.

    By debugging you can find out exactly what your program is doing so you can fix it.

    https://docs.unity3d.com/Manual/ManagedCodeDebugging.html

    Use the above techniques to get the information you need in order to reason about what the problem is.

    You can also use
    Debug.Log(...);
    statements to find out if any of your code is even running. Don't assume it is.

    Once you understand what the problem is, you may begin to reason about a solution to the problem.
     
  3. dstears

    dstears

    Joined:
    Sep 6, 2021
    Posts:
    225
    It may be a good idea to add a Debug.Log statement just prior to the line that sets the "ResetIdle" trigger. That way, you can see when it is being triggered.

    From the ShowDialogueAndReset code, it looks like the ResetIdle happens after the ShowDialogue coroutine finishes. However, the ShowDialogue coroutine only has a single "yield return new WaitForEndOfFrame()" before it will finish. Was ShowDialogue supposed to have a yield when it called TypeDialogue?