-Blender Python で剛体をいっぱい作成-

3. コピーで増殖させる

ここは、Python でスクリプトを組んで、オブジェクトの配置や増減を制御したいというときの、スクリプトの組み方のメモです。
ここでは、スクリプトによってオブジェクトをどんどん作り出すことをしてみます。

■オブジェクトのコピー

オブジェクトをコピーさせる方法は複数あるようなのですが、bpy.ops.object.duplicate()を使ってみます。GUIの更新といった面で効率は悪く、数十個のコピーはともかく数百個とか考えると効率は悪いかもしれません。

効率の良いコピーをするには、もっと(生のデータを扱うような)低レベルなコマンドを使って内部のデータだけを操作して、最後にシーンの更新をまとめてするといったような方法もあるようです。スクリプトはより長く煩雑になりそうですが…

邪魔にならない場所に、コピー元のオブジェクトとして円筒を作成しておきました。
    objectName='Cylinder'    
    copySrc=bpy.data.objects[objectName]
コピー元として copySrc を変数に設定しておきます。
        for ob in bpy.context.scene.objects:
            ob.select = False
        copySrc.select = True
duplicateは select 状態のものを複製するので、ループ内でコピー元一つだけ select 状態になるようにする操作を入れました。

        bpy.ops.object.duplicate(linked=True)
        newObj=bpy.data.objects[objectName+'.001']
        newObj.name='Copy'
        obj.append(newObj)
        bpy.context.scene.objects.active = newObj

duplicate で linked=True にすることで、メッシュ情報などはオリジナルのデータのものをそのまま(コピーはしないで)利用して、位置や回転などだけが独立して動くようになります。(メモリなどの資源が節約されます)

コピー後のオブジェクトを得るのに名前***.001で判定しているので、すでにその名前のものが配置されていないことが必要です。

■最終的なスクリプト

import bpy
import math
import fnmatch
  
def ClearObjects():
    scene = bpy.context.scene
    data = bpy.data
    pattern = 'Copy*'
    IcoA = [objA for objA in scene.objects if fnmatch.fnmatchcase(objA.name, pattern)]
    IcoB = [objB for objB in data.objects if fnmatch.fnmatchcase(objB.name, pattern)]
    IcoC = [objC for objC in data.meshes if fnmatch.fnmatchcase(objC.name, pattern)]
    IcoD = [objD for objD in data.materials if fnmatch.fnmatchcase(objD.name, pattern)]
    
    for item in IcoA:
        if item.type == 'MESH':
            bpy.context.scene.objects.unlink(item)
    for item in IcoB:
        if item.type == 'MESH':
            bpy.data.objects.remove(item)
    for item in IcoC:
        bpy.data.meshes.remove(item)
    for item in IcoD:
        bpy.data.materials.remove(item)
 
def CreateObjects(frame):
    if (bpy.context.screen.is_animation_playing == False):
        return
    
    objectName='Cylinder'    
    copySrc=bpy.data.objects[objectName]
       
    num = 12
    r = 4
    obj = []
    for i in range(num):
        t = 2.0 * math.pi * i / num
        x = 0.8 * r * math.cos(t)
        y = 0.8 * r * math.sin(t)
        
        for ob in bpy.context.scene.objects:
            ob.select = False
        copySrc.select = True
        
        bpy.ops.object.duplicate(linked=True)
        newObj=bpy.data.objects[objectName+'.001']
        newObj.name='Copy'
        obj.append(newObj)
        bpy.context.scene.objects.active = newObj
        obj[i].location=(0,0,0)
        
        bpy.ops.rigidbody.object_add()
        obj[i].rigid_body.kinematic = True
        obj[i].rigid_body.restitution = 0.6
        obj[i].hide_render = True
        obj[i].hide_select = True
        obj[i].keyframe_insert( data_path='hide_render', frame=frame-1 )
        obj[i].keyframe_insert( data_path='hide_select', frame=frame-1 )
        obj[i].keyframe_insert( data_path='location', frame=frame-1 )
        
        obj[i].hide_render = False
        obj[i].hide_select = False
        obj[i].keyframe_insert( data_path='hide_render', frame=frame )
        obj[i].keyframe_insert( data_path='hide_select', frame=frame )
        
        obj[i].location = (x, y, 0.1)           
        obj[i].keyframe_insert( data_path='location', frame=frame )
        obj[i].rigid_body.keyframe_insert( data_path='kinematic', frame=frame )
        
        obj[i].location = (x*1.06, y*1.06, 0.1)
        obj[i].keyframe_insert( data_path='location', frame=frame+1 )
        
        obj[i].rigid_body.kinematic = False
        obj[i].rigid_body.keyframe_insert( data_path='kinematic', frame=frame+2 )   
    
# every frame change, this function is called.
def handler(scene):
    frame = scene.frame_current

    if frame == 2:
        ClearObjects()
    if frame % 10 == 3:
        CreateObjects(frame)
    if frame == scene.frame_end:
        bpy.ops.screen.animation_cancel(restore_frame=False)
        
def handler_render_pre(scene):
    bpy.app.handlers.frame_change_pre.clear()
    
ClearObjects()
    
bpy.app.handlers.render_pre.clear() 
bpy.app.handlers.render_pre.append(handler_render_pre)

bpy.app.handlers.frame_change_pre.clear() 
bpy.app.handlers.frame_change_pre.append(handler)
最終的なスクリプトはこのようになりました。

コピーされたものは Copy.*** という名前になるようにして、クリアするのはその名前のものにしています。

スクリプトを実行するのはアニメーション再生中だけにする。(その分スクリプト実行の瞬間にクリアを1回行う)
といったような手直しをしています。
これで、複雑な(今回は円筒ですが)形状のものも大量に放出することができるようになりました。

スクリプトダウンロード

inserted by FC2 system