Recording character movement to be replayed as a ghost?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By MrKrakens
:warning: Old Version Published before Godot 3 was released.

Hello everyone! Just thought I’d think out loud and ask a question.

Right now I have a platformer game and I want to implement a “ghost” character that can replay the player’s previous runs.

I haven’t implemented this yet but I was thinking of having a struct like dictionary with arrays inside? :

  var prev_run_player_movements = {
    	x_pos = [],
    	y_pos = [],
            grav =  [],
            velocity = []
    }

Then throughout the game until the player dies I would add to the array’s the player’s x,y positions

Then when replaying the previous run, I would just iterate through prev_run_player_movements changing the player object’s x y pos to the held values.

But how would I also save the player’s animations too?

I have a state system (consts from 0 to 4) set up that checks if the player is moving or hit… so maybe instead of recording the exact movements, record at what time (real time value) each state was executed at?

 var prev_run_player_movements = {
        	recorded_state = [],
            at_recorded_time = []
        }

then iterate through that? As I type that actually seems way more logical. I guess the dictionary seems redundant when I could just have the arrays instead.

Thoughts?

:bust_in_silhouette: Reply From: Zylann

I have a work-in-progress of such a system too, which is able to record multiple retries of the same level, but I didn’t get to the point of replaying animations unfortunately.
A bit of advice though:

I save recordings into Track objects, like this:

class Track:
	var times = []
	var positions = []
	var events = []

Each frame, I record the time of the frame, and realtime variables such as position. Recording the time is important because when playing back, framerate may be different from the time the player was playing. Also I use the game time, not real time, because the game can be paused.
So I read the track by interpolating positions to obtain a smooth movement regardless of framerate.Then I do this for rotations or any realtime variables.

Then, I have events. These are dictionaries that contain at least a time and type variables, and each event is logged in order as time increases. They are things like “jump”, “take damage”, “finish level” etc.
When playing back the track, after interpolating variables, I also have a look to the next event. If the time of this event is lower than the current playback time, then I play the event (triggering animations mostly). Then I repeat this step until the next event is ahead of the playback time.

Here is the result of my rough work-in-progress: https://www.youtube.com/watch?v=oyosstz_-4E

As you can see it’s not complete, it’s mostly theory for me at the moment, but one day I may go further if I take the time :wink:

wow that looks really good!

How exactly are you getting the game time?

Right now I was thinking of having something like

func  _fixed_process(delta):
    	mydelta += delta
        pass

then when each state is triggered I would just grab mydelta and add it to an array.

MrKrakens | 2017-11-12 19:43

I’m doing like this, yeah (you don’t need the pass if your function has at least one instruction :wink: )

Zylann | 2017-11-12 19:45

:bust_in_silhouette: Reply From: digitorus

I’ve implemented a ghost character for playback for our game Xemo.
You can see a video example here:

Xemo Ghost on Vimeo

For our robot, I record the transform (position, orientation) and time offset from frame 0 at each frame, and then playback using VCR like controls. A few things to think about:

a) a way to stop recording when the dynamic object stops moving
b) need to record all of the dynamic (moving) objects.
c) I did not record audio for the playback.

In my case, the camera is an orbital camera pointing at the robot. You may want to record the camera position and orientation as well if you want to preserve the view.

:bust_in_silhouette: Reply From: Mrpaolosarino

No need to record and making your life difficult, here’s a tutorial for ghost with no coding whatsoever. I hope it helps:)

:bust_in_silhouette: Reply From: SanchFun

I’m probably too late, but if you still need this weight, then I think this will help