Godot:根据输入读取并输出 JSON 文件作为 Textbook

Godot: read and output JSON file as Textbook according to the input

我希望能够通过此 JSON 文件中指定的编号调用特定短语,因此当我调用 go(1) 时,它仅显示具有 'Num' 的文本设置为 1.

我的JSON文件:

[
    {"Num":0, "Name":"Afely", "Emotion":"Neutral", "Text":"TEST1"},
    {"Num":0, "Name":"Afely", "Emotion":"Neutral", "Text":"TEST2"},
    {"Num":0, "Name":"Afely", "Emotion":"Neutral", "Text":"TEST3"},

    {"Num":1, "Name":"Afely", "Emotion":"Neutral", "Text":"2TEST1"}
]

文本框代码:

extends ColorRect

export var dialogPath = ""
export(float) var textSpeed = 0.005

var dialog

var phraseNum = 0
var finished = false

func go(phraseNum):
    $Timer.wait_time = textSpeed
    dialog = getDialog()
    assert(dialog, "Dialog not found")
    nextPhrase()
    var f = File.new()
    var img = dialog[phraseNum]["Emotion"] + ".png"
    $Portrait.texture = load(img)
    
func _unhandled_input(event):
    if event is InputEventKey:
        if event.pressed and event.scancode == KEY_Q:
            if finished:
                $NEXT.play()
                nextPhrase()
            else:
                $Text.visible_characters = len($Text.text)

func getDialog() -> Array:
    var f = File.new()
    assert(f.file_exists(dialogPath), "File path does not exist")
    
    f.open(dialogPath, File.READ)
    var json = f.get_as_text()
    
    var output = parse_json(json)
    
    if typeof(output) == TYPE_ARRAY:
        return output
    else:
        return []

func nextPhrase() -> void:
    if phraseNum >= len(dialog):
        queue_free()
        return
    
    finished = false
    
    $Name.bbcode_text = dialog[phraseNum]["Name"]
    $Text.bbcode_text = dialog[phraseNum]["Text"]
    
    $Text.visible_characters = 0
    

    
    while $Text.visible_characters < len($Text.text):
        $Text.visible_characters += 1
        $TEXT_AUDIO.play()
        $Timer.start()
        yield($Timer, "timeout")
    
    finished = true
    phraseNum += 1
    return

我怎么称呼它:

$TextBox.show()
$TextBox.go(1)

最后,我遵循的教程: https://www.youtube.com/watch?v=GzPvN5wsp7Y

我该如何处理?

你问的

你问的问题需要做一些额外的工作。让我们从 JSON 文件开始:

[
    {"Num":0, "Name":"Afely", "Emotion":"Neutral", "Text":"TEST1"},
    {"Num":0, "Name":"Afely", "Emotion":"Neutral", "Text":"TEST2"},
    {"Num":0, "Name":"Afely", "Emotion":"Neutral", "Text":"TEST3"},

    {"Num":1, "Name":"Afely", "Emotion":"Neutral", "Text":"2TEST1"}
]

这将解析为 Array[] 之间的所有元素),其中每个元素都是 Dictionary([=25 之间的元素) =] 和 })。这意味着您将需要遍历 Array,检查每个 Num.


在我们这样做之前,我们需要承认我们将使用名称 phraseNum 来表示两件事:

  • 索引在dialogArray
  • 想要的值Num

我们处于这种情况是因为您使用的来源 material。他们将 phraseNum 作为参数(此处:func go(phraseNum))隐藏了 phraseNum 字段(此处为 var phraseNum = 0 )。

这阻碍了交流。如果我告诉你用 phraseNum 做这个,它是哪一个?那势必会给我们带来麻烦。

我将重写 go 所以它需要一个 Num 而不是 dialog Array 中的索引,所以我会保留 phraseNumdialog Array 中的索引,并为 go.

的参数命名

让我们开始重写go:

func go(num):
    pass

现在,让我们得到所有的对话:

func go(num):
    dialog = getDialog()

我们将迭代它们。由于我需要 phraseNum 的索引,因此我将使用索引进行迭代:

func go(num):
    dialog = getDialog()
    for index in dialog.size():
        pass

我们需要检查 Num 是否匹配。如果是,我们得到索引:

func go(num):
    dialog = getDialog()
    for index in dialog.size():
        if num == dialog[index]["Num"]:
            phraseNum = index
            break

我们需要处理我们没有找到它的情况。现在,嗯……来源 material 只有一个 assert,我将保留该方法。所以我们需要一种方法来知道代码没有找到它……

func go(num):
    var found := false
    dialog = getDialog()
    for index in dialog.size():
        if num == dialog[index]["Num"]:
            phraseNum = index
            found = true
            break

    assert(found, "Dialog not found")

接下来你调用nextPhrase(),确定:

func go(num):
    var found := false
    dialog = getDialog()
    for index in dialog.size():
        if num == dialog[index]["Num"]:
            phraseNum = index
            found = true
            break

    assert(found, "Dialog not found")
    nextPhrase()

还有一个没用过的var f = File.new(),我就不加了。

并且你设置了肖像纹理。当然:

func go(num):
    var found := false
    dialog = getDialog()
    for index in dialog.size():
        if num == dialog[index]["Num"]:
            phraseNum = index
            found = true
            break

    assert(found, "Dialog not found")
    nextPhrase()
    var img = dialog[phraseNum]["Emotion"] + ".png"
    $Portrait.texture = load(img)

我已经跳过了定时器的东西,我现在将它插入:

func go(num):
    var found := false
    dialog = getDialog()
    for index in dialog.size():
        if num == dialog[index]["Num"]:
            phraseNum = index
            found = true
            break

    assert(found, "Dialog not found")
    $Timer.wait_time = textSpeed
    nextPhrase()
    var img = dialog[phraseNum]["Emotion"] + ".png"
    $Portrait.texture = load(img)

其他

现在,您说您只想要 具有给定 Num 的短语。这是开放的解释。

需要说明的是,上面的代码将使对话框在您要求的 Num 的第一个实例处启动。但它不会结束 - 也不会跳过 - 当它找到不同的 Num 时。不知道你要不要。

我们有几种方法可以做到这一点。我们可以记住什么是 num 并在 nextPhrase 中检查它。我真的不想那样做。所以,我会给你一个替代方法:让我们制作一个对话框数组,只包含我们想要的元素。

看起来像这样:

func go(num):
    var every_dialog = getDialog()
    dialog = []
    for candidate in every_dialog:
        if num == candidate["Num"]:
            dialog.append(candidate)

    assert(dialog, "Dialog not found")
    phraseNum = 0
    $Timer.wait_time = textSpeed
    nextPhrase()
    var img = dialog[phraseNum]["Emotion"] + ".png"
    $Portrait.texture = load(img)

请注意,在这个例子中我们没有读取 getDialog()dialog。相反,我们正在构建一个只包含我们想要的条目的 dialog 数组。我们通过迭代 getDialog() 的结果来做到这一点(我们用 append 添加到数组)。

这是 dialog 含义的细微变化,因为它不再代表 JSON 文件中的每个条目。相反,它仅代表将要显示的条目。之前这两个东西是一样的,但这次改变后它们就不同了。


你没问的

  • 函数getDialog从JSON文件中读取。每次调用 go 时都会这样做。您可以 _ready.

    中执行一次

    重要的是您了解每个变量代表什么,以及您在哪里读取和写入它们。 上面我提到dialog对替代变化有微妙的意义。您需要考虑到这一点才能进行此更改。

  • 我坚信 nextPhrase 应该处理计时器和肖像。应该不需要设置 go.

  • 我希望你考虑这个替代方案JSON文件结构:

    [
        [
            {"Name":"Afely", "Emotion":"Neutral", "Text":"TEST1"},
            {"Name":"Afely", "Emotion":"Neutral", "Text":"TEST2"},
            {"Name":"Afely", "Emotion":"Neutral", "Text":"TEST3"},
        ],
        [
            {"Name":"Afely", "Emotion":"Neutral", "Text":"2TEST1"}
        ]
    ]
    

    然后你会得到 ArrayArray,其中嵌套 Array 的每个元素都是一个 Dictionary。然后您可以“简单地”通过索引获取嵌套数组,而不必遍历每个元素。

    您得到的结构类似于 JSON 文件的结构。这也意味着您将不得不改变使用它的方式。例如,可以 dialog[num][phraseNum]["Text"].

    而不是 dialog[phraseNum]["Text"]

    现在考虑来源 material。在这种情况下,我们有一个节点既负责解析 JSON 又负责显示角色对话。如果这些代码彼此分开,修改这些代码会更容易。大概作者的意图是你会为不同的对话有不同的 JSON 文件,所以你会在必要时切换 JSON 文件(这也解释了为什么他们每次都读取 JSON 文件时间)。

    但你问的不是这个