正式公開版 Ver 0.2(99/8/20公開)
剛体処理を簡略的に行う方法に関する技術的なサンプルプログラムです。
ワイヤーサンプル ver0.2をダウンロードする(Windows95/98/NT4.0用、LZH圧縮、97KB)
プログラムソースも公開してます(こちらがメインです)。Visual C++ & MFC用です。
ワイヤーサンプルVer0.2プログラムソース(Visual C++6.0用、LZH圧縮、38KB)
8/20日 Ver0.2
あ〜、いかん。こんなことをしている場合ではないのだが、どーも、暇プロの方に走ってしまう(^^;
ということで、自分で言っといてなんなんですが、リアルな3Dワイヤーというのを見てみたくなったので、自分で作ってしまいました。結構リアルで良いです。やはりこれからは物理演算だよね(笑)。グラフィックス貧弱ですが、動きで勝負っと。
#そもそも私が作るのはみんなそうだが。
操作ですが、スペースでアンカー発射、スペース押してる最中にカーソルキーとA、Zキーで、打ち出し方向を変えられます。
こいうことで、これでワイヤーアクションゲームとか作ったら楽しそう。NekoFlightのロボットモードのときにビルにでもアンカー打ち込んで昇れるようにでもすると面白いかもしれず(まあ、めんどうだからやらんだろうが・・)。
8/20日 Ver0.1
単にプログラマ向けのサンプルプログラムです。操作の類はできません。重りの方は普通の跳ね返り運動で、ワイヤーの方にAnimeBodyの手法使ってます。この程度なら普通にバネと仮定して計算しても良いのでしょうけど、こういうのはもっと簡単に実現できるということで。説明はAnimeBodyのプログラマ向けのところ見てください。
ちなみに、プログラム主要部はこれで全部(WireDlg.cppの後半)↓。凄い簡単。
こういう動きをベースにして、X68kにあったアクアレスのワイヤーアクション3D版とか作ったら面白そうだけど、だれかやらんすか?
#define DT 0.1
#define PMAX 20
double px[PMAX], py[PMAX]; // ワイヤ位置
double ox[PMAX], oy[PMAX]; // ワイヤ1ステップ前の位置
double vx[PMAX], vy[PMAX]; // ワイヤ速度
double bx, by, vbx, vby;
// 重り位置・速度
void CWireDlg::Init()
{
// 各位置初期化
for (int i = 0; i < PMAX; i++) {
px[i] = ox[i] = i + 100;
py[i] = oy[i] = (rand() % 100) / 100000.0;
vx[i] = vy[i] = 0;
}
bx = 0;
by = 0;
vbx = 1;
vby = 0;
}
void CWireDlg::Draw()
{
int i;
CClientDC dc(this);
// Windowの大きさを取得
CRect rect;
GetClientRect(&rect);
// ワイヤ加速処理
for (i = 0; i < PMAX; i++)
py[i] += 0.98 * DT;
// 重り加速
vby += 0.98 * DT;
// ワイヤ速度をワイヤ位置に加える
for (i = 0; i < PMAX; i++) {
px[i] += vx[i];
py[i] += vy[i];
}
// 重り位置積分
bx += vbx;
by += vby;
// 制約処理
for (int t = 0; t < 10; t++) {
// ワイヤ末端は重りに接続
ox[0] = px[0] = bx;
oy[0] = py[0] = by;
// ワイヤp[i]とp[i+1]間の距離は常に10
for (i = 0; i < PMAX - 1; i++) {
double dx = px[i + 1] - px[i];
double dy = py[i + 1] - py[i];
double dis = sqrt(dx * dx + dy * dy);
double d = (10 - dis) * 0.2;
px[i] -= dx / dis * d;
py[i] -= dy / dis * d;
px[i + 1] += dx / dis * d;
py[i + 1] += dy / dis * d;
}
// ワイヤと枠との当たり判定(跳ね返り係数0.5)
for (i = 0; i < PMAX; i++) {
if (px[i] < 0)
px[i] = - vx[i] * 0.5;
if (px[i] > rect.right)
px[i] = rect.right - vx[i] * 0.5;
if (py[i] < 0)
py[i] = - vy[i] * 0.5;
if (py[i] > rect.bottom)
py[i] = rect.bottom + vy[i] * 0.5;
}
}
// 枠と重りの当たり判定(跳ね返り係数1)
if (bx < 0) {
bx = 0;
vbx *= -1;
}
if (bx > rect.right) {
bx = rect.right;
vbx *= -1;
}
if (by < 0) {
by = 0;
vby *= -1;
}
if (by > rect.bottom) {
by = rect.bottom;
vby *= -1;
}
// ワイヤ速度成分を求める
for (i = 0; i < PMAX; i++) {
vx[i] = (px[i] - ox[i]) * 0.995;
vy[i] = (py[i] - oy[i]) * 0.995;
}
// ワイヤ位置情報を保存
for (i = 0; i < PMAX; i++) {
ox[i] = px[i];
oy[i] = py[i];
}
// Windowクリア
dc.FillSolidRect(0, 0, rect.right, rect.bottom, RGB(255, 255, 255));
// ワイヤ描画
dc.MoveTo((int)px[0], (int)py[0]);
for (i = 1; i < PMAX; i++)
dc.LineTo((int)px[i], (int)py[i]);
// 重り描画
dc.Ellipse((int)bx - 5, (int)by - 5, (int)bx + 5, (int)by + 5);
Sleep(10);
}