6

私は現在、幻想的なモノゲーム フレームワークを使用してゲームを書いています。タッチ入力に正しく反応できません。ユーザーが水平または垂直にドラッグすると、ドラッグごとに 1 回アクションを実行したいと考えています。問題は、ドラッグごとにジェスチャが複数回発行されることです。これが私のコードです:

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

この例のために、このコードをすべて 1 つのブロックに移動しました。返された RotateRightCommand または RotateLeftCommand は、画面上のオブジェクトを回転させる呼び出しコードによって実行されます。このコード ブロック全体が、モノゲームの更新ループで実行されています。タッチ入力のリセットに関して何か不足していると思います。そのため、1 回のドラッグで 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() をループ内に配置しないことで、フレームごとに 1 つのジェスチャのみを読み取ることになります。利用可能なジェスチャはキューに入れられ、ReadGesture() が呼び出されたときにのみ削除されます。したがって、フレームレートに問題が発生した場合、または OS が読み取れるほど速くジェスチャを読み取ることができない場合 (これは非常に可能性があります)、古い情報で作業していることになります。ここでそれを見ることができます。このアプリケーションでは、各ジェスチャを 1 つまたは 2 つにまとめ、それに基づいて何をすべきかを決定することをお勧めします。

于 2013-03-01T10:50:28.593 に答える
2

それはあなたが1つのドラッグを何と考えるかにかかっていると思います。MonoGame と XNA は、ある更新から次の更新へのタッチ入力の変化の検出をドラッグと見なします。そのため、指を画面に触れてから指を離すまでに 1 つのジェスチャを受け取ると考えている場合、XNA および MonoGame の用語では正しくありません。返されるジェスチャーは、T-1 から T までのデルタです。ここで、T は現在、T-1 は Update への前回の呼び出しです。

于 2013-02-06T17:27:51.183 に答える