JRPG Dialouge/Chat System...

Started by
12 comments, last by Khatharr 11 years, 3 months ago

I have been mulling over how i should do a chat system similar to the one seen in final fantasy 7.

Rendering it isn't an issue (that part works). Mainly I am concerned with how/where I should store it.

Currently my game has a sqlite database that works quite nicely. Also im using unity pro but really that doesnt matter for something like this.

I have identified several requirements like:

-Dialouge between two characters during a "cutscene", but i do not have notion of cutscenes quite yet. (i suspect it will just be a C# script per cutscene that is fetched by the game although when im not 100% sure)

This is triggered by entrance into the scene, and then removed from the database once it is run.

-Then there is a conversation when a player talks to an NPC, which can be influenced by the current event going on in the world.

This is triggered by interacting with the NPC.

-Then there are other arbitrary triggers that can start a dialouge (ie as the player leaves a scene he is stopped and someone talks, a monster says something before fighting, a treasure chest, a door, etc).

My question is general, what fits in my database and what doesn't. Does every bit of dialouge connect to the character that is speaking it? does my table for a piece of dialouge contain foreign keys of all the characters involved? bools of what triggers it (a scene, a trigger, or an interaction with an NPC? Do i record arbitrary triggers in the database so that the game knows a trigger connects to a dialouge?

I am not 100% convinced i have the correct approach, any advice or reference material would be great.

Thanks,

-J

------------------------------

redwoodpixel.com

Advertisement

I personally would aim to make the actual dialog run off of some markup format to allow for an editor. My approach would be as such...

Create a new "Dialogs" table. Basically id, npc_id, conditions, script.

use npc_id as a reference to the NPC table and the ID of the NPC this dialog is for

use conditions as a varchar(255) index of some major influence words (pre-war, friendly, parents-alive)

This would give you a pretty easy means to get a script for a potential dialog scene. Inside that script I would probably use xml serialized objects that could chain multiple "say -> respond" events. Basically you could just have each object be a list of conditions, what is said, what your response options are. Build a little parser that finds the condition matches, display the text get the response and so on. Something along the lines of...


    public class NPCDialogEvent
    {
        // If your not accepting inputs you can use this to chain next dialog event without needing input
        public List<NPCDialogEvent> childEvents = new List<NPCDialogEvent>();

        // This dictionary would serve where you could pass key = value requirements
        public Dictionary<string, string> conditions = new Dictionary<string, string>();

        // This is what is said on this event.
        public string dialogStatement = 'Hello mr player man';

        // If the player says "Yes" to this
        public List<NPCDialogEvent> yesEvents = new List<NPCDialogEvent>();

        // ...
    }

Basically the idea here is that when you initiate this conversation it would search through the SQL DB and load the NPCDialogEvent object that matches the table's conditions. Say it's your neighbor, it's pre-war your friendly and your parents are alive. So the NPCDialogEvent contains a simple little walk through story. This would be a childEvent.

Later on in the game maybe things change and you'll get a different dialog, maybe this time you have to answer some questions. The dialogStatement would ask a question and display answers. You would just add conditional NPCDialogEvent's to the respective lists. When the player chooses an answer, the game would find the best conditional match and go from there.

Hopefully that at least gives you some idea's. May not have been the answer you where looking for but that's the approach I would take.

Dan Mayor

Professional Programmer & Hobbyist Game Developer

Seeking team for indie development opportunities, see my classifieds post

Once upon a time I may have accidentally tripped and fell and landed on a decompiler and happened to see that in their older games Square would trigger the dialog routine with a pointer to the dialog instructions for that particular conversation. Most of the dialog was stored in large groupings littered around the place.

Or so I heard.

I tend not to mess with formal databases, but if you just let the logic sort out which strings are required then you can grab them one at a time as you need them. Does your database allow tiered storage (or whatever it's called)? In other words, can you store a database entry that is a collection of database entries? If so then you could collect your strings into groups appropriate for the NPC/Event/Whatever that you're interacting with.

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.

Hey Guys,

Thanks for the replies.

You guys present two very different approaches, and I think the first approach is perfect for my first demo/proof, but a hybrid of the two sounds like something i should work towards.

When I think about it, script mania ala ff7 totally makes sense, if i really wanted to i could run a script per scene and manage conversations that way. however that seems like it has the possibility of becomming spaghetti code. Thanks for the insight into that! one important fact about that method is it worked well.... lol.

I do like the idea of minimizing what is in the database. Perhaps my first iteration could be the first proposal of a treelike structure.

and i could create forms for inserting chat (i actually wanted to extend the unity editor with a tree editor but i just cant make time for that, so a basic form will have to do).

On a second iteration of the chat i could add a reference script and then have delegate functions that are called before, after, and during a dialouge like so


    public class NPCDialogEvent
    {
        // If your not accepting inputs you can use this to chain next dialog event without needing input
        public List<NPCDialogEvent> childEvents = new List<NPCDialogEvent>();

        // This dictionary would serve where you could pass key = value requirements
        public Dictionary<string, string> conditions = new Dictionary<string, string>();

        public delegate void OnBeforeChat(NPCDialogEvent current);
        public delegate void OnDuringChat(NPCDialogEvent current);
        public delegate void OnAfterChat(NPCDialogEvent current);

        // This is what is said on this event.
        public string dialogStatement = 'Hello mr player man';

        // If the player says "Yes" to this
        public List<NPCDialogEvent> yesEvents = new List<NPCDialogEvent>();

        // ...
    }

this would allow for animations to be played and scripted, while allowing the scripting to be able to hook into the conditions.

------------------------------

redwoodpixel.com

I had intended to suggest a hybrid approach. Most JRPGs I've seen use a kind of markup language for displaying text with escaped characters representing dialog or timing events and allow a dialog to return state codes indicating user selections in interactive situations. I was just commenting on where to store the data. Like I said, I don't know about formal databases, so whatever's clever in that respect. You guys would know better than me.

If I recall correctly the game I'm thinking of examined the game flags and etc and then just used that to feed the dialog proc a pointer to a starting point. After that the dialog continued in a data-driven style. The markup language included things for displaying character names, triggering sound or music effects, changing the font style, waiting for a keypress, presenting a choice and returning a value or setting a flag/variable based on the selection, etc, etc.

The dialog calls were triggered by 'events' which were NPCs, triggers based on passing a line on the map, treasure chests or other things of that nature. There was some bytecode-style scripting that started when an event was triggered and the dialog call was within the script, so that based on the results of the dialog the game could remove or add characters or items. I was actually surprised how similar it was to the method used in RPG Maker, although it was a lot more optimized since it was a console game (string tables, condition tables instead of embedding everything in the event concerned).
void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.

Ah yes, and i didnt reply to your comment about database entries, I think the best way would be to keep all NPCDialouge data in one table, and then each entry stores its parent id and any children IDs (hmm) (and maybe its root id for query optimization).

The key for that kind of setup is the fetching query that checks conditions, something i'm sure i will not get right the first try.

My "API" for my event scripts will likely stem from my main game script, which will provide a set of commands to do the stuff you mentioned.

------------------------------

redwoodpixel.com

The RMXP method was to create a set of 'canned' dialog reactions inside the main game script in a section called "Interpreter". Events had different kinds of triggers that were configurable in the main RMXP UI and those were basically converted into Ruby objects and marshalled for use at runtime. When an event trigger happened the event's internal store of Interpreter commands would get executed, so it was basically a doubly scripted system, which made editing events in the UI very easy but managing events from the Ruby side (which there was no real need for) was very clumsy since the event objects got incredibly complex.

Example of 'Interpreter' method:


class Interpreter
  #--------------------------------------------------------------------------
  # * Show Text
  #--------------------------------------------------------------------------
  def command_101
    # If other text has been set to message_text
    if $game_temp.message_text != nil
      # End
      return false
    end
    # Set message end waiting flag and callback
    @message_waiting = true
    $game_temp.message_proc = Proc.new { @message_waiting = false }
    # Set message text on first line
    $game_temp.message_text = @list[@index].parameters[0] + "\n"
    line_count = 1
    # Loop
    loop do
      # If next event command text is on the second line or after
      if @list[@index+1].code == 401
        # Add the second line or after to message_text
        $game_temp.message_text += @list[@index+1].parameters[0] + "\n"
        line_count += 1
      # If event command is not on the second line or after
      else
        # If next event command is show choices
        if @list[@index+1].code == 102
          # If choices fit on screen
          if @list[@index+1].parameters[0].size <= 4 - line_count
            # Advance index
            @index += 1
            # Choices setup
            $game_temp.choice_start = line_count
            setup_choices(@list[@index].parameters)
          end
        # If next event command is input number
        elsif @list[@index+1].code == 103
          # If number input window fits on screen
          if line_count < 4
            # Advance index
            @index += 1
            # Number input setup
            $game_temp.num_input_start = line_count
            $game_temp.num_input_variable_id = @list[@index].parameters[0]
            $game_temp.num_input_digits_max = @list[@index].parameters[1]
          end
        end
        # Continue
        return true
      end
      # Advance index
      @index += 1
    end
  end

Example of actually setting up an event in the UI:

[attachment=13010:rmxp.JPG]

The improvements in the console game were that the script commands were written in a proprietary bytecode stored in a table. Events were placed on the map and initialized with a specific set of triggers linked to specific entry points in the table. Strings were stored separately in other tables and referenced by the scripts to display dialog. This made the events a lot more lightweight than an RMXP event would be since the logic contained in the event object was very small and just included different entry points to the main logic table.

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.

Very cool....

------------------------------

redwoodpixel.com

Not quite sure all of the triggers are really as effective as they might seem. In actuality the dialog scenes are just text (maybe audio and video interpretations of said text). Seems like the idea of executing logic based on inputs coming from a dialog scene itself is a bit of an outdated way of doing it. Try not to consider so many of the possible options that any dialog could have or need and focus more on what your game needs. Assuming you use good object oriented coding practices it would be easy to get going, even without a database server.

Consider your overall design a little more minimalistically. You are working on a dialog engine for your game, it should be a code library that can be used from an editor and in your game to handle dialog scenes. Dialog scenes are just a collection of potential conversation points depending on some rather simple conditional statements if you design it right. With that in mind you would be looking to develop a system that could load a script, compare the conditionals to see if this NPC will even talk to you. If so scan the collection of child events comparing conditions and select the next event. Say something, either scan the next collection or wait for a response and scan the next collection. So the basic flow of events for a dialog scene would be...

1 Open the dialog file

2 Compare root DialogEvent object's conditions to see if we'll talk

3 If we're going to talk figure out which event in the collection is best (scan through them, compare conditions, somehow pick one)

4 Say something

5 Wait a few seconds or wait for input

- Back to step 3 with the this event's child collection

I personally would try to limit any coding you would have to do in the dialog script to make it easier on creating an editor later on and less exposed to hacking. So the engine at code level would actually do all the comparisons. The dialog scene conditions would just be key names that refer to a referenced value in the engine and the target value. If this doesn't cover enough add in a list of advanced conditions that can contain more variables and so on.the key here is that the script file is just all variable references and values. Then put your code in the engine. Since the engine will convert the file into an object tree for you, you can then use these lists of variables to calculate scores or select options. Unless you want to allow the community to add some really off the wall code conditions there's really no reason you can't just use variables as token names, flags and selections.

I've always been a fan of JRPG's and have considered doing dialog engines and editors before. I might be interested in providing a little code and technical assistance if your interested. You can find all of my relevant contact information on gravidar, feel free to look me up when your around. http://en.gravatar.com/daniwan

Dan Mayor

Professional Programmer & Hobbyist Game Developer

Seeking team for indie development opportunities, see my classifieds post

I decided to go the scripting route, i sense it is not as scalable and user friendly as some of the other ways, but it gives me the most flexability and i have used C# delegates to provide OnComplete and other methods for the scripts to submit methods to.

------------------------------

redwoodpixel.com

This topic is closed to new replies.

Advertisement