【问题标题】:MonoGame reading touch gesturesMonoGame 阅读触摸手势
【发布时间】:2013-02-02 14:03:34
【问题描述】:

我目前正在使用奇妙的 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。我做错了什么?

【问题讨论】:

    标签: c# iphone xamarin.ios monogame


    【解决方案1】:

    编辑:您在这里没有正确使用手势。每次手指在屏幕上移动时,都会生成一个拖动手势。如果您将手指从屏幕的一侧移动到另一侧(非常快),您会得到很多小的拖动手势。这就是 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() 时被移除。因此,如果您的帧速率出现问题,或者您无法像操作系统读取手势那样快(这很有可能),那么您将使用旧信息。你可以看到here。对于这个应用程序,我建议将每个手势组合成一个或两个,然后根据它决定你应该做什么。

    【讨论】:

    • 感谢您的反馈。不过,我的阅读手势在 while 循环中。我刚刚省略了大括号作为它的单行 while 循环。我认为这样做会在有可用手势时停留在 while 循环中。除非我错过了什么:S
    • 你错过了一些东西。手势读取和处理都需要在同一个 while 循环中。您发布的代码所做的是读取手势,直到没有更多可用的手势,然后处理它读取的最后一个手势。如果队列中有 3 个手势,您将丢弃前两个,然后处理第三个。你应该做的是阅读第一个,处理它。阅读第二个,处理它,等等。您正在丢弃数据,这很容易成为问题的一部分。编辑:您正在使用什么平台?我写了很多MonoGame的输入代码,可以看看。
    • 非常感谢您帮助 Ray 对其进行排序。问题就像你正确地指出了我对手势工作方式的误解。我以为它只会给你完整的手势。我看到的问题是,像你说的那样,一个长阻力被解释为 3 或 4 个较小的阻力。我不知道您必须为此编写代码。非常感谢您的帮助。
    • 我刚刚尝试插入此代码,但我遇到了问题,因为 DragComplete 事件的 Delta 始终为 0、0。所以我必须记住之前的事件并使用其中的增量。我不确定这是故意的还是我想指出的。
    • @KevinHolditch 从逻辑上讲,这是有道理的。 DragComplete 将在手指不再移动时发生,即零增量。如果手指在移动时(超过阈值)被抬起,那么它将被视为Swipe'Flick
    【解决方案2】:

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

    【讨论】:

    • 感谢您的帮助。我虽然可能是这种情况,这就是为什么我将 while 循环放在顶部。我假设如果您连续拖动(在屏幕的整个长度上) IsGestureAvailable 将在整个运动中返回 true。在这种情况下,上述代码只能记录一次拖动。这就是为什么我感到困惑的是,当您执行我所描述的那种拖动时,您会得到上面代码返回的 3 或 4 个命令。
    • 尝试将“if”语句与 ReadGesture() 一起包含在“while”中。我看到了更好的结果。但是,它仍然不会等到您完成拖动。 VerticalDrag 和 Horizo​​ntalDrag 只是 FreeDrag 的受限版本,因此如果您在屏幕上长滑,您将获得多个手势。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-29
    • 1970-01-01
    • 2015-10-21
    • 1970-01-01
    相关资源
    最近更新 更多