前言

个人认为 SuperHot 的核心是时间控制和回放,一个一个实现。

试了一下发现时间控制实现起来挺简单的,因为 Unity 已经提供了时间尺度(时间流速)的接口,所以只需要包装一下就好了。

代码暂时先不传,等后面的回放做完再一起传吧。

TimeScaleManager

由于 UnityEngine.Time.timeScale 是 static 的,所以我这边用单例模式进行管理。

之后如果做成联机,让每个玩家都是独立的话,不知道难度怎么样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class TimeScaleManager
{
// Singleton instance
private static TimeScaleManager _instance;

// Data
private float _timeScale = 1f;

public static TimeScaleManager Instance
{
get
{
if (_instance == null)
{
_instance = new TimeScaleManager();
}

return _instance;
}
}

public void SetTimeScale(float scale)
{
if (scale < 0f)
{
throw new System.ArgumentOutOfRangeException(nameof(scale), "Time scale cannot be negative.");
}

Instance._timeScale = scale;
UnityEngine.Time.timeScale = scale;
}

public float GetTimeScale()
{
return Instance._timeScale;
}

public void ResetTimeScale()
{
Instance._timeScale = 1f;
UnityEngine.Time.timeScale = 1f;
}
}

Controller2D

这里主要是加了个 PlayerState 来表示玩家的状态,这样的话就可以根据状态来决定具体如何操控时间。

我这里的话就是实现到只有静止的时候才会减慢时间流速,其他时候都保持正常速度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
public class Controller2D : MonoBehaviour
{
private enum PlayerState
{
Idle,
Walking,
Shooting,
}

private PlayerState _playerState = PlayerState.Idle;

// Walking
private float _moveSpeed = 5f;

private void Awake()
{
TimeScaleManager.Instance.SetTimeScale(1f);
}

private void Update()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
Application.Quit();
}

OnMove();
OnAttack();

OnPlayerStateChange();
}

private void OnMove()
{
var movement = new Vector3(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"), 0f);

if (movement.magnitude > 0.1f)
{
_playerState = PlayerState.Walking;
transform.Translate(movement * (_moveSpeed * Time.deltaTime));
}
else
{
_playerState = PlayerState.Idle;
}
}

private void OnAttack()
{
if (Input.GetMouseButtonDown(0)) // Left mouse button
{
_playerState = PlayerState.Shooting;
Debug.Log("Player is shooting.");
}
}

private void OnPlayerStateChange()
{
// Handle player state changes
if (_playerState == PlayerState.Idle)
{
TimeScaleManager.Instance.SetTimeScale(0.1f);
}
else
{
TimeScaleManager.Instance.ResetTimeScale();
}
}
}