#Dialog Disaster XD

1 messages · Page 1 of 1 (latest)

frigid trench
#

Let me throw up some screenshots and then explain.

ornate hare
#

Alright.

frigid trench
#

These are the first script called Dialog Manager

calm raven
#

if hes at a restaurant.. might be nice to use a paste-bin website..

frigid trench
#

2nd script, Object Dialog

calm raven
#

mobiles gonna be awful to try to read thru that

#

!code

timber lavaBOT
frigid trench
ornate hare
#

Pastebin is about as easy as it gets.

calm raven
#

all the links at top

#

all will do fine

ornate hare
#

@calm raven Thanks!

calm raven
#

copy- paste ur code in there.. hit the save button.. and give us the link it generates in the URL bar

calm raven
frigid trench
#

Ok will do, thanks!

calm raven
#

np.. if u paste the entire scripts, its easier for us..
and we can just look at the Class names to figure out which scripts are what

ornate hare
#

Saves spinning a phone like a steering wheel, too.

#

Choose C# as the syntax highlight option, if you can, too pls.

calm raven
#

im no pro btw, i'd say im a novice intermediate

#

i just came to watch the show 🍿

frigid trench
#

This work?

calm raven
#

my dialog systems consist of a few sentences, an icon, and a toast notification type window

#

yup, thats the main class correct?

#

and u can do the same for any other scripts u have that might matter

#

and then.. just so we're all on teh same page.. can u state the issue?

#
  • whats happening..
  • what you expect to happen..
  • etc
frigid trench
#

Yeah, so what I'd like to happen is that a dark box I made appears for the background and the text types out green in front (rightish). To the left is a character animation of 4 frames.

#

Currently I have nothing happening after trying to fix something. Just before that, I did have the box appear, and the text run, but no character profile. The text had an issue where it would keep typing out each time you hit the mouse until you closed out of the dialog box (it has a "Close" button.

calm raven
#

so the TypeText coroutine working as it should?

frigid trench
calm raven
#

the first video looks like i think it should..

#

except maybe the anchors

frigid trench
calm raven
#

and the text formatting

frigid trench
#

Yeah so in video, the cursor circles around where the character's head animation should be

calm raven
#

im sorta confused on what the AnimateTextures is supposed to do

ornate hare
#

Okay, so just to be clear, are you wanting to add this functionality? Is a partially-working version of this already in the code? I’m just settling to look a it now.

frigid trench
#

I'm trying to keep scripts universal of course so clicking other objects will have different text and character image.

calm raven
#

yea, thats fine.. thats how u should do it..

#

each time u type dialog u should have like an icon, a color, and a string..

#

a string "minimum" lol

frigid trench
#

I had AI try to troubleshoot the issue and the current script that I sent you has removed the textbox entirely, though the component fields in screenshots remain.

calm raven
#

ohh jeez.. i wouldnt tell too many ppl that 😄
guys around these part are anti-AI.. even if ur using it responsibly lol

frigid trench
#

Oh ok. I am definately anti-AI when it comes to image generation, but wasn't sure about the ethics aspect for coding. I get it isn't a 'fix-all' solution to everything so trying to be careful with it.

Yeah, as the video shows, the text and font is easily adjustable per object.

ornate hare
#

Okay, so first of all, I would avoid AddComponent and unless you think you’ll really need to be adding components at runtime. You are better off just placing an Image component through UGUI and referencing it in source. I’m guessing AI wrote this?

#

Is Photo_Sprite the thing that you want to show the animation?

frigid trench
#

I undid the script to the previous one. This is a video of what it does now. It's the same as previous video, but shows how clicking multiple times generates the text again and again until closed.

#

Photo_Sprite is the object in video that says "Friends". It has a separate script to blow it up and that script is separate completly from the dialog.

calm raven
#

u should add a debug at the beginning of ur coroutine.. and at the end of ur coroutine

#

to try to compare that to ur mouse-clicks..

#

see when the coroutine starts and ends..

frigid trench
#

This is to fix the text issue?

calm raven
#

well its a start yes. we need to figure out whats going wrong.. debugging helps do that

#

so we can follow along with the code..

#

maybe catch something happening that shouldn't. or vice versa

#

ur coroutine looks like it should go until the text has all been typed.

#

not only when u click.. (you should probably add a delay in ur script so you cant click again.. until the text is finished) or.. since people would probably hate that.. when u click once the text starts -->
and then it should continue until finished.. or if u click again (or press esc or something).... then that should just end the dialog box right?

frigid trench
calm raven
#

i have a dialog script too.. im trying to open the project so i can compare to mine and see if i notice anything out of place..

#

but its been loading for 10 minutes so far 😄

frigid trench
#

Actually yeah, should I expect the loading to to increase more-and-more as game is built? This is my first scene and was wondering if it is ever normal to separate parts of the game into separate projects?

calm raven
#

ya, im gonna make a decision now..
-# my bet is:

  • u causing multiple coroutines to start and run at the same time..
    when ShowDialog is called again before the previous typing effect is finished, a new coroutine starts while the old one is still running..
#

thats probably whats causing the text to appear in chunks when u click multiple times

calm raven
#

it usually only takes a while the first time.. once its open and all the cache exists in ur c:/boot drive then it'll load up way quicker..

#

and one the quirks of unity is it just gets slower the longer its open...

#

best advice i can give you is restart as often as u need to keep it running fast

frigid trench
calm raven
#

so im guessing this is what ur going for..

#

you need to probably either

  • cache the coroutine when u start it (that way u can stop it if u need to)
  • orr... stop all the coroutines that are running StopCoroutines()
#

when u call it.. (that'll stop the old one.. and start a brand new one..)

#

no overlaps

frigid trench
#

Yep, that is pretty on-point with character profile using 4 frames for animation. Some dialog boxes use other animation with more frames so trying to use a texture array like I have with grabby cursor

ornate hare
#

Okay, so I’ve admittedly been a bit distracted.

#

Do you understand how coroutines work?

frigid trench
#

Not really TBH

calm raven
#

i'll show u my script..

#

but i was gonna do that last resort

#

u dont really learn much by copying tbh

frigid trench
#

That's true

calm raven
#

Omi is over here explaining coroutines..

#

-# Standby.

ornate hare
#

Okay, so AnimateTextures is a coroutune. It’s a bit like Update() in that it runs once per frame.

calm raven
#

yea, im still unsure what thats for

#

unless ur text is image/textures?

frigid trench
#

The animate textures? That was supposed to be for the texture array for the character profile,

calm raven
#

ohh!

frigid trench
#

Let me show you my cursor one which works

calm raven
#

so like a character chattin in the corner?

#

oh cmon bot..

frigid trench
#

Separate script of course, but you see at the bottom I can assign frames to animate

ornate hare
#

Can you post the source to your file directly in Discord with following format?

‘ ‘ ‘ cs
source
‘ ‘ ‘

those should be backticks without spaces.

frigid trench
calm raven
#

` <- these.. u can copy and paste..

#

but its the key typically above the tab key

#

lol, thats for someone else.. but it works here too

ornate hare
#

It turns out I can’t select text in pastebin on my phone. >_>

ornate hare
frigid trench
#

Ok. You need the whole script like that or were you needing just a part of it?

ornate hare
#

If you post the whole thing, I can just scroll up to it.

#

And copy/paste parts in here with edits.

calm raven
#
public void ShowDialog(string message, Texture[] animationFrames)
{
    dialogText.text = "";
    dialogPanel.SetActive(true); 

    if (typingCoroutine != null) // if theres already a coroutine like this running, then stop it
        StopCoroutine(typingCoroutine);

    // here we'll cache the coroutine so we know specifically which one is running
    typingCoroutine = StartCoroutine(TypeText(message));

    CreateImage(animationFrames);
}

private IEnumerator TypeText(string message)
{
    dialogText.text = "";
    foreach (char letter in message.ToCharArray())
    {
        dialogText.text += letter;
        yield return new WaitForSeconds(0.05f);
    }
    typingCoroutine = null; // when we finish we clear out the cached variable of the coroutine
}```

---------
---------
His last posted script 👇
#
using UnityEngine;
using UnityEngine.UI;
using System.Collections;

public class DialogManager : MonoBehaviour
{
    public GameObject dialogPanel;  // Reference to the dialog panel
    public Text dialogText;         // Reference to the text component
    public Button closeButton;      // Reference to the close button
    public Canvas canvas;           // Reference to the Canvas

    void Start()
    {
        dialogPanel.SetActive(false);
        closeButton.onClick.AddListener(CloseDialog);
    }

    public void ShowDialog(string message, Texture[] animationFrames)
    {
        dialogText.text = ""; // Clear previous text
        dialogPanel.SetActive(true); // Show the dialog
        StartCoroutine(TypeText(message)); // Start typing the message
        CreateImage(animationFrames); // Create the image directly
    }

    private void CreateImage(Texture[] textures)
    {
        // Create a new GameObject for the Image
        GameObject imageObject = new GameObject("DialogImage");

        // Set it as a child of the dialog panel
        imageObject.transform.SetParent(dialogPanel.transform);

        // Add the Image component
        Image imageComponent = imageObject.AddComponent<Image>();

        // Set the size and position (adjust as needed)
        RectTransform rectTransform = imageComponent.GetComponent<RectTransform>();
        rectTransform.sizeDelta = new Vector2(100, 100); // Set size
        rectTransform.anchoredPosition = new Vector2(0, 0); // Set position

        // Set the initial sprite as the first texture
        imageComponent.sprite = TextureToSprite(textures[0]);

        // Start the animation coroutine
        StartCoroutine(AnimateTextures(imageComponent, textures));
    }

    private IEnumerator TypeText(string message)
    {
        foreach (char letter in message.ToCharArray())
        {
            dialogText.text += letter; // Add one letter at a time
            yield return new WaitForSeconds(0.05f); // Typing speed
        }
    }

    private IEnumerator AnimateTextures(Image imageComponent, Texture[] textures)
    {
        int index = 0;
        while (true) // Loop through textures indefinitely
        {
            imageComponent.sprite = TextureToSprite(textures[index]);
            index = (index + 1) % textures.Length; // Cycle through textures
            yield return new WaitForSeconds(0.1f); // Adjust the speed of the animation
        }
    }

    private Sprite TextureToSprite(Texture texture)
    {
        return Sprite.Create((Texture2D)texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));
    }

    private void CloseDialog()
    {
        dialogPanel.SetActive(false); // Hide the dialog
    }
}```
ornate hare
#

Thanks! If you put “cs” on the first line with the backticks and then start “public void” on the second line, you get syntax highlighting.

calm raven
#

there ya go @ornate hare

ornate hare
#
public void Yo(int x) {} ```
calm raven
#

about caching the coroutine u currently have running so u can stop it

frigid trench
calm raven
frigid trench
calm raven
#

lol no

#

StopCoroutine(YourCoroutineReference); does

ornate hare
#

No sorry, that was for me.

calm raven
#

or.. StopAllCoroutines()

#

i think

ornate hare
#

Yes, so… coroutines.

#

When you use StarCoroutine(AnimateTextures), that sets a new coroutine on this MonoBehaviour’s coroutine stack, which runs the method AnimateTextures on the next frame when Update() would run - up until where ‘yield return null’ appears in that method.

#

AnimateTextures handles the flipping of the texture on the Image component for that frame.

#

Notice while(true). This will run forever technically, but because of how coroutines work, it exits at yield return and picks up at that same point in. The next frame.

#

Hold on, my wife doesn’t want to drive. Give me 15 minutes.

calm raven
#

ya i checked my script

frigid trench
calm raven
#

when i call Speak("BlablaBla"); <- my dialog
it calls Reveal()
that StopsAllCoroutines() before running the other two

calm raven
# frigid trench

private Coroutine typingCoroutine;

typingCoroutine = StartCoroutine(TypeText(message));

StopCoroutine(typingCoroutine);

#

you'll need to cache the coroutine when u start it.. and then pass that into the StopCoroutine() to stop it.. or StopAllCoroutines

#

but that only fixes the issue of it not being chunky...
if u click repeatedly.. it'll start over instead of being chunky, and inconsistent..

#

after that.. then you'll have the rest of the problems still to solve.. but atleast ur coroutine would be not overlapping anymore

calm raven
ornate hare
#

I was going to recommend storing the coroutine like: Coroutine r = StartCoroutine(AnimateTextures); Then, does the animation need to run forever or just once?

ornate hare
#

Loop once or loop forever?

calm raven
#

but it'd need to be a global variable right?

frigid trench
#

When you say TypeTextmessage though for the Start, does that mean it'd have to be the actual phrase (like in video "...idiot...")?

calm raven
#

it couldn't be local.. unless he plans on stopping it within that same method

ornate hare
#

Okay, starting to drive. I’ll talk soon.

calm raven
#

public method that u can call from other scripts..

frigid trench
#

Ok yeah, that's what I have currently

calm raven
#

WriteDialog("Your string or Anything Else You Want");

or

var newString = "Just Another String";
WriteDialog(newString );

frigid trench
#

Well, ObjectsDialog script has it

calm raven
#

when u do that tho.. u need to stop all current coroutines..

#

and then start ur new ones

#

thats the whole idea of what we're talkin about rn

calm raven
frigid trench
calm raven
#

i assume

#

ya, that all looks fine..

#

now.. start simple.. (add a debug.log) when u call that method..

frigid trench
#

I wonder if this is what Omi was reffering to

calm raven
#

before the dialogManager. line

#

put Debug.Log("We Clicked, Start the dialog.");

frigid trench
ornate hare
#

I could do a voice call.

calm raven
#

if u get multiple somethings wrong..

#

id definitely take Omi up on his offer

#

it'd be much easier to debug this realtime.

ornate hare
#

Okay, got the car audio set up.

calm raven
#

lol... u gonna troubleshoot in ur head?

#

💪 this guy lit

ornate hare
#

Yep.

calm raven
#

welp, work calls.. good luck folks..

#

TLDR: It's def a coroutine issue, imo

frigid trench
calm raven
#

does it still happen in a sequence like that?

#

first the image.. and then the text

frigid trench
#

Yes

calm raven
#

should they both happen at the same time?

frigid trench
#

Clicking again anywhere (except close button) does the overlapping

#

They should, but the big size is separate script so wasn't sure how to deal with yet. Was working out other parts

calm raven
#

thats yet another issue.. u should start both coroutines at the same time.. so it happens all at one..
not sure why it happens in steps like that..

frigid trench
#

I had the big size before ever starting dialog stuff

#

The photo is supposed to grow and be seen, but most other stuff does not, hence the separation so that dialog stuff could be universal

calm raven
#

you might need a boolean to use as a flag too..
something like
isBusyTyping

#

so u could check if thats true before trying to start the coroutine again

ornate hare
#

Okay, stopped at a train.

calm raven
#
private bool isTyping = false;

private IEnumerator TypeText(string message)
{
    if (isTyping) yield break; // Prevent multiple coroutines from running
    isTyping = true;

    foreach (char letter in message.ToCharArray())
    {
        dialogText.text += letter;
        yield return new WaitForSeconds(0.05f);
    }

    isTyping = false;
}```
#

simple fix ^ imo

#

a boolean/flag that gets set to true.. when u begin the coroutine..
doesn't become false until its finished

ornate hare
#

Okay, so let’s start with the TypeText logic.

#

First of all, that Boolean is barking up the wrong tree.

calm raven
#

once thats in place.. i think u can debug a bit better.. (overlappin coroutines is hard to debug with hehe)

calm raven
#

if he doesn't know how coroutines work in the first place..

#

thought it might be a good call.. but carry on

ornate hare
#

Start with this:

IEnumerator TypeText(string textToType) {
}
and pass the text in as a parameter in your ShowDialog method.

calm raven
#

ok.. i really gotta get to work this time.. chao!

frigid trench
#

Groovy, I think overlapping issue is fixed

#

Thanks @calm raven !

calm raven
#

no worries

ornate hare
#

Then the logic is….

while(there is still text to type) {
Add a letter to the display
yield return null;
}

#

That will print one letter, then wait until the next frame.

calm raven
#

ah yea, since ur using coroutines a while would work too.. bool still sufficient tho 😈

frigid trench
#

The clicking issue as far as Having Photo grow and dialog box and text run with one click is still off.

#

Click photo to grow
Click to make dialog box
Click to make dialog (Should happen with dialog box automatically)
Click to remake dialog (shouldn't happen)

ornate hare
calm raven
#

thats fair..

#

i actually use while in my own system

#

just thought bool would be quicker / easier.. but yea.. i see what u mean

frigid trench
#

Sorry, had to move so just reading this now

frigid trench
ornate hare
#

No problem, I just got to my inlaws’ house.

calm raven
frigid trench
#

Can I get a Copy/Paste?

calm raven
#

if u know how long ur string is.. and how many chars have been typed..
u can do something like while(typedChars < totalChars)

calm raven
#

most of us often type in psuedo code..

#

or example code.. (the idea is there.. but we use made up variables and methods and stuff)

frigid trench
#

Gotcha. I may need to review more coding in general

ornate hare
#

Why don’t we rebuild ShowDialog from scratch and go over what all it needs to do?

#

Then we can cover coroutines and their execution step by step to understand what this all does. Coroutines might take some explaining…

calm raven
#

#1253440741322133545 message heres a post i made a bit back... has alot of good resources and some beginner (crash-course) playlists..
and theres always !learn later on.. always helps to go and learn/refresh the basics

timber lavaBOT
#

:teacher: Unity Learn ↗

Over 750 hours of free live and on-demand learning content for all levels of experience!

calm raven
frigid trench
#

Mine is DialogManager, but I'm down with that

calm raven
#

don't delete urs..

#

just build with him.. you'll learn some new stuff and some better ways

ornate hare
#

As I understand, it does two things:

  1. Write text one letter at a time.
  2. Animate an image.
calm raven
#

and u always got urs to fallback on if something happens

frigid trench
#

Gotcha, I'll do a separate one

ornate hare
#

Yeah just comment out that method with /* */

calm raven
#

best of luck.. guys 🍀 🧡

ornate hare
#

Thanks Spawn for helping earlier.

calm raven
frigid trench
#

Thanks a bunch @calm raven !

calm raven
#

||or procrastinating|| 🫠

ornate hare
#

Okay. Text worked so far, but I think we can make this a bit simpler and generic enough to just copy to the animation.

#

It’s honestly the same problem animating the image and typing the text, but I think your AI bloated it a bit.

frigid trench
#

Probably so 😶

ornate hare
#

Let me know when you’re ready to go with a clean ShowDialog() method, complete with the same parameters that you have now.

frigid trench
#

Ok, I made a new script that is blank. You want me to Copy?Paste my old one or have us create a new one entirely?

#

Note, current method uses two scripts

ornate hare
#

Starting blank is probably good. Yeah, we’ll rebuild those IEnumerators as we go. Make sure that ShowDialog matches the signature that your other script expects.

frigid trench
#

After we build this new one, I can change the refering parts in the shorter script to call this new larger one

ornate hare
#

Sure! Either way, start your ShowDialog method (or whatever you want to call it) and we’ll lay out our goals, then build each step from scratch.

#

Understanding coroutines is key for this effect.

frigid trench
#

Ok, so goals are to Click an object to summon the black dialog box with text typing out and the close button fading in after, say, 3 seconds (this hasn't been attempted). At the same time, a character profile animation is on the left.

In this specific case, the animation is four frames, but some others go up to 8, so I wanted it to be adjustable for frames and images for universal use. The main character uses a black background box while all other character use a white version. Little difference otherwise.

Text color for main character is neon green. 3 other characters will each have a different color and the rest of the world has black text. I plan to use a different font than ariel, but plan to add that later when I find one.

ornate hare
#
// Show the box
// Start typing text
// Start animating the portrait
}```
#

This is how I see the task. Agree?

frigid trench
#

Simultaneously occuring, yes

ornate hare
#

We can add colour later, etc.

#

Okay, copy that, and let’s start replacing those comments one at a time.

frigid trench
ornate hare
#

To make this simplest, can you arrange your project so that your gameobject with the background, text and image are on a child object of ShowDialog?

#

So, like this:

ShowDialog
‘- Canvas
‘— Image, Text, Background

#

This way, we can disable the canvas object in the inspector and keep the ShowDialog monobehaviour enabled.

frigid trench
#

Ok, my set up is like this atm. The "Canvas_Locker" has the actual background image. This hold the non-draggable objects like the photo we are working with.

The "Canvas_priority_drag" is invisible and holds all the draggable objects like the trash, books, etc. This is separate so that when items are dragged, they remain on-top of all other images instead of hiding under them during the drag.

Almost everything (dragable and non-) are supposed to have dialog and images as we've discussed in goals.

ornate hare
#

Okay. Is ShowDialog going on “UI”, then?

frigid trench
#

Havn't made it yet, but both canvas are UI. The DialogPanel and such that you see is for the previous set-up

ornate hare
#

Okay, you’re making a UI as we go?

frigid trench
#

Should I make "ShowDialog"->UI->Image under the "Canvas_Locker"?

ornate hare
#

In that chain, the black background would be on UI. ShowDialog should have your monobehaviour and nothing else. The child should have the canvas object.

#

Also, if you didn’t know, it is most performant to use a new canvas for every part of your UI that updates frequently.

#

You can nest a lot of canvases.

frigid trench
#

Ok, so I could actually make ShowDialog and have both canvases under it?

ornate hare
#

Yeah probably.

frigid trench
#

And selecting UI->Image is ok for this? I believe that was what the DialogManager and such was

ornate hare
#

You want to be able to disable the game object to hide the UI, but ShowDialog should always be active in the hierarchy.

#

Oh I see.

#

ShowDialog is one gameobject with your script on it.

#

Put a gameobject under that as a child with a canvas.

#

Add an image using the UI->Image component menu to a new child object under the UI (canvas)

#

So that’s three objects chained as children.

frigid trench
ornate hare
#

Canvas_Locker has the canvas component?

frigid trench
#

Yes, with the image

ornate hare
#

Okay.

frigid trench
ornate hare
#

So now, make sure that the text and portrait image are on child objects under that.

#

Okay, first off, do you have TextMeshPro installed?

frigid trench
#

So I believe that's the DialogPanel

ornate hare
#

Okay. We need to change something here.

frigid trench
#

These fields?

ornate hare
#

Copy your color and settings that you like and delete the Text component.

frigid trench
#

Delete DialogText?

#

Object?

ornate hare
#

You shouldn’t use the Text component as it has been completely replaced with TMP since like 2014. Unity keeps it around for backwards compatibility.

ornate hare
frigid trench
#

Oh the component. Whoops

#

Yeah will do

ornate hare
#

That component is highly inefficient.

frigid trench
ornate hare
#

Yes, add that one with (UI) in the name.

#

Then, install the basic package if prompted by a dialog with two buttons. You don’t need the “extras”.

frigid trench
#

No prompt

ornate hare
#

Okay, your project probably has the dependencies then.

#

Okay, now on a separate object at the same hierarchy level, make sure you have a gameobject with an image for your portrait.

#

The text and image should be equal children of the canvas.

frigid trench
#

If it's 4 frames?

ornate hare
#

We need a box to draw in.

frigid trench
#

Or is this the box/background?

ornate hare
#

It’s a square place to put a graphic.

#

Your portrait. Your background is on the same object as your canvas, right?

frigid trench
frigid trench
ornate hare
#

Okay. So canvas_locker has the canvas, dialog panel is an image and portrait is a different image, just to be sure. (There are many ways you could organize this)

frigid trench
#

Yeah, part of the problem that started this thread was that the character profile wasn't showing up when it should.

ornate hare
#

Perfect. Let’s create a brand new script for your Dialog Panel. Make a new C# script called DialogPanel and have it inherit MonoBehaviour, then put it on you DialogPanel object.

#

This is an object to act as the thing to hold properties for the separate UI objects that make up the parts. This will make sense in a moment.

frigid trench
#

Ok, should I delete old one or edit it?

ornate hare
#

Nope, new file. Well use them all.

#

What we are doing is separating our concerns. ShowDialog is a controller script which tells the DialogPanel what to do. The DialogPanel is a view script that displays the information.

#

We are separating our concerns to keep the code clean and shorter in each file.

frigid trench
ornate hare
#

Great! Delete Update. We won’t need that.

#

Add the following to the top of the script above Start():

TMPro.TextMeshProUGUI textObject;
Image portraitObject;

#

sorry, I’m fighting autocorrect

#

TMPro.TextMeshProUGUI*

frigid trench
#

needs "."?

ornate hare
#

Should be in the UnityEngine.UI namespace. If you press Ctrl*. you might get a quick action to add that.

#

portraitObject should be one word. I got autocorrected on my phone.

#

Put your text cursor on Image and press ctrl+.

#

See if it suggests to add UnityEngine.UI as a reference.

frigid trench
#

I typed it in and went green

ornate hare
#

Perfect! I wanted to see if you could use VS to add it automatically like the rest of us do.

frigid trench
#

Does TMPro. look ok? Is there supposed to be a space?

#

NVM, it doesn't have a space

ornate hare
#

Let me show you something. Remove TMPro. from the start of that line. This will make the rest of that line underline red, but try it anyway for a second.

frigid trench
#

ok, red is there

ornate hare
#

Now, add using TMPro; to the references at the top of the file.

frigid trench
ornate hare
#

Error is gone, because TextMeshProUGUI is being found in the TMPro namespace.

#

using basically makes everything in that namespace accessible to the current file.

frigid trench
#

I see. So does that work with anything typed as "THING.blahblah". Like you can just have "using THING" at top and then have "blahblah" down below?

ornate hare
#

Exactly, and sometimes you need to because class names only have to be unique relative to their namespace.

#

So, NamespaceA.MyClass and NamespaceB.MyClass are not the same class.

#

Different packages sometimes use the same generic names for their parts.

frigid trench
#

Ok, so you don't throw Namespace A & B up top because you need to separate the 2 different "MyClass"?

ornate hare
#

You’ll get an ambiguity error if that happens.

#

Then, you know you only need one, you can get specific like:

using MyClass = NamespaceB.MyClass;

This would not add access to all of NamespaceB, but any reference to MyClass will point to that specific combination of namespace and class name.

#

using OtherClass = NamespaceA.MyClass;

This would bring in the other class under a totally new name for this file only to break ambiguity.

#

This is beyond the scope of the lesson, but you should know how to reference other scripts.

frigid trench
#

ok, makes sense. Altering names for easier reference

ornate hare
#

Exactly, and also to solve naming issues.

#

So now, go into your inspector and find the DialogPanel component. Drag the gameobjects with your text and image into the relevant boxes on it.

#

In the Unity editor, I mean.

#

Should just be drag and drop.

frigid trench
#

Which object am I attaching text and image to though? The DialogPanel uses previous script

ornate hare
#

Okay, with DialogPanel selected, click on your DialogPanel.cs file and drag it over to the inspector. This should add a component called Dialog Panel to your DialogPanel gameobject.

#

That’s the file that you have open in Visual Studio right now.

frigid trench
ornate hare
#

Okay, what is the error showing up in your console?

frigid trench
#

Assets\Scripts\ShowDialog.cs(7,17): error CS0542: 'ShowDialog': member names cannot be the same as their enclosing type

#

Change yellow to DialogPanel?

ornate hare
#

So the class name can’t be the same as the method name.

frigid trench
#

Right, think there was confusion in early set-up

ornate hare
#

This is because typically, in C#, the constructor is a method with the same name as the class, but we don’t use constructors for technical reasons in Unity that I shouldn’t get into.

#

Change one of those words. If you change the class name, rename the file to match.

frigid trench
#

This ok?

ornate hare
#

Monobehaviours must use a class name that matches the file. Personally, I don’t understand why Unity forces that. I think I get it, but it’s due to an old limitation.

#

I would recommend naming the class DialogController and the method ShowDialog. Always put the verb in the method name since the method is the thing that does something. The class name should be a noun.

#

I was going to let you use whatever names you want, but you got it completely backwards. >_>

frigid trench
#

That makes sense

ornate hare
#

If that compiles, add DialogPanel to DialogPanel.

#

(component to the object)

frigid trench
ornate hare
#

Great. Did you set up the DialogPanel object with the component?

frigid trench
#

Since we're doing a new thing, should I remove the DialogManager component?

ornate hare
#

Okay. So far so good. There are two ways to get your object references to show up in the inspector. Do you know what they are?

frigid trench
#

I know I can drag/drop from hierarchy and can drag images and such from window

#

Into appropriate fields that will allow them

ornate hare
#

The answers are, you mark the data as public or flag it to be serialized.

frigid trench
#

Oh like making the fields? I know of [Serialized] ...

ornate hare
#

Try this: add public in front of one of your TextMeshProUGUI or your Image in your file.

#

Just one.

frigid trench
ornate hare
#

Perfect. Now put [SerializeField] in front of the other one.

frigid trench
ornate hare
#

[Serializable] does something else and isn’t relevant here.

#

So, I wanted to show you that both these methods work, but make both [SerializeField] for this class.

#

The difference is that public automatically gets serialized, but we don’t need or want these fields marked public.

frigid trench
#

Like this?

#

Fixed the bracket typo

ornate hare
#

Now, add the following before Start():

string _text;
public string Text {
get => _text;
set {
_text = value;
}
}

frigid trench
ornate hare
#

Sorry, I’m getting a bit distracted. Delete the string _text; line and replace both remaining instances of _text with textObject.text;

#

I starting coding in the wrong direction because I’m thinking on an iPhone lol.

#

public string Text {
get => textObject.text;
set {
textObject.text = value;
}
}

frigid trench
#

NP, lol

ornate hare
#

Great. Now, let’s go over to your other class DialogController.

frigid trench
#

ok

ornate hare
#

Like you did in DialogPanel, let’s add a reference to your object here.

#

Add [SerializeField] DialogPanel dialogPanel; above your method.

#

I am inconsistent about capitalization because of autocorrect btw.

frigid trench
#

NP, I usually am on lookout for that even outside of C#

ornate hare
#

Next, go to the Unity editor and drag DialogPanel into that box on your DialogController component.

frigid trench
ornate hare
#

Great. Let’s make it do something finally, then we’ll explain what we did.

#

In ShowDialog(), add this:

dialogPanel.text = “Hello World”;

frigid trench
ornate hare
#

Next, can you share the source again that calls ShowDialog() that you showed me at the start of this thread?

#

Oh yeah, capital T on text.

#

Also make sure you use this character for the quotes: “

#

If those aren’t the same thing.

frigid trench
#

Fixed and no errors, but null warnings

ornate hare
#

Ugh, let’s ignore the null for now. That’s a bit weird to see since the inspector should be overloading that.

#

I have a theory, but let’s ignore it for now.

#

Show me the code where you called ShowDialog before.

#

(scrolling up in this conversation is hard on my phone)

frigid trench
#

I didn't have a ShowDialog before our construction. I had scripts of DialogManager and ObjectsDialog

ornate hare
#

Okay, it was the other class (the smaller one)

#

Whatever called ShowDialog in its source.

frigid trench
ornate hare
#

Right. Replace every mention of DialogManager with DialogController.

#

Then, cut out animationFrames from the method call so that it reads DialogController.ShowDialog(message);

frigid trench
#

This is in DialogPanel script, yes?

ornate hare
#

ObjectsDialog.

#

That’s the script that you have receiving a mouse click.

frigid trench
#

Right, I thought I was transferring it.

ornate hare
#

I’m trying to tie this in to your existing system.

#

Make sure that DialogController is referenced in ObjectsDialog, since it’ll probably be null now.

#

In the inspector I mean.

#

Then, run your game.

#

You should see Hello World show up in your dialog box.

frigid trench
ornate hare
#

Yep. Make sure that DialogController is assigned in the inspector and run the game.

frigid trench
#

NullReferenceException: Object reference not set to an instance of an object
ObjectsDialog.OnMouseDown () (at Assets/Scripts/ObjectsDialog.cs:11)
UnityEngine.SendMouseEvents:DoSendMouseEvents(Int32)

ornate hare
#

I should be getting home soon, so we can move faster if you have time to continue.

frigid trench
#

Is DialogPanel supposed to have ObjectDialog?

ornate hare
#

I don’t know where you put ObjectsDialog. I know it was running before. Frankly, it shouldn’t matter.

#

You aren’t emotionally attached to that class at all? I think that might have been a button or something?

frigid trench
#

Sorry, it goes on the individual objects and it's there.

#

The problem now is that the Dialog COntroller has to be an object in the Hierarchy. Is that ok if I make one? Previously, this was the DialogManager I believe

#

Just trying to make sure I'm addressing the previous set-up

ornate hare
#

Yeah, I don’t know how you had it. If you don’t necessarily care about the click functionality now, you could just put your function call into Start() on the DialogController class. It’ll perform the click operation when the game starts.

#

I should be heading home soon, where we can probably move a bit quicker, if you have time today/tonight.

frigid trench
#

It's midnight by me. USA EST

ornate hare
#

Ah, 11:20 pm.

#

CST here.

frigid trench
#

Ok, do you want to set a time for tomorrow or something?

ornate hare
#

I can be around whenever tomorrow, I’ll probably be up for another 5 hours tonight.

frigid trench
#

Ok, wanna do 1400EST/1300CST?

ornate hare
#

Sure!

#

Do you understand what we did so far?

#

I didn’t explain the Text property- I was going to explain that once we ran the game one time.

#

But about the object hierarchy: can you guess at least why we’re structuring the objects this way?

frigid trench
#

Awesome! I greatly appreciate it!

I understand producing the fields and assigning parts of the hierarchy. I think there are some issues between set-ups as I know the code we made is supposed to say "Hello World", but I still have the message field in the ObjectsDialog

#

I get the grouping of the DialogPanel being under ShowDialog, But surprised Canvas is within ShowDialog

#

Although actually I suppose it makes sense since I have two canvases and objects in both need dialog

ornate hare
#

Well, we didn’t write enough to really see it, probably, but I want to completely encapsulate the portrait and text inside of one object, so you only really have to interact with dialogPanel instead of a bunch of separate components.

frigid trench
#

Compiling certainly makes it easier

ornate hare
#

Then, DialogController commands the dialog system.

#

DialogPanel is the thing that you see.

#

Setting this up now will make things clearer as those files grow.

#

Leaving,will be back in 15 minutes.

frigid trench
#

Yeah, I'm wondering if I should remove Manager

#

Ok, hope you get a safe trip home. I'll be back on at 1400EST. Thanks again!

frigid trench
#

Hello! I'm available now if you are, but no rush of course 🙂

#

@ornate hare

ornate hare
#

Sorry, I had to run yesterday and wasn’t around at all.

#

@frigid trench

frigid trench
#

No problem, are you available tonight?

ornate hare
#

Hey, yeah! Anytime today/tonight. Ping me when you want.

frigid trench
#

Cool stuff, I'm on now 🙂

#

@ornate hare

ornate hare
#

Awesome, I'm around.

#

Chance you're in Ontario or that part of the eastern US?

frigid trench
#

Maine

ornate hare
#

Ahh, I'm in Winnipeg.

#

Just a moment. I'll grab some coffee. We could do this over voice or keep texting - either is cool with me,.

frigid trench
#

Texting is better since someone else is on the phone.

I wish Maine would get annexed by Canada though, lol

ornate hare
#

Alright! I'm at my computer today, so this should be quicker from my part.

frigid trench
#

Awesome, thanks for your time!

ornate hare
#

My pleasure!

#

So first of all, let's talk about textObject.text for a moment. When you assign to that, it updates the text on the object during play mode. This is notable, because the act of assigning data launches some logic inside the object to cause the text to be drawn.

frigid trench
#

So one thing I wanted to ask about is when we made the ShowDialog object and placed the canvas under it, it shrunk the scene. Moving the canvas above the ShowDialog fixes it back, but I assume we don't want that placed there

ornate hare
#

Ugh, this probably has something to do with a Canvas Scaler component taking over if your canvas was a child or parent of ShowDialog.

#

How many separate Canvas components do you have?

frigid trench
#

The Canvas_Locker which was the original I made and the Canvas_Priority_Drag which was made for the movable object to be in front. I notice now that as a child of ShowDialog, the Rect Transform of the canvas is locked at Bottom Left when before it was full stretch.

ornate hare
#

So, in DialogPanel, what we did was create a property called Text. A property combines a field with up to two methods under one name: one to "get" and one to "set".

#

When you assign a value to DialogPanel.Text, the property acts like a method with a string passed into it as an argument. We pick that value up as value inside the set {} method of the property. This is the same as writing:

public string SetText(string value) {
    textObject.text = value;
}```
#

The get => part is a method for retrieval. In this case, we just read whatever is at textObject.text and return that;

#

The important thing to note here is that we are not just assigning and reading to/from textObject.text, we could add new logic here if we wanted to.

#

Also, textObject.text is a property as well, which is how it handles actually drawing stuff when an assignment is made.

frigid trench
#

So is the 'get' part similar to 'return' for other functions?

ornate hare
#

get is essential a method.

#

private string _internalText;
public string Text {
    get {
      // what to do when reading a value from Text... eg var x = thisObject.Text;
      return _internalText;
    },
    set {
      // what to do when writing a value to this property... eg thisObject.Text = var;
      _internalText = value; // value is a special keyword that is automatically set for us
    }
}```

This is shorthand for:

```cs
private string _internalText;
public string SetText(string value) {
    _internalText = value;
}
public string GetText() {
    return _internalText;
}```

However, outside the class, all you need to interact with is `DialogPanel.Text`, like this:

```cs
DialogPanel myPanel;
myPanel.Text = "Hello";
Debug.Log(myPanel.Text); // Prints 'Hello'
#

"Text" doesn't hold data... it's a word that, when assigned to or read from, kicks off some logic because its actually made up of two methods.

#

I created _internalText as a place to store data inside the object.

#

Any questions?

frigid trench
#

So in my case with the dialog boxes, the get section would be the actual text and the set piece would be how we stop it from having the repeating error?

ornate hare
#

When you set a value to DialogPanel.Text from outside the class, it sets the given value to textObject.text inside the class. textObject is not marked public, so it can't be altered from outside.

frigid trench
#

Isn't it inside the public string though?

ornate hare
#

What is "the public string"?

#

If you're talking about public string Text {}, that is a property. It is two methods under one name.

frigid trench
#

Public means the rest of the script can access it outside brackets and I think can also make fields in components within unity.

string refers to a line of text "fuhikfhk"

#

Yeah so I meant isn't the textObject.text public because it is within public string Text?

ornate hare
#

What this does is guard against direct access to textObject because that is not marked public. Text IS public and is an access point that we use to modify textObject only however we allow it to be used.

#

Here's an example of what this does not allow:

DialogPanel dp;
dp.textObject = null; // Not allowed because textObject is private
#

You can also make Text read-only by omitting a set method altogether.

#
public class DialogPanel : MonoBehaviour {
  public string Text {
    get => textObject.text;
  }
}
dp.Text = "newValue"; // Illegal because there is no set method defined
#

You could technically also make write-only properties where checking the value of DialogPanel.Text would throw an error, but you could write to it.

#
public class DialogPanel : MonoBehaviour {
  public string Text {
    set => textObject.text = value;
  }
}

//-----------------------

dp.Text = "Data to write";
Debug.Log(dp.Text); // Illegal because this is not allowed to be read.
#

Does this make sense so far?

frigid trench
#

I believe so. Values are necessary or else you'll get the Errors describing null values.

frigid trench
#

It defaults to null values I mean

ornate hare
#

What is "it"?

frigid trench
#

Not the code itself, but when trying to run the program, essentially not performing the desired function

#

The methods missing a value(s)

ornate hare
#

What, like regarding set if I only define a get method for some property?

frigid trench
#

Right

ornate hare
#

In that circumstance, the is no get. It's not defined.

#

A thing is not null that does not exist.

frigid trench
#

Gotcha

ornate hare
#

So anyway, if that's clear... lets just talk about my use of => and {}

#

These are equivalent:

get {
  return textObject.text;
}```

```cs
get => textObject.text;
#

Basically, => just assumes that whatever is to the right of it is a value, so omits the need to write return.

#

For set,

set => textObject.text = value;

Is the same as:

set {
  return (textObject.text = value);
}

But set doesn't really need to return anything but void, since there is no return type to an assignment. We would never use a return type here, but technically it can return one.

#

So, we can do this:

public string Text {
  set => textObject.text = value;
  get => textObject.text;
}```

It means the same as what you already had, just wrapped in a smaller package.
frigid trench
#

Ok. And as we did in the DialogPanel, you can mix the formats?

ornate hare
#

Now, a step further, we can do this:

When all the property does is simply assign to or read from some field, there is a shorthand that skips defining set and get entirely:

public string Text => textObject.text; // Contains both a set and get method in a short definition
ornate hare
frigid trench
ornate hare
#

Oh I see. Yeah, I wrote two different "text formats" to represent the same thing.

frigid trench
ornate hare
#

Yes, you can think of the set as the expanded way to write out a method with the whole method body shown.

ornate hare
frigid trench
#

Sounds good

ornate hare
#

With that said, you should change your code to this:

public string Text => textObject.text; // Contains both a set and get method in a short definition```
#

And understand that Text doesn't hold data, it's just an access point for two methods that you can't see.

frigid trench
ornate hare
#
public string Text = "";

^ This is a field. It holds data inside it.

public string Text => "";

^ This is a property, it would return "" and can't be written to.

#

Fields vs properties.

frigid trench
#

Gotcha

ornate hare
#

One final note. You can take it a step further and create a property with an implicit "backer field":

public string Text {get; set;}

This defines a default get and set for a new field, so now Text can actually hold data.

#

Basically, it writes in the private string _text automatically for you, but calls it something like _Text3278198321738211 in the compiler.

#
public string Text {get; set;} = "Hello, world!";

This is two methods and a string field all wrapped up in one line.

frigid trench
#

So when running code, will this print the Hello World or would you still need Console.WriteLine?

ornate hare
#

If you want to write a thing to a console, you can use a method that writes things to a console, yes.

#

Okay, so that was just a bit of housekeeping to cover properties. What I wanted to do was start building the DialogPanel as an object that we can use just like the text object that you were using before. Now, you understand how if we want do add some logic when assigning text, we can open up that line and add more instructions. We'll do more than just change the text later.

#

Is your game played with mouse and keyboard?

frigid trench
#

Yes

ornate hare
#

Okay, I noticed that you were using GetMouseDown() in another script before, and you probably aren't super attached to this being the interaction point in your game I would gather.

#

Does the player have to click on anything in particular to trigger the text to display, or are you only trying to listen for a click anywhere in the game window?

frigid trench
#

Each object is meant to have dialog with it and a different character profile expression to the left

#

In the videos shown the other day, I was using starting with the photo image of two characters

#

This one is a little special as it has a separate script to make it grow big when the polayer clicks on it. Other objects don't do this

ornate hare
#

Right, okay. Does it matter what is under the player's cursor when they click? I was getting visual novel vibes from your graphics.

frigid trench
#

The objects themselves when clicked on.

ornate hare
#

Okay.

#

Hmm, then if you're using UnityMessages to receive clicks, let's maybe stick with that. Do you still have ObjectsDialog (or whatever it is) in your scene so we can test?

frigid trench
#

For example, there are 3 paper balls that need to be drag/drop to the trash can icon to throw away (this is already scripted). But clicking separate paper has different dialog.

Paper 1: "Ugh, this kid is always a mess."
Paper 2: [none]
Paper 3: "I'm afraid to open this."

#

Yes, ObjectsDialog is still available and currently attached to the photo

ornate hare
#

Okay. The photo triggers the text like a button then... (this wasn't entirely clear before).

frigid trench
#

For Photo, One click expands size && should begin dialog (complete with background box, character profile and typing text). When player hits the "Close" button, dialog goes away and photo shrinks.

ornate hare
#

Okay. Can I see the script that you have making the photo grow currently?

#

I'm trying to get a sense of how we should start your dialog system to keep it organized.

frigid trench
ornate hare
#

Okay! I don't know how long you were planning on us talking, but we can really clean this up a bit. I figure that might set you on a good path for the rest of your game, if that works for you.

frigid trench
#

Sure thing, I'm down for clean-up, but will have to get off in about an hour. I have early mornings, but am available every night about the same time and more on weekends.

ornate hare
#

Okay.

#

First of all, what is public static GameObject Photo;? Seeing that marked static stands out as strange to me.

#

Better yet, why is that static? Do you understand what static data is?

frigid trench
#

I think is has to do with utilization for all gameobjects, like a constant. I realize now that it probably isn't necessary since I'm only applying to photo.

This was from a video tutorial and I remember I think I had to switch the bool in order to get it to work properly for some reason.

ornate hare
#

Well, it's a bit confusing here, because this means that ClickToBig has a Photo object that it points to. I don't mean one instance of the component, I mean the idea of ClickToBig has a reference to some specific GameObject in the scene.

frigid trench
#

Yeah, the Photo

ornate hare
#

Technically, what this does is not build the reference to the GameObject on the component (on a specific monobehaviour), it builds it on the class definition.

#

If you have two ClickToBig components in your scene, they will both point to exactly one and only one GameObject.

frigid trench
#

In Unity, I renamed Photo_Sprite for canvas reasons, though that's not really needed at this point

#

I see. In a way that might be beneficial since I dont want others to grow, but I can see it might not be necessary

ornate hare
#

So, because you're referencing transform.localScale in OnMouseDown(), I don't think you need a GameObject reference at all unless this script is supposed to go on something that isn't the same as whatever is growing/shrinking. You aren't using that reference anyway.

ornate hare
#

What I'm saying is, it could be helpful to use static data for your dialog, but this ain't it. 😄

#

Try this:

  1. Remove that line since it's doing nothing.
frigid trench
#

Ok, I removed the static and saved and the photo still functions the same

ornate hare
#
  1. Let's leverage what we learned about properties to make a bool that activates the grown state of your object.
frigid trench
#

Ok, yep still works

ornate hare
#

Create a new public bool IsBig with a get and set section with curly braces. Note that I'm using a capital I. It's typical to name properties with a starting capital letter for clarity.

frigid trench
#

This is at the top above private vector, correct?

ornate hare
#

It could actually go anywhere you want, but I like to put properties below fields and above methods, personally. I would put it above Start, but you can experiment and see that it doesn't matter so long as it's at the same level as everything else indented right now.

frigid trench
#

I'm assuming we can remove the private bool?

ornate hare
#

Ugh, we actually need to keep it. I would personally rename it to _isBig, but there is no rule here. That's going to be our backer field.

#

Then, make the following change:

[SerializeField] private Vector3 _bigScale = new Vector3(20f, 20f, 20f);
[SerializeField] private Vector3 _smallScale = new Vector3(5f, 5f, 5f);
#

This will expose the values so you can tweak them in the editor if you want. You can declare values inline at compile-time so long as they are structs, so using Start() for this is unnecessary.

#

Also, you can set [SerializeField] private bool _isBig = false; in your definition, too.

#

You can completely delete Start();

frigid trench
ornate hare
#

Okay, look at IsBig though, that is a field. You need to add the get and set parts to mark it as a property.

frigid trench
#

I added underscores to bottom isBigs

ornate hare
#
public bool IsBig {
  get {
  }
  set {
  }
}

It isn't a property until you expand it or use a shorter form.

#

The big difference is that ; at the end of it. That completely determines whether it's a shorthand for methods or a field.

frigid trench
ornate hare
#

Perfect! get isn't happy because it has to return something, but set is happy to be empty because doing literally nothing is perfectly valid.

#

So, consider the following code:

public bool IsBig {
  get => _isBig;
  set {
    _isBig = value; // value is special and is set for us
    if (_isBig) {
      transform.localScale = _bigScale;
    } else {
      transform.localScale = _smallScale;
    }
  }
}
#

Since we discussed properties in DialogPanel, do you think you understand what this does?

frigid trench
#

I think the 'get' is targeting the '_isBig' obejects, 'set'ting the value to transform objects 'if' they are '_is big' objects. Otherwise, they remain the same. The transformation values come from the vector lines at top of the script.

Hope that made sense

ornate hare
#

I think I get you.

#

You can remove private Vector3 bigScale, smallScale; since that's doubled up now. Replace OnMouseDown() with this:

private void OnMouseDown() => IsBig = !_isBig;
#

(Note this is the same as ```cs
private void OnMouseDown() {
IsBig = !_isBig;
}

... but if it's only one line, we can shrink it using expression form rather than block form.
#

Once you've done that, add this new code to the file:

private void OnValidate() => IsBig = _isBig; // Pay attention: there is no ! this time

I'll get to explaining what this does when you're ready.

frigid trench
ornate hare
#

Great! Now, go into your editor and click on the object with this component on it.

frigid trench
#

Grows and shrinks with each click 🙂

ornate hare
#

Yeah, so this demonstrates what happens when you change that bool.

#

Now, just like with the text object, you see something happen immediately when you set true or false.

#

What OnValidate does is assign _isBig to IsBig every time something in the inspector changes.

#

OnValidate does nothing on a shipped game, but provides some editor functionality.

#

So now, when you want to expand the object, you would do something like:

myClickToBigComponent.IsBig = true;

And it'll grow right away.

frigid trench
#

Like in another script?

ornate hare
#

You can also edit the scale vectors in real-time, if you haven't tried yet. Any change to any value in the spector will call OnValidate.

ornate hare
#

So, I think this is a good example to show what we were doing earlier in DialogPanel.

#

Do you have any questions about what each of these parts do, or does it sum up well so far?

frigid trench
#

I think I get it. the "myClick..." you just mention I think is what we'll use ro simultaneously grow the object and display the dialog box?

ornate hare
#

Probably, you'll want to change that before your game is finished. What we did was make it modular so it's easy to grow or shrink the object from somewhere else.

#

Now, when the photo grows, you want to display some text, right?

frigid trench
#

Makes sense. In Game Dev, I imagine finished products could be submitted to editors for refining/fresh eyes?

frigid trench
ornate hare
ornate hare
#

And to be clear, the close button is a child of the DialogPanel, if I'm not mistaken?

frigid trench
#

Yes to Close Button.

As for the assignment, I was aiming for the dialog script to be separate so that it is more universal with objects. Most objects won't grow throughout game, especially in this scene where only the 1 object does.

ornate hare
#

So, let's add a new public string to your ClickToBig component, since clicking on it is what chooses what the text to show will be. You can always change how this is stored later. Call it whatever you want and write a message into it in the inspector.

#

This will be the text that prints when the picture grows and the DialogPanel is opened.

frigid trench
#

public string Text = "...idiot...";

#

Like this?

ornate hare
#

Yep.

#

Okay. Now, let's go over to DialogController... we haven't looked at that file in a while.

frigid trench
ornate hare
#

Remind me that this is set up, or make sure:

DialogController is on some object in your scene. It exists globally and so doesn't need any specific place in your scene hierarchy - it only needs to exist. I personally like to put controllers like this under an object called "Systems" or something.

#

Also, what error are you getting on dialogPanel? I assume that's an old photo, because your ClickToBig file wouldn't compile if it was still a problem.

frigid trench
#

Ok, previously I had DialogManager as an object. Will this replace that?

ornate hare
#

Yeah. We're not relying on any of your old scripts anymore.

frigid trench
#

NullReferenceException: Object reference not set to an instance of an object
ObjectsDialog.OnMouseDown () (at Assets/Scripts/ObjectsDialog.cs:11)
UnityEngine.SendMouseEvents:DoSendMouseEvents(Int32)

#

Ok, I'll delete the object and make a new one. Create Em,pty-> Image should be fine?

ornate hare
#

Why an image? Just an empty gameobject is most directly what you need.

frigid trench
#

Yeah true. So I made that Down bottom.

ornate hare
#

Okay. Once you're ready, you'll have to comment out or assign something to a null in the script with the error.

frigid trench
#

It's referring to ObjectsDialog which I think we're also scrapping?

ornate hare
#

Actually, ObjectsDialog and DialogController look like the same thing basically. You can scrap it - we're just rebuilding this.

#

DialogController, as a name, actually says what it does.

frigid trench
#

Controller is synonymous with Manager I believe. ObjectsDialog was a support piece that was assigned to objects

ornate hare
#

When you have one controller that controls a system with one object, the definition can become a bit more nebulous - there it is technically most accurate to name a script a controller.

frigid trench
#

Yeah, but I mean the names were probably inaccurate somewhat

ornate hare
#

Okay. What we're going to do is create something called a singleton. There are plenty of ways to build this thing, and some people will tell you that using singletons in Unity is a bad practice.

What a singleton does is check to see if it is the only one of its kind. If it is the first to arrive in memory, it registers itself as the one true copy. If it finds that something is already registered, it destroys itself.

frigid trench
#

But anyhow, ObjectsDialog and DialogManager scripts are now removed. So we have DialogController and DialogPanel now

ornate hare
#

The truth about whether it is bad practice or not is simple: if you don't use it right, then it is not the thing to use. If you use it correctly, it is the correct thing to use.

#

Before we begin, I want to make a proof of concept. Copy your gameobject with the DialogController component so that there are two of it in the scene. This is going to be what we consider an invalid use of the class and we're going to have it fix this when the game starts.

#

Just select it and hit Ctrl+D.

#

Then, in DialogController, add the following to the top of the class definition:

private static DialogController _instance;
public static DialogController Instance {
  get => _instance;
  set {
    if (_instance == null) {
      _instance = value;
    }
  }
}
#

Consider for a moment what that will do.

#

Let me know if you think you understand what this does. If we're good to go on, I'll actually write you a better version of it.

frigid trench
#

The if statment is changing something null to a value. The Static aspect is affecting everything apart of the DialogController

ornate hare
#

Static makes it live in the class definition and not in any instanced object of the class. So, we don't have to create a component to access DialogController.Instance ... it exists as a data store for the whole program even if there is no instantiated object.

#

So... here's a more advanced version of the same thing:

#
private static DialogController _instance;
public static DialogController Instance {
  get {
    _instance ??= FindObjectOfType(typeof(DialogController), true); // <- Changed this
    return _instance;
  },
  set {
    _instance ??= value;
  }
}
#

When Instance is read (through get), we check to see if there is a value there. If it is null, we search the hierarchy for the first DialogController that we can find and set that to _instance. Then, we return it.

frigid trench
ornate hare
#

??= is called null coalescence. It is a shorthand for:

if (someValue == null) {
    someValue = someThing;
}```

If _instance is null, we assign to it.  If it's not null, we leave it alone.
frigid trench
#

Parenthesis line up so not sure why it's unhappy

ornate hare
#

Sorry, I'm dumb. What is the specific error here?

#

Oh, add a ) after DialogController. My bad... I'll edit my message above. **See my edit

frigid trench
ornate hare
#

Remove the comma.

#

Or, put it outside the ) sorry.

#

,) => ),

frigid trench
ornate hare
#

Ah, the method is obsolete. Try FindAnyObjectByType.

#

FindAnyObjectByType**

frigid trench
#

red for both

ornate hare
#

As long as this class is a MonoBehaviour and you spell it the way I just did in the yellow lettering, it should work.

frigid trench
#

Is DialogController supposed to be string?

#

I'll need to get ready for bed. Continue tomorrow?

ornate hare
#

Sure! Quick thing if you have time. Click on FindAnyObjectByType and press Ctrl+period.

#

Any suggestions come up to fix?

frigid trench
ornate hare
#

Okay, you just created a new method by that name. That's not going to help.

#

Try explicitly using the GameObject namespace (if you're still up):

_instance ??= GameObject.FindAnyObjectByType(typeof(DialogController), true);

frigid trench
ornate hare
#

What version of unity are you using?

frigid trench
#

2019.2.6f1 because Unity 6 was not vibing with VS Code on my comp for some reason. Though I think since I'm just Visual Studio, it was Code that was the problem, but I didn't switch back.

ornate hare
#

Ah, okay. You're using a 5 year old version of the engine, so you might be okay with a method from the old API, but I'll leave it to you to research what that is if you want to do that.

#

Otherwise, just delete that line and stick with return _instance;

frigid trench
#

Alright, will do. Thanks for your help tonight

frigid trench
#

@ornate hare Hello, hope you're doing well. I'll be on most of the day if you're available, but I totally understand if you're busy.

ornate hare
#

Works well for me, if you're still down.

frigid trench
#

Hey there, sorry just saw this.

#

@ornate hare

ornate hare
#

Ah, I should have pinged.

#

@frigid trench

frigid trench
#

Np. Shall we continue where we left off?

ornate hare
#

Sure!

#

Okay, so just get rid of that line beginning with _instance ??=. It's up to you to build a proper singleton if you're going to use an editor version that's 5 years old.

#

Add a new method

private void Awake() {
  _instance ??= this;
  if (_instance != this) {
    Destroy(gameObject);
  }
}

Remove the set method of Instance entirely -> let's not allow that to be set after all.

frigid trench
#

keep the static?

ornate hare
#

Yes.

#

I'm not sure why you removed Instance, you should put that back.

frigid trench
#

Should the Static Instance be _instance?

ornate hare
#

Do you think you can take a look at this and try work out some of the errors?

#

Try working it bavkwards and see if you can learn a bit what is going on here.

frigid trench
#

Ok, since we removed the get/set, I realized that we're not working inside the public static, so brackets need to be removed and private void needs to line up as it's own variable type.

ornate hare
#

Looks better! We need to put back the get element of Instance, because I have no idea why you removed that. Since we don't need set, you can use the shorthand: public static Instance => _instance;

frigid trench
#

Sorry, thought that has to go with the set

ornate hare
#

Now, we don't really need ShowDialog() to exist on the object anymore, since we're turning this class into something globally accessible, but let's leave that in so that it can be used by reference. Add a new method: public static void ShowDialog(string text) => _instance.ShowDialog(text);

#

This will put an alias method with the same name on the class directly.

frigid trench
#

What gameobject is getting destroyed by the way? Is this to remove the dialog panel after reading?

ornate hare
#

Instance needs a type. I forgot to write that in my code. Think that you can fix that?

#

Because you just deleted the other method ShowDIalog, it can't find it anymore. Why did you do that?

frigid trench
#

Getting confused. Do we want the Hello World line?

ornate hare
#
public class DialogController : MonoBehaviour {
  [SerializeField] DialogPanel dialogPanel;
  private static DialogController _instance;
  public static DialogController Instance => _instance;
  
  private void Awake() {
    _instance ??= this;
    if (_instance != this) {
      Destroy(gameObject);
    }
  }

  public static void ShowDialog(string text) => _instance.ShowDialog(text);
  public void ShowDialog(string text) {
    dialogPanel.Text = "Hello world!";
  }
}
#

Just copy this and study it later.

#

I think you need to take a basic C# course because I get the impression you don't understand much of what's going on.

frigid trench
#

Ok

ornate hare
#

Is dialogPanel.Text sill raising an error?

frigid trench
#

yes

ornate hare
#

Okay, what is the source of dialogPanel.Text?

#

What is the error on ShowDialog?

frigid trench
ornate hare
#

Ahhh... okay this related to the version of C# that you are using. Just change the second declaration as follows:

public static void ShowDialog(string text) => _instance.ShowDialogInternal(text);
private void ShowDialogInternal(string text) {
  dialogPanel.Text = text;
}

Doesn't like using the same method name twice, even if one is declared static.

#

I changed "hello world" because we have a parameter we can use. That string literal is outdated now.

frigid trench
ornate hare
#

Right. What's the issue with dialogPanel.Text?

frigid trench
ornate hare
#

Ah, can you post the source for DialogPanel? I might have forgotten what we were doing in there.

frigid trench
ornate hare
#

Ah got it, this is due to your C# version again.

So in this case, public string Text => textObject.text; only provides an alias for get, so you have to expand that out to the longer version with a separate get => and set => . It's a good thing we went over this the other day, because you should know how to do that.

#

I think we even started from the longer version and shortened that code. It turns out that because you're 5 years behind, you need the longer version.

frigid trench
#

Ok, at this point I think I should do the absolute longest version possible with get/set

ornate hare
#

You just need to supply both those keywords, actually, so you could keep both wrapped with => if you wanted to. Whatever you want is fine, though.

frigid trench
#

Not sure why get/set not being recognized now

ornate hare
#

ShowDialogInternal() is a method. Aren't you supposed to be making changes inside of DialogPanel?

#

dialogPanel.Text was not writable. Let's think about how to make that property writeable.

frigid trench
#

OH I see

ornate hare
#

That's looking pretty close!

ornate hare
# frigid trench

Take a look at this state of your singleton class earlier, and try to figure out why this property was valid (ignoring the call to a method that doesn't exist in your version).

#

Hint: set clauses have no return type.

frigid trench
ornate hare
#

You got it!

frigid trench
#

Awesome!

#

And the controller is happy now

ornate hare
#

Does that error on dialogPanel.Text now reinforce what get and set do? I mean, now that we know that you can't rely on the shorthand for set behaviour.

#

Cool. Can you post your "embiggening" script for that photo that we were looking at before? It's time to hook stuff up.

#

Also, is your DialogPanel object in your hierarchy enabled or disabled when the game starts?

frigid trench
ornate hare
#

Okay, DialogPanel enabled or disabled on game start?

frigid trench
#

Currently disabled pre-game start. Can't start game because controller is not happy apparently.

Assets\Scripts\DialogController.cs(13,21): error CS1525: Invalid expression term '='

ornate hare
#

Well that's dumb; I have to assume it's due to your version.

frigid trench
#

Bottom even says "No issues found" so really confusing

ornate hare
#

Replace it with if (_instance == null) _instance = this;

#

I think I know the reason, being how Unity likes to override null internally, but that's a false-positive.

frigid trench
#

The other If statement should be indented with brackets, right?

ornate hare
#

No, {} is only needed for multiline blocks.

#

if (true) {
  DoSomething();
}```
These are equivalent, as long as the contents of `{}` would contain only one statement.
#

...is the same as...

if (true) {
  DoA();
}
DoB();```

Here's a counter-example to show you what happens to the second statement on the line.
frigid trench
ornate hare
#

Well of course they're never assigned to, you're assigning them in the inspector.

#

You need to fix your Visual Studio installation later so these stop showing up.

frigid trench
#

Ok, just making sure it wasn't a "Because it's old" situation

ornate hare
#

You might be able to add = null; to the declarations at the top of your file if you want to shut that up, but I'm personally not a fan of that approach.

frigid trench
#

I'll leave it be for now

ornate hare
#

Looks good.

frigid trench
ornate hare
#

In ClickToBig... under if (_isBig) { add DialogController.ShowDialog(Text);

frigid trench
#

Ok, that was accepted

ornate hare
#

Then, go to DialogController.ShowDialogInternal() and add the following line either before or after the current contents:
dialogPanel.gameObject.SetActive(true);

#

Once done, you should be able to run your game and try clicking on your photo.

frigid trench
#

Where is DialogController.ShowDialogInternal?

ornate hare
#

Right there.

#

The short answer is inside DialogController, like the name would suggest.

frigid trench
ornate hare
#

You have to write code inside a method, not in the class definition.

#

If you check your errors, your IDE will tell you this.

frigid trench
ornate hare
#

Great! I'm deliberately trying not to do the work for you and am using plain english to describe these changes in order to encourage that, so that's somewhat intentional on my part.

#

So now, you should be able to run your game and test.

frigid trench
#

Ok, So game played and when I clicked photo, it became large and made sound like normal, but a pop-up about TextMesh Pro came up. The console has the following error.

MissingReferenceException: The object of type 'DialogPanel' has been destroyed but you are still trying to access it.
Your script should either check if it is null or you should not destroy the object.
DialogController.ShowDialogInternal (System.String text) (at Assets/Scripts/DialogController.cs:24)
DialogController.ShowDialog (System.String text) (at Assets/Scripts/DialogController.cs:20)
ClickToBig.set_IsBig (System.Boolean value) (at Assets/Scripts/ClickToBig.cs:21)
ClickToBig.OnMouseDown () (at Assets/Scripts/ClickToBig.cs:31)
UnityEngine.SendMouseEvents:DoSendMouseEvents(Int32)

ornate hare
#

Ahh, got it. So this is due to the object being destroyed on Awake. Remember how you made a copy of the DialogController object in your hierarchy so that there were two of them?

#

We can debug this later, but for now, just delete the second DialogController object and try again.

frigid trench
#

A copy from DialogManager which is delted now?

ornate hare
#

Yes, last time we spoke, you clicked on your DialogController object in your hierarchy and pressed Ctrl+D to make a copy of it.

frigid trench
#

Ah yes

ornate hare
#

Delete that copy; we were going to protect against there being multiple copies of the same object before realizing that you're using a very old foundation.

frigid trench
#

I think it's already deleted

#

I notice that this controller though doesn't have components. Should it?

ornate hare
#

Okay, at the top of your scene hierarchy, type this into the search bar:

frigid trench
ornate hare
#

"dialo" is not "t:DialogController"

#

Type in "t:DialogController"

frigid trench
#

Copy of the photo object

ornate hare
#

Those are the objects that each have a copy of DialogController added to them. Delete those components until there is only one in your scene.

#

You can only have one DialogController.

frigid trench
#

First I took it off photo and tried leaving it on DialogPanel, but the ClickToBig didn't work on photo. Now I have them switched and I have this.

ornate hare
#

Do you not have a gameobject called DialogController with the only copy of the DialogController component on it?

frigid trench
#

Ok, switched and looks the same. Note I assigned the DialogPanel from hierarchy to the respective field in component.

ornate hare
#

Great. You should never have had 3 different dialog systems in your project. This is why we have code to destroy objects in Awake()... if the monobehaviour script that is running isn't the one stored in _instance, it deletes itself.

frigid trench
#

Ok

#

So is the TextMesh Pro error because there isn't an assigned text somewhere?

ornate hare
#

That's not an error. You need to add the essential package files to your project for TMPro to work.

frigid trench
#

Like the dialog itself I mean, not coding

ornate hare
#

Read what the text in the window says, then click on that button when you understand what you're reading.

frigid trench
#

Ok, I imported both and now partially seeing the text (the white object is blocking some). The button function doesn't work, though the Click list section of the Button component is blank (I forget if originally it was DialogManager was here and if so, that would explain it being blank).

ornate hare
#

Okay, so you've started your game and clicked on your ClickToBig object, and you're telling me that it's not doing anything?

frigid trench
ornate hare
#

Oh, okay. I can see "...idiot..." in very bright green text. You're concerned only about the close button, is that right?

#

I'm going to assume that the dialog panel opens correctly when you click on the photo, even though you aren't showing that in your video.

frigid trench
#

Correct, the background pops up once the photo is clicked on the first time

ornate hare
#

Good. Let's make a way to hide that now.

#

In DialogController, add a new method:

public static void HideDialog() => _instance.HideDialogInternal();
private void HideDialogInternal() {
  dialogPanel.gameObject.SetActive(false);
}
#

This will perform the opposite operation from ShowDialog.

frigid trench
#

Looks straight forward (show/hide and true/false)

ornate hare
#

Next, if you want to use your TMP button script and unityevents to close the dialog panel, drag your DialogController object onto the "click" event in your button script. You'll have to remind me if this works, since we're made DialogController a singleton... but you should be able to find the static method HideDialog() in the list of methods that you can call on click.

#

If you can't find the method HideDialog, just change HideDialogInternal to public and it should show up in that list.

frigid trench
#

I found it, but button isn't closing out

ornate hare
#

The word "Missing" does not mean that you have found a thing.

#

Open that dropdown menu and try to find your method.

frigid trench
ornate hare
#

Nice. Click on HideDialog()

frigid trench
#

Yes, that is what I did before

ornate hare
#

Is <Missing DialogController.HideDialog> replaced in the method drop-down on your component?

frigid trench
ornate hare
#

Okay, if that text no longer contains "missing" then you have done it now.

#

Test your game when ready.

frigid trench
#

No, I'm saying I had already did these selections when I said the button wasn't working when I sent 1st screenshot

ornate hare
frigid trench
#

Yes

ornate hare
#

Okay. Do you have a compilation error in your console?

frigid trench
ornate hare
#

Assuming the error filter is enabled, the answer is no.

#

Did you delete that event handler in OnClick() and add a new one, or did you use the old reference? If you didn't delete it, click on the little [-] in the bottom right corner of the drawer and then create a new event handler.

frigid trench
#

Correct, I keep filters off

ornate hare
#

Great - drop your DialogController object into where it says [None (Object)] and see if you can find DialogController.HideDialog

frigid trench
#

It's a video

ornate hare
#

Again, if you can't, change HideDialogInternal to public and look for that method instead. I don't remember how that version responds to static methods.

#

OOH sorry.

#

That's a little bizarre, but try what I said regarding the access modifier on the internal version.

frigid trench
#

Making that public fixed it

#

Thanks

ornate hare
#

No problem - there's something up with your project domain which is manifesting here.

frigid trench
#

You mean beside the old version?

ornate hare
#

I don't know, it's before the scope of building a dialog system.

#

So anyway, I think we're now at a point where your system is pretty robust, but doesn't do any animation.

#

If I'm right about that, I want to ask you: do you understand what we have done up to this point?

frigid trench
#

I understand that the Controller is like a universal remote and the DialogPanel is focused on the panal (black background) and the text itself, making this a subpart of the controller

I believe now we need a new script that will be another subpart of the Controller that controls the character profile animations that instantiate to the left of the dialog box.

ornate hare
#

Well, the Controller is accessible from anywhere without non-static references to it. You can now call DialogController.ShowDialog("some text") from anywhere to make the dialog panel appear with some text. You can also use DialogController.HidePanel() from anywhere to make it disappear. DialogPanel represents the actual panel that shows up in your game, including the text property which obfuscates the TMPro object underneath.

#

As an exception, we pointed that button to HideDialogInternatl() because the button script requires a non-static method. We made the "internal" class public so that you could link to it directly.

#

So now before we continue, does the system work without issues? I mentioned that I think it does, but I'd like for you to confirm that I'm not just projecting and that it does in fact work as you would expect up to this point.

frigid trench
#

Right, tracking that. One concern is I noticed I can change the text of the panel "...idiot..." within the ClickToBig component. While this regarding the photo is fine, other objects will not have the ClickToBig component.

Will I be able to copy/paste line 21 into another script assign to non-growing objects to adjust text?

ornate hare
#

That line will work universally in your whole project. Same thing with DialogController.HidePanel().

frigid trench
#

Ok, that sounds good then. In previous version, I had text type out like a type writer effect. I don't have the code handy, but I imagine that portion can be applied to ClickToBig and the hypothetical other script, right?

ornate hare
#

So actually, ClickToBig should have no control over that. That's a concern for DialogPanel, since it owns the text object.

frigid trench
#

This is where I was able to change

ornate hare
#

Right. That should change what the text is that shows up in your dialog panel, right?

frigid trench
#

Like if I change "...idiot..." here to "anything", then yes, "anything" will show up in-game.

ornate hare
#

Good. That's your end point for game logic. Now whether or not this script remains how you want to structure your game logic in the future is a matter for another time, but I imagine that you'll be opening and closing the dialog panel thousands of times over the course of your game.

frigid trench
#

Yeah, probably. I'm gonna have to jump off for an hour or two for errands, but could be on tonight if you're ok with that.

ornate hare
#

Sure.

#

So, the logic behind how to write text in your dialog panel should only be covered in one place: inside your DialogPanel. You don't ever need to duplicate that code. Just use DialogController to start drawing text and hide the text.

#

ClickToBig should not control how the text is displayed, except through parameters in DialogContoller.

#

Hypothetically, let's say you wanted to be able to select a rate to print the text at, you would only want to expose the parameters to ClickToBig that it should be concerned with. For example, DialogController.ShowDialog("my text", 0.1f, Color.red); Here is an example of providing the text to type, the speed to type it at and the colour to change the text to. This is an example, of course.

#

The part of your code that you will repeat constantly as you build your game should be as simple as possible, leaving the technical details to the system to make happen.

#

So when you're ready, we will add to DialogPanel to describe in greater detail what it means to change text, that being to write stuff out over time instead of all at once.

frigid trench
#

Hello, sorry for being quiet a week. I got back in today and made some comments within the scripts to keep track of my understanding (hopefully these look correct).

#

@ornate hare

ornate hare
#

Hey! In your first photo, your first 4 comments aren't right:
dialogPanel -> the on-screen object
_instance -> the private storage mechanism for the static DialogController object in memory, as in which object DialogController uses indirectly when called in other scripts
Instance the public reference for _istance. This is a property, not a lambda expression. @frigid trench

#

It took a moment for me to realize that "Create field" in your comments refers to the line of code literally defining a field on the object. Usually, "create" means something gets instantiated, but this fits given your use of the word.

#

As a reminder for the last image: IsBig is a property, so performs an action when its value is set. The methods further down in the file OnMouseDown and OnValidate are just routing to it to trigger it when those Unity events get triggered. The reason for ! in the first method is to flip the value from true to false and vice-versa, so creating a toggle on click, but the OnValidate method doesn't have this, else the opposite would happen than whatever is showing in the inspector.

#

I was going to recommend before continuing on with this script to rename a few things, if you prefer this kind of style:

Rename:
DialogController to Dialog
DialogController.ShowDialog(string text) to Write(text)
DialogController.HideDialog() to Hide(text)

This would be completely optional, but the reasons for these changes would be to make the static function read like some of Unity's internal classes, such as Debug.Log or Input.GetKey. If you're familiar with those other static classes, then DialogController is meant to work the same way. Removing the word "Controller" from the name could possibly represent that better.

frigid trench
#

Thanks for the corrections, I updated the comments. As for the renaming, it's probably a good idea, but I'm gonna keep the terms as is for the moment to help keep overall consistency and prevent breaking something because I forgot to switch something somewhere.

Currently the background image is set to it's own canvas object, but when we began redoing this dialog system, the hierarchy had shrunk the image and objects. I was wondering instead of reordering things, would assigning the image to "ShowDialog" in the hierarchy be problematic with other functions? I would remove the image from the canvas, but leave the canvas to exist in the hierarchy.

frigid trench
#

@ornate hare

frigid trench
#

I converted the file to Unity 6 to fix the scripting errors we've run into. As you can see, there seems to be issues of things not transferring over cleanly and I guess I'll have to reassign images and such.