基于Godot游戏引擎实现Galgame核心功能Step4

其它 编程语言Galgame制作游戏制作GodotC#游戏引擎
浏览数 - 542发布于 - 2024-10-16 - 00:49

在前面的教程中,我们完成了所需脚本的编写

在这篇教程中,我将介绍如何使用我们所编写的脚本

挂载单例

在使用脚本前,我们需要将GDS脚本全部挂载为单例以便访问

C#脚本只需要挂载我们最后编写的有连接按键事件的脚本作为单例即可,也就是显示对话文本

C#的UI管理器脚本不需要是单例,但是其中的初始化方法需要手动调用,且在整个游戏中只需要调用一次即可

你也可以将UI管理器脚本设置为单例,这样做的话,需要其继承自Node类并将初始化方法更改为覆写_Ready方法

public partial class UI管理器 : Node
{
	public override void _Ready(){
            UI场景注册表["对话框"] = ResourceLoader.Load<PackedScene>("res://scenes/DialogueBox.tscn");
	}
}

这样做,你就不需要手动初始化UI注册表了

开启对话

你可以使用以下方法来开启一个对话

GDS

func start_dialogue(root_node: Node, file_name: String, speed: float = 0.05, opacity: float = 1.00, auto: bool = false, close: bool = true, end_callback: Callable = Callable(), waittime: float = 3, callback_params: Array = []):

功能
该函数用于启动一个对话框,逐字显示对话内容,并根据提供的参数进行控制。


参数说明

  • root_node: Node对话框将挂载的父节点(根节点),通常是场景的根或 UI 层节点,使用get_tree().current_scene来附加到当前场景根节点。
  • file_name: String
    对话内容所在的 CSV 文件名。用于加载对话数据。
  • speed: float (可选,默认值:0.05)
    控制文本逐字显示的速度。数值越小,显示速度越快。
  • opacity: float (可选,默认值:1.00)
    对话框的透明度。值为 0 表示完全透明,1 表示完全不透明。
  • auto: bool (可选,默认值:false)
    设置是否自动显示下一行对话。true 表示对话结束后自动切换到下一行。
  • close: bool (可选,默认值:true)
    设置对话结束后是否允许关闭对话框。true 表示对话结束后可以关闭,false 表示对话结束后不自动关闭。
  • end_callback: Callable (可选,默认值:Callable())
    对话结束后执行的回调函数。可以为空,若提供,函数将在对话结束后被调用。
  • waittime: float (可选,默认值:3)
    对话结束后等待执行回调函数的时间(秒)。默认等待时间为 3 秒。
  • callback_params: Array (可选,默认值:[])
    在回调函数中传递的参数数组。可以传递多个参数,默认值为空数组。

返回值
该函数无返回值。它直接控制 UI 元素的状态变化与对话显示。


用法示例

 # 启动对话框,根节点为当前场景根节点,即在当前场景创建对话框,文件名为 'dialogue.csv'
 start_dialogue(get_tree().current_scene, "dialogue.csv")

 # 启动对话框,根节点为 'UI Root',文件名为 'dialogue.csv'
 start_dialogue(get_node("UI Root"), "dialogue.csv")

 # 启动带有自定义显示速度和透明度的对话框
 start_dialogue(get_node("UI Root"), "dialogue.csv",0.02, 0.8)

 # 启动自动切换对话的对话框,并设置一个回调函数
 start_dialogue(get_node("UI Root"), "dialogue.csv",0.05, 1, false,true,Callable(self, "_on_dialogue_finished"))

 # 启动对话框并设置回调函数的等待时间和参数
 start_dialogue(get_node("UI Root"), "dialogue.csv",0.05, 1, false,true,Callable(self, "_on_dialogue_finished"),5,["param1", "param2"])

注意事项

  • file_name 必须是有效的 CSV 文件路径,并且文件格式应符合对话文本格式,即"",""
  • 如果 autotrue,对话内容将自动切换,用户无需手动操作。
  • end_callback 为空时,不会执行任何回调操作;若设置,则必须是有效的 Callable 对象。
  • waittimecallback_params 仅在回调函数不为空时生效。

C#

public void 开始对话(Node 根节点, string 文件全名, float 文字速度 = 0.05f, float 对话框透明度 = 1.0f, bool 自动显示 = false, bool 对话结束后关闭对话框 = true, Action<object[]> 对话结束后回调函数 = null, float 回调函数等待时间 = 3.0f, object[] 回调函数参数 = null)

功能
该方法用于初始化并启动对话框,逐字显示对话内容,并根据提供的参数控制对话框的表现和回调功能。


参数说明

  • 根节点: Node
    对话框将挂载的父节点(根节点)。通常是场景或 UI 层的根节点,使用GetTree().CurrentScene来附加到当前场景根节点。。
  • 文件全名: string
    用于加载对话内容的文件路径。应为包含对话文本的 CSV 文件路径。
  • 文字速度: float (可选,默认值:0.05)
    控制文本逐字显示的速度。数值越小,显示速度越快。
  • 对话框透明度: float (可选,默认值:1.0)
    对话框的透明度。1.0 表示完全不透明,0 表示完全透明。
  • 自动显示: bool (可选,默认值:false)
    是否自动显示下一行对话。true 表示对话结束后自动切换到下一行。
  • 对话结束后关闭对话框: bool (可选,默认值:true)
    是否允许在对话结束后关闭对话框。true 表示可以关闭,false 表示不自动关闭。
  • 对话结束后回调函数: Action<object[]> (可选,默认值:null)
    对话结束后执行的回调函数。可为空。如果提供此参数,函数将在对话结束后调用。
  • 回调函数等待时间: float (可选,默认值:3.0f)
    在执行回调函数前等待的时间,单位为秒。默认等待 3 秒。
  • 回调函数参数: object[] (可选,默认值:null)
    传递给回调函数的参数数组。如果未指定,默认为空数组。

返回值
该方法无返回值。它通过操作 UI 元素和逻辑控制进行对话的显示和结束回调。


用法示例

// 初始化并启动对话框,根节点为当前场景根节点,即在当前场景创建对话框,文件名为 'dialogue.csv'
开始对话(GetTree().CurrentScene, "dialogue.csv");

// 初始化并启动对话框,根节点为 'UI Root',文件名为 'dialogue.csv'
开始对话(GetNode("UI Root"), "dialogue.csv");

// 启动带有自定义显示速度和透明度的对话框
开始对话(GetNode("UI Root"), "dialogue.csv", 文字速度: 0.02f, 对话框透明度: 0.8f);

// 启动自动切换对话的对话框,并设置一个回调函数
开始对话(GetNode("UI Root"), "dialogue.csv", 自动显示: true, 对话结束后回调函数: MyCallbackFunction);

// 启动对话框并设置回调函数的等待时间和参数
开始对话(GetNode("UI Root"), "dialogue.csv", 回调函数等待时间: 5.0f, 对话结束后回调函数参数: new object[] { "param1", "param2" });


注意事项

  • 文件全名 必须是有效的文件路径,并且文件格式应符合对话文本格式,即"",""
  • 如果 自动显示true,则对话内容将自动切换,用户无需手动操作。
  • 如果未指定 对话结束后回调函数,则对话结束后不会执行任何回调操作;若指定,回调函数必须为有效的 Action<object[]> 类型。
  • 回调函数等待时间对话结束后回调函数参数 仅在设置了回调函数时有效。

检测当前行

你可以使用以下方法来检测当前对话内容,是否是指定的行

GDS

check_current_line(line_index: int) -> bool

功能
该函数用于异步检测对话框当前的行索引是否达到指定的目标值,返回 true 表示已达到指定行,false 表示对话未处于激活状态或未能匹配目标行。


参数说明

  • line_index: int
    目标行索引,从0开始,表示需要检测的对话行,索引+1即是对应的行数。

返回值

  • bool
    返回 true 表示当前对话行索引等于目标索引,false 表示对话已结束或未能匹配。

用法示例

# 检查当前对话是否处于第3行(索引为2)
if await check_current_line(2):
    print("当前行是第3行")
else:
    print("未达到第3行或对话已结束")

注意事项

  • 该函数是异步的,会持续每0.01秒检查一次当前行索引,直到对话结束或行索引匹配。
  • 函数运行期间会等待,因此在调用时应注意处理游戏的其他异步操作。
  • 索引从0开始,因此第1行的索引值为0,第2行的索引值为1,以此类推。

C#

public async Task<bool> 检测当前对话行索引(int 行索引)

功能
该方法用于异步检测当前对话的行索引是否达到了指定值。当对话框处于激活状态时,定时检查当前行索引是否与目标索引匹配。


参数说明

  • 行索引: int
    需要检测的目标行索引,从0开始,索引+1即是对应的行数。。

返回值

  • Task<bool>
    异步任务,返回 true 表示当前对话行索引已匹配,false 表示对话已结束或未达到目标索引。

用法示例

// 异步检测当前对话是否处于第3行(索引为2)
bool isAtLine3 = await 检测当前对话行索引(2);
if (isAtLine3)
{
    // Console.WriteLine("当前行是第3行");
    GD.Print("当前行是第3行");
}
else
{
    // Console.WriteLine("未达到第3行或对话已结束");
    GD.Print("当前行是第3行");
}

注意事项

  • 该方法每10毫秒检查一次当前行索引,因此不会对游戏的性能造成较大影响,但应避免在频繁调用的情况下增加额外开销。
  • 调用时需确保方法在异步上下文中运行。
  • 索引从0开始,因此第1行的索引值为0,第2行的索引值为1,以此类推。

案例

对话时启用遮罩

有时我们想要在对话时启用遮罩,让当前场景除对话框以外的UI节点都无法响应玩家的鼠标点击

我们需要在对话时启用遮罩

启用遮罩前,需要在当前场景创建一个遮罩节点

在当前场景根节点选择添加子节点创建一个ColorRect节点,

并将其Layout Mode设置为 Anchors 随后将其Anchor Preset更改为整个矩形,这样就做到了将遮罩覆盖整个屏幕

随后将其的Color属性的alpha值更改为0,也就是R,G,B,A中的A

最后再将其命名为Mask,这样我们就得到了一个屏幕遮罩

使用代码开启/关闭遮罩

GDS

var mask
func _ready():
    # 获取遮罩节点
    mask = $path/to/mask
    # 开启遮罩
    mask.visible = true
    # 开启对话
    start_dialogue(get_tree.current_scene, "dialogue.csv", 0.05, 1, false,true, Callable(self, "on_dialogue_end"),0.01)

# 在对话结束后关闭遮罩
func on_dialogue_end():
    mask.visible = false

C#

private ColorRect 遮罩;

public override void _Ready()
{
    // 获取遮罩节点
    遮罩 = GetNode<ColorRect>("path/to/mask");
    遮罩.Visible = true; // 启用遮罩

    // 开始对话,并设置回调函数
    开始对话(GetTree().CurrentScene, "dialogue.csv",对话结束后回调函数: 对话结束回调 , 回调函数等待时间:0.01f);
}

// 对话结束的回调函数
public void 对话结束回调(object[] 参数)
{
    遮罩.Visible = false; // 隐藏遮罩
}
 

方法都是一致的,你也可以使用这个方法来实现在对话结束后切换场景或执行一些其他的东西

检测当前对话角色执行内容

在对话系统中,你可能需要根据当前对话角色的变化来执行特定的代码或函数。这通常用于在角色说话时触发一些特定事件,比如播放音效、展示特殊效果、或者切换角色立绘等。

检测当前对话角色并执行对应代码或函数

public async Task<bool> 检测当前对话角色并执行(int 行索引)
{
    // 等待直到当前对话行达到指定的行索引
    if (await 检测当前对话行索引(行索引))
    {
        // 获取当前对话行角色名
        string 角色名 = 对话系统工具集.对话系统基类.对话数据[行索引][0];

        // 根据角色名执行对应的代码
        switch (角色名)
        {
            case "角色A":
                // 执行角色A的相关代码或函数
                Console.WriteLine("角色A正在说话,执行角色A相关逻辑...");
                break;
            case "角色B":
                // 执行角色B的相关代码或函数
                Console.WriteLine("角色B正在说话,执行角色B相关逻辑...");
                break;
            default:
                // 默认处理
                Console.WriteLine($"未知角色:{角色名}");
                break;
        }

        return true;
    }

    return false;
}

我们可以修改并使用之前的 检测当前对话行索引 函数,结合 对话系统基类.对话数据[][] 来获取角色名,并自动检测执行与该角色相关的逻辑。

角色立绘切换

在对话系统中,通常我们会根据当前角色来切换屏幕上的立绘

GDS

# 一个存储立绘的字典
var character_sprites = {
    "角色A": preload("res://path/to/characterA.png"),
    "角色B": preload("res://path/to/characterB.png"),
    # 其他角色的立绘...
}

# 角色立绘的节点
@onready var character_sprite_node = $CharacterSprite

# 检测当前对话行并切换立绘
func check_and_switch_sprites(line_index: int) -> void:
    while is_dialogue_active:
        await get_tree().create_timer(0.01).timeout  # 等待10毫秒
        if current_line == line_index:  # 如果当前行索引匹配
            var character_name = DialogueSystemBase.dialogue_data[current_line][0]  # 获取当前角色名
            switch_character_sprite(character_name)  # 切换立绘
            break  # 找到匹配后退出循环

# 切换角色立绘的函数
func switch_character_sprite(character_name: String) -> void:
    if character_name in character_sprites:
        character_sprite_node.texture = character_sprites[character_name]  # 更新立绘
    else:
        print("角色名未找到: ", character_name)  # 错误处理

# 示例用法
func _ready():
    check_and_switch_sprites(0)  # 检查并切换到第0行的立绘

在之前 检测当前对话角色并执行 方法的基础上,可以引申出根据角色切换立绘的逻辑:

C#

public async Task<bool> 检测并切换角色立绘(int 行索引, TextureRect 角色立绘)
{
    // 等待直到当前对话行达到指定的行索引
    if (await 检测当前对话行索引(行索引))
    {
        // 获取当前对话行的角色名
        string 角色名 = 对话系统工具集.对话系统基类.对话数据[行索引][0];

        // 根据角色名切换立绘
        switch (角色名)
        {
            case "角色A":
                角色立绘.Texture = ResourceLoader.Load<Texture>("res://path_to_角色A_立绘.png");
                Console.WriteLine("切换到角色A的立绘");
                break;
            case "角色B":
                角色立绘.Texture = ResourceLoader.Load<Texture>("res://path_to_角色B_立绘.png");
                Console.WriteLine("切换到角色B的立绘");
                break;
            default:
                Console.WriteLine($"未知角色:{角色名},无法切换立绘");
                break;
        }

        return true;
    }

    return false;
}

这个示例代码是自动检测当前角色并切换立绘的,不适合一个角色有多个立绘的场景

多个立绘场景示例代码

GDS

extends Node


# 一个存储立绘的多版本字典
var character_sprites = {
    "角色A": {
        "normal": preload("res://path/to/characterA_normal.png"),
        "angry": preload("res://path/to/characterA_angry.png"),
        "happy": preload("res://path/to/characterA_happy.png"),
    },
    "角色B": {
        "normal": preload("res://path/to/characterB_normal.png"),
        "surprised": preload("res://path/to/characterB_surprised.png"),
    }
}

# 角色立绘的节点
@onready var character_sprite_node = $CharacterSprite

# 手动切换角色立绘的函数
func switch_character_sprite_manual(character_name: String, sprite_version: String) -> void:
    if character_name in character_sprites and sprite_version in character_sprites[character_name]:
        character_sprite_node.texture = character_sprites[character_name][sprite_version]  # 更新立绘
    else:
        print("找不到角色名或立绘版本: ", character_name, sprite_version)  # 错误处理

# 示例用法:手动指定角色立绘
func _ready():
    switch_character_sprite_manual("角色A", "angry")  # 手动切换到角色A的愤怒表情立绘


获取对话相关数据

C#使用以下代码来获取对话数据

对话系统工具集.对话系统基类.对话数据[显示对话文本.逐字显示.当前行索引][0]; // 通过原始对话数据获取角色名

显示对话文本.逐字显示.角色名.Text; // 通过UI控件获取角色名

对话系统工具集.对话系统基类.对话数据[显示对话文本.逐字显示.当前行索引][1]; // 通过原始对话数据获取当前对话文本(可能包含占位符)

显示对话文本.逐字显示.对话内容.Text; // 通过UI控件获取已经逐字显示了的文本

显示对话文本.逐字显示.当前需要显示文本 // 通过迭代数据获取处理过占位符的完整的当前行文本

GDS中也是一样的方法,找到相同功能的变量即可

C#使用以下代码来监听当前对话行

bool 对话行数状态 = await 显示对话文本.逐字显示.检测当前对话行索引(5);
if (对话行数状态)
{
   // 当对话到达指定行数后需要执行的逻辑
    // 例如更新角色的状态,切换场景等
}

GDS也是一样的方法语法是:

if your_auto_load_name.check_current_line(1):
    # 需要执行的逻辑,例如切换角色立绘

监听对话行数适合用于 角色动作切换触发事件 的场景。

你可以使用这个方法扩展至监听其他对话变量,比如当前角色名,来切换立绘或背景音效等内容。

重新编辑于 - 2024-10-16 - 01:08

kohaku