C4D.Python: CineversityのPythonエフェクタを再現

勉強のために、CineversityのNAB 2015 Rewind – Colin Sebestyen: Python Coding for Motion Designの40分ごろのPythonエフェクタを再現した。といってもプレゼンターもコピーして使ってくれと言ってくれていたので、基本そのままコピーしました。なお、Pythonエフェクタのモードは完全コントロールで、ユーザデータも4つ作成しています。

このPythonエフェクタは、各クローンがstepsizeで指定した距離分、XYZ+-の各方向に限定して1フレずつランダムに動くようになっています。width/height/depthの値は、クローンが動く範囲になります。



コード

import c4d
from c4d.modules import mograph as mo
from c4d.utils import *
import random
#Welcome to the world of Python

#タグのユーザデータを取得
width = op[c4d.ID_USERDATA,1]
height = op[c4d.ID_USERDATA,2]
depth = op[c4d.ID_USERDATA,3]
stepsize = op[c4d.ID_USERDATA,4]



#Walkerクラスの作成
class Walker():
    #初期化。毎回変数selfを初期化する関数
    def __init__(self,x,y,z):
        self.x = x
        self.y = y
        self.z = z
    #0~5の整数をランダムに出してその数字によってXYZの+-方向のどこに動かすか判定
    def step(self):
        choice = random.randint(0,5)
        if choice == 0:
            self.x +=stepsize
        elif choice == 1:
            self.x -=stepsize
        elif choice == 2:
            self.y +=stepsize
        elif choice == 3:
            self.y -=stepsize      
        elif choice == 4:
            self.z +=stepsize
        elif choice == 5:
            self.z -=stepsize
        
        #ただし、移動量がwidth/height/depthで決めた範囲を超えないように設定
        self.x = ClampValue(self.x,0,width)
        self.y = ClampValue(self.y,0,height)
        self.z = ClampValue(self.z,0,depth)

#リストwalkerを作成   
walker = []

#MoGraphのデータを取得
md = mo.GeGetMoData(op)
#クローンの複製数を取得
cnt = md.GetCount()

#リストwalkerにwidth/height/depthの値の1/2をクローンの複製分入力
for i in reversed(xrange(0,cnt)):
    walker.append(Walker(width/2,height/2,depth/2))

def main():
    if md==None: return False #MoGraphのデータがなければ終了
    
    marr = md.GetArray(c4d.MODATA_MATRIX) #各クローンのマトリックスを変数marrに入れる

    #各変数marrのマトリックスにリストwalkerの位置ベクトルを入れる
    for i in reversed(xrange(0,cnt)):
        marr[i].off = c4d.Vector(walker[i].x,walker[i].y,walker[i].z)
    
    #各クローンにマトリックスを設定
    md.SetArray(c4d.MODATA_MATRIX,marr,True)
    
    #walkerリストに対して、Walkerクラスのsetp関数を実行
    for i in reversed(xrange(0,cnt)):
        walker[i].step()
        
    return True

新たに自分でクラスWalkerを作って、独自の関数Stepを作成していますが、面白いのがランダムに動く仕組みが、サイコロを使って決めるような方法だという点です。整数のランダム数を生成するrandint()関するを使って0~5(XYZ*2(+とーの2方向))その出た数字でどの方向に移動するかを決めていることです。

実際動くようになりましたが、動かしてみるといろいろ問題点もありました。どうもクローン数を変更するとうまく動かないことがあります。一度カウントした値をどうも覚えてしまうようです。モードをパラメータコントロールに切り替えてから再度完全コントロールに戻すと更新されました。どこかで初期化するコードを入れる必要がありそうです。

このCineversityのプレゼンは非常に面白いのでお勧めです。ベクトルやフォースの解説もしてくれます。自然な動きをアルゴリズム化する方法を紹介しています。特に1時間1分ごろからスクラッチでPythonエフェクタを解説付きで作成してくれるので、勉強になると思います。

こちらのアルゴリズムについては、Nature of Codeという本で紹介されているそうです。日本語版もボーンデジタルさんから出ているそうです。