I have solved my issue with a python script. It is a goofy but updated version. More importantly it works as well as I expected.
It can map the distance between falloff and object location to object z location as shown below
Animation duration, animation start frame, animation end frame, speed, elasticity, frequency, amplitude, initial and expected locations are adjustable.
You just need to have a collection of objects to being effected
and an empty in the scene, change the name_collection="to_your_collection_name"
and make sure there is an empty that named "Empty" or changefalloff="to_your_falloff_object_name"
and it will run.
for changing the direction make falloff.location[along_axis] -= speed
to falloff.location[along_axis] += speed
import bpy
import numpy as np
name_collection="building" # desired effected objects collection name
falloff_name = "Empty"
falloff_initial_loc = 8 # starting position of falloff use Vector() instead
obj_initial_scale = 1 # object initials use Vector() instead
obj_initial_z_loc = 0 # object initials use Vector() instead
expected_z_loc = 1
x, y, z = 0, 1, 2
# sets starting condition of effected object
# this two process reset the objects locations to testing different scenarios
for collection in bpy.data.collections:
if collection.name == name_collection:
buildings = collection
for obj in buildings.all_objects:
obj.location[z] = obj_initial_z_loc
obj.scale[z] = obj_initial_scale
# sets starting condition of falloff
for falloff in bpy.context.scene.objects:
if falloff.name.startswith(falloff_name):
falloff.location[x] = falloff_initial_loc
treshold = 3 # effect distance front of falloff
end_frame = bpy.data.scenes["Scene"].frame_end # get scene duration
current_frame = bpy.data.scenes["Scene"].frame_current
begin = 20 # animation start
end = 120 # animation end
duration = end - begin # animation duration
increment = 1 # frame_increment at each iteration
speed = 0.2 # changing falloff pos parameter by that meter during (#increment) frames
A = .007 # amplitude, scalar
frequency_scale = 1.520 # lesser the number wider the wavelength
elasticity = 6 # will be factored with threshold
def get_falloff():
for falloff in bpy.context.scene.objects:
if falloff.name.startswith(falloff_name):
return falloff
def damping(x, factor=1):
return (np.exp(factor * -x) * np.cos(2 * np.pi * x))
def effector(collection,
frequency_scale,
elasticity,
obj_initial_z_loc,
expected_z_loc,
speed, x, y, z):
for e, obj in enumerate(collection.all_objects):
distance = np.abs(obj.location[x] - falloff.location[x])
if obj.location[x] < falloff.location[x]:
if distance < treshold:
obj.location[z] = 1 / (1 + distance**2) + .05
else:
if distance < treshold * elasticity:
obj.location[z] = (A * damping(distance * frequency_scale, factor=.9) + expected_z_loc) -.06
else:
obj.location[z] = expected_z_loc
obj.keyframe_insert(data_path="location")
if current_frame != begin:
bpy.data.scenes["Scene"].frame_set(begin)
current_frame = bpy.data.scenes["Scene"].frame_current
falloff = get_falloff()
for j in range(duration//increment):
bpy.data.scenes["Scene"].frame_set(current_frame)
falloff.location[x] -= speed
falloff.keyframe_insert(data_path = "location")
effector(buildings, frequency_scale = frequency_scale,
elasticity = elasticity,
obj_initial_z_loc = obj_initial_z_loc,
expected_z_loc = expected_z_loc,
speed=speed, x=x, y=y, z=z)
current_frame += increment
# this is unnecessary step, this can be solve directly setting keyframe to animation_begin parameter
else:
current_frame = bpy.data.scenes["Scene"].frame_current
falloff = get_falloff()
for j in range(duration//increment):
bpy.data.scenes["Scene"].frame_set(current_frame)
falloff.location[x] -= speed
falloff.keyframe_insert(data_path = "location")
effector(buildings, frequency_scale = frequency_scale,
elasticity = elasticity,
obj_initial_z_loc = obj_initial_z_loc,
expected_z_loc = expected_z_loc,
speed=speed, x=x, y=y, z=z)
current_frame += increment