Move an object on an irregular plane with python
There's something wrong with the ray_cast, which messes up on a few casts. But I think this could clear up if placed in a modal operator.

I believe the script from this answer achieves what you want.

The result looks very similiar to your gif.




Convert the collision geometry to a bmesh, apply the transformation matrix and get a bvhtree from the transformed bmesh.
Cast a ray from above the Cube's location straight downwards (0, 0, -1) and check if it intersects with the bvhtree.
Create a keyframe.




import bpy
import bmesh
from mathutils.bvhtree import BVHTree
import mathutils

# the epsilon value for the BVHTree calculations
EPSILON = 0.0000001

# maximum ray distance XY plane
MAXIMUM_Z = 1000

# make sure you have a Camera and an Empty named like this
landscape = bpy.data.objects["Landscape"]
cube = bpy.data.objects["Cube"]

# create the BVHTrees from a bmesh of the "sticky" object
# the bmesh conversion makes it easy to apply the individual objects transformation matrices
bm = bmesh.new()
bm.from_object(landscape, bpy.context.scene)
bmesh.ops.transform(bm, matrix=landscape.matrix_world, verts=bm.verts)
tree = BVHTree.FromBMesh(bm, epsilon=EPSILON)


# the main method for setting the new z position
def reposition_z(scene = bpy.context.scene):

    # create a direction vector, which points downwards and
    # a origin for the raycast
    dir = mathutils.Vector((0, 0, -1))
    origin = mathutils.Vector((*cube.location[:2], MAXIMUM_Z - EPSILON))

    # if there is a hit, move the cube to that location
    loc, no, i, d = tree.ray_cast(origin, dir)
    if d is not None:
        # assign the cube's location
        cube.location = loc

# remove all handlers, then create a new one with the reposition_z function
post_handlers= bpy.app.handlers.scene_update_post
[post_handlers.remove(h) for h in post_handlers if h.__name__ == "reposition_z"]
post_handlers.append(reposition_z)


Setting a keyframe could look like.

for i in range(start, end):
    bpy.data.scenes["Scene"].frame_set(i)
    reposition_z()
    cube.keyframe_insert(data_path = 'location')