これは何?
こんなやつです。なんとなくメダカっぽい動きをします。
ソースコードと簡単な説明
import math
import random
import time
import numpy as np
# ベクトル正規化
def norm( a):
x = np.linalg.norm(a)
if x == 0.0:
x = 1
return a / x
#
# 魚
#
class Fish(object):
VISCOSITY = 2.2 # 粘性係数[1/sec]
RUSH_ACL = 15.0 # 突進加速度[m/sec^2]
RUSH_TIME= 2 # 突進加速する時間間隔[sec]
MAX_POS = 1 # 空間の広さ[m]
fishes = [] # 魚群
def __init__(self,id=-1,pos_x=0,pos_y=0):
self.id = id
self.pt_vel = np.array([0.0,0.0])
self.pt_pos = np.array([pos_x,pos_y])
self.rush_time = 0 # 次回突進までの残り時間
# 更新
def update(self, delta_t,mode='MODE_RAND'):
# 一定時間がたったら突進加速
self.rush_time -= delta_t
if self.rush_time < 0:
acl,tim = 1,1 # 先頭魚用の係数
# 各自ランダムに泳ぐ
if mode == 'MODE_RAND':
pt_dir = norm( np.array([random.uniform(-1,1),random.uniform(-1,1)]))
# 先頭魚を追う
elif mode == 'MODE_HEAD':
if self.id == 0: # 先頭魚
pt_dir = norm( np.array([random.uniform(-1,1),random.uniform(-1,1)]))
acl,tim = 2,0.1 # 10倍の頻度で2倍の加速
else: # その他の魚
head = Fish.fishes[0]
pt_dir = norm( head.pt_pos - self.pt_pos)
pt_acl = pt_dir * acl * Fish.RUSH_ACL * random.uniform(0.8,1.2)
self.rush_time = tim * Fish.RUSH_TIME * random.uniform(0.5,1.2)
else:
pt_acl = self.pt_vel * -Fish.VISCOSITY
# 速度、位置の計算
self.pt_vel += pt_acl * delta_t
self.pt_pos += self.pt_vel * delta_t
self.limitWall()
return (self.pt_pos,self.pt_vel)
# 壁(境界)処理
def limitWall(self):
bLim = False
if self.pt_pos[0] >= Fish.MAX_POS:self.pt_pos[0] = Fish.MAX_POS; bLim = True
elif self.pt_pos[0] < 0: self.pt_pos[0] = 0; bLim = True
elif self.pt_pos[1] >= Fish.MAX_POS:self.pt_pos[1] = Fish.MAX_POS; bLim = True
elif self.pt_pos[1] < 0: self.pt_pos[1] = 0; bLim = True
if bLim: self.pt_vel *= -1 # 反転
#
# main
#
if __name__ == '__main__':
import tkinter as tk
SZ = 500 # 画面サイズ
def toRect(vec,size=5):
x = vec[0] * SZ; y = vec[1] * SZ
return (x-size,y-size,x+size,y+size)
def toColor(c1,c2,i,N):
c = [ int(c1[k]+i/N*(c2[k]-c1[k])) for k in range(3)]
return '#{:02X}{:02X}{:02X}'.format(*c)
def getId_pos(idx):
return 'pos{}'.format(idx+1)
def getId_vel(idx):
return 'vel{}'.format(idx+1)
N_FISH = 15
DELTA_T = 0.033
cv = tk.Canvas(width=SZ,height=SZ,bg='white')
cv.pack()
# 魚群の生成
Fish.fishes = []
for idx in range(N_FISH):
fill = toColor([255,0,0],[255,255,0],idx,N_FISH)
cv.create_oval( 0,0,0,0, tags=getId_pos(idx), fill=fill,outline=fill) # 本体
cv.create_oval( 0,0,0,0, tags=getId_vel(idx), fill=fill,outline=fill) # しっぽ
fish = Fish(idx,random.uniform(0,1),random.uniform(0,1))
Fish.fishes.append( fish)
cv.create_text(10, 10, anchor=tk.NW,text = '', font = ('FixedSys', 20),tags='tMes')
# イベントループ
cur_mode = 'MODE_HEAD'
mode_time = 0
while True:
# 動作モードの切り替え
mode_time -= DELTA_T
if mode_time < 0:
if cur_mode == 'MODE_RAND':
cur_mode = 'MODE_HEAD'
mode_time = 15
elif cur_mode == 'MODE_HEAD':
cur_mode = 'MODE_RAND'
mode_time = 30
cv.itemconfigure('tMes', text=cur_mode)
# 魚が泳ぐ
for idx,fish in enumerate(Fish.fishes):
pt_pos,pt_vel = fish.update(DELTA_T,cur_mode)
# 本体
cv.coords(getId_pos(idx), *toRect(pt_pos,10))
# しっぽ
pt_vel = norm(pt_vel) * (-15/SZ) + pt_pos
cv.coords(getId_vel(idx), *toRect(pt_vel,5))
cv.update_idletasks()
cv.update()
time.sleep(DELTA_T)
tk.mainloop()
画面全体にメダカをランダム位置にばらまきます。
メダカの見た目は大小の円だけでシンプルに表現しています(手抜き)
メダカは約2秒間隔でランダム方向に加速します。
メダカに、進行方向とは逆に速度に比例した粘性加速度をかけます。
30秒に1回、15秒間だけ、先頭のメダカが狂ったように泳ぎます。
このとき、他のメダカは先頭メダカを追います。
感想など
比較的簡単なコードですが、それっぽい動きができました。
パラメータを変えると動きがいろいろ変化します。