This is a modular Unity dialogue system based on MVC architecture. The system employs interface segregation and controller patterns to separate dialogue flow control from specific implementations, achieving high extensibility and maintainability.
- MVC Architecture: Logic and UI decoupling through Control-View separation
- Interface-based Design: Feature extension through IBranchingDialogue and IVoiceDialogue interfaces
- Data-Driven: Dialogue data management using ScriptableObject-based DialogueSO
- Event System: Complete dialogue lifecycle events (start, end, line change, etc.)
- Automated Components: RequireComponent attributes ensure automatic addition of necessary components
public class DialogueControl : MonoBehaviour
{
// Core Events
public event EventHandler OnDialogueStarted;
public event EventHandler OnDialogueEnded;
public event EventHandler<DialogueLineChangedEventArgs> OnDialogueLineChanged;
// Core Methods
public void ShowDialogue()
public void ShowNextLine()
public void SetDialogueSO(DialogueSO)
public void GoDialogueSOToLine(DialogueSO, int)
}public class DialogueController : MonoBehaviour
{
protected DialogueControl dialogueControl;
public virtual void StartDialogue()
public virtual void SkipDialogue()
}[CreateAssetMenu(fileName = "DialogueSO", menuName = "Scriptable Objects/DialogueSO")]
public class DialogueSO : ScriptableObject
{
public string characterName;
public Sprite Character_Image;
public List<string> dialoguelinesList;
}The system consists of the following core components:
- DialogueSO - Dialogue data container (ScriptableObject)
- Stores dialogue content and character information
- Supports character names, portraits, and dialogue text
-
DialogueControl - Core control class
- Manages dialogue flow and content display
- Provides an event system for monitoring dialogue states
-
DialogueController - Dialogue controller base class
- Provides common functionality and interfaces
- Can be inherited to implement different types of dialogue controllers
Derived controllers:
- LinearDialogueController - Linear dialogue implementation
- BranchingDialogueController - Branching dialogue implementation
- VoiceDialogueController - Voice dialogue implementation
To maintain a good code structure, use IBranchingDialogue for branching dialogues and IVoiceDialogue for voice dialogues. If you need both, you can implement both interfaces. You can customize the interface or directly inherit from the DialogueController class to implement your own dialogue controller. It is recommended to add interfaces to maintain a good code structure.
- DialogueControlView - User interface control
- Responsible for the visual presentation of dialogue content
- Implements typewriter effect and user interaction
- In the project panel, right-click -> Create -> Scriptable Objects -> DialogueSO
- Set dialogue content:
- Character name
- Character portrait (optional)
- List of dialogue lines
Hierarchy:
├── DialogueSystem
│ ├── DialogueControl (Add DialogueControl.cs)
│ └── DialogueControlView (Add DialogueControlView.cs)
└── Canvas
└── DialoguePanel (UI panel, set as DialogueControlView's dialogue panel)
└── Button (Dialogue button, set as DialogueControlView's button)
└── Text (TMP) (Dialogue text, set as DialogueControlView's text component)
- Assign the created DialogueSO to the
dialogue_SOfield of DialogueControl - Assign DialogueControlView to the
dialogueViewfield of DialogueControl - Configure UI component references for DialogueControlView:
- Dialogue panel
- Next line button
- Dialogue text component
- Typing speed and other parameters
Based on your requirements, choose to add one (or more) of the following:
- LinearDialogueController: Regular sequential dialogue
- BranchingDialogueController: Branching dialogue with options
- VoiceDialogueController: Dialogue with voice acting
- CustomDialogueController: Custom dialogue controller (you can write your own!)
Dialogue data container that stores dialogue content and character information:
public class DialogueSO : ScriptableObject
{
public string characterName; // Character name 1
public string characterName2; // Character name 2
public Sprite Character_Image; // Character portrait 1
public Sprite Character_Image2; // Character portrait 2
public List<string> dialoguelinesList; // List of dialogue lines
}Core control class that manages dialogue flow:
Important public methods:
ShowDialogue()- Display dialogueShowNextLine()- Show next line of dialogueSetDialogueSO(DialogueSO)- Switch dialogue dataGoDialogueSOToLine(DialogueSO, int)- Jump to a specific dialogue lineSkipDialogue()- Skip current dialogue
Important events:
OnDialogueStarted- Dialogue start eventOnDialogueEnded- Dialogue end eventOnDialogueLineChanged- Dialogue line change event
UI control class responsible for the visual presentation of dialogues:
Important public methods:
ShowDialoguePanel()- Show dialogue panelHideDialoguePanel()- Hide dialogue panelTypeDialogueLine(string)- Display text with typewriter effectCompleteCurrentLine()- Immediately complete current typewriter effectSetTypingSpeed(float)- Set typing speed
The most basic form of dialogue, displays dialogue content in sequence:
// Add LinearDialogueController to an object in the scene
public class LinearDialogueController : DialogueController
{
// Automatically start dialogue in Start()
private void Start()
{
StartDialogue();
}
}Provides multiple dialogue branch options:
public class BranchingDialogueController : DialogueController
{
// Need to configure dialogue options
[SerializeField] private List<DialogueOption> branchOptionsList;
[SerializeField] private GameObject optionsPanel;
[SerializeField] private GameObject optionButtonPrefab;
[SerializeField] private Transform optionsContainer;
// Important methods
public void ShowDialogueOptions() // Display dialogue options
public void ChooseOption(string) // Select dialogue branch
}Dialogue with synchronized voice playback:
[RequireComponent(typeof(AudioSource))]
public class VoiceDialogueController : DialogueController
{
[SerializeField] private AudioSource audioSource;
[Serializable]
public class DialogueAudioClip
{
public int lineIndex; // Dialogue line index
public AudioClip audioClip; // Corresponding voice clip
}
[SerializeField] private List<DialogueAudioClip> dialogueAudioClips; // Mapping between dialogue lines and voice clips
private Dictionary<int, AudioClip> DialogueClipDictionary = new(); // Dictionary for quick lookup of voice clips
// Important methods
// PlayVoice(int lineIndex) - Play voice for a specific index
// OnDialogueLineChanged - Subscribe to dialogue line change events to play voice
}Usage:
- Add VoiceDialogueController to a game object
- Ensure the game object has an AudioSource component (will be added automatically if missing)
- Configure dialogue line indices and corresponding voice clips in the Inspector
- The system will automatically play the voice clip for the corresponding index when a dialogue line changes
(joke)
- Custom dialogue line templates: You can create custom dialogue line templates to implement more complex dialogue content.
- Extension of dialogue line templates: You can extend dialogue line templates to implement more complex dialogue content.
- Combination of dialogue line templates: You can combine multiple dialogue line templates to implement more complex dialogue content.
- Combination and extension of dialogue line templates: You can combine and extend dialogue line templates to implement more complex dialogue content.
- Combination of combinations and extensions of dialogue line templates: You can combine, extend, and combine multiple dialogue line templates to implement more complex dialogue content. (smiley face)(hahaha,just joke,all depends yourself)
Implement custom behavior by subscribing to events:
// Listen for dialogue start and end events
dialogueControl.OnDialogueStarted += OnDialogueStarted;
dialogueControl.OnDialogueEnded += OnDialogueEnded;
// Listen for dialogue line change events (especially useful for voice playback)
dialogueControl.OnDialogueLineChanged += OnDialogueLineChanged;Configure branching dialogue options in the Inspector:
- Create a DialogueSO for each branch
- Add options in the BranchingDialogueController
- Set option text, key value, and corresponding DialogueSO
In branching dialogues, you can automatically display options after dialogue ends:
// Add this in Awake or Start:
if (dialogueControl != null)
{
dialogueControl.OnDialogueEnded += (sender, args) => {
ShowDialogueOptions();
};
}-
Create dialogue data
- Create multiple DialogueSO resources
- Fill in dialogue content and character information
-
Configure the scene
- Add Canvas and dialogue UI elements
- Create DialogueSystem object and add components
-
Set up controllers
- Add appropriate dialogue controllers
- Configure necessary references and parameters
-
Start dialogue
- Call
StartDialogue()via a trigger or other means
- Call
-
Handle dialogue end
- Subscribe to the
OnDialogueEndedevent - Implement post-dialogue logic
- Subscribe to the
Check:
- optionsPanel is correctly set
- optionButtonPrefab contains Button and TextMeshProUGUI components
- ShowDialogueOptions() method has been called
Check:
- nextLineDelay value is set to a positive number
- delayNextWord parameter is correctly passed to TypeDialogueLine
You can extend the system by inheriting from existing controllers or directly modifying the source code:
- Add new dialogue controller types
- Extend DialogueControlView to support more UI effects
- Modify DialogueSO to store more complex dialogue structures
This way, we can create a well-organized and useful dialogue system!