MonoGame чтения жестов

в настоящее время я пишу игру, используя фантастическую структуру моногамии. У меня проблемы с правильной реакцией на сенсорный ввод. Когда пользователь перетаскивает горизонтально или вертикально, я хочу выполнить действие один раз за перетаскивание. Проблема в том, что жест выдается несколько раз для каждого перетаскивания. Вот мой код:

var gesture = default(GestureSample);

while (TouchPanel.IsGestureAvailable)
    gesture = TouchPanel.ReadGesture();

if (gesture.GestureType == GestureType.VerticalDrag)
{
    if (gesture.Delta.Y < 0)
        return new RotateLeftCommand(_gameController);

    if (gesture.Delta.Y > 0)
        return new RotateRightCommand(_gameController);
}

if (gesture.GestureType == GestureType.HorizontalDrag)
{
    if (gesture.Delta.X < 0)
        return new RotateLeftCommand(_gameController);

    if (gesture.Delta.X > 0)
        return new RotateRightCommand(_gameController);
}

я переместил весь этот код в один блок для целей этого примера. В RotateRightCommand или RotateLeftCommand, который возвращается потом выполняется вызывающим кодом, который вращает объект на экране. Весь этот блок кода выполняется в цикле обновления в monogame. Я думаю, что мне не хватает чего-то с точки зрения сброса сенсорного ввода, и поэтому я получаю 3 или 4 RotateCommands, возвращенные для одного перетаскивания. Что я делаю не так?

2 ответов


Edit: вы неправильно используете жесты здесь. Жест перетаскивания генерируется каждый раз, когда палец движется по экрану. Если вы переместите палец с одной стороны экрана на другую (очень быстро), вы получите много небольших жестов перетаскивания. Вот как работают XNA и моногамия.

Это логика, которую вы ищете:

var touchCol = TouchPanel.GetState();

foreach (var touch in touchCol)
{
    // You're looking for when they finish a drag, so only check
    // released touches.
    if (touch.State != TouchState.Released)
        continue;

    TouchLocation prevLoc;

    // Sometimes TryGetPreviousLocation can fail. Bail out early if this happened
    // or if the last state didn't move
    if (!touch.TryGetPreviousLocation(out prevLoc) || prevLoc.State != TouchState.Moved)
        continue;

    // get your delta
    var delta = touch.Position - prevLoc.Position ;

    // Usually you don't want to do something if the user drags 1 pixel.
    if (delta.LengthSquared() < YOUR_DRAG_TOLERANCE)
        continue;

    if (delta.X < 0 || delta.Y < 0)
        return new RotateLeftCommand(_gameController);

    if (delta.X > 0 || delta.Y > 0)
        return new RotateRightCommand(_gameController);   
}

Если вы предпочитаете делать жесты, вы можете просто сделать это:

var gesture = default(GestureSample);

while (TouchPanel.IsGestureAvailable)
{
    gesture = TouchPanel.ReadGesture();

    if (gesture.GestureType == GestureType.DragComplete)
    {
        if (gesture.Delta.Y < 0 || gesture.Delta.X < 0)
            return new RotateLeftCommand(_gameController);
        if (gesture.Delta.Y > 0 || gesture.Delta.X > 0)
            return new RotateRightCommand(_gameController);
    }
}

в любом случае даст вам поведение вы находясь в поиске. Вам все равно нужно поместить логику внутри цикла, как я сказал в исходном сообщении, потому что вы можете иметь несколько жестов в очереди, и вы не хотите предполагать, что последний в стеке является перетаскиванием. (Благодаря мультитач вы можете иметь тащит, краны, перетащите полный и т. д.).

Я не на 100% уверен, что это ваша проблема... но здесь ваша логика неверна. Босс CodeFormatting избивает меня здесь, но это должно быть больше похоже:

var gesture = default(GestureSample);

while (TouchPanel.IsGestureAvailable)
{
    gesture = TouchPanel.ReadGesture();

    if (gesture.GestureType == GestureType.VerticalDrag)
    {
        if (gesture.Delta.Y < 0)
            return new RotateLeftCommand(_gameController);
        if (gesture.Delta.Y > 0)
            return new RotateRightCommand(_gameController);
    }

    if (gesture.GestureType == GestureType.HorizontalDrag)
    {
        if (gesture.Delta.X < 0)
            return new RotateLeftCommand(_gameController);
        if (gesture.Delta.X > 0)
            return new RotateRightCommand(_gameController);
    }
}

нет сенсорная панель.ReadGesure() внутри цикла, Вы читаете только одну gesure за кадр. Доступные жесты помещаются в очередь и удаляются только при вызове ReadGesture (). Поэтому, если ваша частота кадров икота, или если вы не можете прочитать жест так быстро, как вы ОС может прочитать его (что очень возможно), вы будете работать со старой информацией. Вы можете видеть это здесь. Для этого приложения я предлагаю взять каждый жест и объединить его в один или два, а затем решить, что вы должно быть, исходя из этого.


Я думаю, это зависит от того, что вы считаете за одну дозу. MonoGame и XNA считают обнаружение изменения сенсорного ввода от одного обновления к следующему перетаскиванием. Поэтому, если вы думаете, что получите один жест с момента касания пальцем экрана до его удаления, это будет неверно в терминах XNA и MonoGame. Жест, который возвращается, - это Дельта от T-1 до T, где T сейчас, а T-1-предыдущий вызов обновления.