Why are world transform changes for game objects not immediate?

When changing the position of a GO, you can get the position on the next line, and it will be updated. But if you get the world position of the GO, it’s not updated until the next frame. Why? I would assume it’s for performance reasons / to ensure all actual transforms happen all at once?

Would it be reasonable to add an option to force updating the world position of a GO when updating its position when this is useful? So then probably also update any GO world positions that it is a parent/child of.

go.set_position(vmath.vector3(100,0,0), "my_object", {update_world_transform = true})

Or a function to force update the world transform of a GO.

go.update_world_transform("my_object")

I remember other people in the past having grief over world transforms not being immediate.

This thread has a few explanations for why it isn’t done immediately.

If updating a specific GO transform isn’t reasonable then maybe giving control over when to do extra updates of all transforms would be useful.

go.force_update_transforms() – immediately update all transforms of the current collection / world the GO is in

The thread also has a code sample from @sven showing that in the life cycle the transforms are handled by the time the message queue is processed so you can send a message to a script from itself in its update to do steps that rely on the new transform.

1 Like

One workaround is to calculate the world position manually, using the local position:

local function world_to_local(v3, wp, wr, ws)
	-- Translation inverse.
	v3.x = v3.x - wp.x;  v3.y = v3.y - wp.y;  v3.z = v3.z - wp.z
	-- Rotation inverse.
	local r = vmath.conj(wr)
	v3 = vmath.rotate(r, v3)
	-- Scale inverse.
	v3.x = v3.x / ws.x;  v3.y = v3.y / ws.y;  v3.z = v3.z / ws.z
	return v3
end

local function local_to_world(v3, wp, wr, ws)
	-- Scale.
	v3.x = v3.x * ws.x;  v3.y = v3.y * ws.y;  v3.z = v3.z * ws.z
	-- Rotation.
	v3 = vmath.rotate(wr, v3)
	-- Translation.
	v3.x = v3.x + wp.x;  v3.y = v3.y + wp.y;  v3.z = v3.z + wp.z
	return v3
end

local function get_local_position(game_object_world_position, game_object_id)

	local parent = go.get_parent(game_object_id)
	if parent then
		local wp = go.get_world_position(parent)
		local wr = go.get_world_rotation(parent)
		local ws = go.get_world_scale(parent)
		local game_object_local_position = world_to_local(game_object_world_position, wp, wr, ws)
		return game_object_local_position
	else
		return game_object_world_position
	end

end

local function get_world_position(game_object_local_position, game_object_id)
	
	local parent = go.get_parent(game_object_id)
	if parent then
		local wp = go.get_world_position(parent)
		local wr = go.get_world_rotation(parent)
		local ws = go.get_world_scale(parent)
		local game_object_world_position = local_to_world(game_object_local_position, wp, wr, ws)
		return game_object_world_position
	else
		return game_object_local_position
	end
	
end

The code above is only meant to work with one level of game object nesting, since this was enough in my case. This can be changed to be fully recursive I guess.

6 Likes