先ほど放物線の質問をした者です。
前の質問で軽率にも解決にしてしまったのですが、うまくいかなかったので・・・。
こんどは詳しい状況を記載しますので、もう一度アドバイス頂ければと思います。
vx = 敵のX座標 - 自分のX座標;
vy = 敵のY座標 - 自分のY座標;
size = sqrt( vx * vx + vy * vy );
mx = vx / size * ボールの速度;
my = vy / size * ボールの速度;
ボールのX座標 += mx;
ボールのY座標 += my;
今現在、上のコードで
自分がX,Y座標どの位置にいても敵の位置に向かって、ボールがとんでいくようにしています。
敵の座標はボールを投げる瞬間に取得した座標です。
それでボールを投げたときに放物線を描かせたいのですが、どうしたらよいでしょうか?
よろしくお願いします。
また、数学の質問です。
Re:また、数学の質問です。
>ボールを投げたときに放物線を描かせたいのですが、どうしたらよいでしょうか
1 真面目に放物線として計算する
この計算に必要なのはターゲットの座標の他に、重力(上下方向)と初速、打ち上げ角度です。
これらのパラメータが決まれば
[color=#d0d0ff" face="sans-serif]C言語~ゲームプログラミングの館~
ttp://dixq.net/g/#27
[/color]
にあるような放物線の方程式を使えば、原点から指定した角度・速度で投げたときの
時間 tにおける位置がとれます。
(始点は原点となるので、始点・終点共にオフセットする必要があります)
問題はこのケースではターゲットの位置が決まっているので、
軌道の逆計算をする必要があるわけです。
逆計算といっても、2つ以上不明なパラメータがあっては求めることはできないので、
重力・初速・角度のうち2つは適当に決めてしまい、残る1つを計算で求めることに
なります。
とはいえ重力は“世界”に合わせて適当に決めてしまっていいと思うので、
残る初速と打ち上げ角度のどちらかを適当に決めてしまえれば、
もう一方を求められます。
勿論どちらを求めるのかで、計算方法が異なります。
2 適当に補完して誤魔化す
放物線ではなく、曲線的な動きであればいいのなら、こちらがお手軽です。
適当な曲線アルゴリズムを使って放物線ぽい曲線を描きます。
例えば Ferguson / Coons曲線なんかは、位置2つと初速と終点到達時の速度を
指定すると指定した時間に応じた位置を計算することができます。
Ferguson / Coons曲線
ttp://markun.cs.shinshu-u.ac.jp/learn/cg/cg3/index4.html
Catmull-Romスプライン曲線
ttp://markun.cs.shinshu-u.ac.jp/learn/cg/cg3/index5.html
Re:また、数学の質問です。
1の放物線ですが、昔作ったサンプルを発掘しましたので、載せてきます。
このサンプルは、まず適当な角度(35度)、初速 5.0、重力 -1.8で投げた時、
時間 0.02毎に座標を表示し、20個目(時間 0.36)で位置を記録しています。
続いて、その位置を使って、「角度」や「初速」が判らない場合、
calc_ang()や calc_v()を使って求め、元々の計算で使った角度や初速と一致するか
検証するものです。
昔過ぎて今見るとちょっとおかしなところとかあるんですが、
まぁ countとか angとか vとか適当に書き換えて試してみてください。
何かの参考になれば。
[color=#d0d0ff" face="monospace"><hr width="60%" align="left" color="#101010]
#include <stdio.h>
#include <math.h>
// 初速 vと目的値 x, yと重力から、打ち上げ角度を求める(angに出力)
// 戻り値はいくつ角度が求まったか(0~2)
int calc_ang(double v, double x, double y, double g, double ang[2])
{
int count = 0;
double v2 = v*v;
double x2 = x*x;
double c = x2 - (g * g * x2*x2) / (v2*v2) + (2*g*x2*y) / v2;
if(c >= 0)
{
double sqrt_c = sqrt(c);
double t1 = (-x + sqrt_c) / (g * x2) * v2;
double t2 = (-x - sqrt_c) / (g * x2) * v2;
if(t1 >= 0)
ang[count++] = atan(t1);
if(t2 >= 0)
ang[count++] = atan(t2);
}
return count;
}
// 打ち上げ角度 angと目的値 x, yと重力から、初速を求める
int calc_v(double ang, double x, double y, double g, double *result)
{
double s = -g / (2*(x * tan(ang)-y));
if(s < 0) return 0;
*result = (x / cos(ang)) * sqrt(s);
return 1;
}
[/color]
長いので続きます。
このサンプルは、まず適当な角度(35度)、初速 5.0、重力 -1.8で投げた時、
時間 0.02毎に座標を表示し、20個目(時間 0.36)で位置を記録しています。
続いて、その位置を使って、「角度」や「初速」が判らない場合、
calc_ang()や calc_v()を使って求め、元々の計算で使った角度や初速と一致するか
検証するものです。
昔過ぎて今見るとちょっとおかしなところとかあるんですが、
まぁ countとか angとか vとか適当に書き換えて試してみてください。
何かの参考になれば。
[color=#d0d0ff" face="monospace"><hr width="60%" align="left" color="#101010]
#include <stdio.h>
#include <math.h>
// 初速 vと目的値 x, yと重力から、打ち上げ角度を求める(angに出力)
// 戻り値はいくつ角度が求まったか(0~2)
int calc_ang(double v, double x, double y, double g, double ang[2])
{
int count = 0;
double v2 = v*v;
double x2 = x*x;
double c = x2 - (g * g * x2*x2) / (v2*v2) + (2*g*x2*y) / v2;
if(c >= 0)
{
double sqrt_c = sqrt(c);
double t1 = (-x + sqrt_c) / (g * x2) * v2;
double t2 = (-x - sqrt_c) / (g * x2) * v2;
if(t1 >= 0)
ang[count++] = atan(t1);
if(t2 >= 0)
ang[count++] = atan(t2);
}
return count;
}
// 打ち上げ角度 angと目的値 x, yと重力から、初速を求める
int calc_v(double ang, double x, double y, double g, double *result)
{
double s = -g / (2*(x * tan(ang)-y));
if(s < 0) return 0;
*result = (x / cos(ang)) * sqrt(s);
return 1;
}
[/color]
長いので続きます。
Re:また、数学の質問です。
[color=#d0d0ff" face="monospace]
int main()
{
double t;
double g = -1.8; // 重力(上方向が正とする)
double v = 5.0; // 初速
double ang = 35 * (3.1415926535 / 180.0); // 打ち上げ角度(rad)
double xx, yy;
// 角度の sin/cos値を予め出しておく
double sin_theta = sin(ang);
double cos_theta = cos(ang);
// パラメータ
printf("v = %f\nang = %f\n", v, ang);
// まずは普通の放物線(原点からの打ち上げ)を連続して位置を求める
{
int count = 0;
for(t=0.0; ; t+=0.02)
{
// 時間 tの時の xと yの位置
double x = v * t * cos_theta;
double y = v * t * sin_theta + 0.5 * g * t * t;
if(++count < 20)
printf("t = %4.2f / %5.3f %5.3f\n", t, x, y);
else
{
// 検証の為、適当な時間の時の高さと距離を記憶して終了
xx = x;
yy = y;
break;
}
}
}
// 不明なパラメータを仮定し、逆に求める
{
// 打ち上げ角度が判らない場合
double result_ang[2],result_v;
int count = calc_ang(v, xx, yy, g, result_ang);
if(count == 1)
printf("ang = %f\n", result_ang[0]); // resultと angと一致しているはず
else
if(count == 2)
printf("ang = %f / %f\n", result_ang[0], result_ang[1]); // どちらかが angと一致しているはず
else
printf("解無し");
// 次は初速が判らない場合
if(calc_v(ang, xx, yy, g, &result_v))
printf("v = %f\n", result_v); // vと一致
else
printf("解無し");
}
return 0;
}
[/color]
int main()
{
double t;
double g = -1.8; // 重力(上方向が正とする)
double v = 5.0; // 初速
double ang = 35 * (3.1415926535 / 180.0); // 打ち上げ角度(rad)
double xx, yy;
// 角度の sin/cos値を予め出しておく
double sin_theta = sin(ang);
double cos_theta = cos(ang);
// パラメータ
printf("v = %f\nang = %f\n", v, ang);
// まずは普通の放物線(原点からの打ち上げ)を連続して位置を求める
{
int count = 0;
for(t=0.0; ; t+=0.02)
{
// 時間 tの時の xと yの位置
double x = v * t * cos_theta;
double y = v * t * sin_theta + 0.5 * g * t * t;
if(++count < 20)
printf("t = %4.2f / %5.3f %5.3f\n", t, x, y);
else
{
// 検証の為、適当な時間の時の高さと距離を記憶して終了
xx = x;
yy = y;
break;
}
}
}
// 不明なパラメータを仮定し、逆に求める
{
// 打ち上げ角度が判らない場合
double result_ang[2],result_v;
int count = calc_ang(v, xx, yy, g, result_ang);
if(count == 1)
printf("ang = %f\n", result_ang[0]); // resultと angと一致しているはず
else
if(count == 2)
printf("ang = %f / %f\n", result_ang[0], result_ang[1]); // どちらかが angと一致しているはず
else
printf("解無し");
// 次は初速が判らない場合
if(calc_v(ang, xx, yy, g, &result_v))
printf("v = %f\n", result_v); // vと一致
else
printf("解無し");
}
return 0;
}
[/color]