2
$\begingroup$

I'm a complete noob when it comes to scripting in blender and programming in general, (I wrote my first line of code when I started this project about a week ago.)

In this scene there are two cubes and a plane, the cubes have active rigid body physics and the plane is passive. The script is intended to duplicate each cube in the collection "cubes" 1 time when they reach the threshold location as they slide down the ramp.

I have 3 problems.

-The first is that one of the cubes, (the one that hits the threshold second) duplicates continuously after it hits the threshold or one of the cubes doesn't duplicate at all, basically I'm curious why the separate cubes exhibit different behavior. However it does seem like the cube that hits the threshold first usually behaves properly, which means it duplicates once.

-The second problem is that the behavior of the cube's duplication becomes increasingly more strange the more I run the script. By the 5th or so time that I've run the script, both cubes are continuously duplicating or neither of them are some other unpredictable behavior.

-The third stems from the fact that I collaborated with Chat GPT early on in the process of writing the script and a couple of the lines that are left over from what Chat GPT wrote I don't quite understand. For example the line that defines the name of the duplication funtion; duplicate_cube(scene). I'm honestly not sure why the argument is scene. Also in the line bpy.app.handlers.frame_change_pre.append(duplicate_cube) I get the idea of it but I'm not sure why its necessary and it seems possible that its responsible for the continuous duplication.

As far as the first problem goes, I'm really not sure what's going on. Regarding the second problem, it seems like some data is being stored that isn't being reset that effects the variables in the script.

To minimize the effect this second problem is possibly having on the behavior of the cubes I've tried creating the scene from scratch in a new file each time I make modifications to the script before I run it. This ensure that the first run is predictable but regardless the first problem still exists and as I run the script more times it kind of just becomes an unpredictable mess.

Like I said I'm pretty helpless in this area of blender so forgive me if these are ambiguous or misguided questions. I've also only posted on stack once before so let me know if this an acceptable post or if it's too all over the place or whatever. Below is the script.

import bpy

threshold_location_x = 5.0

def add_custom_property_to_cubes():
    collection = bpy.data.collections.get("cubes")
    if collection:
        for cube in collection.objects:
            cube["duplicated"] = 0

add_custom_property_to_cubes()

def duplicate_cube():


    for cube in bpy.data.collections.get("cubes").objects:

        if cube.matrix_world.translation.x >= threshold_location_x:
            if bpy.context.object["duplicated"] == 0:

                duplicate = cube.copy()
                duplicate.data = cube.data.copy()
                cube["duplicated"] = 1
                bpy.context.scene.collection.objects.link(duplicate)
                duplicate.location = cube.matrix_world.translation
            
bpy.app.handlers.frame_change_pre.append(duplicate_cube)

If you want to test the script the scene is simple to create. There are 2 cubes in a collection called "cubes". Both cubes have an active rigid body and the plane is a passive rigid body. All settings on rigid body are default other than the passive on the plane.

Also just to eliminate an other potential variable, after I run the script once I comment out the add_custom_property_to_cubes() funtions and reset the custom property manually back to zero which can be found in the object properties.

This screenshot was captured just after the cubes reached the threshold. As you can see Cube2 behaved normally but Cube1 over duplicated.

$\endgroup$
3
  • $\begingroup$ ChatGPT designed this code in a way that I wouldn't necessarily design it. Can you confirm something for me? Does it look like there is a duplication event every time a cube from a collection hits threshold, and not when the first cube hits the threshold? I think you have exponential expansion in cubes. Because each cube is being checked individually instead of marking the collection as "duplicated" and then skipping the duplication step until the next collection slides down. $\endgroup$ Oct 5, 2023 at 3:39
  • $\begingroup$ Even worse, I think the ``` if bpy.context.object["duplicated"] == 0: ``` line is just flat out a complete error by chat GPT. If the context is the last created cube, then the duplication will occur nearly every frame after, since there will always be a newly created duplicate cube which is selected by the context. $\endgroup$ Oct 5, 2023 at 4:15
  • 1
    $\begingroup$ if you want us to help you, i would recommend providing the blend file then letting do this all manually. $\endgroup$
    – Chris
    Oct 5, 2023 at 5:35

1 Answer 1

2
$\begingroup$

Here's a snippet that should work.

A summary of the modifications :

  • Instead of 1/0 you should use the boolean construct to test for true/false values.
  • You query the active object for duplication in your loop, which is nonsensical in this context, since it can be any object, especially it can be a newly created cube that you don't want to query. Use if not cube["duplicated"]: instead
  • As per the application handlers documentation frame_change_pre takes a mandatory argument, of type bpy.types.Scene, which is automatically filled in when the callback is fired.
  • Instead of manually ensuring and updating the duplicated custom property you can add it to all bpy.types.Object. See the last line.
import bpy

threshold_location_x = 5.0

def duplicate_cube(scene):
    collection = bpy.data.collections.get("cubes")
    if not collection:
        return
    for cube in collection.objects:
        if cube.matrix_world.translation.x >= threshold_location_x:
            if not cube.duplicated:
                cube.duplicated = True
                duplicate = cube.copy()
                duplicate.data = cube.data.copy()
                bpy.context.scene.collection.objects.link(duplicate)
                duplicate.location = cube.matrix_world.translation

bpy.app.handlers.frame_change_pre.append(duplicate_cube)
bpy.types.Object.duplicated = bpy.props.BoolProperty(default=False)

Edit : Alternative script that removes cubes from the collection, so you don't need the duplicated flag. You will need a new collection named cubes_duplicated where the duplicated cubes will go once they have been duped.

import bpy

threshold_location_x = 5.0

def duplicate_cube(scene):
    collection = bpy.data.collections.get("cubes")
    collection_dupes = bpy.data.collections.get("cubes_duplicated")
    if not collection or not collection_dupes:
        return
    for cube in collection.objects:
        if cube.matrix_world.translation.x >= threshold_location_x:
            duplicate = cube.copy()
            duplicate.data = cube.data.copy()
            bpy.context.scene.collection.objects.link(duplicate)
            collection.objects.unlink(cube)
            collection_dupes.objects.link(cube)
            duplicate.location = cube.matrix_world.translation

bpy.app.handlers.frame_change_pre.append(duplicate_cube)

enter image description here

$\endgroup$
3
  • 1
    $\begingroup$ Very excellent, im glad someone beat me to it. If @tylerk would like to run this simulation indefinitely, I would suggest creating logic to purge old cubes from the "cubes" collection or else performance issues can creep in. Regarding OP's 3rd question, you have to add the function to the frame_change_pre handler because otherwise, there would be no way for Blender to poll for the objects position in the scene in order to implement the duplication logic. $\endgroup$ Oct 5, 2023 at 23:03
  • $\begingroup$ Your script worked handsomely, thank you @Gorgious ! I'm going to have to look at it a bit more to understand exactlyhow it works but I'm happy to have some new useful concepts to learn. Also thankyou WhisperingShiba for your suggestion about purging old cubes, that's a good idea ill try that! Humans-1, Chat GPT-0 $\endgroup$
    – tylerk
    Oct 6, 2023 at 4:37
  • 2
    $\begingroup$ @WhisperingShiba thank you ! I edited the answer to match what you described, I agree it's a bit more straightforward. Cheers $\endgroup$
    – Gorgious
    Oct 6, 2023 at 7:53

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .