The operator bpy.ops.mesh.primitive_cube_add()
returns the status of the execution and not a reference to the created object. It does set the newly created object as active though, which means it can be accessed by using bpy.context.active_object
.
Since you already know that you want the location on the x-axis to be zero, you can set this right when the object is created instead of changing it later.
In your current code the objects are not linked to the newly created collection. You can add an object to a collection by using collection.objects.link()
where collection is an object of type bpy.types.Collection
. You can unlink the cubes from the currently active collection where it was originally added to, if you don't want them to appear in both collections.
import bpy
from random import randint
num_cubes = 3
master_collection = bpy.context.scene.collection
collection = bpy.data.collections.new("Cubes")
master_collection.children.link(collection)
for i in range(num_cubes):
y = randint(-10, 10)
z = randint(-10, 10)
bpy.ops.mesh.primitive_cube_add(location=(0, y, z))
active_object = bpy.context.active_object
# Link to the newly created "Cube" collection
collection.objects.link(active_object)
# Unlink from the active collection in the view layer
# (otherwise it will appear in both collections)
bpy.context.view_layer.active_layer_collection.collection.objects.unlink(active_object)
If the changing of the location was just meant as an example and you do need the objects in a list, you can store them at each iteration through the loop:
import bpy
from random import randint
num_cubes = 3
master_collection = bpy.context.scene.collection
collection = bpy.data.collections.new("Cubes")
master_collection.children.link(collection)
cube_list = []
for i in range(num_cubes):
y = randint(-10, 10)
z = randint(-10, 10)
bpy.ops.mesh.primitive_cube_add(location=(0, y, z))
active_object = bpy.context.active_object
# Link to the newly created "Cube" collection
collection.objects.link(active_object)
# Unlink from the active collection in the view layer
# (otherwise it will appear in both collections)
bpy.context.view_layer.active_layer_collection.collection.objects.unlink(active_object)
cube_list.append(active_object)
Alternatively you can retrieve them from the Cubes collection by using:
collection.all_objects()
This assumes that you still have the collection variable in scope with the reference to the collection. Otherwise you will have to access it through:
bpy.context.scene.collection.children["Cube"].all_objects
The latter two examples don't give you a list, but also an object which is iterable of type bpy.types.bpy_prop_collection
. Therefore it can still be used in a loop similar to how you would iterate through a list.
import bpy
objs = bpy.context.scene.collection.children["Cube"].all_objects
for obj in objs:
print(obj.name)
Be aware though that when creating multiple collections with the same name, Blender will automatically append a number to avoid duplicate names. Which means if you're executing the script multiple times, you will have collections called "Cubes", "Cubes.001", "Cubes.002", "Cubes.003" etc. If you want to avoid this and only have one "Cubes" collection, you will have to check if it exists before creating a new collection:
import bpy
name = "Cubes"
master_collection = bpy.context.scene.collection
collection = bpy.data.collections.get(name)
if collection is None:
collection = bpy.data.collections.new(name)
if master_collection.children.get(name) is None:
master_collection.children.link(collection)