Is it possible to record an action replay?

Hi there,

I have a scene with one pretty simple vehicle in it and I’d love to be able to record a replay of it’s movements, so the user could watch it over.
But, I wouldn’t have the first clue on how to do this and I have a feeling it would slow your game right down recording it…

Has anyone got any idea about this?
Thanks
Pete

Hmm, never done and not sure how it’s normally done, but it shouldn’t slow your game right down. The idea is not to record what’s happening to on the screen, but simply the inputs and positions that lead to the action that’s happening on the screen.

For replays of a car racing around a track, two ideas pop into my head.

1 - An array that tracks the user input and how long they do it for (so it basically records “user was steering left for 2 seconds; user straight for 1 second; user hit brakes and steered right for 1 second ; user released brakes and continued steering right for 1 second;”…etc)

As long as there was some reference to the position of the player’s car in there as well… your replay engine could simply place the car at a stored position, and run most of the normal game engine for the replay, but instead of getting user input, it would just read the inputs you had recorded earlier.

2 - A big array of Vector3Ds that simply stored the position and rotation of the car every single frame. More memory needed for replay storage, but it would be heaps easier to record and play back.

Not sure if either of those ideas are any good. I imagine they’d work well for a desktop PC, but a mobile has such a small amount of memory that maybe it would blow it out too quickly?

There’s no one way to do it…for example, the Myth games record what the player did, and when you reload a saved game, it basically replays everything back at high speed. I expect the films are stored the same way. This requires everything to be 100% deterministic, though. (Which the physics engine in Unity is not…different machines etc. can result in slightly different behavior.)

Storing position/rotation for desired objects is fairly typical, but it doesn’t have to be every frame. Just do it a few times per second, and interpolate between positions when replaying films. The result won’t be 100% exact compared to the original, but it’s normally hard to tell the difference.

–Eric

Wow, that sounds kinda promising actually!
I’m not sure though where I would start with recording these values though. Could you point me in the right direction with that?

Hehe, I used to have a game that would sometimes replay things totally differently to how they actually happened. It was kinda funny though. (must have been having some issues with their physics)

I’ve thought about this…

Depending on you control your world, you could essentially make a giant array and store a list of every function called with timing, perhaps.

For example, if every time the player touches a control method you call a GoTohere function, you could store that fact in an array with everything that was parsed to it, and store it in a plist or something.

I mean you might end up with a 500k text file, but that might also store several minutes of gameplay…

Just an idea, I haven’t tried it myself :slight_smile:

I think I’d prefer to record the results though, rather than the inputs.
As Eric said, the physics engine will not always produce the same results.
I’m just not sure how to record or access that data.

Ahh, well… I spose…

If you’ve written it all based on random stuff, then sure.

Unfortunatly, recording every objects xyz//rotation/state for a minute (lets say you have 10 objects onscreen) at an even remotely usable frame rate (lets say 20fps so you can interpolate it for the higher playback speed, which will still be prone to random things happening like things falling through floors etc), it’ll start to get pretty wasteful.

Things like blood splattering or debris etc, as long as its not game play critical is totally arbitrary and doesn’t require recording.

If you blow 30 pieces of debris off an object, would you rather just called a blowStuffUp(30,blah,blah2) or would you rather store xyz/rot for all 30 objects at 20fps?

Yeah, i see what you mean with effects etc.
My game is pretty basic though.
I don’t think I’d have to record too much stuff.

There’s actually a middleware package written for Unity that will record/playback all of this for you:

http://lastbastiongames.com/middleware/mantarecorder/index.shtml

That guy is a legend :slight_smile:

Jcar has a recording feature which I’m sure you can alter to suit your needs.

http://ctrl-j.com.au/pages/jcarsrc.html

MantaRecord won’t work on the iPhone as it uses reflection. I haven’t looked at the recording features in JCar but that sounds like a good place to start. Even if you end up not using JCar, it may give you some ideas.

I currently have a system that I use to save and load scenes. I write the transform, the rotation and and some of the properties from the rigid body. The file size if pretty tiny but I can confirm that the physics system doesn’t always behave exactly the same way. Of course, this may be because I’m not storing enough information about the rigidbody.

I’d try Choinkees second suggestion (store just transform and rotation) just to see how well that works. Once you have some results to look at you can figure out if and how much you need to interpolate.

He’s just storing rotation, position and velocity. Assuming a 30 FPS, 1000 samples would give you 30 seconds. I wonder if reading/writing every 1/15 of a second in the FixedUpdate would be any better?

using UnityEngine;
using System.Collections;
using System;

public class JRecordRoute : MonoBehaviour {

	public int maxSamples = 1000;
		
	JCarPoint[] trail;
	int index = 0;
	int len = 0;
	
	// Use this for initialization
	void Start () {
		trail = new JCarPoint[maxSamples];
	}
	
	// Update is called once per frame
	public void Add(GameObject go, float accel, float steer, int gear) {
		int i = index % maxSamples;
		trail[i].pos = go.transform.localPosition;
		trail[i].rot = go.transform.localRotation;
		trail[i].vel = go.rigidbody.velocity;
		trail[i].accel = accel;
		trail[i].steer = steer;
		trail[i].gear = gear;
		index++;
		if (len < maxSamples) len++;	
	}
	
	public int GetLen() {
		return len;
	}
	
	public int GetPos() {
		return index;
	}
	
	public bool IsValid(int stamp) {
		return !((stamp >= index) || (stamp < index - len));
	}
	
	public JCarPoint GetHistory(int stamp) {
		if ((stamp >= index) || (stamp < index - len)) {
			throw new Exception("trying to read at offset " + stamp + " but have only " + len + " elements, pos " + index);
		}
		return trail[stamp % maxSamples];
	}
}