6

我目前正在使用梦幻般的 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。我究竟做错了什么?

4

2 回答 2

16

编辑:您在这里没有正确使用手势。每次手指在屏幕上移动时,都会生成一个拖动手势。如果您将手指从屏幕的一侧移动到另一侧(非常快),您会得到很多小的拖动手势。这就是 XNA 和 MonoGame 的工作方式。

这是您正在寻找的逻辑:

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);
    }
}

通过不将 TouchPanel.ReadGesure() 放入循环中,您每帧只能读取一个手势。可用的手势被放入队列中,并且仅在调用 ReadGesture() 时被移除。因此,如果您的帧速率出现问题,或者您无法像操作系统读取手势那样快(这很有可能),那么您将使用旧信息。你可以在这里看到。对于这个应用程序,我建议采用每个手势,并将其组合成一个或两个,然后根据它决定你应该做什么。

于 2013-03-01T10:50:28.593 回答
2

我想这取决于你认为什么是阻力。MonoGame 和 XNA 考虑检测触摸输入从一次更新到下一次拖动的变化。因此,如果您认为从手指触摸屏幕直到您将其移除,您将收到一个手势,那么在 XNA 和 MonoGame 术语中这是不正确的。返回的 Gesture 是从 T-1 到 T 的 Delta,其中 T 是 Now,T-1 是之前对 Update 的调用。

于 2013-02-06T17:27:51.183 回答