How to make a Dialogue System with Choices in Unity2D | Unity + Ink tutorial 2021

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey everyone my name's Trever and in this  video i'm going to show you how to create   a dialogue system with choices for a 2d game  in unity by the end of this video we'll have   created a system where the player can walk up to  an npc and then talk to them by pressing a button   the system will be able to support any number of  choices and it'll also be easy to add other npcs   while the example in this tutorial is set up for  a metroidvania or platformer styled game the exact   same logic will apply to most other 2d game genres  including top-down games and even many 3d games   with some minor adjustments and of course you can  find the end result of what we're going to create   as well as the starting point if you want to  follow along on github which i'll put a link   to in the description of this video before we  jump into unity and start creating this let's   take a look at what we're going to be doing and  how this dialog system is going to be structured   we'll be using something called ink to create  the actual dialog ink is a scripting language   for writing dialogue that integrates quite  nicely with unity and it even comes with its   own custom editor called inky how it works is that  we're going to write our dialog logic including   things such as making choices using ink we'll  then save that ink file into our unity project   and unity will automatically compile that into  a json file which is just a data representation   of the dialog we wrote then in a c-sharp script we  can turn that json file into what's called a story   object which gives us an easy and intuitive way  to traverse through our dialogue's flow the main   benefit to using inc for writing our dialogue  is that we can keep our dialogues logic such   as making choices mostly separate from our actual  dialogue system we're going to be creating we're   going to keep the dialogue we write pretty simple  for this tutorial but i do have an entire video on   how to write dialogue using ink which i'll put in  the description of this video if you're interested   in digging deeper so that's how ink works but now  let's go over how our dialog system is going to   work and we'll see how ink fits into it first off  we'll have a unity scene with a player and an npc   the npc will have a specific inc json file  attached to them for the dialog flow they're going   to display when they're talked to they'll also  have a box collider on them that acts as a trigger   to detect whether or not the player is in range  of talking to them if the player is within range   will show a visual cue that tells the player that  they can press a button to interact with the npc   once the player presses that button we'll send  off the inc json file for that npc using a c-sharp   script to something we'll call the dialog manager  the dialog manager will be a singleton class   meaning that there can only be one of them in the  project the dialog manager will take that inc json   file turn it into a story object that we can more  easily work with and then display a dialog panel   with the first line of dialogue from that story  we'll also use the dialog manager to keep track   of whether or not the dialog is currently playing  other scripts can then reference that information   and behave accordingly for example our player  controller script will use that information to   freeze the player's movement if dialog is playing  after we've clicked through all of the dialog for   that npc the dialog manager will hide the dialog  panel and then exit the dialog mode allowing the   player to move again as you can imagine there  are a lot of ways to architect this each with   their own pros and cons as i just described we'll  be using a singleton pattern in this tutorial   however something like an event driven system  would also work quite well if you're familiar   with those concepts if you're not though don't  worry since we're not going to be doing that in   this video it's also important to note that with  this setup i'm assuming that each npc will have   their own ink dialog file for organizational  purposes i think this makes the most sense for   the majority of 2d games although it's important  to note that if you're planning on putting all   of your dialogue in a single ink file which may  make more sense for some narrative driven games   there are likely better ways to architect  your dialog system than what i just described   alright enough talking let's jump into  unity and put all this into action   i've set up a few things ahead of time to  help us get started i created a couple icons   we're going to use which are in the project's art  directory then in the scene i have a main camera   which the only thing i've changed here is the  background color there's also some ground which   is just a square sprite that's stretched across  the x-axis with a box collider 2d and rigid body   of type static so that the player collides with  it of course there's also a player which is just   a square sprite with a box collider 2d and dynamic  rigid body to handle collisions the player also   has a very simple character controller 2d script  that will let us move around horizontally and jump   it's also important to note that i've given the  player the player tag which will be important   later in this tutorial and last i have this  manager's game object which has a child game   object called input manager i'll be using unity's  new player input system along with this custom   script to make things a bit easier for this  tutorial if you're unfamiliar with unity's new   player input system all you really need to know  for this tutorial is that when i press a button   for example the jump button it's going to map to  a function in my input manager script the input   manager script is a singleton class that's simply  keeping track of if a button was pressed so that   way i can more easily reference that information  in other scripts across the project if we look   at the input action asset i already have input  actions set up for jump move interact and submit   we'll also be using something called text mesh pro  in this tutorial which is just a more flexible way   of creating text in unity that's become somewhat  of an industry standard it can be installed by   going to window package manager make sure unity  registry is selected from this drop down and then   search for text mesh pro hit install and then once  installed go to window text mesh pro import tmp   essential resources then click import again and  once this finishes you should see a text mesh pro   folder show up in your assets directory of course  we'll need to install the ink unity plugin as well   you can find this on the unity asset store which  i'll put a link to in the description of this   video once you've added the asset to your account  you can go to window package manager select my   assets from this drop down and then you should  see ink unity integration as one of the options   select it and then click import you can deselect  demos if you want to but i'm going to leave it   selected and then hit import again you should see  a folder called inc show up in your project assets   after the import completes you'll also likely  want the custom editor for ink called inky this   can be downloaded from inklestudio's website which  i'll also put a link to in the description of this   video finally just to make sure everything is set  up properly let's create a dialog folder and in   that folder i'll right-click create ink which will  create a new ink file we'll just call this one   test and then if you have ink's custom editor  installed you should be able to double click it   to open it up in the inky editor we'll just  write some plain text in this file and save it   then back in unity we can click on the compile  json file and in our inspector we should see some   data that looks like this you'll notice that we  can see the two lines that we added as part of the   data and if we switch to selecting the ink file  we actually get a nice editor where we could play   through the dialog if you're missing the compiled  json file or the data just looks off you can right   click on the ink file and then select recompile  ink which should hopefully fix the issue if you   get this far it's safe to assume that everything  is working as intended and just to note if you   download this project from github and use the  starting point branch all of this should be set   up to this point in the video now that we're all  set up let's create an npc to talk to in the scene   we'll create a new empty game object called npc  then we'll create a child object that's going to   be a 2d object sprites square and then we'll call  this one visual this will represent the visual for   our mpc we'll go ahead and change the scale of the  visual just a bit and we'll also change the color   to a yellow color then we'll create another empty  game object under the npc and call it trigger this   object is going to handle detecting our player and  triggering the dialog ui we'll put a box collider   2d on this one and make sure the trigger checkbox  is checked and then we'll change the scale of the   box collider to be a bit bigger than the visual  last we're going to create one more 2d sprite   object underneath the npc object and we'll call  this one visual cue we'll position this right   above the npc visual for the sprite we're just  going to use this icon that i made ahead of time   we'll make the icon a bit smaller and then i'll  move the entire npc down to match the ground   that's good for the npc but now we need to write  a script to handle showing the visual cue as   well as starting the dialog with an ink file  when the user presses a button to keep things   organized let's create a new folder in our scripts  folder and call it dialog next let's create a new   c-sharp script and call it dialog trigger then  double-click it to open it up in your code editor   first let's get rid of these placeholder  start and update methods taking this one   step at a time let's first focus on making  the visual cue show up only when the player   is in bounds of the box collider we'll first  create a gameobject variable for the visual cue   and use serialize field so that way it shows up  in the inspector then let's create an awake method   and we'll initialize the visual cue to be inactive  at the start of the game which will make it hidden   we'll also need to keep track of if the player  is in range so let's create a boolean called   player and range to keep track of this we'll add  that to our awake method and then initialize it as   false we can use these built-in unity functions  called ontrigger inter 2d and ontriggerexit2d   to detect when another collider enters or  exits the collider of the game object that   this script is attached to now this will detect  any other collider which includes the ground   meaning that we need to make sure that the other  collider belongs to the player because we have the   player tagged as player in the unity inspector  we can check the tag of the incoming collider   to make sure it's the player from there when the  player enters we set player and range to be true   and when they exit we set player and range  to be false then in the update method we can   add a simple if statement on whether or not to  show the visual cue if the player is in range   we'll set it to be active to show it otherwise  we'll set it to be inactive to hide it last if   we're in range we also want to listen for  if the player presses the interact button   if they do we're going to eventually call the  dialog manager with the appropriate inc json file   of course we haven't created the dialog manager  yet but we can get things stubbed out for right   now let's create a serialize field for the inc  json which is going to be of type text asset then   in our update method under the case where the  player is in range we'll check for if the player   has pressed the interact button eventually we'll  have a call to a method in our dialog manager but   for now we'll just print out the ink json text to  make sure this is working now back in unity we'll   drag this new script onto the trigger game object  that we created under the npc for the visual   cue slot we'll drag in our visual cue object and  finally for the inc json for now we'll drag in the   json data from the test ink file that we created  during the setup part of this tutorial now if we   hit play you can see that the visual cue only  shows up when the player is near the npc and if   i click the i button to interact with the npc we  can see that our json data file is being printed   out which is exactly what we want next we need  to create a dialog panel to display our dialog   in the scene hierarchy we'll right-click ui canvas  which will create a new canvas and event system   click on the canvas and then under the canvas  scalar component select scale with screen size   for the ui scale mode this will make it so  that our ui objects scale with the screen size   then right click on the canvas go to ui and  then panel to create a new panel we'll call this   dialog panel then we'll resize it a bit and then  position it so that it's at the top of the screen   to do so we can click on this box here and then  hold alt and shift and then click on the top   center option this will move and anchor the panel  to the top center i'm going to round off the width   and height to be 750 by 150 then we'll change the  y position to negative 10 to give it a little bit   of space from the top next we'll right click on  the dialog panel and then go to ui text text mesh   pro to create some text we'll call this dialog  text and change the font size to 32 and then make   sure the alignment is horizontally to the left  and then vertically to the top we'll then position   this to the top left of the panel a position  x of 20 position y of negative 20 and then   a width of 200 and height of 50 seem to be just  fine and we'll also need to drag out this yellow   border to fill the panel then for one final touch  we're going to right click on the dialog panel go   to ui and then image and then we'll name this one  continue icon we'll change the image to one that i   made ahead of time called continue icon then we'll  change the size to be a width and height of 25   anchor the position to the bottom center and then  give it a position y of 15. that will do for the   first cut of the dialog panel but one last thing  if you're using unity's new input system like i am   you'll need to click on the event system game  object and then click this replace with input   system ui input module button we'll come back to  this later to configure it but if you don't do   this now you'll get an error later when we try to  play the project so next let's create the dialog   manager script which will manage and display the  dialog that's written to the ui we just created   in the scripts dialog folder will create a  new c-sharp script and call it dialog manager   then double-click it to open it up as i mentioned  before this will be a singleton class so let's set   that up we'll create a static instance of the  dialog manager then in the awake method we'll   initialize that instance and last we'll create  a public static get instance method that will   return that instance one other thing that's good  to do when creating a singleton class is to give   ourselves a warning or error if there's ever more  than one of these in the scene since by design   that should never happen so i'll add a warning  log if we tried to initialize it when the instance   has already been created next let's create a few  variables that we're going to need we're going to   need access to the dialog panel so that way we can  hide or show it depending on if dialog is plain   so i'll add a serialized field game object for  that we'll also need access to the dialog text so   we can set it according to the ink file contents  we want to display this will be of type text mesh   pro u gui which to use will need to declare that  we're using tm pro in our using statements we'll   also need to keep track of the current ink file  to display we can do this in a private variable of   type story called current story to use the story  type we'll need to add another using statement   to our file called inc dot run time and finally  we'll also want to keep track of whether or not   the dialog is currently plain so for now i'll  create a private boolean called dialog is plane   then let's create a start method and make  sure dialog is plane initializes to false   and we'll also make sure to set the dialog panel  to be inactive now let's create a method to enter   dialog mode which we can call from our trigger  dialog script it'll be a public method that   takes in a text asset which is our inc json file  the first thing we need to do is set the current   story of the dialog manager to a new story that's  initialized using the ink json text that's passed   in then we should set dialog as plane to be true  and then set the dialog panel to be active so it's   shown now we need to display the first line of  our dialog we can see if there's dialogue to be   played by accessing the boolean can continue for  our current story if there is we'll set the dialog   text dot text to equal current story dot continue  the continue method not only gives us the next   line of dialogue but you can kind of think of it  like it's popping that line off of a stack as such   next time we call currentstory.continue it'll give  us the next line of dialogue and so on of course   we should handle if the story can't continue which  ultimately means that an empty inc json file was   passed in in that case we'll call a method that  we're going to create called exit dialog mode   we'll then create a private method for this  called exit dialog mode when we exit we should   set the dialog as plane to be false and then  set the panel to be inactive again so that way   it no longer shows and we'll also reset the dialog  text.text to an empty string for good measure next   let's finish this off by creating an update method  to handle traversing the logic of the ink story   first off if dialog isn't playing at all we'll  return right away since we only want to update   when dialog is playing otherwise we'll check for  player input on if they've pressed the submit   button now what we need to do here is actually the  exact same as what we did in the enter dialog mode   method i'm going to create a new method called  continue story and then we'll move this chunk of   code in enter dialog mode into continue story now  i can call continue story and enter dialog mode as   well as in the update method when the player  presses the submit button and that should be   it for the first cut of the dialog manager now  let's hop back into unity and see if this works   under my manager's game object i'm going to  create an empty game object and call it dialog   manager then i'll drag the dialog manager script  onto it next we just need to drag the dialog panel   and dialog text into the appropriate slots and  finally we need to hook up our trigger script to   the dialog manager so we can actually see this  work in the scripts dialog folder i'll double   click on the dialog trigger script to open it  up all we need to do here is switch out this   line where we're printing out the ink json with  a call to the dialog manager so i'll replace this   with dialog manager.getinstance.enter dialog mode  and then pass in the inc json textasset variable   now let's go back into unity and hit play  we'll see that if i'm near the npc and i   hit the i button to interact a dialog panel is  displayed with the text from our test ink file   that we wrote earlier you'll notice a few things  though first the player can still move around and   second if we click i again while the dialog is  playing it just resets back to the first line   so let's fix those things real quick back in the  dialog manager script i'm going to change dialog   as plane to be public then i'll add this little  bit after the variable which makes it read only   to outside scripts this is because i only want  outside scripts to be able to read the value   and not be able to modify it and just a quick note  you could also do this for the instance variable   if you wanted to which would allow you  to get rid of the get instance method   then we'll open the character controller 2d script  that's handling all of the player movement in this   character controller all of the movement happens  in fixed update so the easiest way for me to   freeze the player is to check if dialog is playing  and then return if it is obviously depending on   your player controller script freezing the player  movement may be a bit different or more involved   next i'll switch over to the dialog trigger script  in the update method i'm going to modify this to   say if the player is in range and dialog is not  playing now if dialog is playing the visual cue   will become inactive and we also won't be able to  trigger dialog again until the current dialog has   finished if we hit play with these changes we can  now see that when we talk to the npc the player is   frozen and we also can't restart the dialogue  midway by pressing the interact button again   but there's still one last thing that we need to  fix you probably notice that when the dialogue   finishes the player jumps in our case this is  because the jump button and the submit button   are both the spacebar which is pretty common in  metroidvania and platformer style games and we're   unfreezing the player at the same time we're doing  the final submission so both input actions fire   off at the same time we can fix this by waiting  a short amount of time before exiting the dialog   back in the dialog manager script we'll scroll  down to the exit dialog mode method and we're   going to turn this into a co-routine first  we'll change the return type to an ienumerator   then we'll add this line that says yield return  new weight for seconds 0.2 f this line tells our   script to wait for 0.2 seconds before continuing  last anytime we call this method which in our case   is just once in the continue story method we need  to call it like this where we put the function   call inside of a start co routine call now if we  play this again we can see that after the dialog   finishes the player no longer jumps now that  our dialog system is working for lines of dialog   let's do something more interesting and add the  functionality for the player to make choices   under the dialog panel we're going to  make a new empty game object called dialog   choices right click on dialog choices and go to  ui button text mesh pro and call it choice 0.   this creates a button as well as a child object  which is our text mesh pro text let's click on the   text object and change the font size to 18 and  make sure the alignment is horizontally to the   left and vertically centered we'll also change the  text color to white to match the dialog panel text   then on the button we'll change the normal color  to have a transparency of 0 and the selected color   to be a bit darker then we'll position the dialog  choices parent object to be on the far left and   directly below the dialog panel next we'll select  choice 0 in the scene hierarchy and then use the   shortcut ctrl d to duplicate it we'll call the  new one choice 1 then we'll position choice 1 to   be a bit below choice 0 in the scene to navigate  through these choices we're going to use unity's   built-in event system just to note if you're not  using unity's new input system what i'm about   to do is going to look slightly different we'll  click on the event system game object in the scene   then under action asset select the input action  asset that contains your control scheme that   you'll be using for the ui in my case it's called  controls which i showed briefly during the project   setup portion of this video how it automatically  filled out is exactly what i'm looking for   which is that to move it's going to use the same  controls the player uses to move and to select the   choice it's going to use the submit button we'll  also uncheck the deselect on background checkbox   and now let's move back into the dialog manager  script and add some code to support making choices   we'll create a section for the choices ui and  we'll add a game object array called choices   this will allow us to have any number of choices  as long as it's supported by the ui we'll also add   a text mesh pro u gui array and call it choices  text which we'll use to keep track of the text for   each choice in our start method we'll initialize  choices text to be an array of the same length as   our choices then for each choice in the choices  array we'll initialize the corresponding text   using an index for that choice so that they  match we can get the choices text through   the git component in children method since  the text is a child object for each choice   and don't forget to increment our index after  each iteration of the loop now we need to display   the choices appropriately based on the ink story  let's create a new method called display choices   first we'll get the list of choices if there  are any from our current story we can do this by   calling current choices on our current story  which will return a list of choice objects   next we should do a bit of a defensive programming  check and just make sure our current ui can   actually support the amount of choices that are  coming in if there are more current choices than   in our choices array which represents how many  choices our ui can support we'll log in error this   next part can be a bit tricky but we need to loop  through all of our choice game objects in the ui   and display them according to the current choices  from the ink story we'll start by declaring an   integer index and setting it equal to zero then  we'll loop through and enable the choice objects   in our ui up to the amount of current choices from  the ink story for each choice in current choices   we'll set our choice ui game object for that index  to be active and also set the ui text to be equal   to the choice text and then of course increment  our index that loop will put our ui and the   current choices in sync but we might have leftover  choices in our ui that we need to hide in a for   loop we'll initialize an integer i as the index we  left off on in the previous loop then we'll loop   while i is less than the number of choices our  ui can support and of course we'll increment i   after each iteration then inside this loop we'll  set any other choices that are in our ui to be   inactive and of course we actually need to call  the display choices method that we just created   we can do that right in our continue story method  right after we've set the text for the dialog line   now back in unity if i go to the dialog manager  in the scene hierarchy i can add on to our choices   array and drag in the choice buttons from the  ui i'm also actually going to create one more   choice so that we have three choices in total  also the test ink file that we've been using   doesn't have any choices let's create a new ink  file call it pokemon and then open it up in the   inky editor by double clicking it i'm going  to paste in an example where we're prompted   to choose a pokemon and then at the end we get  some text telling us what we chose if you're   interested in how this syntax works i have another  video where i go over this in great detail which   i'll put a link to in the description of this  video be sure to save this and then in our npc   trigger game object let's drag in the pokemon  json file as the inc json to replace the test one   if we play this we'll notice that we actually  can't select any of our choices this is because   in unity's event system we need to define a first  selected choice back in the dialog manager script   let's add a using statement for unity's event  systems then it's kind of strange how we need   to go about doing this perhaps in the future there  will be a better way to do this but for right now   the best way that i could find was by setting  the first selected choice using a co-routine   so let's create that real quick first we'll create  a private method with an ienumerator return type   called select first choice in it we need to first  set the event system's current game object to   be null then we have to wait for the end of the  frame and finally we can set the new selected game   object which in this case will always be our first  choice game object again from what i could gather   unity's event system requires it to be cleared  first and then have the selected object be set   in a different frame but if you know a better way  to do this be sure to let me know in the comments   for this video finally we can call this co-routine  at the end of our display choices method   if we go back into play mode we can now select the  choice using the arrow keys but you'll notice this   time that when we select the choice it actually  ends the dialogue instead of continuing on   this is because we need to inform our story object  that a choice was selected back in the dialogue   manager script we'll add a public method called  make choice which is going to take in an integer   called choice index then to inform our story of  which choice we've selected we can do current   story dot choose choice index and then pass in  the choice index back in the unity inspector   for each of our choice buttons we can simply add  an on click method then we can drag in the dialog   manager and then select the make choice function  that we just created for choice zero the choice   index will be zero for choice one it'll be one  and so on if we go back into play mode we'll see   that the dialog system is functioning just like  we'd want it to with choices and that's it for   the dialog system the last thing i'll show is  how easy it is to add new npcs with this setup   we'll create a new folder called prefabs and then  drag in the npc game object to create a prefab   of course we'll zero out its positional values for  the prefab then i'll drag the npc prefab back into   the scene position it change its color and then  switch out the ink json file to our test file   from before now if we go back into play mode  we've got a new npc with different dialogue   and that's it thank you so much for watching  and if you enjoyed this video please give it   a thumbs up so more people see it and if you  want to see more from me be sure to hit the   subscribe button as well i know this was  a long video so if you made it to the end   i just wanted to say thanks again have an  excellent day and i hope this was helpful
Info
Channel: Trever Mock
Views: 43,335
Rating: undefined out of 5
Keywords: trevermock, trevormock, trevor, trever, mock, unity tutorial, unity dialogue system, unity dialogue, unity dialogue system with choices, unity dialogue box, dialogue system unity 2d, dialog system unity, unity dialog system, unity dialog system with choices, unity dialog system tutorial, unity dialogue system tutorial, ink unity integration, ink unity plugin, unity tutorial 2021, unity tutorial 2021 2d, unity 2d, unity2d, how to make a dialogue system with choices, ink dialogue, ink
Id: vY0Sk93YUhA
Channel Id: undefined
Length: 29min 45sec (1785 seconds)
Published: Mon Sep 20 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.