Forum begins after the advertisement:
[Part 28] Missing scripts in the video
Home › Forums › Video Game Tutorial Series › Creating a Farming RPG in Unity › [Part 28] Missing scripts in the video
- This topic has 11 replies, 4 voices, and was last updated 1 week, 5 days ago by
Jonathan Teo.
-
AuthorPosts
-
March 15, 2025 at 3:15 am #17542::
The
ActorAction
script was not shown clearly in the Part 28 video. Here is the full script:using System; using System.Collections; using System.Collections.Generic; using UnityEngine; //Add or move an actor public class ActorAction : CutsceneAction { public enum ActionType { Create, Move, Remove } //The type of action that is to be done public ActionType actionType; //Can be player or NPC public string actorName; public Vector3 position; public override void Execute() { Debug.Log("Cutscene: Executing actor action"); //If the action type is of type 'Remove' if (actionType == ActionType.Remove) { Remove(); onExecutionComplete?.Invoke(); return; } CreateOrMove(); } //Handles the creation or moving of the actor void CreateOrMove() { CutsceneManager.Instance.AddOrMoveActor(actorName, position, onExecutionComplete); } //Handles the removal of the actor void Remove() { } }
March 16, 2025 at 5:25 am #17550::Here is the script for the
CutsceneManager
:using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.AI; public class CutsceneManager : MonoBehaviour { public static CutsceneManager Instance { get; private set; } //The list of all the actors in the scene Dictionary<string, NavMeshAgent> actors; //The player prefab [SerializeField] PlayerController player; private void Awake() { //If there is more than one instance, destroy the extra if (Instance != null && Instance != this) { Destroy(this); } else { //Set the static instance to this instance Instance = this; } } const string CUTSCENE_PREFIX = "Cutscene"; Queue<CutsceneAction> actionsToExecute; //Things to do once cutscene ends public Action onCutsceneStop; public void OnLocationLoad() { //Get current scene string location = SceneTransitionManager.Instance.currentLocation.ToString(); //Load in all the candidate cutscenes Cutscene[] candidates = Resources.LoadAll<Cutscene>("Cutscenes/" + location); Cutscene cutsceneToPlay = GetCutsceneToPlay(candidates); //Check if there is a cutscene to play if (!cutsceneToPlay) return; Debug.Log($"The cutscene to play is {cutsceneToPlay.name}"); StartCutsceneSequence(cutsceneToPlay); } public void StartCutsceneSequence(Cutscene cutsceneToPlay) { //Disable time,player, npcs TimeManager.Instance.TimeTicking = false; player = FindAnyObjectByType<PlayerController>(); player.enabled = false; player.GetComponent<CharacterController>().enabled = false; NPCManager.Instance.Pause(); //Reset the actors actors = new(); //Save to the blackboard GameBlackboard blackboard = GameStateManager.Instance.GetBlackboard(); blackboard.SetValue((CUTSCENE_PREFIX + cutsceneToPlay.name), true); //Convert into a queue actionsToExecute = new Queue<CutsceneAction>(cutsceneToPlay.action); UpdateCutscene(); } public void UpdateCutscene() { //Check if there are any more actions in the queue if(actionsToExecute.Count == 0) { EndCutscene(); return; } //Dequeue CutsceneAction actionToExecute = actionsToExecute.Dequeue(); //Start the action sequence and have it call this function once done actionToExecute.Init(() => { UpdateCutscene(); }); } public void EndCutscene() { //Clean up the actors ClearActors(); //Enable time and player TimeManager.Instance.TimeTicking = true; player.gameObject.SetActive(true); player.enabled = true; player.GetComponent<CharacterController>().enabled = true; NPCManager.Instance.Continue(); onCutsceneStop?.Invoke(); } void ClearActors() { foreach(var actor in actors) { if (actor.Key == "Player") { //Destroy only the navmesh agent Destroy(actor.Value); continue; } Destroy(actor.Value.gameObject); } actors.Clear(); } public static Cutscene GetCutsceneToPlay(Cutscene[] candidates) { Cutscene cutsceneToPlay = null; //Replace the cutscene set with the highest condition score int highestConditionScore = -1; foreach(Cutscene candidate in candidates) { //Check if candidate is recurring if (!candidate.recurring) { //Get the blackboard key GameBlackboard blackboard = GameStateManager.Instance.GetBlackboard(); //Check if the event has played already if(blackboard.ContainsKey(CUTSCENE_PREFIX + candidate.name)) continue; } //Check if conditions met first if (candidate.CheckConditions(out int score)) { if (score > highestConditionScore) { highestConditionScore = score; cutsceneToPlay = candidate; Debug.Log("Will play " + candidate.name); } } } return cutsceneToPlay; } public void AddOrMoveActor(string actor, Vector3 position, Action onExecutionComplete) { //Convert the position to a place on the navmesh NavMesh.SamplePosition(position, out NavMeshHit hit, 10f, NavMesh.AllAreas); position = hit.position; Debug.Log($"CUTSCENE: Trying to add/move {actor} on {position}"); //The movement component of the actor NavMeshAgent actorMovement; bool actorExists = actors.TryGetValue(actor, out actorMovement); //Actor exists, create actor if (actorExists) { Debug.Log($"CUTSCENE: {actor} exists. Moving actor to {position}"); actorMovement.SetDestination(position); StartCoroutine(WaitForDestination(actorMovement, position, onExecutionComplete)); return; } //If actor is player if (actor == "Player") { //Give it a navmesh agent actorMovement = player.gameObject.AddComponent<NavMeshAgent>(); actors.Add("Player", actorMovement); actorMovement.SetDestination(position); StartCoroutine(WaitForDestination(actorMovement, position, onExecutionComplete)); return; } Debug.Log($"CUTSCENE: {actor} doesnt exist. Creating actor at {position}"); //Get NPC CharacterData characterData = NPCManager.Instance.Characters().Find(x => x.name == actor); GameObject npcObj = Instantiate(characterData.prefab, position, Quaternion.identity); actors.Add(actor, npcObj.GetComponent<NavMeshAgent>()); onExecutionComplete?.Invoke(); } //Coroutine that waits for the actor to reach the destination IEnumerator WaitForDestination(NavMeshAgent actorAgent, Vector3 destination, Action onExecutionComplete) { while(Vector3.SqrMagnitude(actorAgent.transform.position - destination) > 0.25f) { if (actorAgent == null) break; yield return new WaitForEndOfFrame(); } //Mark execution as complete onExecutionComplete?.Invoke(); } public void KillActor(string actor) { GameObject objToDestroy = actors[actor].gameObject; actors.Remove(actor); //Prevent killing the player if (actor == "Player") { player.gameObject.SetActive(false); return; } Destroy(objToDestroy); } }
March 17, 2025 at 12:47 am #17552::Here is the
CutsceneManager
:using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.AI; public class CutsceneManager : MonoBehaviour { public static CutsceneManager Instance { get; private set; } //The list of all the actors in the scene Dictionary<string, NavMeshAgent> actors; //The player prefab [SerializeField] PlayerController player; private void Awake() { //If there is more than one instance, destroy the extra if (Instance != null && Instance != this) { Destroy(this); } else { //Set the static instance to this instance Instance = this; } } const string CUTSCENE_PREFIX = "Cutscene"; Queue<CutsceneAction> actionsToExecute; //Things to do once cutscene ends public Action onCutsceneStop; public void OnLocationLoad() { //Get current scene string location = SceneTransitionManager.Instance.currentLocation.ToString(); //Load in all the candidate cutscenes Cutscene[] candidates = Resources.LoadAll<Cutscene>("Cutscenes/" + location); Cutscene cutsceneToPlay = GetCutsceneToPlay(candidates); //Check if there is a cutscene to play if (!cutsceneToPlay) return; Debug.Log($"The cutscene to play is {cutsceneToPlay.name}"); StartCutsceneSequence(cutsceneToPlay); } public void StartCutsceneSequence(Cutscene cutsceneToPlay) { //Disable time,player, npcs TimeManager.Instance.TimeTicking = false; player = FindAnyObjectByType<PlayerController>(); player.enabled = false; player.GetComponent<CharacterController>().enabled = false; NPCManager.Instance.Pause(); //Reset the actors actors = new(); //Save to the blackboard GameBlackboard blackboard = GameStateManager.Instance.GetBlackboard(); blackboard.SetValue((CUTSCENE_PREFIX + cutsceneToPlay.name), true); //Convert into a queue actionsToExecute = new Queue<CutsceneAction>(cutsceneToPlay.action); UpdateCutscene(); } public void UpdateCutscene() { //Check if there are any more actions in the queue if(actionsToExecute.Count == 0) { EndCutscene(); return; } //Dequeue CutsceneAction actionToExecute = actionsToExecute.Dequeue(); //Start the action sequence and have it call this function once done actionToExecute.Init(() => { UpdateCutscene(); }); } public void EndCutscene() { //Clean up the actors ClearActors(); //Enable time and player TimeManager.Instance.TimeTicking = true; player.gameObject.SetActive(true); player.enabled = true; player.GetComponent<CharacterController>().enabled = true; NPCManager.Instance.Continue(); onCutsceneStop?.Invoke(); } void ClearActors() { foreach(var actor in actors) { if (actor.Key == "Player") { //Destroy only the navmesh agent Destroy(actor.Value); continue; } Destroy(actor.Value.gameObject); } actors.Clear(); } public static Cutscene GetCutsceneToPlay(Cutscene[] candidates) { Cutscene cutsceneToPlay = null; //Replace the cutscene set with the highest condition score int highestConditionScore = -1; foreach(Cutscene candidate in candidates) { //Check if candidate is recurring if (!candidate.recurring) { //Get the blackboard key GameBlackboard blackboard = GameStateManager.Instance.GetBlackboard(); //Check if the event has played already if(blackboard.ContainsKey(CUTSCENE_PREFIX + candidate.name)) continue; } //Check if conditions met first if (candidate.CheckConditions(out int score)) { if (score > highestConditionScore) { highestConditionScore = score; cutsceneToPlay = candidate; Debug.Log("Will play " + candidate.name); } } } return cutsceneToPlay; } public void AddOrMoveActor(string actor, Vector3 position, Action onExecutionComplete) { //Convert the position to a place on the navmesh NavMesh.SamplePosition(position, out NavMeshHit hit, 10f, NavMesh.AllAreas); position = hit.position; Debug.Log($"CUTSCENE: Trying to add/move {actor} on {position}"); //The movement component of the actor NavMeshAgent actorMovement; bool actorExists = actors.TryGetValue(actor, out actorMovement); //Actor exists, create actor if (actorExists) { Debug.Log($"CUTSCENE: {actor} exists. Moving actor to {position}"); actorMovement.SetDestination(position); StartCoroutine(WaitForDestination(actorMovement, position, onExecutionComplete)); return; } //If actor is player if (actor == "Player") { //Give it a navmesh agent actorMovement = player.gameObject.AddComponent<NavMeshAgent>(); actors.Add("Player", actorMovement); actorMovement.SetDestination(position); StartCoroutine(WaitForDestination(actorMovement, position, onExecutionComplete)); return; } Debug.Log($"CUTSCENE: {actor} doesnt exist. Creating actor at {position}"); //Get NPC CharacterData characterData = NPCManager.Instance.Characters().Find(x => x.name == actor); GameObject npcObj = Instantiate(characterData.prefab, position, Quaternion.identity); actors.Add(actor, npcObj.GetComponent<NavMeshAgent>()); onExecutionComplete?.Invoke(); } //Coroutine that waits for the actor to reach the destination IEnumerator WaitForDestination(NavMeshAgent actorAgent, Vector3 destination, Action onExecutionComplete) { while(Vector3.SqrMagnitude(actorAgent.transform.position - destination) > 0.25f) { if (actorAgent == null) break; yield return new WaitForEndOfFrame(); } //Mark execution as complete onExecutionComplete?.Invoke(); } public void KillActor(string actor) { GameObject objToDestroy = actors[actor].gameObject; actors.Remove(actor); //Prevent killing the player if (actor == "Player") { player.gameObject.SetActive(false); return; } Destroy(objToDestroy); } }
March 18, 2025 at 2:08 am #17553::Hi, so I actually don’t get any errors in the game, however, the entire cutscene just doesn’t work (none of the actions are run). These is my Cutscene element: Conditions: Key: Location Value Type: Int Equal 1
Recurring: False
Action: Spawn in Uncle Ben (Actor Action) Intro Speech (Speech Action) Walk Away (Actor Action)
Intro Speech: Speaker: Ben Message: Hello!
Spawn in Uncle Ben: Action Type: Create Actor Name: Ben Position: -1, 1, -0,5
Walk Away: Action Type: Remove Actor Name: Ben Position: -1, 1, -0,5
Since this doesn’t work, could you please send your procedures?
March 18, 2025 at 6:57 am #17554::We are not using conditions to set locations in cutscenes. The logic of cutscenes is that they are loaded from the folder associated with the scenes they are to be loaded from.
Your cutscene has to be saved in Resources/Cutscenes/[LocationName]/So in this case it should be Resources/Cutscenes/Farm/
Also, ensure the CutsceneManager GameObject is in the Essentials prefab and is being properly initialised. The Manager needs to subscribe to SceneTransitionManager’s events.
You probably already have this done correctly but also make sure your resources folder is organised similarly for the characters for them to load in correctly too. Resources/Characters/Ben.asset
March 18, 2025 at 3:13 pm #17560::Thank you. I didn’t realise that cutscenes are being loaded based on their folder. But what should the condition then be? Also, I don’t have a “CutsceneManager” GameObject, but I’ve placed the script on the “Manager” GameObject in “Essentials”.
I am still unsure of what the “Introduction” cutscene should be given for “Conditions” and “Action”, and what those actions should be given (I’ve written my current values in the earlier post).
March 18, 2025 at 3:19 pm #17561::Also, in “SceneTransitionManager” I have assumed that after “onLocationLoad?.Invoke();” I should write “CutsceneManager.Instance.onLicationLoad();” Let me know if this is correct.
March 18, 2025 at 6:10 pm #17562::Update: The cutscene actually works, if I just make a condition that is always true (here Key: Location, Greater Than Or Equal To 0), but I would still like to know the actual conditions used, as well as the actual actions.
March 19, 2025 at 6:40 am #17570::We set the conditions to that one to be empty because it only triggers the first time the player goes out and doesn’t need anything else.
The actions are:
- Spawn Player (Actor Action)
Type: Move Pos: -4.19, 1, -1.047 - Spawn Ben (Actor Action)
Type: Move Pos: -1.909, -0.739, -1.51 - Ben Introduction (Speech Action) dialogueLines:
- speaker: Ben message: ‘Hi there. Are you the new guy on the farm? ‘
- speaker: Ben message: ‘I”m Ben. Nice to meet you. ‘
- speaker: Ben message: ‘What is your name? ‘
- speaker: Ben message: Ah I see. Have a nice day!
- Scene Change Action
Location To Switch: Farm
March 19, 2025 at 2:20 pm #17578March 19, 2025 at 2:25 pm #17579March 20, 2025 at 6:34 am #17584::Now that you ask so politely :)
Whenever I try to visit Ben after the first day (I am not certain when specifically, but at least after 12 PM), he just doesn’t seem to appear anywhere. I am pretty certain that the only reamaining cause for this is the character data information regarding the dialogues, since I often get an error about a condition not being able to be defined. Since it isn’t fully shown in the tutorial, I am pretty uncertain on what conditions actually apply for the different dialogues in the Character Data (both concerning Ben and Jeff). Could you tell me (like with the cutscene) the different dialogues for Jeff and Ben, and their corresponding conditions? – Thank you in advance, this series has really made me learn a lot about game development.
March 20, 2025 at 6:11 pm #17585::Could you make a new post detailing the errors that you have? Also include all the schedule and dialogue files that you have set.
- Spawn Player (Actor Action)
-
AuthorPosts
- You must be logged in to reply to this topic.
Advertisement below: