1
$\begingroup$

I am currently using blender 2.8 and I am trying to make a scripted animation where I move a set of cubes from one end to the other on the x-axis with delays in between animations. This isn't really a real animation because I am not using the timeline. I am trying to create a real-time demo of a personal project. If you look below I want to make the collection of cubes to travel to the right in delayed steps. My current script is like this:

import time

collection = bpy.data.collections["layer_1_filters"].objects # I grouped the 5x5 cube grid to the collection called "layer_1_filters"

for steps in range(23): #steps to the right 23 times

    for cube in collection: # loop each cube

        cube.location[0] = cube.location[0] + 3 # move all the cubes to the right by 3 on the x-axis

    time.sleep(0.3) # pause 0.3 seconds before start moving right again.
                    #THIS HANGS and BREAKS BLENDER!

If I use time.sleep to delay the actions blender hangs then crashes, if I don't use some sort of delay the cubes would just immediately teleport to the end. How can I slove this?

enter image description here

$\endgroup$

1 Answer 1

2
$\begingroup$

Modal Timer operator

For the most part don't use time.sleep() in blender, as you have seen it doesn't work as expected.

Modify the template text editor > templates > python > operator modal timer

This line

self._timer = wm.event_timer_add(0.3, window=context.window)

creates a timer that fires a 'TIMER' event every 0.3 seconds.

Additionally I've added a count class property to only move the object that many times.

For example sake, only moves the context.object edit to suit

import bpy


class ModalTimerOperator(bpy.types.Operator):
    """Operator which runs its self from a timer"""
    bl_idname = "wm.modal_timer_operator"
    bl_label = "Modal Timer Operator"

    _timer = None
    count = 0

    def modal(self, context, event):
        if event.type in {'RIGHTMOUSE', 'ESC'}:
            self.cancel(context)
            return {'CANCELLED'}

        if event.type == 'TIMER' and self.count < 24:
            context.object.location.x += 3
            self.count += 1


        return {'PASS_THROUGH'}

    def execute(self, context):
        self.count = 0
        context.object.location.x = 0
        wm = context.window_manager
        self._timer = wm.event_timer_add(0.3, window=context.window)
        wm.modal_handler_add(self)
        return {'RUNNING_MODAL'}

    def cancel(self, context):
        wm = context.window_manager
        wm.event_timer_remove(self._timer)

Related:

Time delay when selecting objects in a python script!

How to run a python script at regular intervals?

Application Timers in 2.8

2.8 brings in a new timer class.

See my answer to Blender Python: Running multiple timers

Move an object every second

import bpy
import functools

def move_obj(ob):
    ob.location.x += 0.1
    return 1

bpy.app.timers.register(functools.partial(move_obj, bpy.context.object), first_interval=1)
$\endgroup$
2
  • $\begingroup$ Thanks for the reply, I managed to do this in a different way following this docs.blender.org/api/blender2.8/bpy.app.timers.html $\endgroup$
    – Inkplay_
    Commented Feb 16, 2019 at 2:59
  • $\begingroup$ Yes tossed up whether to go that way, added link to prior answer regarding 2.8 application timers. $\endgroup$
    – batFINGER
    Commented Feb 16, 2019 at 3:33

You must log in to answer this question.

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