前回のスクリプトの改良版。
速度に上限をつけるのではなく、抵抗力による減速を導入することでより物理らしくなっています。ガタガタと動かずにきれいに収束するのも特徴です。
また、粒子の追加および初期化の機能をつけました。
Java移植版#module member id, x, y, vx, vy
#include "hspmath.as"
// id ... 個別認識用ID(int)
// x, y ... 位置(double)
// vx, vy ... 速度(double)
#const PARTY_DIST 120 // 仲間と保とうとする距離
#const RADIUS 5 // 描画時の半径
#modinit double _x, double _y
x = _x : y = _y : vx = 0.0 : vy = 0.0
id = next_id : next_id++
return
#deffunc make_member double new_x, double new_y
newmod members, member, new_x, new_y
return
// -----------------------------
// ここから各メンバーごとに用いる命令
// IDを返す
#modfunc get_id
return id
// 位置を返す
#modfunc get_position array v
v = x, y
return
// 自分自身の速度の計算
#modfunc calc_speed
ax = 0.0 : ay = 0.0
// 仲間との相互作用(一定の距離を保とうとする)
foreach members
// 自分自身とは判定を行わない
get_id members(cnt)
if id == stat : continue
// 仲間との距離を計算
this_member_pos = x, y
get_position members(cnt), target_member_pos
dist = distance2(this_member_pos, target_member_pos) // 対象の仲間との距離(double)
// 仲間との角度を計算
theta = atan(y - target_member_pos(1), target_member_pos(0) - x)
// 距離に応じて加速
s = absf(dist - PARTY_DIST) * (dist - PARTY_DIST) / 5000
ax += cos(theta) * s
ay += sin(theta) * s
loop
// 速度の変更(加速・減速)
vx = 0.9 * (vx + ax)
vy = 0.9 * (vy - ay)
return
// 自分自身の移動
#modfunc move
x = limitf(x + vx, 0, ginfo_winx)
y = limitf(y + vy, 0, ginfo_winy)
return
// 自分自身を描画
#modfunc draw
circle x - RADIUS, y - RADIUS, x + RADIUS, y + RADIUS
return
// -----------------------------
// ここから外部から呼び出す群れ全体を扱う命令
// 全員の速度を計算
#deffunc calc_all_speed
foreach members
calc_speed members(cnt)
loop
return
// 全員の移動
#deffunc move_all
foreach members
move members(cnt)
loop
return
// 全員の描画
#deffunc draw_all
foreach members
draw members(cnt)
loop
return
// 全員消去
#deffunc kill_all
if vartype(members) != 5 : return
foreach members
delmod members(cnt)
loop
return
#global
next_id@member = 0 // IDの初期化
// -----------------------------
// モジュールここまで
#const NUM_MEMBERS 12 // 群れを構成するメンバーの数(初期値)
randomize
onclick goto *flag_onclick
title "左クリック:粒子の追加 / 右クリック:やりなおし"
*restart
// すべてのメンバーを削除
kill_all
// 群れのメンバーをランダムな位置に発生
repeat NUM_MEMBERS
make_member rnd(ginfo_winx), rnd(ginfo_winy)
loop
// 色を決定
color_h = rnd(192)
*main
calc_all_speed // 全員の速度を計算
move_all // 全員を移動
redraw 0
color : boxf : hsvcolor color_h, 255, 255
draw_all // 全員を描画
redraw 1
wait 3
goto *main
*flag_onclick
if iparam == 0 {
// 左クリックなら、粒子を追加
make_member mousex, mousey
goto *main
} else {
// それ以外は初期化
goto *restart
}
2007年12月2日日曜日
相互作用が働く粒子の運動2
2007年12月1日土曜日
相互作用が働く粒子の運動
HSPコンテスト2007にて株式会社秀和システム章を受賞しました。
おにたまさんから「ある意味ではモジュールの達人」と言っていただけまして、とても嬉しいです。モジュール(変数)は1時期とっても勉強した部分なので……。
モジュール変数の扱いをもっと勉強していきたいですね。
というわけで受賞記念に、得意(?)のモジュールを利用したスクリプトを。
「遠い時はくっつきたがるけど、近い時は離れたがる」という特殊な粒子の運動のシミュレートです。現実では原子がこれにあたります。3次元ですが。
ひとつのモジュール変数でひとつの粒子を表しています。決まった距離を保つために、距離の2乗に従って速度を算出しています。力学のポテンシャルの考え方に近いでしょうか。
粒子数が同じなら、最初の条件が違っても大抵は同じ陣形に収束するのが面白いです。#module member id, x, y, vx, vy
#include "hspmath.as"
// id ... 個別認識用ID(int)
// x, y ... 位置(double)
// vx, vy ... 速度(double)
#const PARTY_DIST 120 // 仲間と保とうとする距離
#const MAX_SPEED 4 // 最大スピード
#const RADIUS 5 // 描画時の半径
#modinit int _x, int _y
x = _x : y = _y
id = next_id : next_id++
return
#deffunc make_member double new_x, double new_y
newmod members, member, new_x, new_y
return
// -----------------------------
// ここから各メンバーごとに用いる命令
// IDを返す
#modfunc get_id
return id
// 位置を返す
#modfunc get_position array v
v = x, y
return
// 自分自身の速度の計算
#modfunc calc_speed
ax = 0.0 : ay = 0.0
// 仲間との相互作用(一定の距離を保とうとする)
foreach members
// 自分自身とは判定を行わない
get_id members(cnt)
if id == stat : continue
// 仲間との距離を計算
this_member_pos = x, y
get_position members(cnt), target_member_pos
dist = distance2(this_member_pos, target_member_pos) // 対象の仲間との距離(double)
// 仲間との角度を計算
theta = atan(y - target_member_pos(1), target_member_pos(0) - x)
// 距離に応じて加速
s = (dist - PARTY_DIST) * (dist - PARTY_DIST) * sgn(dist - PARTY_DIST) / 150
ax += cos(theta) * s
ay += sin(theta) * s
loop
// 速度の変更
vx += ax : vy -= ay
// 速度が大きすぎる場合は調節する
velo = sqrt(vx * vx + vy * vy)
if absf(velo) > MAX_SPEED {
velo = limitf(velo, -MAX_SPEED, MAX_SPEED)
theta = atan(vy, vx)
vx = cos(theta) * velo
vy = sin(theta) * velo
}
return
// 自分自身の移動
#modfunc move
x = limitf(x + vx, 0, ginfo_winx)
y = limitf(y + vy, 0, ginfo_winy)
return
// 自分自身を描画
#modfunc draw
circle x - RADIUS, y - RADIUS, x + RADIUS, y + RADIUS
return
// -----------------------------
// ここから外部から呼び出す群れ全体を扱う命令
// 全員の速度を計算
#deffunc calc_all_speed
foreach members
calc_speed members(cnt)
loop
return
// 全員の移動
#deffunc move_all
foreach members
move members(cnt)
loop
return
// 全員の描画
#deffunc draw_all
foreach members
draw members(cnt)
loop
return
#global
next_id@member = 0 // IDの初期化
// -----------------------------
// モジュールここまで
#const NUM_MEMBERS 12 // 群れを構成するメンバーの数
// 群れのメンバーをランダムな位置に発生
randomize
repeat NUM_MEMBERS
make_member rnd(ginfo_winx), rnd(ginfo_winy)
loop
*main
calc_all_speed // 全員の速度を計算
move_all // 全員を移動
redraw 0
color : boxf : color ,,255
draw_all // 全員を描画
redraw 1
wait 3
goto *main
2007年5月4日金曜日
砲弾の射出(d3module)
目標地点を指定すると、そのために必要な射出方向を計算する。
射出速度は一定なので、遠方を狙うほど早く着弾する。// ang ... 角度, pos ... 位置(座標), velo... 速さ
// distance ... 距離, b ... 放物線の第2係数
#include "d3m.hsp"
#module powModule
#defcfunc pow double d1, int i2, local st
st = 1.0
repeat i2
st *= d1
loop
return st
#global
#const GRAVITY 1.0 // 重力加速度
#const VELOCITY 30.1 // 射出速度
#const DISTANCE_MAX VELOCITY * VELOCITY / GRAVITY // 最大飛距離
#const GRID_MAX 8 // グリッドの本数
#const GRID_DISTANCE 100 // グリッドの間隔
#const TARGET_RADIUS 10 // 照準の半径
#const BULLET_RADIUS 8 // 砲弾の半径
screen 0, 480, 480
// 射出点の座標
posCannonX = double(GRID_MAX) * GRID_DISTANCE / 2
posCannonY = 0.0
posCannonZ = 0.0
// 照準の座標
posTargetX = double(GRID_MAX) * GRID_DISTANCE / 2
posTargetY = double(GRID_MAX) * GRID_DISTANCE / 2
posTargetZ = 0.0
d3setcam GRID_MAX * GRID_DISTANCE / 2, -300, 200, GRID_MAX * GRID_DISTANCE / 2, GRID_MAX * GRID_DISTANCE / 2, 0
*main
gosub *move
gosub *draw
wait 4
goto *main
*move
stick key, 15
if key & 16 : gosub *shot
// 照準の移動
posTargetX += 10 * (((key >> 2) & 1) - (key & 1))
posTargetY += 10 * (((key >> 1) & 1) - ((key >> 3) & 1))
// 砲弾の移動
if posBulletZ >= 0 {
posBulletX += veloBulletX
posBulletY += veloBulletY
posBulletZ += veloBulletZ
veloBulletZ -= GRAVITY
}
return
*shot
// 射出 ここらへんのスクリプトの理解は力学と微分の知識が要ります
posBulletX = posCannonX
posBulletY = posCannonY
posBulletZ = posCannonZ
angBullet = atan(posTargetY - posCannonY, posTargetX - posCannonX)
distance = limitf(sqrt(pow(posTargetX - posCannonX, 2) + pow(posTargetY - posCannonY, 2)), 0.0, DISTANCE_MAX)
b = DISTANCE_MAX / distance + sqrt(pow(DISTANCE_MAX / distance, 2) - 1)
veloBulletR = sqrt(GRAVITY * distance / b / 2)
veloBulletZ = b * veloBulletR
veloBulletX = veloBulletR * cos(angBullet)
veloBulletY = veloBulletR * sin(angBullet)
return
*draw
redraw 0
color : boxf
color 0, 255 // グリッド
repeat GRID_MAX + 1
d3line cnt * GRID_DISTANCE, 0, 0, cnt * GRID_DISTANCE, GRID_MAX * GRID_DISTANCE, 0
d3line 0, cnt * GRID_DISTANCE, 0, GRID_MAX * GRID_DISTANCE, cnt * GRID_DISTANCE, 0
loop
color 255 // 照準
d3circle posTargetX, posTargetY, posTargetZ, TARGET_RADIUS, 0
if posBulletZ >= 0 {
color 0, 0, 255 // 砲弾
d3circle posBulletX, posBulletY, posBulletZ, BULLET_RADIUS
color 191, 191, 191 // 影
d3circle posBulletX, posBulletY, 0, BULLET_RADIUS
}
redraw 1
return
3次元空間を反射する光線
モジュール変数によるキャラクター管理法。
d3moduleを利用しています。
d3box命令を使って修正。(2007/10/02)#include "d3m.hsp"
#const global BOX_X 200
#const global BOX_Y 300
#const global BOX_Z 150
#const CAMERA_DISTANCE 500
#const BALL_MAX 30
#module physics3d x, y, z, vx, vy, vz
#modinit double d1, double d2, double d3, double d4, double d5, double d6
x = d1, d1, d1, d1
y = d2, d2, d2, d2
z = d3, d3, d3, d3
vx = d4 : vy = d5 : vz = d6
return
#modfunc moveBall
x(1) = x, x(1), x(2)
y(1) = y, y(1), y(2)
z(1) = z, z(1), z(2)
x += vx : y += vy : z += vz
if ( x < 0 ) | ( BOX_X < x ) : vx *= -1
if ( y < 0 ) | ( BOX_Y < y ) : vy *= -1
if ( z < 0 ) | ( BOX_Z < z ) : vz *= -1
return
#modfunc drawBall
d3initlineto
repeat 4
d3lineto x(cnt), y(cnt), z(cnt)
loop
return
#global
screen 0, 400, 370
title "3次元を反射する光線"
theta = 0.0 // カメラの角度 (極座標)
phi = 3.14 / 2 // カメラの角度2(極座標)
gosub *setcam
repeat BALL_MAX
newmod ball, physics3d, rnd(200), rnd(300), rnd(150), (rnd(9)-4)*3, (rnd(9)-4)*4, (rnd(9)-4)*3
loop
*main
gosub *moveCam
gosub *draw
wait 4
goto *main
*moveCam
stick key, 15
if key & 15 {
theta += 0.1*((key & 1) - (key >> 2 & 1))
phi = limitf(phi + 0.1*((key >> 1 & 1) - (key >> 3 & 1)), 0.1, 3.13)
// ↑ カメラのXY座標がターゲットと同じにならないための対策
gosub *setcam
}
return
*draw
redraw 0
color : boxf
hsvcolor 30, 255, 100
d3box 0, 0, 0, BOX_X, BOX_Y, BOX_Z
hsvcolor 30, 255, 255
foreach ball
moveBall ball(cnt)
drawBall ball(cnt)
loop
redraw
return
*setcam
d3setcam sin(phi)*cos(theta)*CAMERA_DISTANCE + 100, sin(phi)*sin(theta)*CAMERA_DISTANCE + 150, cos(phi)*CAMERA_DISTANCE + 75, 100, 150, 75
return