Attempting to create a replay system and I continue to get errors

On godot v4.2.1

Hello, I’m attempting to create basic replay system that stores my player character’s position alongside an ID number alongside it so that I may then replay it in the order it was stored. I continue to get the error “Invalid get index ‘1’ (on base: ‘Dictionary’).” Any and all help would be appreciated!!

script that collects and stores replay data

var count = 0
var current_replay_data: Dictionary = {}

func _physics_process(_delta):
	var player = get_parent()
	if current_global.laps > 0 and !current_global.laps >= 4 and $replay_delay.is_stopped():
		count += 1
		current_replay_data[count] = {"position": player.global_position}
		$replay_delay.start()

script that ideally SHOULD be able to read and replay stored replay data

extends Node2D

var current_replay_data: Dictionary = {}
var replaying = false
var count = 0

func _ready():
	if (current_global.current_mode == 2 or current_global.current_mode == 2.5) and save_load.data["replay_on"] and save_load.data["stage_%d_replay" % + current_global.current_stage_id] != null:
		if !save_load.data["dev_replay"]:
			current_replay_data = save_load.data["stage_%d_replay" % + current_global.current_stage_id]
	else:
		queue_free()

func _process(_delta):
	if current_global.laps > 0 and !replaying:
		count += 1
		play()
		replaying = true

func play():
	var tween = create_tween()
	tween.tween_property(self, "global_position", current_replay_data[count]["position"], 0.1)
	await get_tree().create_timer(0.1).timeout
	replaying = false

How “stage_%d_replay” % + current_global.current_stage_id (or replay data) is stored:
“1”:{“position”:“(7101, 276)”}, “2”:{“position”:“(7102, 274)”}, etc, etc, etc

Dictionary objects don’t have indexes. Use an Array instead.

1 Like

Which line is giving you this error? Are you sure your current_replay_data has any data in it? Can you print the dictionary before using it?

1 Like

I get an error at tween.tween_property(self, "global_position", current_replay_data[count]["position"], 0.1) and I can print my dictionary before using it.

All of my data is stored as a dictionary. How might I transfer that data to an array and still use it as a key and value I can pass onto a series of tweens?

Using an Array won’t be much different since you are using int indexs, though you will have to start at 0.

Instead of adding to a dictionary by [key] = value you can use .append

func _physics_process(_delta):
	var player = get_parent()
	if current_global.laps > 0 and !current_global.laps >= 4 and $replay_delay.is_stopped():
		current_replay_data.append({"position": player.global_position})
		$replay_delay.start()

Indexing still works by [key] but again you want to start with zero, so swap these two lines to increment count after playing

func _process(_delta):
	if current_global.laps > 0 and !replaying:
		play()
		count += 1
		replaying = true

I don’t think this will solve your problem, but it may be more efficient. What did printing your dictionary output? Does it have a key 1?


Using process and physics process seems odd, you should use a timer and it’s .timeout signal directly instead, or on playback use the tween’s .finished. This will remove the superfluous replaying variables and process functions.

func start_replay() -> void:
	count = 0
	play()

func play() -> void:
	if count < current_replay_data.size():
		var tween = create_tween()
		tween.tween_property(self, "global_position", current_replay_data[count]["position"], 0.1)
		tween.finished.connect(play) # repeats the function 
		count += 1
1 Like

when I print my dictionary I get { “1”: { “position”: “(590.7505, 340)” }, “2”: { “position”: “(630.6117, 340)” }, “3”: { “position”: “(670.4729, 340)” }, “4”: { “position”: “(710.3341, 340)” }, “5”: { “position”: “(750.3342, 340)” }, etc, etc, for however many frames I recorded my data.

Change that to:

var current_replay_data: Array

Should pretty much fix the problems with your code.

Also you do not need to initialize empty Dictionary or Array objects.

You’re trying to use the Dictionary key as an index, just use the Array index.

One thing that perplexes me here is that your output is showing “1” and not simply 1 as the dictionary keys.
Is that accurate or are you retyping the output?
Consider the output of this code:

	var d:Dictionary = {1:"oop", 2:"poop"}
	print(d)

{ 1: "oop", 2: "poop" }

So why are you getting quoted (string) keys?
Everything I can see in your code suggests that instead of this:
“1”: { “position”: “(590.7505, 340)” },
you should be getting this:
1: { “position”: “(590.7505, 340)” },

And also regarding dragonforge recommendation, remember that an array can contain Dictionary objects:
var current_replay_data:Array[Dictionary]

1 Like

Through pure stubbornness I think i figured it out. Thank you everyone who helped!!

func replay():
	var frame_key = str(count)
	var frame_data = current_replay_data.get(frame_key)
	if frame_data == null:
		frame_data = current_replay_data.get(count)
	
	if frame_data is Dictionary and frame_data.has("position"):
		var raw_pos = frame_data["position"]
		var target_position
		if raw_pos is String:
			if raw_pos.begins_with("("):
					raw_pos = "Vector2" + raw_pos
			target_position = str_to_var(raw_pos)
		else:
			target_position = raw_pos
		if target_position is Vector2:
			var tween = create_tween()
			tween.tween_property(self, "global_position", target_position, 0.1)
			tween.finished.connect(replay)
			count += 1
		else:
			count += 1 
			call_deferred("replay")
	else:
		await get_tree().create_timer(0.1).timeout
		count += 1
		replay()

It’s jank and it could use some work, but it works!!