AからBにジャンプできますか?


10

横スクロール用の基本的なAIを作成しています。ジャンプを実行するだけで、AIユニットがポイントAからポイントBに到達できるかどうかを知る必要があります。

私の文字の飛行軌道はとても古典とは異なり、彼らは(例えばジャズJackrabbitの2のように)空中に力を加えることができるように、ビットunusalで発射の弾道についてです...

投射または発射された発射体が推進力なしに進む経路(...)

...私の問題は、推進力を備えた発射体(ロケットなど)に関するものだと思います。

これを説明するために、ジャンプして「左ボタン」を押し続けると、これは私のキャラクターの飛行曲線のようになります(左端では異なります。空中でマニューバを作成していた場所です)。 ここに画像の説明を入力してください

飛行中に加えられる力は常にX軸に平行であるため、「左」を押し続けるF =(-f、0)、「右」を押し続けるF =(f、0)になります。

彼はスキージャンパーのように非常に動くことができます:

ここに画像の説明を入力してください

したがって、それは単に放物線である古典的な軌道とは大きく異なります(出典:ウィキペディア)。

ここに画像の説明を入力してください

さらに難しくするために、単純な空気抵抗をシミュレートしているので、キャラクターはある最大速度値までしか加速できません。

これは、反対の移動方向に小さな力を加えることによって行われます

b2Vec2 vel = body->GetLinearVelocity();
float speed = vel.Normalize(); //normalizes vector and returns length
body->ApplyForce( AIR_RESISTANCE_MULT * speed * speed * -vel, body->GetWorldCenter() );

AIR_RESISTANCE_MULTは定数で、私の場合は0.1です。

私の性格は無限に小さいと仮定しましょう。

そして、私は障害物を考慮に入れていないので、私の質問は次のようになります...

初期速度V、ジャンプ時にキャラクターに適用する衝撃J =(0、-j)、重力G =(0、g)、力F =(+ -f)を指定して(少なくとも確実に推測する)を決定する方法、0)飛行中に継続的に適用され、空気抵抗を考慮することにした場合はAIR_RESISTANCE_MULT (これはオプションです) 、ポイントが、キャラクターがたどるパスによって描かれた曲線の下にあるかどうか

文字通り、どこから計算を始めればよいのかまったくわかりません。実際、必ずしも正確な答えに興味があるわけではありません。AIが完全に動作する必要はないので、うまく機能するハック/近似は素晴らしいでしょう。

編集: Jasonが示唆するように、シミュレーションを使用してこれを解決することにしましたが、そのような場合の対処方法は? ここに画像の説明を入力してください

CからDへのセグメントを描画し、目的のポイントがこのセグメントの下にあるかどうかを確認する必要がありますか?

または、CDの間のタイムステップをバイナリ検索して、目的のポイントに対して水平距離が十分に近いポイントを探し、その後、垂直方向の差を確認する必要がありますか?(私には少しやり過ぎに思えます)


:私は、我々が考慮に空気抵抗を取ることはありませんケースのためのソリューション見つけたと思う gamedev.stackexchange.com/questions/37916/...
はPatryk Czachurski

回答:


4

あなたが述べているように、最良の選択は、この場合は数値スキームを使用して概算することです。時間を大きなタイムステップ(たとえば100〜300ミリ秒)に分割し、各タイムステップに放物線近似を使用します。空気抵抗以外は、力は同じです。放物線の経路は基本的には一定の加速度に対するものですが、空気抵抗により、力は速度に依存するため、加速度が変化します。合理的な概算は、空気抵抗を各タイムステップにわたって一定として扱うことです。ただし、積分時に2次(つまり、放物線)近似を使用すると、はるかに大きなタイムステップを処理できます。次に、放物線が水平方向の目的のポイントを横切るまで計算し、高さを比較します。

編集:比較についてもう少し詳しく。タイムステップ(ゲームフレームでは多くなる可能性があります)の間に、プレーヤーがターゲットを横切ることを知ってい<targetx,targety>ます。それらのパスは、次の位置によって記述されます<ax*t^2 + bx*t + cx, ay*t^2 + by*t + cy>

ax = 1/2 * accel.x
bx = velocity.x
cx = position.x

tは、タイムステップ(0 <= t <= dt)を介した時間ですy。つまりt=0、キャラクターが前の位置にあるときt=dt、およびが次の位置にあるときです。これは基本的にオイラー更新であり、軌道に沿ってどこでも計算できるようにdt置き換えられてtいることに注意してください。これで、x位置が2次関数であることを理解できたので、キャラクターがターゲットの真上または真下にいるステップ中に、2回まで(最大で)解決 ax*t^2 + bx*t + cx = targetxおよび取得できます。次に、範囲[0、dt]、これらは現在のタイムステップにないため。(堅牢性のために、範囲の両端に小さな定数を追加して、丸めの問題が発生しないようにします)。これで、(フィルタリング後の)解決策はありません。その場合、このタイムステップでターゲットに到達しません。そうでない場合ay*t^2 + by*t + cyは、解で評価し、このyをと比較しtargetyます。軌道のある時点で目標より上になり、後でそれより下になる可能性があることに注意してください(またはその逆)。そのような状況は、何をしたいのかに従って解釈する必要があります。

一連のタイムステップを考慮することは、元の問題の分析的解決策を見つけるよりもはるかに簡単であり、モーションモデルを変更でき、これでも大まかに機能するため、はるかに柔軟です。

たとえば、最初の1秒間が100ミリ秒(10ポイント)、次の2秒間が200ミリ秒(さらに10ポイント)、4秒間で400ミリ秒など、可変ステップを使用した場合のボーナスポイント。抵抗が下がり、とにかく大きなタイムステップは必要ありません。この方法では、T秒の複雑さがO(T)ではなくO(log T)であるため、あまり多くの処理をせずに本当に長いジャンプを処理できます。

キャラクターがジャンプの途中でブーストを停止したとき、または逆にブーストを開始したときに何が起こるかをシミュレートすることもできます。上記のトリックでは、複雑さはO((log T)^ 2)であり、それほど悪くはありません。


+1、すばらしい回答です。どうすれば実際のシミュレーションを検討できなかったのでしょう。「放物線近似」について詳しく教えてください(よくわかりません)?たとえば、RK4やオイラーのように、速度を統合する方法を意味しているだけですか?もしそうなら、それを説明するか、少なくともそれを実行する方法に関するいくつかの情報にリンクできますか?
Patryk Czachurski、2014

1
通常はあなたが行いますx'= x + v*dt。代わりにを使用してくださいx' = x + v*dt + 1/2*a*dt*dtdtが小さい場合は小さいdt^2ので、通常、ゲームの従来のオイラー統合では除外されます。ここdtは小さくないので、加速項が必要です。以来、dt2乗され、これは次の統合であり、経路は、従って放物線、放物線近似です。RK4は本質的に高次の導関数を計算するため、3次、4次、5次などの近似を与えることができます。安定性は重要ではないので、RK4はこの可能性が高すぎます。

速度自体を従来のオイラーのように統合する必要があると思いますか?v' = v + a*dt
Patryk Czachurski、2014

1
うん。ジャークはなく、ゼロであると想定しています。

編集内容をご覧ください。
Patryk Czachurski、2014

4

わーい!やったよ!

ターゲットポイントの垂直軸の後ろに着地する最初の位置を取る単純なシミュレーションを使用しています-そこから、前のシミュレーションされた位置を取り、セグメントを作成します。次に、ターゲットポイントがこのセグメントの下にあるかどうかを確認します。もしそうなら-私たちはそこにジャンプすることができます。

ここに画像の説明を入力してください

これは、gifでプレイヤーが制御するキャラクターです。ピンクは予測される経路であり、黄色のセグメントは後続のステッピング位置が予測され、ターゲットポイントがその下にある場合は最後のセグメントが白になり、そうでない場合は赤になります。赤い曲線は実際の飛行経路です。いくつかありますわずかオンによる物理状態の補間に不正確で。

計算は驚くほど簡単であることがわかりましたが、私の環境をこれらの純粋な計算と同じように機能させることは...お尻の大きな苦痛でした。少なくとも私はいくつかの深刻なバグを解決したので、結局それは有用な練習でした。

元の問題を解決するために使用されたLuaの完全なコードは次のとおりです(このコードは、独自の「debug_draw」ルーチンと、「length_sq」(長さの2乗)、「正規化」または演算子+、*などの基本メソッドを持つ独自のベクトルクラスがあることを前提としています:

function simple_integration(p, dt)
    local new_p = {}

    new_p.acc = p.acc
    new_p.vel = p.vel + p.acc * dt 
    new_p.pos = p.pos + new_p.vel * dt
    -- uncomment this if you want to use quadratic integration
    -- but with small timesteps even this is an overkill since Box2D itself uses traditional Euler
    -- and I found that for calculations to be accurate I either way must keep the timesteps very low at the beginning of the jump
     --+ p.acc * dt * dt * 0.5

    return new_p
end

function point_below_segment(a, b, p)
    -- make sure a is to the left
    if a.x > b.x then a,b = b,a end

    return ((b.x - a.x)*(p.y - a.y) - (b.y - a.y)*(p.x - a.x)) < 0
end

-- returns true or false
function can_point_be_reached_by_jump
(
gravity, -- vector (meters per seconds^2)
movement_force, -- vector (meters per seconds^2)
air_resistance_mult, -- scalar
queried_point, -- vector (meters)
starting_position, -- vector (meters)
starting_velocity, -- vector (meters per seconds)
jump_impulse, -- vector (meters per seconds)
mass -- scalar (kilogrammes)
)

    local my_point = {
        pos = starting_position,
        vel = starting_velocity + jump_impulse/mass
    }

    local direction_left = movement_force.x < 0
    local step = 1/60

    while true do           
        -- calculate resultant force
        my_point.acc = 
        -- air resistance (multiplier * squared length of the velocity * opposite normalized velocity)
        (vec2(my_point.vel):normalize() * -1 * air_resistance_mult * my_point.vel:length_sq()) / mass
        -- remaining forces
        + gravity + movement_force/mass

        -- I discard any timestep optimizations at the moment as they are very context specific
        local new_p = simple_integration(my_point, step)

        debug_draw(my_point.pos, new_p.pos, 255, 0, 255, 255)
        debug_draw(new_p.pos, new_p.pos+vec2(0, -1), 255, 255, 0, 255)

        if (direction_left and new_p.pos.x < queried_point.x) or (not direction_left and new_p.pos.x > queried_point.x) then
            if point_below_segment(new_p.pos, my_point.pos, queried_point) then
                debug_draw(new_p.pos, my_point.pos, 255, 0, 0, 255)
                return true
            else
                debug_draw(new_p.pos, my_point.pos, 255, 255, 255, 255)
                return false
            end
        else 
            my_point = new_p
        end
    end

    return false
end

Acceptは、私を正しい方向に導いてくれたJasonに行きます!ありがとう!


2

答えを「計算する」だけでもいいかもしれませんが、「自由落下」の物理学は非常にインタラクティブな性質を持っているので、いったん解いたら不十分だと思うでしょう。

別のアプローチの使用を検討してください:検索。スーパーマリオAIの場合は、次のようになります。http//aigamedev.com/open/interview/mario-ai/

AからBに到達するために可能なパスを検索すると、空中での無制限の対話性が可能になり、計算効率も向上します。


1
これは特定の世界でのみ実用的です。特にマリオは、おおよそ線形であり、限られた数の速度を持ち、優れたヒューリスティックを持つことにより、検索グラフのサイズを制限します。ゲームによっては、これは当てはまらない場合があります。また、このAIは複数のキャラクター/敵に対して機能する可能性が高いため、計算効率は相対的ですが、マリオでは1つしか制御できません。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.