Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Moving an NPC to the right and stopping

Discussion in 'Scripting' started by DustyShinigami, May 22, 2019.

  1. DustyShinigami

    DustyShinigami

    Joined:
    Jan 5, 2018
    Posts:
    529
    As usual, I'm not sure how I'd achieve this. I've tried following a couple of tutorials but they've mostly been for 2D games. Basically, after finishing talking to my NPC, I want them to move to the right and stop at a lever before pulling it. The closest I've gotten is my NPC floating along to the right, but they start to rise and move in a slightly diagonal position. They also stopped on top of the lever instead of on my Destination GameObject. I can't seem to replicate that and all the NPC does now is face the right direction. This is the code I have at the moment:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.SceneManagement;
    5.  
    6. public class NPC : MonoBehaviour
    7. {
    8.     public Animator anim;
    9.     public float moveSpeed;
    10.     public float gravityScale;
    11.     public bool canMove;
    12.     public static bool interact = false;
    13.     public static bool allowInteract = false;
    14.     public Transform[] target;
    15.  
    16.     private CharacterController controller;
    17.     private Vector3 moveDirection;
    18.     private int current;
    19.  
    20.     void Start()
    21.     {
    22.         controller = GetComponent<CharacterController>();
    23.         canMove = false;
    24.     }
    25.  
    26.     public void Walk()
    27.     {
    28.         canMove = true;
    29.     }
    30.  
    31.     public void Update()
    32.     {
    33.         if (canMove)
    34.         {
    35.             if (transform.position != target[current].position)
    36.             {
    37.                 Vector3 pos = Vector3.MoveTowards(transform.position, target[current].position, moveSpeed * Time.deltaTime);
    38.                 controller.Move(moveDirection * Time.deltaTime);
    39.                 transform.eulerAngles = new Vector2(0, 90);
    40.                 anim.SetBool("isGrounded", controller.isGrounded);
    41.                 anim.SetFloat("Speed", moveSpeed);
    42.             }
    43.             else current = (current + 1) % target.Length;
    44.         }
    45.     }
    46. }
     
  2. DustyShinigami

    DustyShinigami

    Joined:
    Jan 5, 2018
    Posts:
    529
    Okay, I've managed to get the NPC to 'float' from his start position to his destination. However, I can't seem to get his walking animation to work. Nor does the Animator recognise that he's grounded like it does for my player.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.SceneManagement;
    5.  
    6. public class NPC : MonoBehaviour
    7. {
    8.     public Animator anim;
    9.     public float moveSpeed;
    10.     public bool canMove;
    11.     public static bool interact = false;
    12.     public static bool allowInteract = false;
    13.     public Transform target;
    14.  
    15.     //private Rigidbody theRigidbody;
    16.     private CharacterController controller;
    17.     private Vector3 moveDirection;
    18.     private int current;
    19.  
    20.     void Start()
    21.     {
    22.         controller = GetComponent<CharacterController>();
    23.         canMove = false;
    24.     }
    25.  
    26.     public void Walk()
    27.     {
    28.         canMove = true;
    29.     }
    30.  
    31.     public void Update()
    32.     {
    33.         if (canMove)
    34.         {
    35.             transform.eulerAngles = new Vector2(0, 90);
    36.             target.transform.position = new Vector3(-12.45f, 12.005f, -0.695f);
    37.             transform.position = Vector3.MoveTowards(transform.position, target.position, moveSpeed * Time.deltaTime);
    38.             //controller.Move(transform.position * moveSpeed);
    39.             //anim.SetFloat("Speed", Mathf.Abs(Input.GetAxis("Horizontal")));
    40.         }
    41.  
    42.         if (controller.isGrounded)
    43.         {
    44.             anim.SetBool("isGrounded", controller.isGrounded);
    45.         }
    46.  
    47.         /*Vector3 move = new Vector3(moveSpeed * Time.deltaTime, 0, 0);
    48.         controller.Move(move * Time.deltaTime * moveSpeed);
    49.         anim.SetBool("isGrounded", controller.isGrounded);
    50.         anim.SetFloat("Speed", moveSpeed);*/
    51.         /*if (transform.position != target[current].position)
    52.         {
    53.             Vector3 pos = Vector3.MoveTowards(transform.position, target[current].position, moveSpeed * Time.deltaTime);
    54.             controller.Move(moveDirection * Time.deltaTime);
    55.             anim.SetBool("isGrounded", controller.isGrounded);
    56.             anim.SetFloat("Speed", moveSpeed);
    57.         }
    58.         else current = (current + 1) % target.Length;*/
    59.     }
    60. }
     
  3. DustyShinigami

    DustyShinigami

    Joined:
    Jan 5, 2018
    Posts:
    529
    I really need help with this. No matter how I set up my animations in the NPC script or the conditions in the Animator, the only one it recognises is Idle. It never switches to Walk or Interact. :( Even if I try to set things up like in my PlayerController script, it just won't work.

    NPC:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.SceneManagement;
    5.  
    6. public class NPC : MonoBehaviour
    7. {
    8.     public Animator anim;
    9.     public float moveSpeed;
    10.     public bool canMove;
    11.     public static bool interact = false;
    12.     public static bool allowInteract = false;
    13.     public Transform target;
    14.     public bool targetReached;
    15.  
    16.     //private Rigidbody theRigidbody;
    17.     private CharacterController controller;
    18.     private Vector3 moveDirection;
    19.     private int current;
    20.  
    21.     void Start()
    22.     {
    23.         controller = GetComponent<CharacterController>();
    24.         canMove = false;
    25.     }
    26.  
    27.     public void Walk()
    28.     {
    29.         canMove = true;
    30.     }
    31.  
    32.     public void Update()
    33.     {
    34.         if (canMove)
    35.         {
    36.             transform.eulerAngles = new Vector2(0, 90);
    37.             target.transform.position = new Vector3(-12.45f, 12.005f, -0.695f);
    38.             transform.position = Vector3.MoveTowards(transform.position, target.position, moveSpeed * Time.deltaTime);
    39.             if (transform.position == target.position)
    40.             {
    41.                 targetReached = true;
    42.                 if (targetReached)
    43.                 {
    44.                     allowInteract = true;
    45.                     anim.SetTrigger("Interact Trigger");
    46.                     anim.SetBool("Interact", controller.isGrounded);
    47.                 }
    48.             }
    49.             //controller.Move(transform.position * moveSpeed);
    50.             //anim.SetFloat("Speed", Mathf.Abs(Input.GetAxis("Horizontal")));
    51.         }
    52.  
    53.         /*if (controller.isGrounded)
    54.         {
    55.             if (allowInteract)
    56.             {
    57.                 interact = true;
    58.                 if (interact)
    59.                 {
    60.                     anim.SetBool("Interact", controller.isGrounded);
    61.                 }
    62.             }
    63.         }*/
    64.  
    65.         anim.SetBool("isGrounded", controller.isGrounded);
    66.         anim.SetFloat("Speed", moveSpeed);
    67.  
    68.         /*Vector3 move = new Vector3(moveSpeed * Time.deltaTime, 0, 0);
    69.         controller.Move(move * Time.deltaTime * moveSpeed);
    70.         anim.SetBool("isGrounded", controller.isGrounded);
    71.         anim.SetFloat("Speed", moveSpeed);*/
    72.         /*if (transform.position != target[current].position)
    73.         {
    74.             Vector3 pos = Vector3.MoveTowards(transform.position, target[current].position, moveSpeed * Time.deltaTime);
    75.             controller.Move(moveDirection * Time.deltaTime);
    76.             anim.SetBool("isGrounded", controller.isGrounded);
    77.             anim.SetFloat("Speed", moveSpeed);
    78.         }
    79.         else current = (current + 1) % target.Length;*/
    80.     }
    81. }
    PlayerController:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.SceneManagement;
    5.  
    6. public class PlayerController : MonoBehaviour
    7. {
    8.     public Animator anim;
    9.     public float moveSpeed;
    10.     public float jumpForce;
    11.     public bool jumped;
    12.     public bool attack;
    13.     public static bool interact = false;
    14.     public static bool allowInteract = false;
    15.     public float gravityScale;
    16.     public float knockBackForce;
    17.     public float knockBackTime;
    18.     public float invincibilityLength;
    19.     public Renderer playerRenderer;
    20.     public Material textureChange;
    21.     public Material textureDefault;
    22.     public bool allowCombat;
    23.     public bool allowJump;
    24.     public bool notDestroyed;
    25.     public static bool canMove;
    26.  
    27.     private Vector3 moveDirection;
    28.     private Vector3 extraDirections;
    29.     private float knockBackCounter;
    30.     private float invincibilityCounter;
    31.     private CharacterController controller;
    32.  
    33.     void Start()
    34.     {
    35.         Cursor.visible = false;
    36.         controller = GetComponent<CharacterController>();
    37.         canMove = true;
    38.         if (SceneManager.GetActiveScene() == SceneManager.GetSceneByName("start_area"))
    39.         {
    40.             allowCombat = false;
    41.             allowJump = true;
    42.         }
    43.         if (SceneManager.GetActiveScene() == SceneManager.GetSceneByName("hut_interior"))
    44.         {
    45.             allowCombat = false;
    46.             allowJump = false;
    47.         }
    48.         if (SceneManager.GetActiveScene() == SceneManager.GetSceneByName("start_area 2"))
    49.         {
    50.             allowCombat = false;
    51.             allowJump = true;
    52.         }
    53.         if (SceneManager.GetActiveScene() == SceneManager.GetSceneByName("level 1"))
    54.         {
    55.             allowCombat = true;
    56.             allowJump = true;
    57.         }
    58.     }
    59.  
    60.     void Update()
    61.     {
    62.         if (knockBackCounter <= 0 && canMove)
    63.         {
    64.             float moveHorizontal = Input.GetAxis("Horizontal");
    65.             float moveVertical = Input.GetAxis("Vertical");
    66.             moveDirection = new Vector2(moveHorizontal * moveSpeed, moveDirection.y);
    67.             extraDirections = new Vector2(moveVertical * moveSpeed, extraDirections.y);
    68.             controller.Move(moveDirection * Time.deltaTime);
    69.  
    70.             if (moveHorizontal > 0)
    71.             {
    72.                 transform.eulerAngles = new Vector2(0, 90);
    73.             }
    74.             else if (moveHorizontal < 0)
    75.             {
    76.                 transform.eulerAngles = new Vector2(0, -90);
    77.             }
    78.             //To possibly prevent diagonal movement with some control setups, try adding 'else if'
    79.             else if (SceneManager.GetActiveScene() == SceneManager.GetSceneByName("hut_interior"))
    80.             {
    81.                 if (moveVertical > 0)
    82.                 {
    83.                     transform.eulerAngles = new Vector2(0, 0);
    84.                 }
    85.                 //Use this to make the character face towards the camera.
    86.                 /*else if (moveVertical < 0)
    87.                 {
    88.                     transform.eulerAngles = new Vector3(0, 180);
    89.                 }*/
    90.             }
    91.             if (controller.isGrounded)
    92.             {
    93.                 if (allowJump)
    94.                 {
    95.                     moveDirection.y = -1f;
    96.                     //GetKeyDown will require the player to press the button each time they want to jump. GetKey will allow the player to spam the jump button if they keep pressing it down.
    97.                     if (Input.GetKeyDown(KeyCode.KeypadPlus) || Input.GetKeyDown("joystick button 1"))
    98.                     {
    99.                         moveDirection.y = jumpForce;
    100.                         jumped = true;
    101.                     }
    102.                     else if (!Input.GetKeyDown(KeyCode.KeypadPlus) || !Input.GetKeyDown("joystick button 1"))
    103.                     {
    104.                         jumped = false;
    105.                     }
    106.                 }
    107.  
    108.                 if (allowCombat)
    109.                 {
    110.                     if (Input.GetKey(KeyCode.Space) || Input.GetKey("joystick button 7"))
    111.                     {
    112.                         attack = true;
    113.                         playerRenderer.material = textureChange;
    114.                     }
    115.                     else if (!Input.GetKey(KeyCode.Space) || !Input.GetKey("joystick button 7"))
    116.                     {
    117.                         attack = false;
    118.                         playerRenderer.material = textureDefault;
    119.                     }
    120.                 }
    121.                 else if (!allowCombat)
    122.                 {
    123.                     attack = false;
    124.                     playerRenderer.material = textureDefault;
    125.                 }
    126.  
    127.                 if (allowInteract)
    128.                 {
    129.                     if (SceneManagement.xbox360Controller == 1)
    130.                     {
    131.                         if (Input.GetKeyDown("joystick button 2"))
    132.                         {
    133.                             interact = true;
    134.                         }
    135.                     }
    136.                     else if (SceneManagement.ps4Controller == 1)
    137.                     {
    138.                         if (Input.GetKeyDown("joystick button 0"))
    139.                         {
    140.                             interact = true;
    141.                         }
    142.                     }
    143.                     else
    144.                     {
    145.                         interact = true;
    146.                     }
    147.                 }
    148.             }
    149.         }
    150.         else
    151.         {
    152.             knockBackCounter -= Time.deltaTime;
    153.         }
    154.  
    155.         moveDirection.y = moveDirection.y + (Physics.gravity.y * gravityScale * Time.deltaTime);
    156.  
    157.         anim.SetBool("isGrounded", controller.isGrounded);
    158.         //If the character can't move, then the Speed is set to 0. Otherwise it'll use the horizontal input value.
    159.         anim.SetFloat("Speed",
    160.             !canMove
    161.             ? 0f
    162.             : Mathf.Abs(Input.GetAxis("Horizontal")));
    163.  
    164.         //anim.SetFloat("Speed", Mathf.Abs(Input.GetAxis("Horizontal")));
    165.  
    166.         if (interact)
    167.         {
    168.             if (Input.GetKeyDown(KeyCode.Return))
    169.             {
    170.                 anim.SetBool("Interact", controller.isGrounded);
    171.                 FindObjectOfType<Pickup>().ObjectActivation();
    172.                 interact = false;
    173.                 allowInteract = false;
    174.             }
    175.         }
    176.         if (attack)
    177.         {
    178.             anim.SetTrigger("Attack");
    179.         }
    180.     }
    181.  
    182.     public void Knockback(Vector3 direction)
    183.     {
    184.         knockBackCounter = knockBackTime;
    185.  
    186.         moveDirection = direction * knockBackForce;
    187.         moveDirection.y = knockBackForce;
    188.     }
    189. }
     
  4. DustyShinigami

    DustyShinigami

    Joined:
    Jan 5, 2018
    Posts:
    529
    I'm guessing for NPCs, setting up conditions and using the Animator in the same way as you would for a player character isn't the way to go...? Would Animator.Play("Walk") etc. be better in this case...? How do you stop animations though? I don't get why there's an Animator.Play but no Animator.Stop. :-\
     
  5. DustyShinigami

    DustyShinigami

    Joined:
    Jan 5, 2018
    Posts:
    529
    Anyone? I feel like I’m just talking to myself here.
     
  6. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,615
    Why not?
    That works too. When the player stops moving, the animation doesn't stop- it just switches to the idle state and plays that animation instead.
     
  7. DustyShinigami

    DustyShinigami

    Joined:
    Jan 5, 2018
    Posts:
    529
    Thanks for replying. :)

    I dunno; I've not been sure how to set things up for an NPC considering for a player, you assign key presses to trigger animations/movements. But as an NPC isn't controlled by the player...

    The problem with the Animator.Play option is that I have to make my walk animation loop otherwise the animation finishes playing before the NPC reaches its destination. But with loop on, whenever the character does stop, the loop doesn't end and doesn't revert back to Idle.

    After further experimenting, this is what I have set up so far. Things are working fine, though I still need to implement my interact animation at the right time. Plus, I don't know if this is the most proficient way of setting it up.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.SceneManagement;
    5.  
    6. public class NPC : MonoBehaviour
    7. {
    8.     public Animator anim;
    9.     public float moveSpeed;
    10.     public bool canMove;
    11.     public static bool interact = false;
    12.     public static bool allowInteract = false;
    13.     public Transform target;
    14.     public bool targetReached;
    15.  
    16.     private CharacterController controller;
    17.  
    18.     void Start()
    19.     {
    20.         anim = GetComponent<Animator>();
    21.         controller = GetComponent<CharacterController>();
    22.         canMove = false;
    23.         Idle();
    24.     }
    25.  
    26.     public void Idle()
    27.     {
    28.         anim.SetBool("isMoving", false);
    29.         if (anim.GetBool("isMoving") == false)
    30.         {
    31.             anim.SetTrigger("Idle");
    32.         }
    33.     }
    34.  
    35.     public void Walk()
    36.     {
    37.         canMove = true;
    38.     }
    39.  
    40.     public void Update()
    41.     {
    42.         if (canMove)
    43.         {
    44.             transform.eulerAngles = new Vector2(0, 90);
    45.             target.transform.position = new Vector3(-12.45f, 12.005f, -0.695f);
    46.             transform.position = Vector3.MoveTowards(transform.position, target.position, moveSpeed * Time.deltaTime);
    47.             anim.SetBool("isMoving", true);
    48.             anim.ResetTrigger("Idle");
    49.             anim.SetFloat("Speed", 0.2f);
    50.             anim.SetTrigger("Walk");
    51.             if (transform.position == target.position)
    52.             {
    53.                 anim.SetFloat("Speed", 0f);
    54.                 anim.ResetTrigger("Walk");
    55.                 targetReached = true;
    56.                 if (targetReached)
    57.                 {
    58.                     //anim.ResetTrigger("Walk");
    59.                     Idle();
    60.                     allowInteract = true;
    61.                 }
    62.                 /*allowInteract = true;
    63.                 if (targetReached && allowInteract)
    64.                 {
    65.                     Idle();
    66.                     interact = true;
    67.                     //anim.SetTrigger("Interact");
    68.                 }*/
    69.             }
    70.         }
    71.     }
    72. }
     
  8. DustyShinigami

    DustyShinigami

    Joined:
    Jan 5, 2018
    Posts:
    529
    I've been at this for literally hours! I'm so close to the end, but I can't seem to get my NPC to move back to the left and the starting position after pulling a lever. Everything I've tried just makes things go wrong. :( I think I may have overdone it with the bools as well...

    Current script:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.SceneManagement;
    5.  
    6. public class NPC : MonoBehaviour
    7. {
    8.     public Animator anim;
    9.     public float moveSpeed;
    10.     public bool moveRight;
    11.     public bool moveLeft;
    12.     public bool nPCMoving;
    13.     public bool nPCIdle;
    14.     public bool interact = false;
    15.     public Transform target;
    16.     public Transform startPoint;
    17.  
    18.     private CharacterController controller;
    19.     private bool leverPulled;
    20.     //private bool playerIdle;
    21.  
    22.     void Start()
    23.     {
    24.         anim = GetComponent<Animator>();
    25.         controller = GetComponent<CharacterController>();
    26.         moveRight = false;
    27.         moveLeft = false;
    28.         nPCIdle = true;
    29.         Idle();
    30.     }
    31.  
    32.     public void Idle()
    33.     {
    34.         if (nPCIdle)
    35.         {
    36.             anim.ResetTrigger("Walk");
    37.             anim.SetBool("isMoving", false);
    38.             anim.SetFloat("Speed", 0f);
    39.             anim.SetTrigger("Idle");
    40.         }
    41.     }
    42.  
    43.     public void Walk()
    44.     {
    45.         moveRight = true;
    46.         if (moveRight)
    47.         {
    48.             if (nPCMoving)
    49.             {
    50.                 anim.ResetTrigger("Idle");
    51.                 anim.SetBool("isMoving", true);
    52.                 anim.SetFloat("Speed", 0.2f);
    53.                 anim.SetTrigger("Walk");
    54.             }
    55.         }
    56.         if (moveLeft)
    57.         {
    58.             if (nPCMoving)
    59.             {
    60.                 anim.ResetTrigger("Idle");
    61.                 anim.SetBool("isMoving", true);
    62.                 anim.SetFloat("Speed", 0.2f);
    63.                 anim.SetTrigger("Walk");
    64.             }
    65.         }
    66.     }
    67.  
    68.     public void Interact()
    69.     {
    70.         if (interact)
    71.         {
    72.             anim.ResetTrigger("Walk");
    73.             anim.SetBool("isMoving", false);
    74.             anim.SetFloat("Speed", 0f);
    75.             anim.SetTrigger("Interact");
    76.         }
    77.     }
    78.  
    79.     public void Update()
    80.     {
    81.         if (moveRight)
    82.         {
    83.             transform.eulerAngles = new Vector2(0, 90);
    84.             target.transform.position = new Vector3(-12.45f, 12.005f, -0.695f);
    85.             transform.position = Vector3.MoveTowards(transform.position, target.position, moveSpeed * Time.deltaTime);
    86.             nPCMoving = true;
    87.             if (nPCMoving)
    88.             {
    89.                 nPCIdle = false;
    90.                 Walk();
    91.             }
    92.         }
    93.         if (transform.position == target.position)
    94.         {
    95.             moveRight = false;
    96.             nPCMoving = false;
    97.             interact = true;
    98.             if (interact)
    99.             {
    100.                 Interact();
    101.             }
    102.         }
    103.         if (leverPulled)
    104.         {
    105.             nPCMoving = true;
    106.             moveLeft = true;
    107.             if (nPCMoving && moveLeft)
    108.             {
    109.                 Walk();
    110.             }
    111.         }
    112.         if (moveLeft)
    113.         {
    114.             transform.eulerAngles = new Vector2(0, -90);
    115.             startPoint.transform.position = new Vector3(-14.5f, 11.98f, -0.606f);
    116.             transform.position = Vector3.MoveTowards(transform.position, startPoint.position, moveSpeed * Time.deltaTime);
    117.         }
    118.         if (transform.position == startPoint.position)
    119.         {
    120.             moveLeft = true;
    121.             nPCMoving = false;
    122.             transform.eulerAngles = new Vector2(0, -180);
    123.         }
    124.     }
    125. }
     
  9. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,615
    I can't really follow what your code is doing at this point, but there are a few things that don't make sense.
    like this part:
    Code (csharp):
    1.  
    2.   if (leverPulled)
    3.         {
    4.             nPCMoving = true;
    5.             moveLeft = true;
    6.             if (nPCMoving && moveLeft)
    7.             {
    8.                 Walk();
    9.             }
    10.         }
    11.  
    You are setting nPCMoving and moveLeft to true and then you immediately check if they are true. Why bother? They're always going to be true there no matter what, so why check? Then you call a method called Walk(), but did you realize that the first line of that method sets moveRight to true? At that point moveLeft and moveRight are both true, which is confusing. Maybe that is causing unexpected behavior in your code?

    You should try "hand running" your code. That's when you act like you were the computer and pretend to run each line of code one-at-a-time. Use a sheet of paper to track the value of your variables if you need. It's tedious, but it will often reveal problems that you didn't realize before.

    Also, it looks like you have a lot of bools that are mutually exclusive. For example, I assume that you never want moveRight and moveLeft to both be true. For that sort of thing, it would actually be better to have a single variable to store whether the NPC is moving left, right or not at all. This is a whole other topic, so you might want to skip it for now and just get your code working BUT....In C# there's a datatype called an enum that is convenient for this sort of thing. It's described here:
    https://www.tutorialsteacher.com/csharp/csharp-enum

    In your case you could create and enum like this:
    Code (csharp):
    1.  
    2. enum npcState
    3. { moveLeft,
    4.   moveRight,
    5.   Idle
    6. }
    7.  
    Whatever values you want. Then you create an instance variable to store a value:
    Code (csharp):
    1.  
    2. npcState currentState= npcState.Idle;
    3.  
    You can set it to whatever value you want and compare it to other values:
    Code (csharp):
    1.  
    2. if (currentState == npcState.moveRight)
    3. { // do something
    4. }
    5. else if (currentState == npcState.moveLeft)
    6. { // do something else.
    7. }
    8.  
     
  10. DustyShinigami

    DustyShinigami

    Joined:
    Jan 5, 2018
    Posts:
    529
    Nope. I haven’t got a clue what I’m doing. Not when it comes to coding. A lot of it is guess work, experimenting and just getting things to work. Making assets and setting them up in Unity is more my strong suit, but I’m still up for learning whatever I can when it comes to scripting.

    I’ll have to look into getting rid of some of those bools next time. Thanks for suggesting enums. I think someone mentioned them before in another thread, but I’ve never used them yet.
     
  11. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,615
    A lot of programming concepts are not natural to the human brain. :) It takes practice to get the hang of it, but things that seem difficult now will seem easy later on (if you stick with it).
     
    DustyShinigami likes this.
  12. DustyShinigami

    DustyShinigami

    Joined:
    Jan 5, 2018
    Posts:
    529
    How exactly would I set up an enum? I can't set up multiple currentStates it seems. Am I better off doing the comparisons within a method? Also, I'm currently referencing the NPC script from my DialogueManager. Originally I was calling the Walk function once the characters finish talking. How could I do that now with the enum?
     
    Last edited: May 23, 2019
  13. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,615
    Which ever way you're comfortable with. I was only suggesting enums because I thought it might simplify things, but maybe not. In any case, I would try to simplify your code as much as possible, because it looks like there's some redundancy.
     
  14. DustyShinigami

    DustyShinigami

    Joined:
    Jan 5, 2018
    Posts:
    529
    I want to give them a try. I've not used them before and if they help simplify things... :)
     
  15. DustyShinigami

    DustyShinigami

    Joined:
    Jan 5, 2018
    Posts:
    529
    I'm still not sure how I can call each of my currentStates when I need them. I already have NPCState currentState = NPCState.Idle set before the Start function, but how I can switch in the Update function when I need the character to move? :-\
     
  16. DustyShinigami

    DustyShinigami

    Joined:
    Jan 5, 2018
    Posts:
    529
    I've tidied up my original code. I can't quite figure out though how to trigger the character to walk back to the left at the right time.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.SceneManagement;
    5.  
    6. public class NPC : MonoBehaviour
    7. {
    8.     public Animator anim;
    9.     public float moveSpeed;
    10.     public bool moveRight;
    11.     public bool interact;
    12.     public Transform target;
    13.     public Transform startPoint;
    14.  
    15.     private CharacterController controller;
    16.  
    17.     /*enum NPCState
    18.     {
    19.         moveRight,
    20.         Idle,
    21.         Interact
    22.     }
    23.  
    24.     NPCState currentState = NPCState.Idle;*/
    25.  
    26.     void Start()
    27.     {
    28.         anim = GetComponent<Animator>();
    29.         controller = GetComponent<CharacterController>();
    30.         Idle();
    31.     }
    32.  
    33.     /*public void NPCStates()
    34.     {
    35.  
    36.         if (currentState == NPCState.moveRight)
    37.         {
    38.             anim.ResetTrigger("Idle");
    39.             anim.SetBool("isMoving", true);
    40.             anim.SetFloat("Speed", 0.2f);
    41.             anim.SetTrigger("Walk");
    42.         }
    43.         else if (currentState == NPCState.Idle)
    44.         {
    45.             anim.ResetTrigger("Walk");
    46.             anim.SetBool("isMoving", false);
    47.             anim.SetFloat("Speed", 0f);
    48.             anim.SetTrigger("Idle");
    49.         }
    50.         else if (currentState == NPCState.Interact)
    51.         {
    52.             anim.ResetTrigger("Walk");
    53.             anim.SetBool("isMoving", false);
    54.             anim.SetFloat("Speed", 0f);
    55.             anim.SetTrigger("Interact");
    56.         }
    57.     }*/
    58.  
    59.     public void Idle()
    60.     {
    61.         anim.ResetTrigger("Walk");
    62.         anim.SetBool("isWalking", false);
    63.         anim.SetFloat("Speed", 0f);
    64.         anim.SetTrigger("Idle");
    65.     }
    66.  
    67.     public void Move()
    68.     {
    69.         moveRight = true;
    70.         anim.ResetTrigger("Idle");
    71.         anim.SetBool("isWalking", true);
    72.         anim.SetFloat("Speed", 0.2f);
    73.         anim.SetTrigger("Walk");
    74.     }
    75.  
    76.     public void Interact()
    77.     {
    78.         interact = true;
    79.         anim.ResetTrigger("Walk");
    80.         anim.SetBool("isWalking", false);
    81.         anim.SetFloat("Speed", 0f);
    82.         anim.SetTrigger("Interact");
    83.     }
    84.  
    85.     public void Update()
    86.     {
    87.         if (moveRight)
    88.         {
    89.             transform.eulerAngles = new Vector2(0, 90);
    90.             target.transform.position = new Vector3(-12.45f, 12.005f, -0.695f);
    91.             transform.position = Vector3.MoveTowards(transform.position, target.position, moveSpeed * Time.deltaTime);
    92.         }
    93.         /*if (!moveRight)
    94.         {
    95.             transform.eulerAngles = new Vector2(0, -90);
    96.             target.transform.position = new Vector3(-14.5f, 11.98f, -0.606f);
    97.             transform.position = Vector3.MoveTowards(transform.position, startPoint.position, moveSpeed * Time.deltaTime);
    98.         }*/
    99.         if (transform.position == target.position)
    100.         {
    101.             moveRight = false;
    102.             Interact();
    103.         }
    104.         else if(transform.position == startPoint.position)
    105.         {
    106.             moveRight = false;
    107.             transform.eulerAngles = new Vector2(0, -180);
    108.         }
    109.     }
    110. }
     
  17. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,615
    Does your code actually work aside from that? I don't see what triggers the character to start moving. Are you calling the Move method from somewhere else?
     
  18. DustyShinigami

    DustyShinigami

    Joined:
    Jan 5, 2018
    Posts:
    529
    Yeah, it does. The trigger comes from my DialogueManager on line 122.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5. using TMPro;
    6.  
    7. public class DialogueManager : MonoBehaviour
    8. {
    9.     public GameObject dialogueBox;
    10.     public TextMeshProUGUI nameDisplay;
    11.     public GameObject[] buttonPrompts;
    12.     public TextMeshProUGUI textDisplay;
    13.     public string[] sentences;
    14.     public string[] characterName;
    15.     public float typingSpeed;
    16.  
    17.     private int xbox360Controller = 0;
    18.     private int pS4Controller = 0;
    19.     private int index;
    20.     private BoxCollider dialogueTrigger;
    21.     private int currentLine;
    22.     private bool returnPressed;
    23.     private bool spacePressed;
    24.     private bool endDialogue;
    25.     private bool continueAllowed;
    26.     private bool characterVicinity;
    27.     private bool dialogueActive;
    28.     private NPC theNPC;
    29.  
    30.     void Start()
    31.     {
    32.         dialogueTrigger = GetComponent<BoxCollider>();
    33.         theNPC = FindObjectOfType<NPC>();
    34.     }
    35.  
    36.     IEnumerator Type()
    37.     {
    38.         foreach (char letter in sentences[index].ToCharArray())
    39.         {
    40.             textDisplay.text += letter;
    41.             yield return new WaitForSeconds(typingSpeed);
    42.         }
    43.     }
    44.  
    45.     public void NextSentence()
    46.     {
    47.         buttonPrompts[3].SetActive(false);
    48.         //If Index has less than the number of elements in the 'sentences' array by -1
    49.         if (index < sentences.Length - 1)
    50.         {
    51.             index++;
    52.             //Resets textDisplay so sentences don't stack
    53.             textDisplay.text = "";
    54.             StartCoroutine(Type());
    55.         }
    56.         else
    57.         {
    58.             textDisplay.text = "";
    59.         }
    60.     }
    61.  
    62.     public void OnTriggerStay(Collider other)
    63.     {
    64.         if (other.gameObject.name == "Player")
    65.         {
    66.             characterVicinity = true;
    67.             ControllerDetection();
    68.             if (pS4Controller == 1)
    69.             {
    70.                 PS4Prompts();
    71.             }
    72.             else if (xbox360Controller == 1)
    73.             {
    74.                 Xbox360Prompts();
    75.             }
    76.             else
    77.             {
    78.                 PCPrompts();
    79.             }
    80.         }
    81.     }
    82.  
    83.     public void OnTriggerExit(Collider other)
    84.     {
    85.         characterVicinity = false;
    86.     }
    87.  
    88.     public void Update()
    89.     {
    90.         if (characterVicinity)
    91.         {
    92.             dialogueActive = false;
    93.             //Set conditions to prevent button spamming. The Return key will only work if the dialogue box isn't active. If it is, it won't do anything.
    94.             if (Input.GetKeyUp(KeyCode.Return) && !dialogueBox.activeSelf)
    95.             {
    96.                 PlayerController.canMove = false;
    97.                 returnPressed = true;
    98.                 if (returnPressed)
    99.                 {
    100.                     continueAllowed = false;
    101.                     nameDisplay.enabled = true;
    102.                     dialogueBox.SetActive(true);
    103.                     StartCoroutine("Type");
    104.                 }
    105.             }
    106.             if (nameDisplay.enabled && dialogueBox.activeSelf)
    107.             {
    108.                 dialogueActive = true;
    109.                 //The button prompt must be active in conjunction with the Space bar being pressed before the next line will show.
    110.                 if(buttonPrompts[3].activeSelf && Input.GetKeyUp(KeyCode.Space))
    111.                 {
    112.                     currentLine++;
    113.                 }
    114.                 if(currentLine >= sentences.Length)
    115.                 {
    116.                     nameDisplay.enabled = false;
    117.                     dialogueBox.SetActive(false);
    118.                     dialogueActive = false;
    119.                     endDialogue = true;
    120.                     characterVicinity = false;
    121.                     dialogueTrigger.enabled = false;
    122.                     theNPC.MoveRight();
    123.                 }
    124.             }
    125.             if (textDisplay.text == sentences[index])
    126.             {
    127.                 buttonPrompts[3].SetActive(true);
    128.                 continueAllowed = true;
    129.                 //Placing this 'if' statement here within the previous one will stop the button from being spammed.
    130.                 if (Input.GetKeyUp(KeyCode.Space))
    131.                 {
    132.                     spacePressed = true;
    133.                     continueAllowed = false;
    134.                     NextSentence();
    135.                 }
    136.                 else
    137.                 {
    138.                     spacePressed = false;
    139.                 }
    140.             }
    141.         }
    142.  
    143.         if (xbox360Controller == 1)
    144.         {
    145.             if (Input.GetKeyDown("joystick button 2"))
    146.             {
    147.                 dialogueActive = true;
    148.                 PlayerController.canMove = false;
    149.             }
    150.         }
    151.         else if (pS4Controller == 1)
    152.         {
    153.             if (Input.GetKeyDown("joystick button 0"))
    154.             {
    155.                 dialogueActive = true;
    156.                 PlayerController.canMove = false;
    157.             }
    158.         }
    159.     }
    160.  
    161.     public void Timer()
    162.     {
    163.         buttonPrompts[3].SetActive(true);
    164.     }
    165.  
    166.     public void Hide()
    167.     {
    168.         buttonPrompts[0].SetActive(false);
    169.         buttonPrompts[1].SetActive(false);
    170.         buttonPrompts[2].SetActive(false);
    171.     }
    172.  
    173.     public void Xbox360Prompts()
    174.     {
    175.         buttonPrompts[1].SetActive(true);
    176.         Invoke("Hide", 3f);
    177.     }
    178.  
    179.     public void PS4Prompts()
    180.     {
    181.         buttonPrompts[2].SetActive(true);
    182.         Invoke("Hide", 3f);
    183.     }
    184.  
    185.     public void PCPrompts()
    186.     {
    187.         buttonPrompts[0].SetActive(true);
    188.         Invoke("Hide", 3f);
    189.     }
    190.  
    191.     public void ControllerDetection()
    192.     {
    193.         string[] names = Input.GetJoystickNames();
    194.         for (int x = 0; x < names.Length; x++)
    195.         {
    196.             //print(names[x].Length);
    197.             if (names[x].Length == 19)
    198.             {
    199.                 //print("PS4 CONTROLLER IS CONNECTED");
    200.                 pS4Controller = 1;
    201.                 xbox360Controller = 0;
    202.                 if (pS4Controller == 1)
    203.                 {
    204.                     //Debug.Log("PS4 controller detected");
    205.                 }
    206.             }
    207.             else if (names[x].Length == 33)
    208.             {
    209.                 //print("XBOX 360 CONTROLLER IS CONNECTED");
    210.                 pS4Controller = 0;
    211.                 xbox360Controller = 1;
    212.                 if (xbox360Controller == 1)
    213.                 {
    214.                     //Debug.Log("Xbox 360 controller detected");
    215.                 }
    216.             }
    217.             else
    218.             {
    219.                 pS4Controller = 0;
    220.                 xbox360Controller = 0;
    221.             }
    222.  
    223.             if (xbox360Controller == 0 && pS4Controller == 0)
    224.             {
    225.                 //Debug.Log("No controllers detected");
    226.             }
    227.         }
    228.     }
    229. }
    230.  
     
  19. DustyShinigami

    DustyShinigami

    Joined:
    Jan 5, 2018
    Posts:
    529
    Managed to get my NPC to turn round and the walk animation triggers after activating the lever through a Coroutine, but for some odd reason I can't work out, he only walks on the spot. He doesn't move along.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.SceneManagement;
    5.  
    6. public class NPC : MonoBehaviour
    7. {
    8.     public Animator anim;
    9.     public float moveSpeed;
    10.     public bool moveRight;
    11.     public bool interact;
    12.     public bool leverActivated;
    13.     public Transform target;
    14.     public Transform startPoint;
    15.  
    16.     private CharacterController controller;
    17.  
    18.     /*enum NPCState
    19.     {
    20.         moveRight,
    21.         Idle,
    22.         Interact
    23.     }
    24.  
    25.     NPCState currentState = NPCState.Idle;*/
    26.  
    27.     void Start()
    28.     {
    29.         anim = GetComponent<Animator>();
    30.         controller = GetComponent<CharacterController>();
    31.         Idle();
    32.     }
    33.  
    34.     /*public void NPCStates()
    35.     {
    36.  
    37.         if (currentState == NPCState.moveRight)
    38.         {
    39.             anim.ResetTrigger("Idle");
    40.             anim.SetBool("isMoving", true);
    41.             anim.SetFloat("Speed", 0.2f);
    42.             anim.SetTrigger("Walk");
    43.         }
    44.         else if (currentState == NPCState.Idle)
    45.         {
    46.             anim.ResetTrigger("Walk");
    47.             anim.SetBool("isMoving", false);
    48.             anim.SetFloat("Speed", 0f);
    49.             anim.SetTrigger("Idle");
    50.         }
    51.         else if (currentState == NPCState.Interact)
    52.         {
    53.             anim.ResetTrigger("Walk");
    54.             anim.SetBool("isMoving", false);
    55.             anim.SetFloat("Speed", 0f);
    56.             anim.SetTrigger("Interact");
    57.         }
    58.     }*/
    59.  
    60.     IEnumerator ReturnToStart()
    61.     {
    62.         yield return new WaitForSeconds(3f);
    63.         anim.ResetTrigger("Interact");
    64.         leverActivated = true;
    65.         interact = false;
    66.         MoveRight();
    67.     }
    68.    
    69.     public void Idle()
    70.     {
    71.         anim.SetBool("isWalking", false);
    72.         anim.SetFloat("Speed", 0f);
    73.         anim.SetTrigger("Idle");
    74.     }
    75.  
    76.     public void MoveRight()
    77.     {
    78.         moveRight = true;
    79.         anim.ResetTrigger("Idle");
    80.         anim.SetBool("isWalking", true);
    81.         anim.SetFloat("Speed", 0.2f);
    82.     }
    83.  
    84.     public void Interact()
    85.     {
    86.         interact = true;
    87.         anim.SetBool("isWalking", false);
    88.         anim.SetFloat("Speed", 0f);
    89.         anim.SetTrigger("Interact");
    90.         StartCoroutine("ReturnToStart");
    91.     }
    92.  
    93.     public void Update()
    94.     {
    95.         if (moveRight)
    96.         {
    97.             transform.eulerAngles = new Vector2(0, 90);
    98.             target.transform.position = new Vector3(-12.45f, 12.005f, -0.695f);
    99.             transform.position = Vector3.MoveTowards(transform.position, target.position, moveSpeed * Time.deltaTime);
    100.         }
    101.         if (moveRight && leverActivated)
    102.         {
    103.             transform.eulerAngles = new Vector2(0, -90);
    104.             startPoint.transform.position = new Vector3(-14.5f, 11.98f, -0.606f);
    105.             transform.position = Vector3.MoveTowards(transform.position, startPoint.position, moveSpeed * Time.deltaTime);
    106.         }
    107.         if (transform.position == target.position)
    108.         {
    109.             moveRight = false;
    110.             Interact();
    111.         }
    112.         else if(transform.position == startPoint.position)
    113.         {
    114.             transform.eulerAngles = new Vector2(0, -180);
    115.         }
    116.     }
    117. }
     
  20. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,615
    Probably because two of your movement cases (moveRight) and (moveRight && leverActivated) are both going to be true at the same time.
    Instead of
    Code (CSharp):
    1. if (moveRight)
    2.         {
    3.             transform.eulerAngles = new Vector2(0, 90);
    4.             target.transform.position = new Vector3(-12.45f, 12.005f, -0.695f);
    5.             transform.position = Vector3.MoveTowards(transform.position, target.position, moveSpeed * Time.deltaTime);
    6.         }
    7.         if (moveRight && leverActivated)
    8.         {
    9.             transform.eulerAngles = new Vector2(0, -90);
    10.             startPoint.transform.position = new Vector3(-14.5f, 11.98f, -0.606f);
    11.             transform.position = Vector3.MoveTowards(transform.position, startPoint.position, moveSpeed * Time.deltaTime);
    12.         }
    try
    Code (csharp):
    1.  
    2.  if (moveRight && leverActivated)
    3.         {
    4.             transform.eulerAngles = new Vector2(0, -90);
    5.             startPoint.transform.position = new Vector3(-14.5f, 11.98f, -0.606f);
    6.             transform.position = Vector3.MoveTowards(transform.position, startPoint.position, moveSpeed * Time.deltaTime);
    7.         }
    8. else if (moveRight)
    9.         {
    10.             transform.eulerAngles = new Vector2(0, 90);
    11.             target.transform.position = new Vector3(-12.45f, 12.005f, -0.695f);
    12.             transform.position = Vector3.MoveTowards(transform.position, target.position, moveSpeed * Time.deltaTime);
    13.         }
    14.  
    By the way, since already seem to know how coroutines work, have you considered doing the whole thing in one coroutine? You could eliminate a lot of these confusing cases and state variables?

    Essentially it would be:
    Code (csharp):
    1.  
    2. IEnumerator PullLever ()
    3. {
    4.    // Walk Right
    5.    anim.ResetTrigger("Idle");
    6.    anim.SetBool("isWalking", true);
    7.    anim.SetFloat("Speed", 0.2f);
    8.    transform.eulerAngles = new Vector2(0, 90);
    9.    target.transform.position = new Vector3(-12.45f, 12.005f, -0.695f);
    10.    while(transform.position != target.position)
    11.    {
    12.        transform.position = Vector3.MoveTowards(transform.position, target.position, moveSpeed * Time.deltaTime);
    13.        yield return;
    14.   }
    15.     // Pull the Lever
    16.     anim.SetBool("isWalking", false);
    17.     anim.SetFloat("Speed", 0f);
    18.     anim.SetTrigger("Interact");
    19.     yield return new WaitForSeconds(3f);
    20.  
    21.     // Walk Left
    22.     anim.ResetTrigger("Idle");
    23.    anim.SetBool("isWalking", true);
    24.    anim.SetFloat("Speed", 0.2f);
    25.    transform.eulerAngles = new Vector2(0, 90);
    26.    target.transform.position = new Vector3(-14.5f, 11.98f, -0.606f);
    27.    while(transform.position != target.position)
    28.    {
    29.        transform.position = Vector3.MoveTowards(transform.position, target.position, moveSpeed * Time.deltaTime);
    30.        yield return;
    31.   }
    32. }
    33.  
    34.  
    35.  
     
  21. DustyShinigami

    DustyShinigami

    Joined:
    Jan 5, 2018
    Posts:
    529
    Thanks for that example. Doing it all via a Coroutine did occur to me, but I guess I just wanted to experiment and learn a bit first. I may change it and do it all within one at some stage. :)