//////////////////////////////////////////////////////////////////////////////// // aaa.hsp // ( An Abandoned Area ) // // ■ 概略 // いしにいしをふきこんで、ぼいど(VOID, BOID)をつくるゲーム // // ■ 設定メモ // 時代は?: 神様が「色」の概念を思いついてまだ間もないころ。 // 主人公は?: 自分が何のために生まれたのか分からなくて困ってる薄弱泣き虫な神様。 // すなわちゴッドのこと。 // 石って?: 勝手に沸いてくる。 // 神様の力で意志が吹き込まれる。 // たくさんくっつけようとすると疲れる。 // VOIDって?: 神様の空虚な気持ちを補うために生まれた生物。( VOID = 空っぽ <- BOID ) // 自分が輝いたり他のVOIDの光を吸収して満たされ鮮やかになっていく。 // 鮮やかVOIDを見ると、神様ちょっと幸せ気分。 // 場所は?: 一週間後に終焉を迎える。薄れゆくことなく非連続的離散的にすかっと消失予定。 // 住所は?: 道頓堀。 // // // ■ アルゴリズム体操 // 群れの動きには、おなじみの"boid"というアルゴリズムを使ってます。 // http://www.red3d.com/cwr/boids/ // // アルゴリズムについては、上記のWebページに譲るとして、 // とりあえずは、その他すごいboid達の紹介 // // 縁日の金魚を表現してみた(Flashの実装、金魚) // http://satoshi.blogs.com/life/2007/05/ui.html // // Demonstrating Bird Flocking using jogl(Javaの3D版実装、重いけど凄い) // http://www.dcs.shef.ac.uk/~paul/publications/boids/index.html // // // (本)「複雑系」とは何か(吉岡 良正) // http://shop.kodansha.jp/bc2_bc/search_view.jsp?b=1493280 // // この本には具体的アルゴリズムは書いてません。が、ハッカーが複雑系を発見、 // 開発するにあたって経た人間的ドラマが丹念かつ情熱的に語られてておもしろいですよ。 // 複雑系は。 // //////////////////////////////////////////////////////////////////////////////// // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // 定数 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;#define __DEBUG // デバッグ用 ;#define __NO_HSPTV // HSPTV機能いらない(デバッグ用) ;#define __FISH // さかな(かわいくできないので廃案) #define PI_2 1.5707963267949 // PI / 2 // ZERO DOUBLE_ZERO = 0.0 // ゼロ(浮動小数点) ZERO_VECT = DOUBLE_ZERO, DOUBLE_ZERO // 零ベクトル #define ctype _double(%1) (DOUBLE_ZERO + %1) // 小数点にして返す // font FONT_FACE = "MS ゴシック" // ボイド(void) #enum global _v_x_=0 #enum global _v_y_ #enum global _v_sx_ #enum global _v_sy_ #enum global _v_life_ #enum global _v_hue_ #enum global _v_saturation_ #enum global _v_width_ #enum global _v_rad_ #enum global _v_size_ #const VOIDS_CAPACITY 64 // voidの行動係数の設定 // NOTE: 各々の係数は、単位ベクトルに加わるわけで無いため、 // これらの係数の大小関係が、処理の優先度に繋がるというわけでは無い // 引き離れる力 #define SEPARATION_COEFFICIENT 0.50 // 整列する力 #define ALIGMENT_COEFFICIENT 0.30 // 重心へ向かう #define COHESION_COEFFICIENT 0.0002 // 巣へ変える力 #define HOMING_COEFFICIENT 0.003 // ストラクチャー(障害物) #enum global _s_x_=0 #enum global _s_y_ #enum global _s_life_ #enum global _s_hue_ #enum global _s_size_ #const STRUCTURES_CAPACITY 64 #const STRUCTURES_INITIAL_SIZE 4 // luminous(光) #enum global _l_x_=0 #enum global _l_y_ #enum global _l_count_ #enum global _l_size_ #const LUMINOUSES_CAPACITY 64 // window #const MAIN_WINDOW_WIDTH 640 #const MAIN_WINDOW_HEIGHT 480 // hue 0 ~ 191 ... #const HUE_MAX 192 // phase #enum ON_TITLE = 0 #enum ON_PLAY // Xを // -PI / 2 <= X <= PI / 2 // にまるめる // NOTE: 閉区間で返ってくる・・・(稀に誤動作の原因になりうるかも) #define global ctype NORMALIZE_RADIAN(%1) (atan(sin(%1), cos(%1))) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // モジュール // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #module "m" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // vv1とvv2の差から向きだけ持っている単位ベクトルを抽出して // power倍して、outに加算 // // グローバルな場所で途中の計算過程を参照するため、 // ret_sub に、vv1 - vv2 を // ret_abs に、ret_sub の絶対値を // 入れておく // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #define global mul_add_vect(%1,%2,%3, %4) a %1, %2, %3, %4 #deffunc a array out, array vv1, array vv2, double power // 差をとる ret_sub = -vv2 + vv1, -vv2._v_y_ + vv1._v_y_ // 絶対値取る ret_abs = sqrt( ret_sub * ret_sub + ret_sub._v_y_ * ret_sub._v_y_ ) // どっちにしろ、絶対値ゼロなら何をかけても変わらないけど、 // ゼロ除算は避ける if( ret_abs == DOUBLE_ZERO@ ) { ret_abs = 0.001 } // power倍して加算 out += ret_sub / ret_abs * power out._v_y_ += ret_sub._v_y_ / ret_abs * power return // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // 光の追加 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #define global entry_luminous(%1,%2,%3) _ %1, %2, %3 #deffunc _ int x, int y, int count repeat LUMINOUSES_CAPACITY@ dup luminous, luminouses@.0.cnt if( luminous._l_count_ <= 0 ) { luminous = x, y, count break } loop return // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // ブロック追加 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #define global entry_structure z #deffunc z x = rnd(560) + 40 y = rnd(440) + 20 repeat STRUCTURES_CAPACITY@ dup structure, structures@.0.cnt if( structure._s_life_ == 0 ) { structure = x, y, 1, rnd(HUE_MAX@) entry_luminous x, y, 16 break } loop return #global // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // グローバル // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #ifdef __NO_HSPTV if 0 { # deffunc hsptv_send var dummy1, int dummy2 return } #else // hsptv # runtime "hsptv" # regcmd 18 # cmd hsptv_send 0 #endif // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // voidの意志を可視化する(デバッグ用) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #ifdef __DEBUG #module #deffunc inspect_void_vect array void, double sx, double sy mag = 10.0 color line void._v_x_, void._v_y_, void._v_x_ + mag * sx, void._v_y_ + mag * sy return #global #endif // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // メイン // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // いつもの randomize // hsptvの初期化 hsptv_send buf, -1 notesel buf repeat redraw : await 33 : redraw 0 count-- // 背景初期化 pget 3200 gmode 4, 0, 0, 216 grect 320, 240, 0, 640, 480 // マウス getkey left_button // クリックされた(前回押されて無い and 今押されている) if( pre_left_button - left_button < 0 ) { start_mousex = mousex start_mousey = mousey } if( phase ) : else { // 塗る color 240, 240, 240 boxf 0, 0, MAIN_WINDOW_WIDTH, 128 boxf 0, 440, MAIN_WINDOW_WIDTH, MAIN_WINDOW_HEIGHT // ロゴ font FONT_FACE, 32, 17 caption = "An", "Abandoned", "Area" repeat 3 hsvcolor 16, 24, cnt * 32 + 128 pos 16 + cnt * 24, cnt * 32 + 16 mes caption.cnt loop // 線書く boxf 0, 128, MAIN_WINDOW_WIDTH, 128 boxf 0, 440, MAIN_WINDOW_WIDTH, 440 // スコア font FONT_FACE, 12, 17 repeat 30 pos 74 + ( cnt / 15 ) * 300, 152 + ( cnt \ 15 ) * 18 noteget name, cnt * 3 + 1 noteget score, cnt * 3 mes strf( "%2d", cnt + 1 ) + strf( "%8d", int(score) ) + "★" + name loop // ゲームオーバー直後、誤ってクリックしてタイトルを再開させないよう、 // 一定時間待ってからクリック可能とする if( ( count < 0 ) & left_button ) { phase++ gosub*tiny_init } continue } // 終焉 if( count < 0 ) { // 無機質なの書いて pos 120, 240 font FONT_FACE, 32, 17 color mes "Time is up" // 再描画 redraw // 一秒待つ wait 100 hsptv_send buf, happiness phase-- count = 16 continue } // とにかく押されてる if( left_button ) { drag_width = abs( start_mousex - mousex ) drag_height = abs( start_mousey - mousey ) middle_x = ( start_mousex + mousex ) / 2 middle_y = ( start_mousey + mousey ) / 2 // ソレっぽく点滅 gmode 4, , , sin(0.5 * count) * 32 + 72 color 0x99, 0x99, 0x99 // 選択範囲を色づけ grect middle_x, middle_y, 0, drag_width, drag_height } gmode 4, , , 192 repeat VOIDS_CAPACITY // 軽量化の dup void, voids.0.cnt #define void_x_ void dup void_y_, void._v_y_ dup void_sx_, void._v_sx_ dup void_sy_, void._v_sy_ dup void_life_, void._v_life_ dup void_saturation_, void._v_saturation_ dup void_width_, void._v_width_ void_life_-- // 死んだ瞬間 if( void_life_ = 0 ) { // 光る entry_luminous void_x_, void_y_, void_saturation_ / 16 + 4 } // 死んでいる・・・ if( void_life_ <= 0 ) { // 処理しない continue } // ランダムでhappiness if( rnd(100) == 0 ) { // 輝度地に応じて幸せ気分 happiness += int(void_saturation_ / 16) // 鮮度に応じて瞬く光 entry_luminous void_x_, void_y_, ( void_life_ / 50 ) } id = cnt // 加速力の係数 // 基本的には係数0.15で加速するけれど // 約32フレームごと、ランダムで強い加速をさせるみたいなイメージで c = 0.15 + 0.5 * rnd(3) * (rnd(32) == 0) // 重心を求める ddim center, 2 // 重心入れる ddim center_s, 2 // 向き nearest_dist = 10000000 dim another_exists dim n // 数 repeat VOIDS_CAPACITY // 処理相手 dup t_void, voids.0.cnt // 自分じゃない and 生きている if( ( id != cnt ) && t_void._v_life_ > 0 ) { // 自分の他に生存者がいる another_exists++ // 変数subはダミー // 自分と相手の距離を求めるため(だけ)に関数を呼び出す ddim sub, 2 mul_add_vect sub, void, t_void, 1 // 近い人記録更新 if( nearest_dist > ret_abs@m ) { nearest_dist = ret_abs@m dup nearest_void, voids.0.cnt } // 可視範囲 if( ret_abs@m < 80 ) { // いろいろ center += t_void center._v_y_ += t_void._v_y_ center_s += t_void._v_sx_ center_s._v_y_ += t_void._v_sy_ n++ } } loop // 「どっちに動こうか」意志ベクトル // 事情がなければ、今動いている方向に進みたい、方向転換したくない wills = void_sx_, void_sy_ #define will_sx wills dup will_sy, wills.1 // 他がいるなら if( another_exists ) { // 一定の距離を置くようにする(近すぎず、遠すぎず) ratio = limitf( nearest_dist / 30, 0, 4 ) if( nearest_dist < 30 ) { ratio = -ratio } // 意志ベクトルに対して、自分から相手へのベクトルを加算 mul_add_vect wills, nearest_void, void, ratio * SEPARATION_COEFFICIENT } // 可視範囲に誰かいるなら if( n ) { // 重心求める center /= n center._v_y_ /= n // 周りの向きに合わせる // 意志ベクトルに対して、みんなの向いている方向のベクトルを加算 // ( ベクトルA - 零ベクトル = ベクトルA ) mul_add_vect wills, center_s, ZERO_VECT, ALIGMENT_COEFFICIENT // 重心へ向かう // 意志ベクトルに対して、自分から重心へのベクトルを加算 mul_add_vect wills, center, void, COHESION_COEFFICIENT } // 障害物避ける repeat STRUCTURES_CAPACITY dup structure, structures.0.cnt if( structure._s_life_ <= 0 ) { continue } ddim sub, 2 mul_add_vect sub, structure, void, 1 // 角度差を求める diff_rad = atan( sub._v_y_, sub ) - atan( will_sy, will_sx ) // 正規化 diff_rad = NORMALIZE_RADIAN(diff_rad) dist = ret_abs@m // 前にある and 直線状にある and 近くの if( ( absf(diff_rad) < PI_2 && absf(ret_abs@m * sin(diff_rad)) < 24 ) && ret_abs@m < 81 ) { // 右に回るか左に回るか rotateDirection = ( diff_rad >= 0 ) - ( diff_rad < 0 ) // 90度回転させた単位ベクトルを加算(避ける動作) // 遠くならゆったり、近くにいるときは思いっきりさけたがる power = ( 9.0 - sqrt(ret_abs@m) ) * rotateDirection will_sx += sub._v_y_ * power will_sy -= sub * power ;void_sx_ *= 0.98 ;void_sy_ *= 0.98 ;c = 0.15 } loop // 外から内へ if( void_x_ < 40 || void_x_ > 600 ) { will_sx += ( 320.0 - void_x_ ) * HOMING_COEFFICIENT } if( void_y_ < 60 || void_y_ > 420 ) { will_sy += ( 240.0 - void_y_ ) * HOMING_COEFFICIENT } // 意志を行動に繁栄 dup void_s, void._v_sx_ mul_add_vect void_s, wills, ZERO_VECT, c // 摩擦で減速 void_sx_ *= 0.93 void_sy_ *= 0.93 // 加速度 加算 void_x_ += void_sx_ void_y_ += void_sy_ // 大きさ #ifdef __FISH void_width_ += 0.8 * c void_width_ *= 0.91 #else void_width_ += 4.0 * c void_width_ *= 0.75 #endif w = void_width_ + 8.0 // 回転 void._v_rad_ += 0.05 + c // 色 hsvcolor void._v_hue_, limit( void_saturation_, 0, 0xff ), limit( void_life_ / 4 + 32, 0, 230 ) // 書く #ifdef __FISH // 尻尾 repeat 8 theta = atan( void_sy_, void_sx_ ) t_t = theta + sin( void_width_ + void._v_rad_ + 0.5 * cnt ) * void_width_ * 0.1 r = 4 grect void_x_ - cos( t_t ) * cnt * r, void_y_ - sin( t_t ) * cnt * r, t_t + PI_2 / 2, ( 8 - cnt ) * 1 + 1, ( 8 - cnt ) * 1 + 1 loop #else repeat 3, 1 grect void_x_ - void_sx_ * cnt * 4, void_y_ - void_sy_ * cnt * 4, void._v_rad_, 4, 4 loop grect void_x_, void_y_, void._v_rad_, w, w #endif ;o 頭の後ろに尻尾が生えていると考えて差し支えない(何が?) #ifdef __DEBUG inspect_void_vect void, will_sx, will_sy #endif loop // ストラクチャ centerx = DOUBLE_ZERO centery = DOUBLE_ZERO dim n dim hue dim _structure_life, STRUCTURES_CAPACITY repeat STRUCTURES_CAPACITY dup structure, structures.0.cnt dup structure_y_, structure._s_y_ if( structure._s_life_ <= 0 ) { continue } // 書く hsvcolor structure._s_hue_, 96, 240 #define structure_x_ structure grect structure_x_, structure_y_, 0, 10, 10 // マウス離されたとき if( left_button - pre_left_button < 0 ) { // ドラッグ選択されたとき if( abs( structure_x_ - middle_x ) < drag_width / 2 && abs( structure_y_ - middle_y ) < drag_height / 2 ) { hue += structure._s_hue_ centerx += structure_x_ centery += structure_y_ n++ _structure_life.cnt = 1 } } loop if( n ) { // 使用する幸せ p = n * n * 5 if( p <= happiness ) { // ストラクチャを実際に壊すのはこっち repeat STRUCTURES_CAPACITY structures._s_life_.cnt ^= _structure_life.cnt loop // void作る repeat VOIDS_CAPACITY dup void, voids.0.cnt if( void._v_life_ <= 0 ) { void = centerx / n, centery / n, DOUBLE_ZERO, DOUBLE_ZERO, 200.0 + n * 400, _double( hue \ HUE_MAX ), 32.0 happiness -= p break } loop } } // 光 repeat LUMINOUSES_CAPACITY dup luminous, luminouses.0.cnt if( luminous._l_count_ > 0 ) { gmode 4, 0, 0, rnd(32) w = luminous._l_count_ * 8 #define luminous_x_ luminous dup luminous_y_, luminous._l_y_ hsvcolor rnd(255), 96, 240 grect luminous_x_, luminous_y_, 0, w, w grect luminous_x_, luminous_y_, 0, abs(w - 8), abs(w - 8) luminous._l_count_-- w_2 = w / 2 repeat VOIDS_CAPACITY dup void, voids.0.cnt if( void._v_life_ <= 0 ) { continue } if( abs( luminous_x_ - void ) < w_2 & abs( luminous_y_ - void._v_y_ ) < w_2 ) { void._v_saturation_++ } loop } loop if( count \ ( 32 + count / 70 ) == 0 ) { entry_structure } color 128, 128, 128 gmode 4, 0, 0, 128 gmode 4, 0, 0, 128 grect 320, 11, 0, 640, 22 grect 320, 469, 0, 640, 22 // 幸福 pget 3200 pos 4, 4 font FONT_FACE, 16, 17 mes "★ " + happiness // 時間 pos 540, 4 mes strf( "@%4.3fDays ..", 0.001 * count ) // キー情報の保存 pre_left_button = left_button loop *tiny_init // void初期化 ddim voids, _v_size_, VOIDS_CAPACITY // 光 dim luminouses, _l_size_, LUMINOUSES_CAPACITY // 障害物初期化 dim structures, _s_size_, STRUCTURES_CAPACITY repeat STRUCTURES_INITIAL_SIZE entry_structure loop count = 7000 // 幸福感 happiness = 20 return /* * TODO: * * FIXME: * * NOTE: * */