7
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Tkinterでメダカっぽい動きを表現する

Posted at

これは何?

fish.gif

こんなやつです。なんとなくメダカっぽい動きをします。

ソースコードと簡単な説明

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秒間だけ、先頭のメダカが狂ったように泳ぎます。
このとき、他のメダカは先頭メダカを追います。

感想など

比較的簡単なコードですが、それっぽい動きができました。
パラメータを変えると動きがいろいろ変化します。

7
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?