1
$\begingroup$

I created a series of objects that spawn individually over a time line. Something like a drop ship where one ship comes out at a time.

After all the objects are in their position, I want the to merge all at the same time back to one position.

So on the outer loop, I want it to take those objects and then change their final position and add a key frame to each one of them.

Is there a simple way to iterate over the objects created in the nested for loop or do I have to create a list, then iterate over that list on the first for loop? I know this may be more of a coding question but was wondering if the the blender library could call all objects just created.

import bpy
import time
from random import randint

number = 10

for i in range(0,number):
    for i in range (0,number):  ##Create Batch of 10 Objects
        bpy.ops.mesh.primitive_uv_sphere_add(location=(0,0,0)) ##Create UV Sphere
        bpy.ops.anim.keyframe_insert_menu(type='Location')  ##Add Keyframe for recently created Sphere
        x=randint(-10,10)
        y=randint(-2,2)
        z=randint(-10,-1)
        bpy.context.scene.frame_set(bpy.context.scene.frame_current + randint(1,10)) #Move Frame to a random Location
        bpy.ops.transform.translate(value=(x,y,z))  #Move Object to new location
        bpy.ops.anim.keyframe_insert_menu(type='Location') #Add Keyframe for new Location
        x=randint(-10,10)
        y=randint(-10,10)
        y=randint(-2,2)
        z=randint(-10,10)
        bpy.context.scene.frame_set(bpy.context.scene.frame_current + randint(1,10))
        bpy.context.object.location = (x, y, z)
        bpy.ops.anim.keyframe_insert_menu(type='Location')

    #Now I want the objects that were created in the for loop, to all move together to one point at the same time.    
    bpy.context.scene.frame_set(bpy.context.scene.frame_current + 5)
    bpy.context.object.location = (0, 0, 0)
    bpy.ops.anim.keyframe_insert_menu(type='Location')

enter image description here

$\endgroup$
2
$\begingroup$

Create a list in a scope of the code where these objects will need to exist. Use the for loop to add them to you list. Access those objects from that list.

$\endgroup$
  • $\begingroup$ That was my first instinct, and it does not add too many lines of code. I was just thinking that there might be a way to access those objects without a list. Thank you! $\endgroup$ – Migpics Jan 3 at 11:03
1
$\begingroup$

Copy and link

After calling a create primitive operator in object mode the new object is context.object.

Operators slow down over time, see Python performance with Blender operators

Instead can create one sphere with operator and duplicate, and link to collection(s) as many times as we like. Add to the location instead of transform operator. And use keyframe insert method. Reducing the amount of operator calls from 5 * number * number to 1.

Made a flat list of number * number objects. They can be indexed using obs[row * 10 + col]. No reason not to make a 2D list of lists.

Use keyframe_insert(...) instead of bpy.ops.anim.keyframe_insert_menu We can specify the datapath of the keyframe, in this case location, the frame and the index. eg add keyframe for location x, y, z at frame 33

ob.keyframe_insert("location", frame=33, index=-1)

index=-1 is the default. To insert only z location

ob.keyframe_insert("location", frame=33, index=2)

the default for frame is context.scene.frame_current.

enter image description here Test run number = 7

Here is a test script showing these concepts. Didn't put in the second keyframe.

import bpy
from random import randint
from mathutils import Vector

number = 10
frame_start = 1

context = bpy.context
scene = context.scene
collection = context.collection or scene.collection

# create a sphere
bpy.ops.mesh.primitive_uv_sphere_add()
sphere = context.object

f = frame_start #  or>> scene.frame_current
obs = [sphere.copy() for i in range(number * number)]


for i, ob in enumerate(obs):
    row, col = i // number, i % number
    print(row, col, ob.name)
    ob.keyframe_insert("location", frame=f)
    ob.location += Vector((randint(-10, 10),
                           randint(-10, 10),
                           randint(-10, 10)))
    f += randint(1, 10)
    ob.keyframe_insert("location", frame=f)

f += 5
for o in obs:
    o.location = (0, 0, 0)
    o.keyframe_insert("location", frame=f)
    collection.objects.link(o)

# remove original
bpy.data.objects.remove(sphere)
$\endgroup$
  • $\begingroup$ This is very elegant and you do a wonderful job of breaking down all contexts of the problem. I really appreciate your thoroughness and the time you took into preparing this. But what is the reason to elminate the bpy? I guess I need to do a bit more digging in how these structures work. $\endgroup$ – Migpics Jan 3 at 11:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for?Browse other questions tagged or ask your own question.