# GoPositionSetter Native Extension

**URL:** <https://forum.defold.com/t/gopositionsetter-native-extension/82098>\
**Category:** The Defoldmine\
**Created:** [December 15, 2025, 5:49am UTC](https://forum.defold.com/t/gopositionsetter-native-extension/82098 "2025-12-15T05:49:28Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![d954mas](https://forum-defold.b-cdn.net/user_avatar/forum.defold.com/d954mas/32/5401_2.png) [@d954mas](https://forum.defold.com/u/d954mas)\
**Post date:** [December 15, 2025, 5:49am UTC](https://forum.defold.com/t/gopositionsetter-native-extension/82098/1 "2025-12-15T05:49:29Z")

</div>

# GoPositionSetter Native extension

Fast way to update position for thousands of game objects.

**30000 bunnies 53.71 fps**

 ![update_single](https://forum-defold-uploads.b-cdn.net/original/3X/c/2/c260a986f9d00d3500862158c566fade8790cdc2.jpeg)

 ![go_position_setter](https://forum-defold-uploads.b-cdn.net/original/3X/1/3/130dc1fd88222edaea135e4da5b25205893d7954.jpeg)

**30000 bunnies 67.69 fps**

Tested on my PC.

## What changed and why it’s faster

```lua
function update(self, dt)
	bunnymark.update()
	local bunnies = bunnymark.bunnies
	for i=#bunnymark.bunnies,1,-1 do
		local bunny = bunnies[i]
		bunny.velocity = bunny.velocity - 1200 * dt

		local p = bunny.position
		p.y = p.y + bunny.velocity * dt
		if p.y < 50 then
			p.y = 50
			bunny.velocity = -bunny.velocity
		end
		--go.set_position(p, bunny.id)--this is used in update single
	end
	self.position_setter:update() --this is used in go_position_setter
	label.set_text("#label", ("Bunnies: %d FPS: %.2f. Click to add more"):format(bunnymark.get_bunny_count(), bunnymark.get_fps()))	
end

```

It is faster because:

1. It uses one C++ call instead of thousands of go.set\_position calls.
2. It avoids some checks. You as the developer guarantee that the vector and gameobject are valid and available.

```cpp
//This is how go.set_position works:
static Instance* ResolveInstance(lua_State* L, int instance_arg)
{
    ScriptInstance* i = ScriptInstance_Check(L);
    Instance* instance = i->m_Instance;
    if (lua_gettop(L) == instance_arg && !lua_isnil(L, instance_arg)) {
        dmMessage::URL receiver;
        dmScript::ResolveURL(L, instance_arg, &receiver, 0x0);
        if (receiver.m_Socket != dmGameObject::GetMessageSocket(i->m_Instance->m_Collection->m_HCollection))
        {
            luaL_error(L, "function called can only access instances within the same collection.");
        }

        instance = GetInstanceFromIdentifier(instance->m_Collection->m_HCollection, receiver.m_Path);
        if (!instance)
        {
            luaL_error(L, "Instance %s not found", lua_tostring(L, instance_arg));
            return 0; // Actually never reached
        }
    }
    return instance;
}

int Script_SetPosition(lua_State* L)
{
    Instance* instance = ResolveInstance(L, 2);
    dmVMath::Vector3* v = dmScript::CheckVector3(L, 1);
    dmGameObject::SetPosition(instance, dmVMath::Point3(*v));
    return 0;
}

//this is how go_position_setter.update() works:
//no checks. Just SetPosition to the instance
void PositionSetterUserdata::update() {
    for (int i = 0; i < instances.Size(); ++i) {
        InstancePositionData instancePositionData = instances[i];
        dmGameObject::SetPosition(instancePositionData.rootInstance, dmVMath::Point3(*instancePositionData.position));
    }
}

```

## How to use

1. Create a go\_position\_setter.new() in your collection

```lua
self.position_setter = go_position_setter.new()

```

1. Add bunny to position\_setter

```lua
	self.position_setter:add(bunny.id, bunny.position)

```

1. Update bunny.position

```lua
	bunny.position.y = bunny.position.y + bunny.velocity * dt

```

1. Update all positions

```lua
	self.position_setter:update()

```

## Credits

Based on Defold bunnymark test [GitHub - britzl/defold-bunnymark: Defold bunnymark test · GitHub](https://github.com/britzl/defold-bunnymark)  
Bunny graphics from [PixiJS Bunny Mark](https://pixijs.github.io/bunny-mark/)
