【问题标题】:Godot: read and output JSON file as Textbook according to the inputGodot:根据输入读取JSON文件并将其输出为Textbook
【发布时间】:2022-01-21 12:13:45
【问题描述】:

我希望能够通过在这个 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

我将如何做到这一点?

【问题讨论】:

    标签: arrays json game-engine godot


    【解决方案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"}
    ]
    

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


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

    • dialog 中的索引Array
    • Num 的期望值

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

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

    我将重写go,所以它需要一个Num而不是dialog中的索引Array,所以我将保留phraseNum作为dialog中的索引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
    

    我们需要处理我们没有找到它的情况。现在,嗯……源材料只有一个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"}
          ]
      ]
      

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

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

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

      但这不是你问的。

    【讨论】:

    • 事实证明这非常有帮助并解决了我想做的事情。我也很欣赏深入的解释!它帮助我更好地了解我正在使用什么。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-12-05
    • 2016-02-25
    • 2018-08-07
    • 1970-01-01
    • 1970-01-01
    • 2017-01-22
    • 2022-11-30
    相关资源
    最近更新 更多