# How to add objects on specific keyframes

I understand that it's possible to keyframe visibility, and that might have to be at least part of the solution. However, I intend to create many objects at once, with a script to generate them, so I can't think how to apply visibility keyframes, when I'd have so many objects which initially don't exist.

If it helps, here's the (messy) code I'm working with:

import bpy

CoList = []

def DeleteExisting():
bpy.ops.object.select_all(action='TOGGLE')
bpy.ops.object.delete(use_global=False)
bpy.ops.object.select_all(action='TOGGLE')
bpy.ops.object.delete(use_global=False)

DeleteExisting()

def Create_List(Width):
for x in range(0,Width):
CoList.append([x])
for i in CoList:
x = i[0]
i.pop(0)
for y in range(0,Width):
i.append([x,y])
return CoList

def Generate_Cubes(CoList):
for x in CoList:
for y in x:
X_Value = y[0]
Y_Value = y[1]
while type(X_Value) == list:
if type(X_Value) == list:
X_Value = X_Value[0]
if type(Y_Value) == list:
Y_Value = Y_Value[0]
bpy.ops.mesh.primitive_cube_add(view_align=False, enter_editmode=False, location=(X_Value, Y_Value, 0), layers=(True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False))
bpy.ops.transform.resize(value=(0.25, 0.25, 0.25), constraint_axis=(False, False, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1)

for X in range(1,6):
bpy.context.scene.frame_current = X
Width = X+2
DeleteExisting()
CoList = Create_List(Width)
Generate_Cubes(CoList)
bpy.ops.object.select_all(action='TOGGLE')
bpy.ops.object.select_all(action='TOGGLE')


This creates a grid of cubes, which looks like this:

And it also advances the current frame, however once an object is created, it exists on all frames, regardless of any keyframes that I know of. So how would I implement this so that objects only exist on the frame which they are created on?

Driver based method,

driving the hide property, simply hiding a cube if its "visframe" custom property is greater than the frame number. Creates a 10 x 10 grid of cubes where only one is visible on frame 1, 2 On frame 2, ... 100 on frame 100. Drive the hide_render property to render.

import bpy
context = bpy.context
scene = context.scene
cube = context.active_object
cube.scale *= 0.25

for i in range(10):
x = 2 * i
for j in range(10):

cube["visframe"] = 10 * i + j
driver = fcurve.driver
var = driver.variables.new()
var.name = "vis"
var.targets[0].id = cube
var.targets[0].data_path = '["visframe"]'
# scene.frame_current is defined as frame to driver namespace
driver.expression = "vis >= frame" # show if cube["visframe"] > frame
y = 2 * j
cube.location.xy = (x, y)
cube = cube.copy()


Frame Change Handlers

This one uses a frame change pre handler to remove all cubes marked with a visframe custom property, and a post handler to create them in a frame by frame grid.

import bpy
context = bpy.context
scene = context.scene

def delcubes(scene):
cubes = [c for c in scene.objects if "visframe" in c.keys()]
for cube in cubes:
bpy.data.objects.remove(cube)

def makecubes(scene):
w = scene.frame_current
cube = context.active_object
cube.scale *= 0.25
grid(cube, w)

def grid(cube, w):
for i in range(w):
x = 2 * i
for j in range(w):
print(i, j)
cube["visframe"] = True
y = 2 * j
cube.location.xy = (x, y)
cube = cube.copy()

# for testing pays to clear the handlers.
bpy.app.handlers.frame_change_pre.clear()
bpy.app.handlers.frame_change_post.clear()

# set up a frame change handler.
bpy.app.handlers.frame_change_pre.append(delcubes)
bpy.app.handlers.frame_change_post.append(makecubes)


Each object has two properties called hide and hide_render - the first is viewport visibility and the second is render visibility.

To use python to set a keyframe for a property you set the value, then use keyframe_insert(data_path, frame=f) to keyframe it. So if you only want to object visible on one frame, you make it visible and set a keyframe on the relevant frame, then hide it and add a keyframe for the previous and next frame.

import bpy

# clear any existing objects
for o in bpy.data.objects:
o.hide = False
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete()

grid_size = 7
# the default cube is 2 units wide, add another .2 for gap
spacing = 2.2

for x in range(grid_size):
for y in range(grid_size):
f = (x*grid_size) + y
obj = bpy.context.active_object
# key as visible on the current frame
obj.keyframe_insert('hide',frame=f)
obj.keyframe_insert('hide_render',frame=f)
# hide it
obj.hide = True
obj.hide_render = True
# key as hidden on the previous frame
obj.keyframe_insert('hide',frame=f-1)
obj.keyframe_insert('hide_render',frame=f-1)
# key as hidden on the next frame
obj.keyframe_insert('hide',frame=f+1)
obj.keyframe_insert('hide_render',frame=f+1)