2014年2月15日土曜日

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

前回ボールの跳ね返りをやりました
残るはリングとの衝突と跳ね返り処理です
これはかなり悩みました、3日ほど悩んでむりくりひねり出した結論ですが

※以下の考え方は物理的に正しいかどうかは不明です※

まず例としてこんなボールとリングの衝突と跳ね返りがあるとします
ごく当たり前の跳ね返りですが、これをどうプログラムで計算するのか?
とりあえず衝突点を求めます
そしてリングの断面の中心とボールの中心点を結んで衝突面の法線ベクトルを求めます

その衝突法線ベクトルが作る面に対しての3次元速度ベクトルの反射処理と考えました

この考え方を元にまずボールの移動前、移動後の位置を与えてリング当たっていれば衝突位置を返すメソッドを作成します
//リングとの衝突判定関数
public static Plot hitRing(Plot prev, Plot move) {
    Plot ret = null;
    //大雑把な判定条件として落下方向への移動、移動のZ座標がリング高さをまたがっている
    if (move.z < prev.z && move.z - Ball.R <= RING_H && prev.z - (Ball.R / 2) > RING_H) {
        int r = (int)((((prev.z - Ball.R) - RING_H) * 100) / (prev.z - move.z));
        //ボール下面がリング高さに来る位置取得
        Plot pos = Utl.getPlotInLine(r, prev, move);
        //リング中央との距離取得
        int d = Utl.getDistancePtoP(RING_CENTER, new Plot(pos.x, pos.y));
        //リングに当たりそうな距離にあればさらに詳しく判定
        if (d > (RING_R - Ball.R) && d <= RING_R + Ball.R) {
            float dagl = Utl.getRadians(Court.RING_CENTER, pos);
            int dx = (int)(Court.RING_CENTER.x + Court.RING_R * (Math.cos(dagl)));
            int dy = (int)(Court.RING_CENTER.y + Court.RING_R * (Math.sin(dagl)));
            int bw = Utl.getDistancePtoP(new Plot(dx, dy), new Plot(pos.x, pos.y));
            int bz = (int)(Ball.R * Math.sin(Math.acos((double)(Ball.R - bw) / (double)Ball.R)));
            r = (int)(((prev.z - (pos.z - bz)) * 100) / (prev.z - move.z));
            //実際にリングに当たる位置取得
            pos = Utl.getPlotInLine(r, prev, move);
            //リング中央との距離取得
            d = Utl.getDistancePtoP(Court.RING_CENTER, new Plot(pos.x, pos.y));
            //取得した距離からリングに当たるかどうか判定
            if (d > (Court.RING_R - Ball.R) && d <= Court.RING_R + Ball.R) {
                ret = new Plot(pos);
            }
        }
    }
    return ret;
}
こんな感じ
このメソッドをボールの移動処理にはさみます
Plot hr = hitRing(prev, move);
if (hr != null) {
    //法線ベクトル取得
    float angle = Utl.getRadians(Court.RING_CENTER, hr);
    int dx = (int)(Court.RING_CENTER.x + Court.RING_R * (Math.cos(angle)));
    int dy = (int)(Court.RING_CENTER.y + Court.RING_R * (Math.sin(angle)));
    //衝突時の速度ベクトルと法線ベクトルの内積を計算
    float ip = Utl.getInnerProduct(new Plot(xSpeed, ySpeed, zSpeed), new Plot(hr.x - dx, hr.y - dy, hr.z - Court.RING_H));
    //内積が+のときは裏からの当たりなので処理しない
    if (ip < 0) {
        //速度ベクトルの跳ね返り後の速度を取得
        Plot dv = getReflect(new Plot(xSpeed, ySpeed, zSpeed), new Plot(hr.x - dx, hr.y - dy, hr.z - Court.RING_H));
        float dk = 0.6f;
        //反発係数dkを速度にかける
        xSpeed = (int)(dv.x * dk);
        ySpeed = (int)(dv.y * dk);
        zSpeed = (int)(dv.z * dk);
        //衝突後の経過時間を計算
        int citv = (int)((interval * Utl.getDistancePtoP(hr, move)) / Utl.getDistancePtoP(prev, move));
        //移動後の位置を変更速度から再計算
        move.x = (int)(hr.x + ((xSpeed * citv) / 1000));
        move.y = (int)(hr.y + ((ySpeed * citv) / 1000));
        move.z = (int)(hr.z + ((zSpeed * citv) / 1000));
        prev = new Plot(hr);
    }
}
こんな感じ、いろいろと処理がひどいですが見ないでやってください(良い子はまねしちゃダメ)
試したところやや動きが怪しいところがありますが、基本方針としてはよさそう

0 件のコメント:

コメントを投稿