MonoGame阅读触摸手势

时间:2013-02-02 14:03:35

标签: c# iphone xamarin.ios monogame

我目前正在使用梦幻般的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 个答案:

答案 0 :(得分:15)

编辑:你这里没有正确使用手势。每次手指在屏幕上移动时都会生成拖动手势。如果您将手指从屏幕的一侧移动到另一侧(非常快),您将获得许多小拖动手势。这就是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);
    }
}

无论哪种方式都会为您提供您正在寻找的行为。您仍然需要像我在原始帖子中所说的那样将逻辑放在循环内部,因为您可以排队多个手势,并且您不希望假设堆栈中的最后一个是拖动。 (多边触控,你可以使用拖动,点击,拖动完成等)。

我不是百分百确定这是否是你的问题...但你的逻辑在这里是错误的。 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()放在循环中,那么每帧只能读取一个gesure。可用的手势被放入队列中,只有在调用ReadGesture()时才会被删除。因此,如果您的帧率打嗝,或者您无法像操作系统可以读取的那样快速读取手势(这很可能),您将使用旧信息。你可以看到here。对于这个应用程序,我建议采取每个手势,并将其组合成一个或两个,然后根据它决定你应该做什么。

答案 1 :(得分:2)

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