原理
每个input会在Update()时检测输入,判断属于什么PlayerAction.
选择初始化不同的input(键盘或鼠标或手柄或触摸)之后,update接口保持一致即可。
*同时,当游戏层需要有什么去影响到操作时,只需要让input类订阅特定事件即可。
public Game(Board board, IPlayerInput input)
{
this.board = board;
this.input = input;
PieceFinishedFallingEvent += input.Cancel;
}
比如上面代码中,当方格已经完成掉落后,会出发input.Cancel事件,该事件主要是清空之前对于案件部分的检测的历史等。
使用方法:
universalInput = new UniversalInput(new KeyboardInput(), boardView.touchInput);
在主循环里,调用
input.Update();
var action = input?.GetPlayerAction();
if (action.HasValue)
{
HandlePlayerAction(action.Value);
}
——————————————————
自定义控制Input类,需要实现接口:
public interface IPlayerInput
{
PlayerAction? GetPlayerAction();
void Update();
void Cancel();
}
比如:
StubInput:
public class StubInput : IPlayerInput
{
public PlayerAction? action;
public void Update() { }
public void Cancel() { }
public PlayerAction? GetPlayerAction()
{
return action;
}
KeyboardInput
public class KeyboardInput : IPlayerInput
{
KeyCode pressedKey = KeyCode.None;
float nextRepeatedKeyTime;
Dictionary<KeyCode, PlayerAction> actionForKey = new Dictionary<KeyCode, PlayerAction>
{
{KeyCode.LeftArrow, PlayerAction.MoveLeft},
{KeyCode.RightArrow, PlayerAction.MoveRight},
{KeyCode.DownArrow, PlayerAction.MoveDown},
{KeyCode.UpArrow, PlayerAction.MoveUp},
{KeyCode.Space, PlayerAction.Rotate},
};
readonly List<KeyCode> repeatingKeys = new List<KeyCode>()
{
KeyCode.LeftArrow,
KeyCode.RightArrow,
KeyCode.DownArrow
};
public PlayerAction? GetPlayerAction()
{
var actionKeyDown = GetActionKeyDown();
if (actionKeyDown != KeyCode.None)
{
StartKeyRepeatIfPossible(actionKeyDown);
return actionForKey[actionKeyDown];
}
if (Input.GetKeyUp(pressedKey))
{
Cancel();
}
else
{
return GetActionForRepeatedKey();
}
return null;
}
public void Update() { }
public void Cancel()
{
pressedKey = KeyCode.None;
}
void StartKeyRepeatIfPossible(KeyCode key)
{
if (repeatingKeys.Contains(key))
{
pressedKey = key;
nextRepeatedKeyTime = Time.time + Constant.Input.KeyRepeatDelay;
}
}
KeyCode GetActionKeyDown()
{
foreach (var key in actionForKey.Keys)
{
if (Input.GetKeyDown(key))
{
return key;
}
}
return KeyCode.None;
}
PlayerAction? GetActionForRepeatedKey()
{
if (pressedKey != KeyCode.None && Time.time >= nextRepeatedKeyTime)
{
nextRepeatedKeyTime = Time.time + Constant.Input.KeyRepeatInterval;
return actionForKey[pressedKey];
}
return null;
}
}
TouchInput
public class TouchInput : IPlayerInput
{
public float blockSize;
public bool Enabled
{
get => enabled;
set
{
enabled = value;
cancelCurrentTouch = false;
playerAction = null;
}
}
Vector2 initialPosition = Vector2.zero;
Vector2 processedOffset = Vector2.zero;
PlayerAction? playerAction;
bool moveDownDetected;
float touchBeginTime;
readonly float tapMaxDuration = 0.25f;
readonly float tapMaxOffset = 30.0f;
readonly float swipeMaxDuration = 0.3f;
bool cancelCurrentTouch;
private bool enabled = true;
public void Update()
{
playerAction = null;
if (Input.touchCount > 0)
{
var touch = Input.GetTouch(0);
if (cancelCurrentTouch)
{
cancelCurrentTouch &= touch.phase != TouchPhase.Ended;
}
else if (touch.phase == TouchPhase.Began)
{
TouchBegan(touch);
}
else if (touch.phase == TouchPhase.Moved)
{
var offset = touch.position - initialPosition - processedOffset;
HandleMove(touch, offset);
}
else if (touch.phase == TouchPhase.Ended)
{
var touchDuration = Time.time - touchBeginTime;
var offset = (touch.position - initialPosition).magnitude;
if (touchDuration < tapMaxDuration && offset < tapMaxOffset)
{
playerAction = PlayerAction.Rotate;
}
else if (moveDownDetected && touchDuration < swipeMaxDuration)
{
playerAction = PlayerAction.Fall;
}
}
}
else
{
cancelCurrentTouch = false;
}
}
public PlayerAction? GetPlayerAction()
{
return Enabled ? playerAction : null;
}
public void Cancel()
{
cancelCurrentTouch |= Input.touchCount > 0;
}
void TouchBegan(Touch touch)
{
initialPosition = touch.position;
processedOffset = Vector2.zero;
moveDownDetected = false;
touchBeginTime = Time.time;
}
void HandleMove(Touch touch, Vector2 offset)
{
if (Mathf.Abs(offset.x) >= blockSize)
{
HandleHorizontalMove(touch, offset.x);
playerAction = ActionForHorizontalMoveOffset(offset.x);
}
if (offset.y <= -blockSize)
{
HandleVerticalMove(touch);
playerAction = PlayerAction.MoveDown;
}
}
void HandleHorizontalMove(Touch touch, float offset)
{
processedOffset.x += Mathf.Sign(offset) * blockSize;
processedOffset.y = (touch.position - initialPosition).y;
}
void HandleVerticalMove(Touch touch)
{
moveDownDetected = true;
processedOffset.y -= blockSize;
processedOffset.x = (touch.position - initialPosition).x;
}
PlayerAction ActionForHorizontalMoveOffset(float offset)
{
return offset > 0 ? PlayerAction.MoveRight : PlayerAction.MoveLeft;
}
}
UniversalInput
然后用UniversalInput包裹自定义的内容:
public class UniversalInput : IPlayerInput
{
List<IPlayerInput> inputs = new List<IPlayerInput>();
public UniversalInput(params IPlayerInput[] inputs)
{
this.inputs = new List<IPlayerInput>(inputs);
}
public void Update() => inputs.ForEach(input => input.Update());
public void Cancel() => inputs.ForEach(input => input.Cancel());
public PlayerAction? GetPlayerAction()
{
foreach (var input in inputs)
{
var action = input.GetPlayerAction();
if (action != null)
{
return action;
}
}
return null;
}
版权声明:本文为killian0213原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。