0
\$\begingroup\$

I am creating a game in Unity 3D and I have recently come up with my own simple replay system. It works well except for one small problem: it doesn't always start off where I would prefer it to in the recording of the gameplay. Basically every replay should be 8 seconds long. All I am doing for my replay system is storing the position and rotation of all objects in the scene, since there are only a few, in an array and then playing them back once an even is triggered.

I have the logical idea of how I should pick a starting point for the replay to start playing. I have a counter that increments every time my StorePosition() function is called in FixedUpdate(). That counter essentially gives me the number of indexes in the array so I know how many positions have been stored in total. All I should have to do is take that number and subtract from it the number of iterations that would occur during the 8 second replay.

My issue is that if I do the math for this: FixedUpdate() is called every .02 seconds which equates to 50 calls per second which would mean FixedUpdate() should be called 400 times over the time of my replay. So should I just be taking that total length of the stored positions array and subtracting that 400 and that would be the starting position in the array to start my replay?

That is the way I work it out in my head but it doesn't seem to work out quite right in the game. My counter seems to get into the 4000s or more over the 8 second replay so just subtracting the 400 from it doesn't give me anywhere near the amount of the replay that I want to see.

Is my logic flawed here? Any ideas?

2/21/2016

Here is some code for an example of how I handle this:

static Dictionary<GameObject, Vector3[]> positions = new Dictionary<GameObject, Vector3[]>();
public static int positionFillLength = 0;
static int index = 0;

public void StorePositions(GameObject[] objects)
{
    foreach (var obj in objects)
    {
        if (obj != null)
        {
            if (!positions.ContainsKey(obj))
                positions.Add(car, new Vector3[10800]);
            positions[car][index] = car.transform.position;
            positionFillLength++;
            index++;
        }
    }
}

public void PlayReplay(int index, GameObject[] gobjects)
{
    foreach (var obj in positions)
    {
        if (Array.IndexOf(gobjects, obj.Key) > -1)
        {
            if (positions[obj.Key][index] != Vector3.zero)
            {
                gobjects[Array.IndexOf(gobjects, obj.Key)].transform.position = positions[obj.Key][index];
            }
        }
    }
}

So if I am storing values I just call StorePositions() at ever call of FixedUpdate() and send in the objects to get the positions of.

When it is time to play a replay, I just start calling PlayReplay(). I create an index that starts at size 0 and increments every time that PlayReplay is called, which will also be every time FixedUpdate() is called, and pass that along with the objects into PlayReplay().

\$\endgroup\$
  • \$\begingroup\$ Your math looks right if you're doing this in FixedUpdate, and each object has its own array or each array entry is a structure containing all objects' data. Can you show us the relevant snippet of your code? Another debugging step you can try is to record a timestamp each time you save a position — by looking at the timestamp replay you'll be able to determine when data is being written. \$\endgroup\$ – DMGregory Feb 21 '16 at 13:19
  • \$\begingroup\$ @DMGregory Please see my edit. \$\endgroup\$ – saboehnke Feb 21 '16 at 16:05
  • \$\begingroup\$ Looks like there may be some code missing? I don't see where index is defined in StorePositions or what positionFillLength is used for. \$\endgroup\$ – DMGregory Feb 21 '16 at 16:08
  • \$\begingroup\$ @DMGregory I cut out some code so I must have missed index. It is fixed now. Also, positionFillLength is just what I use to keep track of the amount of saved positions I have. That is the total size of the saved positions I would subtract the amount of interations over the replay length to find the starting position of the replay itself. \$\endgroup\$ – saboehnke Feb 21 '16 at 16:13
1
\$\begingroup\$

Loos like the problem is here:

public void StorePositions(GameObject[] objects)
{
    foreach (var obj in objects)
    {
        if (obj != null)
        {
            if (!positions.ContainsKey(obj))
                positions.Add(car, new Vector3[10800]);
            positions[car][index] = car.transform.position;
            positionFillLength++; // <-- these lines
            index++;              // <--
        }
    }
}

positionFillLength and index are being incremented once for every object getting a position update, instead of once per update of the complete set of objects.

That means if you have 10 objects, then in the first FixedUpdate() pass, one gets its position stored in index 0, the next gets its position stored in index 1.... until by the end of the frame index and positionFillLength are both 10, even though you've only stored one position for each object. By the second FixedUpdate you're up to 20... By 8 seconds in (400 FixedUpdates) you're up to around the 4000th index, exactly as you describe.

You end up leaving strides of 9 empty entries between valid position stores in each array.

Try moving the incrementing of positionFillLength and index to the bottom, outside of the foreach loop.

|improve this answer|||||
\$\endgroup\$
  • \$\begingroup\$ Wow I can't believe I didn't see that. I went ahead and fixed that. My math doesn't seem to be working out though. I went ahead and created an index to test a count of iterations over the replay period. I took that count and subtracted it from the positionsFillLength variable and it still seems to go back too far so the part I want to see in the replay is cut off. \$\endgroup\$ – saboehnke Feb 21 '16 at 17:10
  • \$\begingroup\$ I figured out the issue. Just needed to make sure the index passed into PlayReplay() was getting initiated correctly. Thank you for your help. \$\endgroup\$ – saboehnke Feb 21 '16 at 17:44
  • 1
    \$\begingroup\$ Awesome. :) One other thing you may find useful is storing this data in a ring buffer. \$\endgroup\$ – DMGregory Feb 21 '16 at 17:46
  • \$\begingroup\$ Good pointer. Right now I just reinitialize the positions Dictionary as a new Dictionary every time I finish playing the replay. If that turns out to not be efficient enough, then a ring buffer will probably be my next step. I appreciate all the help. =) \$\endgroup\$ – saboehnke Feb 21 '16 at 17:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.