用户控制 键盘鼠标自定义 脚本

原理

每个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;
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值