1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-17 16:42:53 +08:00
osu-lazer/osu.Game/Modes/Replays/FramedReplay.cs

156 lines
6.1 KiB
C#
Raw Normal View History

2017-03-04 18:02:36 +08:00
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
2017-03-13 18:11:57 +08:00
using osu.Framework.Extensions.IEnumerableExtensions;
2017-03-04 18:02:36 +08:00
using osu.Framework.Input;
using osu.Framework.MathUtils;
using osu.Game.Input.Handlers;
using OpenTK;
using OpenTK.Input;
using KeyboardState = osu.Framework.Input.KeyboardState;
using MouseState = osu.Framework.Input.MouseState;
namespace osu.Game.Modes.Replays
2017-03-04 18:02:36 +08:00
{
public abstract class FramedReplay : Replay
2017-03-04 18:02:36 +08:00
{
protected List<ReplayFrame> Frames = new List<ReplayFrame>();
public override ReplayInputHandler CreateInputHandler() => new FramedReplayInputHandler(Frames);
2017-03-04 18:02:36 +08:00
/// <summary>
/// The ReplayHandler will take a replay and handle the propagation of updates to the input stack.
/// It handles logic of any frames which *must* be executed.
/// </summary>
public class FramedReplayInputHandler : ReplayInputHandler
2017-03-04 18:02:36 +08:00
{
private readonly List<ReplayFrame> replayContent;
2017-03-04 18:02:36 +08:00
public ReplayFrame CurrentFrame => !hasFrames ? null : replayContent[currentFrameIndex];
public ReplayFrame NextFrame => !hasFrames ? null : replayContent[nextFrameIndex];
2017-03-06 13:18:44 +08:00
2017-03-09 12:55:29 +08:00
private int currentFrameIndex;
2017-03-06 13:18:44 +08:00
private int nextFrameIndex => MathHelper.Clamp(currentFrameIndex + (currentDirection > 0 ? 1 : -1), 0, replayContent.Count - 1);
2017-03-04 18:02:36 +08:00
public FramedReplayInputHandler(List<ReplayFrame> replayContent)
2017-03-04 18:02:36 +08:00
{
this.replayContent = replayContent;
}
2017-03-06 13:18:44 +08:00
private bool advanceFrame()
2017-03-04 18:02:36 +08:00
{
2017-03-06 13:18:44 +08:00
int newFrame = nextFrameIndex;
2017-03-04 18:02:36 +08:00
//ensure we aren't at an extent.
if (newFrame == currentFrameIndex) return false;
currentFrameIndex = newFrame;
return true;
}
public void SetPosition(Vector2 pos)
{
}
private Vector2? position
{
get
{
if (!hasFrames)
return null;
return Interpolation.ValueAt(currentTime, CurrentFrame.Position, NextFrame.Position, CurrentFrame.Time, NextFrame.Time);
}
}
public override List<InputState> GetPendingStates()
{
2017-03-13 18:11:57 +08:00
var buttons = new HashSet<MouseButton>();
if (CurrentFrame?.MouseLeft ?? false)
buttons.Add(MouseButton.Left);
if (CurrentFrame?.MouseRight ?? false)
buttons.Add(MouseButton.Right);
2017-03-04 18:02:36 +08:00
return new List<InputState>
{
new InputState
{
2017-03-13 18:11:57 +08:00
Mouse = new ReplayMouseState(ToScreenSpace(position ?? Vector2.Zero), buttons),
2017-03-04 18:02:36 +08:00
Keyboard = new ReplayKeyboardState(new List<Key>())
}
};
}
public bool AtLastFrame => currentFrameIndex == replayContent.Count - 1;
public bool AtFirstFrame => currentFrameIndex == 0;
public Vector2 Size => new Vector2(512, 384);
2017-03-07 10:29:55 +08:00
private const double sixty_frame_time = 1000.0 / 60;
2017-03-04 18:02:36 +08:00
2017-03-09 12:55:29 +08:00
private double currentTime;
private int currentDirection;
2017-03-04 18:02:36 +08:00
/// <summary>
/// When set, we will ensure frames executed by nested drawables are frame-accurate to replay data.
/// Disabling this can make replay playback smoother (useful for autoplay, currently).
/// </summary>
public bool FrameAccuratePlayback = true;
private bool hasFrames => replayContent.Count > 0;
2017-03-09 12:55:29 +08:00
private bool inImportantSection =>
2017-03-04 18:02:36 +08:00
FrameAccuratePlayback &&
//a button is in a pressed state
((currentDirection > 0 ? CurrentFrame : NextFrame)?.IsImportant ?? false) &&
2017-03-04 18:02:36 +08:00
//the next frame is within an allowable time span
Math.Abs(currentTime - NextFrame?.Time ?? 0) <= sixty_frame_time * 1.2;
/// <summary>
/// Update the current frame based on an incoming time value.
/// There are cases where we return a "must-use" time value that is different from the input.
/// This is to ensure accurate playback of replay data.
/// </summary>
/// <param name="time">The time which we should use for finding the current frame.</param>
2017-03-06 13:20:44 +08:00
/// <returns>The usable time value. If null, we should not advance time as we do not have enough data.</returns>
2017-03-04 18:02:36 +08:00
public override double? SetFrameFromTime(double time)
{
currentDirection = time.CompareTo(currentTime);
if (currentDirection == 0) currentDirection = 1;
if (hasFrames)
{
//if we changed frames, we want to execute once *exactly* on the frame's time.
2017-03-06 13:18:44 +08:00
if (currentDirection == time.CompareTo(NextFrame.Time) && advanceFrame())
2017-03-04 18:02:36 +08:00
return currentTime = CurrentFrame.Time;
//if we didn't change frames, we need to ensure we are allowed to run frames in between, else return null.
if (inImportantSection)
return null;
}
return currentTime = time;
}
protected class ReplayMouseState : MouseState
2017-03-04 18:02:36 +08:00
{
2017-03-13 18:11:57 +08:00
public ReplayMouseState(Vector2 position, IEnumerable<MouseButton> list)
2017-03-04 18:02:36 +08:00
{
Position = position;
2017-03-13 18:11:57 +08:00
list.ForEach(b => PressedButtons.Add(b));
2017-03-04 18:02:36 +08:00
}
}
protected class ReplayKeyboardState : KeyboardState
2017-03-04 18:02:36 +08:00
{
public ReplayKeyboardState(List<Key> keys)
{
Keys = keys;
}
}
}
}
}