2007年12月2日日曜日

相互作用が働く粒子の運動2

前回のスクリプトの改良版。
速度に上限をつけるのではなく、抵抗力による減速を導入することでより物理らしくなっています。ガタガタと動かずにきれいに収束するのも特徴です。

また、粒子の追加および初期化の機能をつけました。

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, 0ginfo_winx)
    y = limitf(y + vy, 0ginfo_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, 255255
        draw_all        // 全員を描画
    redraw 1
    wait 3
    goto *main

*flag_onclick
    if iparam == 0 {
        // 左クリックなら、粒子を追加
        make_member mousexmousey
        goto *main
    } else {
        // それ以外は初期化
        goto *restart
    }

0 件のコメント: