Change gravity at each frame

Hi all,

I am running a simulation in Blender and I need to change the value and direction of the gravity acceleration at each frame. Is this possible?

I try doing the (very basic) scene.gravity = [value of gravity], but it doesn’t seem to change the value of the acceleration, even if I do ‘scene.frame_set(nFrame)’ and ‘bpy.context.view_layer.update()’.

Other kinds of forces do not fit my purposes.

Setting up the script from Python.

Thanks a lot!

You could do the calculation mathematically, then translate accordingly, that way your animation is not dependent on the scene gravity at all. This is just a suggestion, since I see no one has responded on this issue.

It worked for me, did you do the same thing?

import bpy
import random

graflip = 1
def my_handler(scene):
    global graflip
    
    # every 10 frames
    if scene.frame_current % 10 == 0:

        scene.gravity[2] = graflip * random.uniform(0, 10)
        print('gravity:', scene.gravity[2])
        graflip = 1 if scene.frame_current % 20 == 0 else -1
        print('graflip:', graflip)

bpy.app.handlers.frame_change_pre.clear()
bpy.app.handlers.frame_change_pre.append(my_handler)

changegravity

Hi there const!

First of all, thanks for the answer. Up to now, I was deleting and baking all frames after the acceleration value was changed at each frame, which takes super long to run whenever the number of frames goes a bit high (nFrames need to be baked nFrames times, so it scales with nFrames**2).

I have been able to reproduce your code and seems to work fine. In fact, if I visually check the acceleration value, I see how it changes. However, when I try to implement that in my code, it does not seem to work the same. I think I am having some trouble understanding the use of handlers because the acceleration value I see in my simulation remains constant from the beginning.

So you can understand the situation better, my code would look something like this:

  • script.py → script that is called from command line together with the blender.exe so it runs it in background.

  • GenerateScenario → Function that is called within script.py that sets up the environment

  • I define my_handler inside GenerateScenario

  • Add the lines:

bpy.app.handlers.frame_change_pre.clear()
bpy.app.handlers.frame_change_pre.append(my_handler)

to Generate scenario, right after I define my_handler

  • Run a “while” loop where I iterate through frames to extract positions and compute velocities of the object.

Thanks for your help!

This solution favors real-time simulation better. Though it won’t be ideal caching since for example after the simulation is cached, the handler is not needed anymore. But once you try to change some setting and then the simulation is cached again you will end up with something totally different than the previous and it might ruin your previous desired result.

I think there can be some good idea here you can try. I saw in the Doom2 source code that for performance at the time they did not use the “random” function in C but they had an array with pregenerated random values. You can try to cache all random values in a binary (frame-value) and load this list at startup.

These are just ideas on the subject.

1 Like

So, what I have found works best is to add a keyframe with the gravity value at each frame.

Problem is that I (at least I have not found another way to do it) need to re-bake all the dynamics at each step, which, for nFrames larger than 100 (at least in my computer), becomes too slow to be operative in any way.

Would there be a way to do this using handlers instead? or any way to bake just specific frames instead of all of them? Or any other solution that still works but at a much lower computational effort?

code for reference:

scene.frame_set(nFrame)

acc = FunctionThatGetsAcceleration(in1, in2)

scene.gravity[0] = acc[0] 
scene.gravity[1] = acc[1] 
scene.gravity[2] = acc[2] 

scene.keyframe_insert(data_path="gravity", frame=nFrame)

Thanks!