About 'JSON and File'!

gdt_s02_json_and_file

#1

In this video (objectives)…

1 Learn how to write JSON files 2 How to open and parse JSON in GDScript

After watching (learning outcomes)…

Move everything that isn't code out of your script. Learn how to create, load and parse JSON files!

(Unique Video Reference: 8_LL_GDT)

We would love to know…

  • What you found good about this lecture?
  • What we could do better?

Remember that you can reply to this topic, or create a new topic. The easiest way to create a new topic is to follow the link in Resources. That way the topic will…

  • Be in the correct forum (for the course).
  • Be in the right sub-forum (for the section)
  • Have the correct lecture tag.

Enjoy your stay in our thriving community!


#2

Hello, I put the stories.json File into the LoonyLips directory.

Everytime I start the get_from_json function I get the followin Error: “Invalid get index ‘prompt’ (on base: ‘Nil’)”

0- res://LoonyLips.gd:9

What am I doing wrong?

func _ready():
    $Blackboard/Storytext.text = "Willkommen zu diesem Spiel.\nBitte gib die angefragen Woerter ein.\nUmlaute werden leider nicht erkannt. \n \nGib mir bitte "+ current_story.prompt[player_words.size()] +"."
    # get_random_story()
    get_random_story("stories.json")
    
func get_random_story():
    var stories = get_from_json("stories.json")
    randomize()
    current_story = stories [randi() % stories.size()]
 
func get_from_json(filename):
    var file = File.new() #Datei Objekt
    file.open(filename, File.Read) #Datei muss vorhanden sein
    var text = file.get_as_text()
    var data = parse_json(text)
    return data
    file.close()

#3

Interesting - it’s hard to tell with only part of the script , but at a guess, did you mean to comment out get_random_story()? Because at the moment your script isn’t setting the current story (that function isn’t being called), and without that it’s not going to know which prompt to pull.


#4

Yes you’r right, I did it to test if I can call the get_from_json func directly.

My json file lies under: /home/jan/Godot/LoonyLips/stories.json
Exactly the place where I put the gfx folder in.

Here is my whole skript:

extends Node2D

var player_words=[] #words given by player

var current_story

func _ready():
$Blackboard/Storytext.text = “Willkommen zu diesem Spiel.\nBitte gib die angefragen Woerter ein.\nUmlaute werden leider nicht erkannt. \n \nGib mir bitte “+ current_story.prompt[player_words.size()] +”.”
set_random_story()

func set_random_story():
var stories = get_from_json(“stories.json”)
randomize()
current_story = stories [randi() % stories.size()]

func get_from_json(filename):
var file = File.new()
file.open(filename, File.Read)
var text = file.get_as_text()
var data = parse_json(text)
return data
file.close()

func _on_TextureButton_pressed():
if story_done():
get_tree().reload_current_scene()
else:
var new_text = $Blackboard/Textbox.get_text()
_on_Textbox_text_entered(new_text)

func _on_Textbox_text_entered(new_text):
player_words.append(new_text)
$Blackboard/Textbox.text=""
check_player_word_length()

func story_done ():
return player_words.size() == current_story.prompt.size()

func prompt_player():
$Blackboard/Storytext.text =“Gib mir bitte “+current_story.prompt[player_words.size()] +”.”

func check_player_word_length():
if story_done():
tell_story()
else:
prompt_player()

func tell_story():
$Blackboard/Storytext.text = current_story.story %player_words
end_game()

func end_game():
$Blackboard/Textbox.queue_free()
$Blackboard/TextureButton/ButtonLabel.text =“Noch einmal”

Here is my stories.json:

[ {
“prompt” :[
"einen Namen ",
"einen Gegenstand ",
"einen weiteren Gegenstand ",
“ein Gefuehl”
],
“story” :
“Eines Tages nahm %s %s um damit %s kaputt zu machen. Doch es fuehlte sich %s an.”
},
{
“prompt” :[
“eine Automarke”,
“eine Beleidigung”,
"einen Namen "
],
“story” :
“In einem %s faehrt ein %s. Ach nein es ist doch nur %s.”
},
{
“prompt” :[
"eine Eigenschaft ",
"einen Ort ",
"eine zweite Eigenschaft ",
"einen Gegenstand ",
"einen Namen "
],
“story”:
“Auf einer %s %s liegt ein %s %s. Nein das ist ja %s”
}
]


#5

Ah! I think I have it.

You’re pulling the prompts from the JSON, but you’ve still calling the prompt in the intro text as if it was in the script. In other words, you’re calling current_story.prompt, which is great for dictionaries, but you want current_story["prompt"] to get the key from JSON.

We create a separate other_string.json later in the video to deal with that, but if go to line 8 and replace

 current_story.prompt[player_words.size()]

with

current_story["prompt"][player_words.size()]

You’ll also want to make sure you’re doing that after it knows what the current story is, so move the introduction text ($Blackboard/Storytext.text = “Willkommen zu diesem Spiel etc down a line so it’s after the set_random_function() call.

See if that works.


#6

Yes that solved the problem!

Thank you.

Maybe it would be an idea to tell students that they can’t test it before they watched the rest of the video or to tell them the solution. I just stumbled over it because I wanted do see if it works before trying to make your challenge.

Thank you again.


#7

I think that you could usefully talk more about the structure and use of JSON files. I think that you passed over this quite quickly, which is understandable in what is already quite a long lecture. I would suggest that you either break this lecture into two parts, or insert a more theoretical lecture covering the topic in more detail as Ben has done in some of the Unity courses.

On a related note you mention that JSON files contain either list or dictionary formats, on reading further I believe that this was the case for early JSON standards but that it has since been expanded to incorporate other formats. If this is the case (and it is quite possible that I am wrong) does Godot support the later formats or ist it just limited to lists and dictionaries?


#8

Should file.close come before return data
and if gdscript only returns at end of function (I dont know if it does or not) wouldn’t you do it for readability anyway?


#9

here is my slightly different script

extends Node2D

var player_words = []
var template = get_from_json(“stories.json”)
var ui_text = get_from_json(“other-strings.json”)
var current_story

func _ready():
change_story()
$Blackboard/UserResponse.text = “”
prompt_player(ui_text[“intro_text”])

func change_story():
randomize()
current_story = template[ randi() % template.size() ]

func get_from_json( filename ):
var file = File.new()
file.open(filename, File.READ)
var text = file.get_as_text()
var data = parse_json(text)
file.close()
return data

func _on_TextureButton_pressed():
if all_questions_asked():
player_words.clear()
change_story()
# get_tree().reload_current_scene()
var new_text = $Blackboard/UserResponse.get_text()
_on_UserResponse_text_entered(new_text)

func _on_UserResponse_text_entered(new_text):
player_words.append(new_text)
$Blackboard/UserResponse.text = “”
check_player_word_length()

func check_player_word_length():
if all_questions_asked():
tell_story()
else:
prompt_player("")

func prompt_player(leadText):
$Blackboard/TextureButton/RichTextLabel.text = ui_text[“ok”]
$Blackboard/UserResponse.show()
$Blackboard/StoryText.bbcode_text = (leadText + ui_text[“prompt”] % current_story.prompt[player_words.size()])

func tell_story():
$Blackboard/StoryText.bbcode_text = current_story.story % player_words
end_game()

func end_game():
$Blackboard/UserResponse.hide()
$Blackboard/TextureButton/RichTextLabel.text = ui_text[“again”]

func all_questions_asked():
return player_words.size() == current_story.prompt.size()


#10

The fix for file.close() happening after return data is being uploaded now! Yes, that was a mistake, not a feature :smiley:


#11

I get the (debugger)error “Invalid call. Nonexistent function ‘size’ in base ‘Nil’.” on lines 8 and 17 in my code,
I’ve tried everything my mind can muster but the errors still persist.

extends Node2D

var player_words = [] #words chosen by the player
var current_story
var strings # All the text that's displayed to the player

func _ready():
	set_random_story()
	strings = get_from_json("other_strings.json")
	$Blackboard/StoryText.text = strings["intro_text"]
	prompt_player()
	$Blackboard/TextBox.text = ""

func set_random_story():
	var stories = get_from_json("stories.json")
	randomize()
	current_story = stories [randi() % stories.size()]

func get_from_json(filename):
	var file = File.new() #the file object
	file.open(filename, File.READ) #Assumes the file exists
	var text = file.get_as_text()
	var data = parse_json(text)
	file.close()
	return data

func _on_TextureButton_pressed():
	if is_story_done():
		get_tree().reload_current_scene()
	var new_text = $Blackboard/TextBox.get_text()
	_on_TextBox_text_entered(new_text)

func _on_TextBox_text_entered(new_text):
	player_words.append(new_text)
	$Blackboard/TextBox.text = ""
	$Blackboard/StoryText.text = ""
	check_player_word_length()

func is_story_done():
	return player_words.size() == current_story.prompt.size()


func prompt_player():
	var next_prompt = current_story["prompt"][player_words.size()]
	$Blackboard/StoryText.text = (strings["prompt"] % next_prompt)
	
func check_player_word_length():
	if is_story_done():
		tell_story()
		pass
	else:
		prompt_player()
	
func tell_story():
	$Blackboard/StoryText.text = current_story.story % player_words
	$Blackboard/TextureButton/ButtonLabel.text = strings["again"]
	
func end_game():
	$Blackboard/TextBox.queue_free()

Attached is the file structure of my LoonyLips directory/folder/whatever
LoonyLips


#12

That’s very odd - I copy and pasted your script into mine and it worked just fine. Godot saying that it doesn’t know what size() is worrying, as that’s a built in function. Unless there’s something going on in your json files?


#13

Thank you, Yann. You were right, I replaced my modified .json fies with the ones in the lecture resources and it works fine now.


#14

I spend a good portion of time (a lot of time pulling hair) on this issue and I realised that I was referring to other_string.json in my code, but the downloaded file is named other-string.json. (as it is in your directory as well) The file is named other_string.json in the tutorial list and in the code in the video, but for some odd reason, it downloads as other-string.json. When I fixed the name error in my code, it all worked for me. (could also rename the json-file)