NPC Replaced by another one

Good afternoon everone! My RPG is mainly based on dialogues, but still I’ve created a system of “healthPoints” that depending on the healthPoint is going to show a DialogueGameObject or another, everytime you hit click on the character it adds a dialogueNumber, so if it is dialogueNumber1 it is going to show the dialogue gameobject 1 of an array of dialogues, if it’s dialogueNumber2 it shows the dialogue gameObject 2.
The situation comes as follows: Once a character has reached a certain number of dialogueNumber, it instantiate several gameObjects that represent a fake copy of this NPC who you can hit and destrón and also it instantiate a gameObject that is a NPC who you can talk again and he congratúlates you for descovering who he is. The problem comes when I go out the room and when I come back this new NPC is not there, it is there the old one so I have to talk again, reach the dialogueNumber so the copies appears again and the new NPC…it is like it doesn’t save the situation of the scene. But still, if I hit LOAD it shows the new NPC etc. I’m going to show you a video about how the error happens. BTW, every eveygameobject has its savable script. My game issue - YouTube

There are a couple of things going on here, and making them work with the saving system will require a bit of adjusting…

First, rather than having two different NPC characters for this, I would have one NPC, whose position changes based on the current state.

On the script responsible for creating the fakes, you’ll need to save/restore the positions/statuses of the fakes.

For the npc itself, once the npc has transitioned to the new state, a bool should be set indicating it has transitioned. This will be captured/restored. When it’s been restored, if the bool is true, then the npc should be in it’s new position.

1 Like

Thank you for the quick response Brian, I really appreciate it. I’ve been troubleshooting and doing as you said but a new bug shows up. Since the character moves to a different position instead of destroying itself, the character follows it, I’ve tried to change the layer to ignoreraycast, gameobject.SetActive(false), but still the player follows it doing the animation of talking. No errors show up in the console but its behaviour is weird and the gameplay impossible. I’m going to keep trying to salve it, but if you or anyone comes up with a solution I’ll be greateful, I’m a beginner in programming so forgive my lack of knowledge.
Video of new error: error2 - YouTube
I’m going to copy paste the responsible scripts for these behaviours.

using UnityEngine;
using System.Collections.Generic;
using UnityEngine.UI;
using theprinceofnerg.Control;
using theprinceofnerg.Combat;
using theprinceofnerg.Saving;
using theprinceofnerg.SceneManagement;
namespace theprinceofnerg.Conversation {
    public class DialogueManager : MonoBehaviour,ISaveable {
        #region ############## VARIABLES
        public bool isThire1;
        public bool isThire2;
        public List<GameObject> dialogues = new List<GameObject>();
        public List<GameObject> dialogues2 = new List<GameObject>();
        public Text textDialogue;
        public GameObject dialogueParent;
        public GameObject dialogueParent2;
        public int dialoguesNumber = 0;
        public GameObject ps;
        public GameObject thireCopy;
        public Transform[] thireCopyTransforms;
        //public Transform thire2Transform;
        public GameObject thire2;
        public GameObject portal;
        #endregion

        #region ############## EVENTS
        private void Awake() {

        }

        void Start() {
            //rellenamos la lista
            foreach (Transform item in dialogueParent.transform) {
                dialogues.Add(item.gameObject);
            }
            if (isThire1) {
                //rellenamos la lista
                foreach (Transform item in dialogueParent2.transform) {
                    dialogues2.Add(item.gameObject);
                }
            }
            if (isThire1||isThire2) {
                portal = GameObject.FindGameObjectWithTag("Portal");
            }

        }


        void Update() {

        }
        #endregion

        #region ############# METHODS

        public void Dialogue() {
            if (isThire1) {
                DialogueThire1();
            }
            if (isThire2) {
                
                DialogueThire2();
            }
           
        }

        void DialogueThire1() {
            DisableMovements();
            if (dialoguesNumber > 0) {
                dialogues[dialoguesNumber - 1].SetActive(false);
            }

            if (dialoguesNumber < dialogues.Count) {
                dialogues[dialoguesNumber].SetActive(true);
                dialoguesNumber++;
            }
            //dialoguesNumber++;

            if (dialoguesNumber >= dialogues.Count) {
                gameObject.layer = 2;
                EnabledMovements();
                portal.GetComponent<BoxCollider>().enabled = false;
                foreach (var thireCopyTransform in thireCopyTransforms) {
                    Instantiate(ps, thireCopyTransform.position + new Vector3(0, 2, 0), thireCopyTransform.rotation);
                    Instantiate(thireCopy, thireCopyTransform.position, thireCopyTransform.rotation);
                }
                transform.position += new Vector3(3, 0, 0);
                Instantiate(ps, gameObject.transform.position, transform.rotation);
                dialoguesNumber = 0;
                isThire1 = false;
                isThire2 = true;
                
                //Instantiate(thire2, thire2Transform.position, thire2Transform.rotation);
                //Instantiate(ps, thire2Transform.position, thire2Transform.rotation);
                //Destroy(gameObject);
            }
        }

        void DialogueThire2() {
            DisableMovements();
            if (dialoguesNumber > 0) {
                portal.GetComponent<BoxCollider>().enabled = true;
                FindObjectsOfType<CombatTarget>();
                dialogues2[dialoguesNumber - 1].SetActive(false);
            }
            if (dialoguesNumber<dialogues2.Count) {
                dialogues2[dialoguesNumber].SetActive(true);
                dialoguesNumber++;
            }
            if (dialoguesNumber>=dialogues2.Count) {
                EnabledMovements();
            }
        }

        void DisableMovements() {
            GameObject player;
            player = GameObject.FindGameObjectWithTag("Player");
            player.GetComponent<PlayerController>().canNotMove = true;
            player.GetComponent<Animator>().SetBool("Talk", true);
        }

        void EnabledMovements() {
            GameObject player;
            player = GameObject.FindGameObjectWithTag("Player");
            player.GetComponent<PlayerController>().canNotMove = false;
            player.GetComponent<Animator>().SetBool("Talk", false);

        }

        public object CaptureState() {
            return dialoguesNumber;
        }

        public void RestoreState(object state) {
            //dialoguesNumber = (int) state;
            //if (isThire1&&dialoguesNumber>dialogues.Count) {
            //    Destroy(gameObject);
            //}
        }
        #endregion
    }
}
using System.Collections;
using System.Collections.Generic;
using theprinceofnerg.Combat;
using theprinceofnerg.Movement;
using theprinceofnerg.Conversation;
using theprinceofnerg.Prop;
using UnityEngine;
using UnityEngine.AI;
//utilizamos el namespace theprinceofnerg para las dependencias
namespace theprinceofnerg.Control {
    public class PlayerController : MonoBehaviour {
        #region ############## VARIABLES

        public Camera cam;
        public bool canNotMove;
        #endregion

        #region ############## EVENTS

        void Start() {
            cam = FindObjectOfType<Camera>();

        }


        void Update() {
            
            if (InteractWithCombat()) return;
            if (InteractWithCharacterThire()) return;
            if (InteractWithMovement()) return;
        }
  
        private bool InteractWithCombat() {
            RaycastHit[] hits = Physics.RaycastAll(GetMouseRay());
            //hacemos un foreach en los elementos de hits con la variable raycasthit
            foreach (RaycastHit hit in hits) {
                CombatTarget target = hit.transform.GetComponent<CombatTarget>();
                //seguimos con el loop si no se cumple la condición
                if (target == null) continue;
                    
     
                if (Input.GetMouseButtonDown(0)) {
                    GetComponent<Fighter>().Attack(target);                }
                //ponemos true aquí porque queremos que cambie el cursor
                //cuando podamos combatir
                return true;
            }
            //no hemos encontrado ningun objetivo de lucha
            return false;
        }
        #endregion

        #region ############# METHODS
        /// <summary>
        /// nos movemos hacia donde apunta el cursor con un rayo
        /// </summary>
        private bool InteractWithMovement() 
        {
            
                //información con la que chocamos con el raycast
                RaycastHit hit;
                //almacenamos la posicion donde choca el raycast
                //cuando has chocado nos movemos hacia la posición
                bool hasHit = Physics.Raycast(GetMouseRay(), out hit);
                if (hasHit == true && canNotMove==false) {
                    if (Input.GetMouseButton(0)) {
                         GetComponent<Movement.Mover>().StartMoveAction(hit.point);
                    }
                    return true;
                }
                //si no interactuamos con nada hacia donde podamos movernos no se moverá el player
            return false;
        }
        private bool InteractWithCharacterThire() {
            RaycastHit[] hits = Physics.RaycastAll(GetMouseRay());
            //hacemos un foreach en los elementos de hits con la variable raycasthit
            foreach (RaycastHit hit in hits) {
                DialogueManager target = hit.transform.GetComponent<DialogueManager>();
                //seguimos con el loop si no se cumple la condición
                if (target == null) continue;


                if (Input.GetMouseButtonDown(0)) {
                    GetComponent<PlayerConversation>().Conversation(target);
                    //target.GetComponent<DialogueManager>().dialoguesThireNumber -= 1;
                }

                return true;
            }
            return false;
        }
        private bool InteractWithChest() {
            RaycastHit[] hits = Physics.RaycastAll(GetMouseRay());
            //hacemos un foreach en los elementos de hits con la variable raycasthit
            foreach (RaycastHit hit in hits) {
                ChestInteraction target = hit.transform.GetComponent<ChestInteraction>();
                //seguimos con el loop si no se cumple la condición
                if (target == null) continue;


                if (Input.GetMouseButtonDown(0)) {
                    target.GetComponent<ChestInteraction>().Opening();
                    //target.GetComponent<DialogueManager>().dialoguesThireNumber -= 1;
                }

                return true;
            }
            return false;
        }
        /// <summary>
        /// lanzamos un rayo desde la cámara al hacer click
        /// </summary>
        /// <returns></returns>
        private Ray GetMouseRay() {
            return cam.ScreenPointToRay(Input.mousePosition);
        }


        #endregion
    }

}
using UnityEngine;
using theprinceofnerg.Movement;
using theprinceofnerg.Core;
using theprinceofnerg.Conversation;
namespace theprinceofnerg.Conversation {
    public class PlayerConversation : MonoBehaviour, IAction {
        #region ############## VARIABLES
        //rango para hablar
        [SerializeField] float talkRange = 2f;
        //hacia donde nos dirigimos
        Transform target;
        //referencia al script mover
        public Mover mover;
        [SerializeField] Animator anim;
        public bool canTalk;
        #endregion

        #region ############## EVENTS

        void Start() {

        }


        void Update() {
            //si el target es null no hacemos ninguno de estos comportamientos
            if (target == null) return;
            if (!GetIsInRange()) {
                mover.MoveTo(target.position);
                canTalk = false;
            } else {
                //si estamos en el alcance paramos
                mover.Cancel();
                transform.LookAt(target.position);
                TalkBehaviour();
            }
        }
        #endregion

        #region ############# METHODS
       
        private bool GetIsInRange() {
            return Vector3.Distance(transform.position, target.position) < talkRange;
        }
         private void TalkBehaviour() {
            canTalk = true;
            transform.LookAt(target.transform.position);
            //anim.SetTrigger("Talk");
        }
        public void Conversation(DialogueManager dialogueManager) {
            GetComponent<ActionScheduler>().StartAction(this);
            target = dialogueManager.transform;
            //anim.SetTrigger("Talk");
            DialogueFunction();
        }

        public void DialogueFunction() {
            target.GetComponent<DialogueManager>().Dialogue();
        }
        /// <summary>
        /// cancelamos el objetivo para que podamos movernos a otro lugar
        /// que no sea el objetivo
        /// </summary>
        public void Cancel() {
            //anim.SetTrigger("stopAction");
            target = null;
        }

        #endregion
    }
}

Just solved it :smiley: Thank you kindly!

I’m curious as to your solution.

I show you right now at the bottom of the message, It still needs to be polished, but at least there’s no player bug like before. I’m trying to destroy the copies once the boolean dieCopies is set to true… I’m trying to set the EnemyHealth component that is attached to the copies the condition if (healthPoints==0|| FindObjectOfType().dieCopies==true) Die(); but it’s not working I have to find out more info.
I have one question, can the method CaptureState(); return two booleans or one boolean and one int? If so, could you give me an example?
I’m really thankful for the help, have a good day.
My solution:

using UnityEngine;
using System.Collections.Generic;
using UnityEngine.UI;
using theprinceofnerg.Control;
using theprinceofnerg.Combat;
using theprinceofnerg.Saving;
using theprinceofnerg.SceneManagement;
namespace theprinceofnerg.Conversation {
    public class DialogueManager : MonoBehaviour,ISaveable {
        #region ############## VARIABLES
        public bool isThire1;
        public bool isThire2;
        public List<GameObject> dialogues = new List<GameObject>();
        public Text textDialogue;
        public GameObject dialogueParent;
        private int dialoguesNumber = 0;
        public GameObject ps;
        public GameObject thireCopy;
        public Transform[] thireCopyTransforms;
        public GameObject portal;
        public float timer = 5;
        public bool timerOn=false;
        public bool dieCopies;
        #endregion

        #region ############## EVENTS
        private void Awake() {

        }

        void Start() {
            if (isThire2==true) {
                isThire1 = false;
                dialogueParent = GameObject.FindGameObjectWithTag("dialogue2");
                foreach (Transform item in dialogueParent.transform) {
                    dialogues.Add(item.gameObject);
                }
            }
            if (isThire1) {
                //rellenamos la lista
                foreach (Transform item in dialogueParent.transform) {
                    dialogues.Add(item.gameObject);
                }
            }
          
            if (isThire1||isThire2) {
                portal = GameObject.FindGameObjectWithTag("Portal");
            }

        }


        void Update() {
            if (timerOn) {
                timer -= Time.deltaTime;
                if (timer <= 0) {
                    isThire1 = false;
                    isThire2 = true;
                    gameObject.layer = 0;
                    dialoguesNumber = 0;
                    dialogueParent = GameObject.FindGameObjectWithTag("dialogue2");
                    //rellenamos la lista
                    foreach (Transform item in dialogueParent.transform) {
                        dialogues.Add(item.gameObject);
                    }
                    timerOn = false;
                }
            }
        }
        #endregion

        #region ############# METHODS

        public void Dialogue() {
            if (isThire1) {
                DialogueThire1();
            }
            if (isThire2) {
                isThire1 = false;
                Debug.Log("thire2");
                DialogueThire2();
            }
           
        }

        void DialogueThire1() {
            DisableMovements();
            if (dialoguesNumber > 0) {
                dialogues[dialoguesNumber - 1].SetActive(false);
            }

            if (dialoguesNumber < dialogues.Count) {
                dialogues[dialoguesNumber].SetActive(true);
            }
            dialoguesNumber++;

            if (dialoguesNumber > dialogues.Count) {
                EnabledMovements();
                portal.GetComponent<BoxCollider>().enabled = false;
                
                foreach (var thireCopyTransform in thireCopyTransforms) {
                    Instantiate(ps, thireCopyTransform.position + new Vector3(0, 2, 0), thireCopyTransform.rotation);
                    Instantiate(thireCopy, thireCopyTransform.position, thireCopyTransform.rotation);
                }
                dialogues.Clear();
                timerOn = true;
               
                gameObject.layer = 2;
               
                
            }
        }

        void DialogueThire2() {
            
            DisableMovements();
            if (dialoguesNumber > 0) {
                portal.GetComponent<BoxCollider>().enabled = true;
                dieCopies = true;
                FindObjectsOfType<CombatTarget>();
                dialogues[dialoguesNumber - 1].SetActive(false);
            }
            if (dialoguesNumber<dialogues.Count) {
                dialogues[dialoguesNumber].SetActive(true);
                dialoguesNumber++;
            }
            if (dialoguesNumber>=dialogues.Count) {
                EnabledMovements();
            }
        }


        void DisableMovements() {
            GameObject player;
            player = GameObject.FindGameObjectWithTag("Player");
            player.GetComponent<PlayerController>().canNotMove = true;
            player.GetComponent<Animator>().SetBool("Talk", true);
        }

        void EnabledMovements() {
            GameObject player;
            player = GameObject.FindGameObjectWithTag("Player");
            player.GetComponent<PlayerController>().canNotMove = false;
            player.GetComponent<Animator>().SetBool("Talk", false);

        }

        public object CaptureState() {
            return isThire2;
            
        }

        public void RestoreState(object state) {
            
            isThire2 = (bool)state;
        }
        #endregion
    }
}

Helpful hint: When pasting in code, type three backwards single quotes on their own line (the one next to the 1/! key on the keyboard. Then paste in the code on the next line, and finally three more of the quotes. For example:
```
public class SomeSillyClass()
{
public void DoSomeSillyThing()
{
Debug.Log(“Bring me a Shrubbery!”);
}
}
```
becomes

public class SomeSillyClass()
{
     public void DoSomeSillyThing()
     {
          Debug.Log("Bring me a Shrubbery!");
     }
}

I’ve taken the liberty of editing your previous code to format it.

I have to step out to work, but I’ll provide an example of saving multiple variables later this evening.

Perfect Brian, thank you ! I’ll bear in mind what you’ve said of ```I didn’t know it.

I’ve just solved the issue with the destroying the copies of the wizard :smiley: What happened was that I set a bool in the DialogueManager once you’ve found the original wizard, but I was checking the boolean in the TakeDamage method. But as I am not accessing to the TakeDamage method when running into the original wizard, I cannot check if the bool is true or not ( that’s what I was doing), so I tried to check in the Update instead and it works. Maybe it is not the cleanest thing to do but it works and that’s the purpose for the time being.

1 Like

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

Privacy & Terms