2014年2月12日水曜日

新作Androidアプリ開発 ボールの跳ね返り編

前回ボールを投げることが出来たので今回はバックボードとリングの接続部(フランジ)との跳ね返りの処理を作成したいと思います
処理の大まかな流れは以下

対象との衝突判定
 ↓
衝突位置を計算
 ↓
衝突後の速度を計算
 ↓
衝突後の位置を計算

まずはバックボードの部分、ボールの移動前、移動後の位置を与えて当たっていれば衝突位置を返すメソッドを作成します
final private static int R = 122;     //半径(mm)
final private static int BOARD_Y = 1400;
final private static Plot BOARD_LEFT = new Plot(6600, BOARD_Y + R);
final private static Plot BOARD_RIGHT = new Plot(8400, BOARD_Y + R);
final private static Rect BOARD_H = new Rect(6600 - R, 0, 8400 + R, 10);
final private static Rect BOARD_V = new Rect(0, 2900, 10, 3950);

//バックボートとの衝突判定関数
public Plot hitBoard(Plot prev, Plot move) {
    Plot ret = null;
    //大雑把な判定条件としてボード方向へ移動している、移動のY座標がボード位置をまたがっている
    if (move.y < prev.y && (move.y - Ball.R <= BOARD_Y && prev.y + Ball.R > BOARD_Y)) {
        //移動前後のZ座標にボールの半径を加味したRectを生成
        Rect rv = new Rect(0, (prev.z < move.z ? prev.z : move.z) - Ball.R, 10, (prev.z < move.z ? move.z : prev.z) + Ball.R);
        //移動前後のX座標にボールの半径を加味したRectを生成
        Rect rh = new Rect(prev.x < move.x ? prev.x : move.x, 0, prev.x < move.x ? move.x : prev.x, 10);
        //ボードの垂直方向Rect(BOARD_V)と水平方向Rect(BOARD_H)と生成したrv、rhを交差判定する
        if (Rect.intersects(BOARD_V, rv) && Rect.intersects(BOARD_H, rh)) {
            //移動直線とボードから水平方向交点を取得(getLineVSLineは2直線の交点を返す関数)
            ret = Utl.getLineVSLine(new Plot(prev.x, prev.y), new Plot(move.x, move.y), BOARD_LEFT, BOARD_RIGHT);
        }
    }
    //衝突がなければnullをあれば衝突位置を返す
    return ret;
}
こんな感じ
このメソッドをボールの移動処理にはさみます
final private float bk = 0.95f;

//位置変更
Plot prev = new Plot(position);
Plot move = new Plot(position);
move.x += ((xSpeed * interval) / 1000);
move.y += ((ySpeed * interval) / 1000);
move.z += ((zSpeed * interval) / 1000);
Plot hb = hitBoard(prev, move);
if (hb != null)  {
    //反発係数bkを速度にかける
    xSpeed *= bk;
    ySpeed *= -bk; //y方向の速度を反転
    zSpeed *= bk;
    //衝突後の経過時間を計算
    int citv = (int)((interval * Utl.getDistancePtoP(hb, move)) / Utl.getDistancePtoP(prev, move));
    hb.z = (int)(prev.z + ((zSpeed * (interval - citv)) / 1000));
    prev = new Plot(hb);
    //移動後の位置を変更速度から再計算
    move.x = (int)(hb.x + ((xSpeed * citv) / 1000));
    move.y = (int)(hb.y + ((ySpeed * citv) / 1000));
    move.z = (int)(hb.z + ((zSpeed * citv) / 1000));
}
こんな感じ、これを動かしてみます
よさそうですね
次にフランジ部分、ほぼバックボードと一緒です
速度がY軸方向ではなくZ軸方向に反転します
final private float fk = 0.92f;

Plot hf = hitFlange(prev, move);
if (hf != null) {
    //反発係数fkを速度にかける
    xSpeed *= fk;
    ySpeed *= fk;
    zSpeed *= -fk; //z方向の速度を反転
    prev = new Plot(hf);
    //衝突後の経過時間を計算
    int citv = (int)((interval * Utl.getDistancePtoP(hf, move)) / Utl.getDistancePtoP(prev, move));
    //移動後の位置を変更速度から再計算
    move.x = (int)(hf.x + ((xSpeed * citv) / 1000));
    move.y = (int)(hf.y + ((ySpeed * citv) / 1000));
    move.z = (int)(hf.z + ((zSpeed * citv) / 1000));
}

//フランジとの衝突判定関数
public Plot hitFlange(Plot prev, Plot move) {
    Plot ret = null;
    //大雑把な判定条件として落下方向へ移動している、移動のZ座標がフランジ高さをまたがっている
    if (move.z < prev.z && move.z - Ball.R <= FLANGE_H && prev.z - Ball.R > FLANGE_H) {
        //フランジ高さに達するまでの比率を計算
        int r = (int)((((prev.z - Ball.R) - FLANGE_H) * 100) / (prev.z - move.z));
        //フランジ高さに達した時の位置を取得
        Plot pos = Utl.getPlotInLine(r, prev, move);
        //フランジとの交差判定
        if (FLANGE.contains(pos.x, pos.y)) {
            //フランジとの衝突位置をセット
            ret = new Plot(pos.x, pos.y, FLANGE_H + Ball.R);
        }
    }
    //衝突がなければnullをあれば衝突位置を返す
    return ret;
}
動かしてみます

よさそうです
衝突判定の仕方や衝突位置の取得方法はいろんなやり方があるので、それぞれのやりやすい方法でいいと思います
特にフランジの衝突位置計算なんかは強引に比率で出しちゃってますが、良い子はマネしちゃダメ

Rect.intersectsはRect同士の交差判定ができますので大雑把な判定するのに便利です
バックボードとフランジは軸に平行なため、跳ね返り処理は軸の法線方向への速度反転だけで済むので割と簡単ですね

あとゴールリング(リム)との跳ね返りがありますが、こちらは少し面倒なので次回にします

0 件のコメント:

コメントを投稿