by implementing other UI, I meant go and reflect on previous lessons… The ones I purposely skipped because my UI implementation sounded too complex, and I didn’t know what to do. I wasn’t going to start working on something that follows up from something that doesn’t work yet anyway, xD (for now I only have the pause menu, traits (which I’ll be swapping it’s UI out) and Control Issues to go through next. Hopefully it’ll be fine)
Anyway, let’s answer this one by one:
Well, when I get close to my Quest Giver (he’s my Dialogue AI NPC), and hit the ‘C’ button, the button works (confirmed), but no dialogue shows up… the button clicks, but nothing beyond that happens
Yes
Not working…
Neither worked…
Here is the function:
private void InputReader_HandleDialogueEvent()
{
if (stateMachine.ConversantFinder.GetNearestConversant())
{
PlayerConversantState conversantState = new PlayerConversantState(stateMachine, stateMachine.ConversantFinder.CurrentTarget);
Debug.Log("Target Found");
stateMachine.SwitchState(new PlayerFacingState(stateMachine, stateMachine.ConversantFinder.CurrentTarget.transform.position, conversantState));
Debug.Log("Switching To Dialogue-Facing State...");
}
}
Didn’t work either… here is the code as well:
using UnityEngine;
namespace RPG.States.Player {
public class PlayerFacingState : PlayerBaseState
{
private PlayerBaseState nextState;
private Vector3 target;
private float timer;
private static readonly int IdleHash = Animator.StringToHash("Idle");
public PlayerFacingState(PlayerStateMachine stateMachine, Vector3 target, PlayerBaseState nextState) : base(stateMachine)
{
this.nextState = nextState; // depending on who sends the request, they will have their own nextState (could be a shopper, banker, etc)
this.target = target; // who exactly are you pointing at...?!
}
public override void Enter()
{
if (nextState == null)
{
stateMachine.SwitchState(new PlayerFreeLookState(stateMachine));
return;
}
stateMachine.Animator.CrossFadeInFixedTime(IdleHash, AnimatorDampTime);
Debug.Log("Player Turning State Entered");
}
public override void Tick(float deltaTime)
{
// if the player is looking at the NPC, he can start the conversation:
if (Vector3.Angle(stateMachine.transform.forward, target - stateMachine.transform.position) < 5.0f)
{
stateMachine.SwitchState(nextState);
return;
}
timer += deltaTime;
if (timer >= 0.5f)
{
stateMachine.SwitchState(nextState);
return;
}
FaceTarget(target, deltaTime);
Move(deltaTime);
}
public override void Exit()
{
Debug.Log("Player Exited Turning State");
}
}
}
Umm… didn’t work either… it’s like they’re not being lead on for some reason
using RPG.Dialogue;
using UnityEngine;
namespace RPG.States.Player
{
public class PlayerConversantState : PlayerBaseState
{
private AIConversant targetConversant;
public PlayerConversantState(PlayerStateMachine stateMachine, AIConversant targetConversant) : base(stateMachine)
{
this.targetConversant = targetConversant;
Debug.Log("Player Conversant State Constructor called");
}
public override void Enter()
{
Debug.Log("Player Conversant State Entered");
if (!targetConversant)
{
stateMachine.SwitchState(new PlayerFreeLookState(stateMachine));
return;
}
stateMachine.PlayerConversant.onConversationUpdated += PlayerConversant_OnConversationUpdated;
stateMachine.PlayerConversant.StartDialogue(targetConversant, targetConversant.GetDialogue());
}
public override void Tick(float deltaTime) {}
public override void Exit()
{
stateMachine.PlayerConversant.onConversationUpdated += PlayerConversant_OnConversationUpdated;
}
private void PlayerConversant_OnConversationUpdated()
{
if (!stateMachine.PlayerConversant.IsActive())
{
stateMachine.SwitchState(new PlayerFreeLookState(stateMachine));
}
}
}
}
I also want to point out that for some reason, when I hit the ‘C’ key, the InputReader fires the Debug 3 times
I didn’t know there was a ‘base.OnDisable()’… Anyway, here is that script as well
using UnityEngine;
using UnityEngine.UI;
using RPG.Dialogue;
using TMPro;
using GameDevTV.UI; // so we can use 'ShowHideUI.cs' to deactivate the Dialogue when the game is paused
namespace RPG.UI {
public class DialogueUI : WindowController
{
PlayerConversant playerConversant;
[SerializeField] TextMeshProUGUI AIText;
[SerializeField] Button nextButton;
[SerializeField] GameObject AIResponse;
[SerializeField] Transform choiceRoot;
[SerializeField] GameObject choicePrefab;
[SerializeField] Button quitButton; // quit button, to quit the dialogue midway through
[SerializeField] TextMeshProUGUI conversantName; // name of our conversant in a dialogue conversation
// Start is called before the first frame update
void Start()
{
nextButton.onClick.AddListener(() => playerConversant.Next()); // calls the 'Next' function (yes, the function not a variable) Subscriber (to the onClick event) when our player hits 'Next' in the dialogue, using the Lambda "() => {}" function
quitButton.onClick.AddListener(() => playerConversant.Quit()); // Lambda Function, "() => {}", is used here to add a listener for our quit button. When clicked the script is closed
// ShowHideUI.OnModalActive += playerConversant.Quit; // Turn off the Dialogue UI when the game is paused
UpdateUI();
}
// ShowHideUI.OnModalActive is a static event, hence this function is mandatory:
void OnDestroy() {
// ShowHideUI.OnModalActive -= playerConversant.Quit;
}
// Update is called once per frame
void UpdateUI()
{
/* Debug.Log($"UpdateUI playerConversant.IsActive() == {playerConversant.IsActive()}");
gameObject.SetActive(playerConversant.IsActive());
if (!playerConversant.IsActive())
{
Debug.Log("Exiting DialogueUI as playerConversant is not active");
return;
}
Debug.Log("Continuing with DialogueUI as playeConversant is active");
//rest of method */
// The following line ensures the dialogue is invisible until the return time
// of the IEnumerator in 'playerConversant.cs' time counter is over
gameObject.SetActive(playerConversant.IsActive());
if (!playerConversant.IsActive()) {
return;
}
conversantName.text = playerConversant.GetCurrentConversantName();
AIResponse.SetActive(!playerConversant.IsChoosing());
choiceRoot.gameObject.SetActive(playerConversant.IsChoosing());
if (playerConversant.IsChoosing())
{
BuildChoiceList();
}
else {
AIText.text = playerConversant.GetText();
nextButton.gameObject.SetActive(playerConversant.HasNext());
}
}
private void BuildChoiceList()
{
foreach (Transform item in choiceRoot) {
// avoids Dangling Nodes in the Hierarchy, which will eventually slow our entire game down
Destroy(item.gameObject);
}
foreach (DialogueNode choice in playerConversant.GetChoices())
{
GameObject choiceInstance = Instantiate(choicePrefab, choiceRoot);
var textComp = choiceInstance.GetComponentInChildren<TextMeshProUGUI>();
textComp.text = choice.GetText();
Button button = choiceInstance.GetComponentInChildren<Button>();
button.onClick.AddListener(() => { // "() => {}" is a 'lambda' function ("()" is the argument, "{}" is the internals of the function), which only works when a button is clicked (do some research about this)
playerConversant.SelectChoice(choice);
});
}
}
protected override void Subscribe()
{
playerConversant = GameObject.FindGameObjectWithTag("Player").GetComponent<PlayerConversant>();
playerConversant.onConversationUpdated += UpdateUI;
}
protected override void Unsubscribe()
{
playerConversant.onConversationUpdated -= UpdateUI;
}
protected override void OnDisable()
{
playerConversant.Quit();
}
}
}
That’s because the method fires for various events in the lifecycle of the keypress like Started, Performed, and Cancel