@luminocity/lemonate-engine 26.3.0 → 26.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -41307,7 +41307,7 @@
41307
41307
  }
41308
41308
  }
41309
41309
 
41310
- var engine$1 = "26.3.0";
41310
+ var engine$1 = "26.3.1";
41311
41311
  var bullet = "3.26";
41312
41312
  var lua = "5.4.3";
41313
41313
  var packageVersionInfo = {
@@ -130998,7 +130998,7 @@
130998
130998
 
130999
130999
  var lua_api_sceneentry = "\nlocal Class = require('engine/class')\nlocal _internal = require('engine/_internal');\n\n--- @class SceneEntry\nlocal SceneEntry = Class.new()\n\nSceneEntry._id = nil\n\n--- Hook for the Class type when a new object is created\n---@private\nfunction SceneEntry:__new(id)\n self._id = id\nend\n\n--- Load the scene\nfunction SceneEntry:load()\n self:setLoaded(true)\nend\n\n--- Unload the scene\nfunction SceneEntry:unload()\n self:setLoaded(false)\nend\n\n--- Activate the scene, meaning it will be rendered\nfunction SceneEntry:activate()\n self:setActivated(true)\nend\n\n--- Deactivate the scene, meaning it will not be rendered anymore\nfunction SceneEntry:deactivate()\n self:setActivated(false)\nend\n\n--- Set the activation state of the scene. An active scene will be rendered, an inactive one won't\n---@param value boolean true or false\nfunction SceneEntry:setActivated(value)\n _internal.sendMessage('sceneentry.setActivated', { id = self._id, value = value })\nend\n\n--- Set the loading state of the scene. An unloaded scene will not be rendered and freed from memory\n---@param value boolean true or false\nfunction SceneEntry:setLoaded(value)\n _internal.sendMessage('sceneentry.setLoaded', { id = self._id, value = value })\nend\n\n--- Returns the activation state of the scene\n---@return boolean Activation state\nfunction SceneEntry:isActive()\n return _internal.sendMessage('sceneentry.isActive', { id = self._id })\nend\n\n--- Returns the loading state of the scene\n---@return boolean Loading state\nfunction SceneEntry:isLoaded()\n return _internal.sendMessage('sceneentry.isLoaded', { id = self._id })\nend\n\nreturn SceneEntry\n";
131000
131000
 
131001
- var lua_api_sceneobject = "\nlocal _internal = require('engine/_internal');\nlocal Object = require 'engine/object'\nlocal handle = require 'engine/handle'\nlocal Transform = require 'engine/math/transform'\nlocal Vector3 = require 'engine/math/vector3'\nlocal Tools = require 'engine/tools'\nlocal Promise = require 'engine/promise'\n\n------------------------------------------------------------------\n-- SceneObject class\n------------------------------------------------------------------\n\n--- @class SceneObject\nlocal SceneObject = Class.new(Object)\nSceneObject._isSceneObject = true\nSceneObject._type = \"SceneObject\"\n\n--- Enumeration of the available types to use with SceneObject.create()\n--- @enum Type\nSceneObject.Type = {\n Audio = \"SgAudio\",\n AudioListener = \"AudioListener\",\n Billboard = \"SgBillboard\",\n Box = \"SgBox\",\n Camera = \"SgCamera\",\n CircleShadow = \"SgCircleShadow\",\n Cone = \"SgCone\",\n Cylinder = \"SgCylinder\",\n GaussianSplats = \"SgGaussianSplats\",\n Group = \"SgGroup\",\n Lightsource = \"SgLightsource\",\n Mesh = \"SgMesh\",\n Particles = \"SgParticles\",\n Plane = \"SgPlane\",\n Points = \"SgPoints\",\n PositionalAudio = \"SgPositionalAudio\",\n Prefab = \"SgPrefab\",\n Ring = \"SgRing\",\n Scene = \"Scene\",\n Sky = \"SgSky\",\n Sphere = \"SgSphere\",\n Text = \"SgText\",\n Torus = \"SgTorus\",\n Vehicle = \"SgVehicle\"\n}\n\n---@private\nfunction SceneObject:__new(handle, nodeId)\n Object.__new(self, handle)\n self._type = \"SceneObject\"\n self._isSceneObject = true\n self._nodeId = nodeId\nend\n\nfunction capitalizeFirstLetter(str)\n local firstChar = str:sub(1, 1)\n if firstChar:lower() == firstChar then\n firstChar = firstChar:upper()\n return firstChar .. str:sub(2)\n end\n return str\nend\n\n---@private\nfunction SceneObject:__customIndex(key)\n -- any private properties of scene objects start with an underscore. if so, just use rawget, otherwise\n -- get the value of the field from the engine and return it\n local value = rawget(self, key)\n if value or type(key) ~= 'string' or string.sub(key, 1, 1) == '_' then\n return value\n else\n return self:get(key)\n end\nend\n\n---@private\nfunction SceneObject:__customNewIndex(key, value)\n -- any private properties of scene objects start with an underscore. if so, just use rawset, otherwise\n -- write the value to the field in the engine\n if type(key) ~= 'string' or string.sub(key, 1, 1) == '_' then\n rawset(self, key, value)\n else\n self:set(key, value)\n end\nend\n\n--- Finds all objects with the given name.\n---\n--- The search can be restricted to a specific hierarchy by passing a `root` node.\n--- If no `root` is provided, the search covers the entire scene.\n---\n--- This function supports both static and instance usage:\n---\n--- **Static call**\n--- SceneObject.findAllByName(name root?)\n--- - Searches the entire scene unless a `root` node is provided.\n---\n--- **Instance call**\n--- obj:findAllByName(name, root?)\n--- - Uses `obj` as the root of the search unless an explicit `root`\n--- argument is passed.\n--- - Ideal when searching only within a specific hierarchy.\n---\n--- @param name string Name of the objects in the scenegraph.\n--- @param root (optional) SceneObject The root node to restrict the search to.\n--- @return table A table containing all found objects, or an empty table if none are found.\n--- @usage\n--- -- Global search\n--- local objects = SceneObject.findAllByName(\"Enemy\")\n--- for _, obj in ipairs(objects) do\n--- print(\"Found object with type:\", obj:getType())\n--- end\n---\n--- -- Scoped search under a specific parent node\n--- local level = SceneObject.findByName(\"Level1\")\n--- local objects = SceneObject.findAllByName(\"Enemy\", level)\n--- for _, obj in ipairs(objects) do\n--- print(\"Found object under parent with type:\", obj:getType())\n--- end\n---\n--- -- Instance call: search within a specific object's hierarchy\n--- local level = SceneObject.findByName(\"Level1\")\n--- local enemies = level:findAllByName(\"Enemy\")\n--- for _, obj in ipairs(enemies) do\n--- print(\"Found enemy under level:\", obj:getName())\n--- end\nfunction SceneObject.findAllByName(self, name, root)\n -- Detect if called as instance method\n if type(self) == \"table\" and self._isSceneObject then\n -- Instance call: default root to self if not provided (and self is not the class)\n if root == nil and self ~= SceneObject then\n root = self\n end\n else\n -- Static call: shift parameters\n name, root = self, name\n end\n \n __assertTrace(name, \"name is empty\")\n \n local objects = _internal.sendMessage('sceneobject.findAllByName', { \n name = name, \n root = root and root._handle or nil \n })\n \n for key, value in pairs(objects) do\n objects[key] = handle.createObject(value)\n end\n \n return objects\nend\n\n--- Finds the first occurrence of an object with the given name.\n---\n--- The search can be restricted to a specific hierarchy by passing a `root` node.\n--- If no `root` is provided, the search covers the entire scene.\n---\n--- This function supports both static and instance usage:\n---\n--- **Static call**\n--- SceneObject.findByName(name root)\n--- - Searches the entire scene unless a `root` node is provided.\n---\n--- **Instance call**\n--- obj:findByName(name root)\n--- - Uses `obj` as the root of the search unless an explicit `root`\n--- argument is passed.\n--- - Ideal when searching only within a specific hierarchy.\n---\n--- @param name string Name of the object in the scenegraph.\n--- @param root (optional) SceneObject The root node to restrict the search to.\n--- @return Object|nil The first object found with the given name, or `nil` if not found.\n--- @usage\n--- -- Global search\n--- local obj = SceneObject.findByName(\"Player\")\n--- if obj then\n--- print(\"Found object:\", obj:getName())\n--- end\n---\n--- -- Scoped search under a specific parent node\n--- local obj = SceneObject.findByName(\"Player\", parentNode)\n--- if obj then\n--- print(\"Found object under parent:\", obj:getName())\n--- end\n---\n--- -- Instance call: search within a specific object's hierarchy\n--- local player = SceneObject.findByName(\"Player\")\n--- local weapon = player:findByName(\"Weapon\")\n--- if weapon then\n--- print(\"Found weapon under player:\", weapon:getName())\n--- end\nfunction SceneObject.findByName(self, name, root)\n -- Detect if called as instance method\n if type(self) == \"table\" and self._isSceneObject then\n -- Instance call: default root to self if not provided\n if root == nil and self ~= SceneObject then\n root = self\n end\n else\n -- Static call: shift parameters\n name, root = self, name\n end\n \n __assertTrace(name, \"name is empty\")\n \n return handle.createObject(_internal.sendMessage('sceneobject.findByName', { \n name = name, \n root = root and root._handle or nil \n }))\nend\n\n---Find an object by its unique ID and return it.\n---\n--- @param id string The unique ID of the object.\n--- @return Object The found object or nil if not found.\n--- @usage\n--- local obj = SceneObject.findById(\"2135a78324fe\")\n--- if obj then\n--- print(\"Found object: \", obj:getName())\n--- end\nfunction SceneObject.findById(self, id)\n if type(self) == 'string' then\n id = self\n end\n if not id then\n return nil\n end\n return handle.createObject(_internal.sendMessage('sceneobject.findById', { id=id }))\nend\n\n--- Finds all objects that have the specified tag.\n---\n--- The search can be limited to a specific hierarchy by passing a `root` node.\n--- If no `root` is provided, the search scans the entire scene.\n---\n--- This function supports both static and instance usage:\n---\n--- **Static call**\n--- SceneObject.findByTag(tag, root)\n--- - Searches the entire scene unless a `root` node is provided.\n---\n--- **Instance call**\n--- obj:findByTag(tag, root)\n--- - Uses `obj` as the root of the search unless an explicit `root`\n--- argument is passed.\n--- - Ideal when searching only within a specific hierarchy.\n---\n--- @param tag string The tag to look for.\n--- @param root (optional) SceneObject The root node to restrict the search to.\n--- @return table A table of objects that have the given tag. May be empty if no matches are found.\n--- @usage\n--- -- Global search\n--- local enemies = SceneObject.findByTag(\"Enemy\")\n--- for _, enemy in ipairs(enemies) do\n--- print(\"Found enemy:\", enemy:getName())\n--- end\n---\n--- -- Scoped search under a specific parent node\n--- local tagged = SceneObject.findByTag(\"Interactable\", parentNode)\n--- for _, obj in ipairs(tagged) do\n--- print(\"Found interactable under parent:\", obj:getName())\n--- end\n---\n--- -- Instance call: search within a specific object's hierarchy\n--- local level = SceneObject.findByName(\"Level1\")\n--- local enemies = level:findByTag(\"Enemy\")\n--- for _, enemy in ipairs(enemies) do\n--- print(\"Found enemy under level:\", enemy:getName())\n--- end\nfunction SceneObject.findByTag(self, tag, root)\n -- Detect if called as instance method\n if type(self) == \"table\" and self._isSceneObject then\n -- Instance call: default root to self if not provided (and self is not the class)\n if root == nil and self ~= SceneObject then\n root = self\n end\n else\n -- Static call: shift parameters\n tag, root = self, tag\n end\n \n __assertTrace(tag, \"tag is empty\")\n \n local objects = _internal.sendMessage('sceneobject.findByTag', { \n tag = tag, \n root = root and root._handle or nil \n })\n \n for key, value in pairs(objects) do\n objects[key] = handle.createObject(value)\n end\n \n return objects\nend\n\n---Create a new object in the scenegraph. This is a generic function, for specialized versions, see the respective Sg<name> classes like SgBox.\n--- @param objectType string Type of object.\n--- @param options table The options to use. For information about the possible options, see the specialized functions below.\n--- @param parent Object the parent object to add the new one to or nil to put it at root level\n--- @return Promise a promise resolving to a SceneObject\n--- @usage\n--- local promise = SceneObject.create(SceneObject.Type.Sphere, { radius = 1 }, nil)\n--- promise:next(function(obj)\n--- obj:setActive(true)\n--- end)\nfunction SceneObject.create(self, objectType, options, parent)\n if (type(self) == \"string\") then\n objectType, options, parent = self, objectType, options\n end\n\n __assertTrace(type(objectType) == 'string', 'objectType must be a string')\n __assertTrace(parent == nil or (type(parent) == 'table' and parent._isSceneObject), 'parent must be a SceneObject')\n\n local p = _internal.sendMessage('sceneobject.create', {\n type = objectType,\n options = options,\n parent = parent and parent._handle or nil\n })\n return Promise:new(p)\nend\n\n--- Get the ID of the scene object.\n--- @return string The unique ID of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Cube\")\n--- print(\"Object ID:\", obj:getId())\nfunction SceneObject:getId()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return self._nodeId\nend\n\n--- Get the name of the scene object.\n--- @return string The user-defined name of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Cube\")\n--- print(\"Object name:\", obj:getName())\nfunction SceneObject:getName()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.getName', { obj=self._handle })\nend\n\n--- Return the display name of the scene object.\n--- In case the object has no user-given name, a name will be generated\n--- in the format \"type <id>\"\n--- @return string Displayname\n--- @usage\n--- local obj = SceneObject.findByName(\"Enemy\")\n--- print(\"Display name:\", obj:getDisplayName())\nfunction SceneObject:getDisplayName()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.getDisplayName', { obj=self._handle })\nend\n\n--- Return the type of the object\n--- @return string Type\n--- @usage\n--- local obj = SceneObject.findByName(\"Wall\")\n--- print(\"Object type:\", obj:getType())\nfunction SceneObject:getType()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.getType', { obj=self._handle })\nend\n\n--- Set a field of the scene object to a new value.\n---\n--- @param name string The name of the field.\n--- @param value any The new value to set.\n--- @usage\n--- local obj = SceneObject.findByName(\"Light\")\n--- obj:set(\"Brightness\", \"High\")\nfunction SceneObject:set(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value })\nend\n\n--- Get a field value from the scene object.\n---\n--- @param name string The name of the field.\n--- @return string The value of the field.\n--- @usage\n--- local obj = SceneObject.findByName(\"Lamp\")\n--- local intensity = obj:get(\"Intensity\")\n--- print(\"Intensity: \" .. intensity)\nfunction SceneObject:get(name)\n\n -- This is a replacement for the old get() function without parameters that would just return a node reference\n -- It needs to stay in place as long as there are still scripts that depend on it.\n if name == nil then\n return self\n end\n\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n local field = _internal.sendMessage('sceneobject.get', { obj=self._handle, name=name })\n if field == nil then\n return nil\n end\n return toType(field.value, field.type)\nend\n\n--- Set a field of the scene object to a new value.\n---@deprecated 25.11 Use ``set`` which is the new shorter version\n--- @param name string The name of the field.\n--- @param value any The new value to set.\n--- @usage\n--- local obj = SceneObject.findByName(\"Light\")\n--- obj:setFieldValue(\"Brightness\", \"High\")\nfunction SceneObject:setFieldValue(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value })\nend\n\n--- Get a field value from the scene object.\n---@deprecated 25.11 Use ``get`` which is the new shorter version\n--- @param name string The name of the field.\n--- @return string The value of the field.\n--- @usage\n--- local obj = SceneObject.findByName(\"Lamp\")\n--- local intensity = obj:getFieldValue(\"Intensity\")\n--- print(\"Intensity: \" .. intensity)\nfunction SceneObject:getFieldValue(name)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n local field = _internal.sendMessage('sceneobject.get', { obj=self._handle, name=name })\n if field == nil then\n return nil\n end\n return toType(field.value, field.type)\nend\n\n--- Animate a field from its current value to a new value.\n---\n--- @param name string The name of the field\n--- @param value any The new value to animate to.\n--- @param duration number The duration in milliseconds for the animation to take\n--- @param ease string The type of easing to use. Possible values: 'linear', 'easeInOutQuad'\n--- @return number animation id. Can be used to cancel animation using cancelAnimation(id)\nfunction SceneObject:animateFieldValue(name, value, duration, ease)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(not (type(value) == 'table'), \"Cannot animate objects!\");\n name = capitalizeFirstLetter(name)\n return _internal.sendMessage('sceneobject.animateFieldValue', { obj=self._handle, name=name, value=value, duration=duration, ease=ease })\nend\n\n--- Cancel a running animation\n---\n--- @param name string The name of the field\nfunction SceneObject:cancelAnimation(id)\n _internal.sendMessage('sceneobject.cancelAnimation', { obj=self._handle, id=id })\nend\n\n--- Set a field of the scene object to a new color value.\n---@deprecated 25.09 Use ``set`` which handle the correct type automatically.\n--- @param name string The name of the field.\n--- @param value Color The new color value.\n--- @usage\n--- local obj = SceneObject.findByName(\"Cube\")\n--- obj:setFieldColorValue(\"Color\", Color.new(1, 0, 0, 1)) -- Sets the object to red.\nfunction SceneObject:setFieldColorValue(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(value._isColor, \"value is not a Color\")\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value:toData() })\nend\n\n--- Set a field of the scene object to a new number value.\n---@deprecated 25.09 Use ``set`` which handle the correct type automatically.\n--- @param name string The name of the field.\n--- @param value number The new number value.\nfunction SceneObject:setFieldNumberValue(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(type(value) == \"number\", \"value must be a number\")\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value })\nend\n\n---Set a field of the scene object to a new enum value\n--- @param name string\n--- @param value number\nfunction SceneObject:setFieldEnumValue(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value })\nend\n\n---Set a field of the scene object to a new boolean value\n---@deprecated 25.09 Use ``set`` which handle the correct type automatically.\n--- @param name string\n--- @param value boolean\nfunction SceneObject:setFieldBooleanValue(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n if value then value = true else value = false end\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value })\nend\n\n--- Get the layers of the scene object\n--- @return table A table containing layer indices\nfunction SceneObject:getLayers()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n local internal_layers = self:getFieldValue(\"Layers\")\n __assertTrace(type(internal_layers) == \"table\", \"Expected 'Layers' to be a table\")\n\n -- convert internal 0-based indices to frontend 1-based indices\n local layers = {}\n for _, layer_idx in ipairs(internal_layers) do\n table.insert(layers, layer_idx + 1)\n end\n\n return layers\nend\n\n--- Set the layers of the scene object.\n--- Layers control rendering visibility and interaction with other objects.\n---\n--- @param layers table A table containing layer indices.\n--- @usage\n--- local obj = SceneObject.findByName(\"Character\")\n--- obj:setLayers({1, 3, 5}) -- Assigns the object to layers 1, 3, and 5.\nfunction SceneObject:setLayers(layers)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(type(layers) == \"table\", \"Layers must be provided as a table\")\n self:setFieldValue(\"Layers\", layers)\nend\n\n--- Get the value of a scene object's field as color value.\n---@deprecated 25.09 Use ``get`` which always returns the correct type.\n--- @return Color value of the field\n--- @usage\n--- local obj = SceneObject.findByName(\"Lamp\")\n--- local color = obj:getFieldColorValue(\"Color\")\n--- print(\"Color: \" .. color)\nfunction SceneObject:getFieldColorValue(name)\n return self:getFieldValue(name)\nend\n\n--- Get the value of a scene object's field as number value.\n---@deprecated 25.09 Use ``get`` which always returns the correct type.\n--- @return number value of the field\nfunction SceneObject:getFieldNumberValue(name)\n return self:getFieldValue(name)\nend\n\n--- Get the value of a scene object's field as boolean value.\n---@deprecated 25.09 Use ``get`` which always returns the correct type.\n--- @return boolean value of the field\nfunction SceneObject:getFieldBooleanValue(name)\n return self:getFieldValue(name)\nend\n\n--- Get the value of a scene object's field as string value.\n---@deprecated 25.09 Use ``get`` which always returns the correct type.\n--- @return string value of the field\nfunction SceneObject:getFieldEnumValue(name)\n return self:getFieldValue(name)\nend\n\n--- Get the value of a scene object's field as string value\n--- @return string value of the field\nfunction SceneObject:getFieldEnumOptions(name)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.getFieldOptions', { obj=self._handle, name=name })\nend\n\n--- Set a field of the scene object to a new boolean value.\n---@deprecated 25.09 Use ``set`` which handle the correct type automatically.\n--- @param name string The name of the field.\n--- @param value boolean The new boolean value.\n--- @usage\n--- local obj = SceneObject.findByName(\"Door\")\n--- obj:setFieldBooleanValue(\"PhysicsEnabled\", true)\nfunction SceneObject:setFieldBooleanValue(name, value)\n self:setFieldValue(name, not not value)\nend\n\n---Get a link of the scene object to a new value\n---@deprecated 25.11 Use ``get`` which handles items now too\n--- @param name string name of the link field\n--- @return number handle of the linked item or nil if not set\nfunction SceneObject:getLinkItem(name)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n return handle.createObject(_internal.sendMessage('sceneobject.getLinkItem', { obj=self._handle, name=name }))\nend\n\n---Set a link from the scene object\n---@deprecated 25.11 Use ``set`` which handles items now too\n--- @param name string name of the link field\n--- @param item string value can be either an item ID or an Item object\nfunction SceneObject:setLinkItem(name, item)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(not item or (item and item._handle and item._isItem), \"item has to be an object of Item class or nil!\");\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.setLinkItem', { obj=self._handle, name=name, item=item })\nend\n\n--- Translate (move) the scene object.\n--- Moves the object by the given offset along each axis.\n---\n--- @param x number Offset along the X-axis.\n--- @param y number Offset along the Y-axis.\n--- @param z number Offset along the Z-axis.\n--- @usage\n--- local obj = SceneObject.findByName(\"Character\")\n--- if obj then\n--- obj:translate(0, 1, 0) -- Moves the object up by 1 unit\n--- end\nfunction SceneObject:translate(x, y, z)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.translate', { obj=self._handle, x=x, y=y, z=z })\nend\n\n--- Rotate the scene object.\n--- Rotation is applied in degrees and follows Euler angles (XYZ order).\n---\n--- @param x number Rotation around the X-axis in degrees.\n--- @param y number Rotation around the Y-axis in degrees.\n--- @param z number Rotation around the Z-axis in degrees.\n--- @usage\n--- local obj = SceneObject.findByName(\"Cube\")\n--- if obj then\n--- obj:rotate(45, 0, 0) -- Rotates 45 degrees around the X-axis\n--- end\nfunction SceneObject:rotate(x, y, z)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.rotate', { obj=self._handle, x=x, y=y, z=z })\nend\n\n--- Scale the scene object.\n---\n--- @param x number Scale factor along the X-axis.\n--- @param y number Scale factor along the Y-axis.\n--- @param z number Scale factor along the Z-axis.\n--- @usage\n--- local obj = SceneObject.findByName(\"Tree\")\n--- if obj then\n--- obj:scale(2, 2, 2) -- Doubles the size of the object\n--- end\nfunction SceneObject:scale(x, y, z)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.scale', { obj=self._handle, x=x, y=y, z=z })\nend\n\n--- Get the local transform of a scene object.\n--- The transform contains position, rotation, and scale relative to the object's parent.\n---\n--- @param cacheLocal boolean if set to true, the transform will be cached locally for the future for performance reasons. This should only be used if no other script changes the transform\n--- @return Transform The object's local transform.\n--- @usage\n--- local obj = SceneObject.findByName(\"Character\")\n--- local transform = obj:getTransform()\n--- print(\"Local position:\", transform.position)\nfunction SceneObject:getTransform(cacheLocal)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n if self._transform == nil or not cacheLocal then\n self._transform = Transform.fromData(_internal.sendMessage('sceneobject.getTransform', { obj=self._handle }))\n end\n return self._transform:clone()\nend\n\n--- Apply a function to modify the object's transform, then update it.\n--- This allows making changes to the transform while ensuring they are properly applied back.\n---\n--- @param func function A function that takes a Transform object as its only argument.\n--- @param cacheLocal boolean if set to true, the transform will be cached locally for the future for performance reasons. This should only be used if no other script changes the transform\n--- @usage\n--- local obj = SceneObject.findByName(\"Cube\")\n--- obj:withTransform(function(transform)\n--- transform.position.x = transform.position.x + 1 -- Move object 1 unit to the right\n--- end)\nfunction SceneObject:withTransform(func, cacheLocal)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(type(func) == 'function', \"argument func is not a function\")\n local transform = self:getTransform(cacheLocal)\n func(transform)\n self:setTransform(transform)\nend\n\n--- Get the world transform of a scene object.\n--- The world transform includes absolute position, rotation, and scale in the scene.\n---\n--- @return Transform The object's world transform.\n--- @usage\n--- local obj = SceneObject.findByName(\"Light\")\n--- local worldTransform = obj:getWorldTransform()\n--- print(\"World position:\", worldTransform.position)\nfunction SceneObject:getWorldTransform()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Transform.fromData(_internal.sendMessage('sceneobject.getWorldTransform', { obj=self._handle }))\nend\n\n--- Get the forward direction vector in world space.\n--- Returns the local -Z axis transformed by the object's rotation.\n---\n--- @return Vector3 The forward direction in world space.\nfunction SceneObject:getForward()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Vector3.fromData(_internal.sendMessage('sceneobject.getForward', { obj=self._handle }))\nend\n\n--- Get the right direction vector in world space.\n--- Returns the local +X axis transformed by the object's rotation.\n---\n--- @return Vector3 The right direction in world space.\nfunction SceneObject:getRight()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Vector3.fromData(_internal.sendMessage('sceneobject.getRight', { obj=self._handle }))\nend\n\n--- Get the up direction vector in world space.\n--- Returns the local +Y axis transformed by the object's rotation.\n---\n--- @return Vector3 The up direction in world space.\nfunction SceneObject:getUp()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Vector3.fromData(_internal.sendMessage('sceneobject.getUp', { obj=self._handle }))\nend\n\n--- Set the local transform of a scene object.\n--- Updates the object's position, rotation, and scale relative to its parent.\n---\n--- @param transform Transform The new local transform to apply.\n--- @usage\n--- local obj = SceneObject.findByName(\"Door\")\n--- local newTransform = Transform.new()\n--- newTransform.position = Vector3.new(0, 2, 0) -- Move the object up 2 units\n--- obj:setTransform(newTransform)\nfunction SceneObject:setTransform(transform)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(transform and transform._isTransform, \"transform has to be an object of type Transform\")\n self._transform = transform\n _internal.sendMessage('sceneobject.setTransform', { obj=self._handle, transform=self._transform:toData() })\nend\n\n--- Set the physics position of the scene object.\n---\n--- This directly updates the position of the object inside the physics engine.\n--- The position is expressed in world coordinates.\n--- Only works for objects added to the physics engine\n--- @param position Vector3 New world position of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Box\")\n--- obj:setPhysicsPosition(Vector3.new(0, 5, 0))\nfunction SceneObject:setPhysicsPosition(position)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(position and position._isVector3, \"position has to be an object of type Vector3\")\n _internal.sendMessage('sceneobject.setPhysicsPosition', { obj=self._handle, position=position:toData() })\nend\n\n--- Set the physics rotation of the scene object.\n---\n--- This directly updates the rotation of the object inside the physics engine.\n--- The rotation is expressed in world space.\n--- Only works for objects added to the physics engine\n--- @param rotation Quaternion New world rotation of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Box\")\n--- obj:setPhysicsRotation(Quaternion.fromEuler(0, math.pi, 0))\nfunction SceneObject:setPhysicsRotation(rotation)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(rotation and rotation._isQuaternion, \"rotation has to be an object of type Quaternion\")\n _internal.sendMessage('sceneobject.setPhysicsRotation', { obj=self._handle, rotation=rotation:toData() })\nend\n\nfunction SceneObject:setPhysicsRotation(rotation)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(rotation and rotation._isQuaternion, \"rotation has to be an object of type Quaternion\")\n _internal.sendMessage('sceneobject.setPhysicsRotation', { obj=self._handle, rotation=rotation:toData() })\nend\n\n--- Get the linear velocity of the scene object.\n--- This function returns the object's velocity in world coordinates.\n--- Only works for objects added to the physics engine with `watchLinearVelocity` enabled.\n---\n--- @return Vector3 The linear velocity of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Ball\")\n--- local velocity = obj:getLinearVelocity()\n--- print(\"Velocity:\", velocity)\nfunction SceneObject:getLinearVelocity()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Vector3.fromData(_internal.sendMessage('sceneobject.getLinearVelocity', { obj=self._handle }))\nend\n\n--- Set the linear velocity of the scene object.\n--- This function assigns a new velocity to the object, but only works if the object is in the physics engine.\n---\n--- @param velocity Vector3 The new linear velocity to apply.\n--- @usage\n--- local obj = SceneObject.findByName(\"Car\")\n--- obj:setLinearVelocity(Vector3.new(10, 0, 0)) -- Move object at speed 10 in the X direction.\nfunction SceneObject:setLinearVelocity(velocity)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(velocity and velocity._isVector3, \"velocity must be an object of type Vector3\")\n local data = velocity:toData()\n _internal.sendMessage('sceneobject.setLinearVelocity', { obj=self._handle, velocity=data })\nend\n\n--- Get the angular velocity of the scene object.\n--- Only works for objects added to the physics engine with `watchAngularVelocity` enabled.\n---\n--- @return Vector3 The angular velocity of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Wheel\")\n--- local angVel = obj:getAngularVelocity()\n--- print(\"Angular velocity:\", angVel)\nfunction SceneObject:getAngularVelocity()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Vector3.fromData(_internal.sendMessage('sceneobject.getAngularVelocity', { obj=self._handle }))\nend\n\n--- Set the angular velocity of the scene object.\n---\n--- @param velocity Vector3 The new angular velocity to apply.\n--- @usage\n--- local obj = SceneObject.findByName(\"Propeller\")\n--- obj:setAngularVelocity(Vector3.new(0, 10, 0)) -- Rotate around Y-axis.\nfunction SceneObject:setAngularVelocity(velocity)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(velocity and velocity._isVector3, \"velocity must be an object of type Vector3\")\n local data = velocity:toData()\n _internal.sendMessage('sceneobject.setAngularVelocity', { obj=self._handle, velocity=data })\nend\n\n--- Apply an impulse to the scene object.\n---\n--- An **impulse** is a sudden force applied to an object that immediately\n--- changes its velocity. This is different from a continuous force, which\n--- is applied over time.\n---\n--- This function only works on objects that are physics-enabled.\n---\n--- @param impulse Vector3 The force impulse to apply.\n--- @param relPos Vector3 The relative position where the impulse is applied. Default is (0, 0, 0)\nfunction SceneObject:applyImpulse(impulse, relPos)\n relPos = relPos or Vector3.new(0, 0, 0)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(impulse and impulse._isVector3, \"impulse must be an object of type Vector3\")\n __assertTrace(relPos and relPos._isVector3, \"relPos must be an object of type Vector3\")\n local impulseData = impulse:toData()\n local relPosData = relPos:toData()\n _internal.sendMessage('sceneobject.applyImpulse', { obj=self._handle, impulse=impulseData, relPos=relPosData })\nend\n\n--- Apply an impulse to the scene object that respects constraints.\n---\n--- Only works on physics-enabled objects.\n---\n--- @param impulse Vector3 The force impulse to apply.\n--- @param relPos Vector3 The relative position where the impulse is applied. Default is (0, 0, 0)\nfunction SceneObject:applyPushImpulse(impulse, relPos)\n relPos = relPos or Vector3.new(0, 0, 0)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(impulse and impulse._isVector3, \"impulse must be an object of type Vector3\")\n __assertTrace(relPos and relPos._isVector3, \"relPos must be an object of type Vector3\")\n _internal.sendMessage('sceneobject.applyPushImpulse', { obj=self._handle, impulse=impulse:toData(), relPos=relPos:toData() })\nend\n\n--- Apply an impulse to the center of the object.\n---\n--- Applies a sudden velocity change at the object's center. Only works on physics-enabled objects.\n---\n--- @param impulse Vector3 The force impulse to apply.\n--- @usage\n--- local obj = SceneObject.findByName(\"Box\")\n--- obj:applyCentralImpulse(Vector3.new(0, 5, 0)) -- Apply upward impulse.\nfunction SceneObject:applyCentralImpulse(impulse)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(impulse and impulse._isVector3, \"impulse must be an object of type Vector3\")\n _internal.sendMessage('sceneobject.applyCentralImpulse', { obj=self._handle, impulse=impulse:toData() })\nend\n\n--- Apply a central impulse that respects constraints.\n---\n--- Only works on physics-enabled objects and respects any joint or constraint applied.\n---\n--- @param impulse Vector3 The force impulse to apply.\n--- @usage\n--- local obj = SceneObject.findByName(\"Box\")\n--- obj:applyCentralPushImpulse(Vector3.new(0, 5, 0)) -- Apply upward impulse.\nfunction SceneObject:applyCentralPushImpulse(impulse)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(impulse and impulse._isVector3, \"impulse must be an object of type Vector3\")\n _internal.sendMessage('sceneobject.applyCentralPushImpulse', { obj=self._handle, impulse=impulse:toData() })\nend\n\n--- Apply a force to the scene object.\n---\n--- Unlike impulses, **forces** are applied over time, influencing the object's acceleration.\n--- Only works on physics-enabled objects.\n---\n--- @param force Vector3 The force vector to apply.\n--- @param relPos Vector3 The relative position where the force is applied. Default is (0, 0, 0)\nfunction SceneObject:applyForce(force, relPos)\n relPos = relPos or Vector3.new(0, 0, 0)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(force and force._isVector3, \"force must be an object of type Vector3\")\n __assertTrace(relPos and relPos._isVector3, \"relPos must be an object of type Vector3\")\n _internal.sendMessage('sceneobject.applyForce', { obj=self._handle, force=force:toData(), relPos=relPos:toData() })\nend\n\n--- Clear any forces on a scene object.\n--- Only works on physics-enabled objects.\n--- @usage\n--- local obj = SceneObject.findByName(\"Ball\")\n--- obj:clearForces()\nfunction SceneObject:clearForces()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.clearForces', { obj=self._handle })\nend\n\n--- Delete the scene object from the scene.\n---\n--- @usage\n--- local obj = scene:findByName(\"TemporaryObject\")\n--- if obj then\n--- obj:delete()\n--- end\nfunction SceneObject:delete()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.delete', { obj=self._handle })\n if self._handle then\n handle.invalidate(self._handle)\n end\n self._handle = nil\nend\n\n--- Clone a scene object to a new position and return it.\n---\n--- The position can be provided as:\n--- 1. Separate `x, y, z` coordinates.\n--- 2. A `Vector3` containing the position.\n---\n--- If no position is provided, the clone will appear in the same location as the original.\n---\n--- @param x number | Vector3 The X-coordinate of the new position, or a `Vector3` containing `x, y, z`. If a `Vector3` is passed, `y` and `z` should be omitted.\n--- @param y number (optional) The Y-coordinate of the new position. Omit this if passing a `Vector3`.\n--- @param z number (optional) The Z-coordinate of the new position. Omit this if passing a `Vector3`.\n--- @param parent SceneObject (optional) The parent object to attach the new object to.\n--- @return Promise A promise resolving to a SceneObject.\n---\n--- @usage\n--- local obj = scene:findByName(\"Tree\")\n--- obj:clone(5, 0, 2):next(function(clone)\n--- clone:setActive(true) -- Activate the cloned object\n--- end)\n---\n--- local pos = Vector3.new(3, 1, 0)\n--- obj:clone(pos):next(function(clone)\n--- clone:setFieldValue(\"Name\", \"ClonedTree\") -- Rename the cloned object\n--- end)\n---\n--- local parentObj = scene:findByName(\"Group\")\n--- obj:clone(pos, parentObj):next(function(clone)\n--- clone:setFieldValue(\"Name\", \"AnotherClonedTree\") -- Rename the cloned object\n--- end)\nfunction SceneObject:clone(x, y, z, parent)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n\n -- Handle case where first argument is a Vector3 and second argument is the parent\n if type(x) == \"table\" and x._isVector3 then\n x, y, z, parent = x.x, x.y, x.z, y -- Shift arguments\n end\n\n local p = _internal.sendMessage('sceneobject.clone', {\n obj = self._handle,\n x = x,\n y = y,\n z = z,\n parent = parent and parent._handle or nil\n })\n\n return Promise:new(p)\nend\n\n--- Create a new object of same type from this one. If no options are specified this will be basically a clone.\n--- If an options table is passed, this will specify all fields with their values that should be changed.\n--- At the very least, this should contain a new Transform, otherwise the new object will be at the exact location\n--- of the old one.\n--- @param options table the table of fields to set on the new object\n--- @param parent SceneObject (optional) The parent object to attach the new object to.\n--- @return Promise A promise resolving to a SceneObject.\n---\n--- @usage\n--- local obj = SceneObject.findByName(\"Tree\")\n--- local parentObj = SceneObject.findByName(\"Group\")\n--- local clone = await(obj:createFromThis({\n--- transform = Transform.new(Vector3.new(4, 6, 8)),\n--- active = true,\n--- name = \"Cloned tree\"\n--- }, parentObj)\nfunction SceneObject:createFromThis(options, parent)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(parent == nil or parent._isSceneObject, \"parent is not nil or a SceneObject\")\n\n local p = _internal.sendMessage('sceneobject.createFromThis', {\n obj = self._handle,\n options = options,\n parent = parent and parent._handle or nil\n })\n\n return Promise:new(p)\nend\n\n--- Move the scene object to a different parent. This parent can not be a child of the scene object\n--- @param parent SceneObject (optional) The parent object to attach the new object to. if nil, the object will be moved to the scenegraph root\nfunction SceneObject:moveToParent(parent)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(not parent or parent._isSceneObject, \"parent is not a SceneObject\")\n _internal.sendMessage('sceneobject.moveToParent', { obj = self._handle, parent = parent and parent._handle or nil })\nend\n\n--- Set the active state of the scene object.\n--- @param value boolean True to activate, false to deactivate.\nfunction SceneObject:setActive(value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n self:setFieldValue(\"Active\", value)\nend\n\n--- Get the active state of the scene object.\n--- @return boolean True if the object is active, false otherwise.\nfunction SceneObject:getActive()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return self:getFieldBooleanValue(\"Active\")\nend\n\n--- Check if the object has a specific tag.\n--- @param tagName string The tag name to check.\n--- @return boolean True if the object has the tag, false otherwise.\nfunction SceneObject:hasTag(tagName)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n local tags = self:getFieldValue(\"Tags\")\n for _, value in pairs(tags) do\n if value == tagName then\n return true\n end\n end\n return false\nend\n\n--- Get a list of all tags assigned to the object.\n--- @return table An array of tag names.\nfunction SceneObject:getTags()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return self:getFieldValue(\"Tags\")\nend\n\n--- Assign a set of tags to the object.\n--- @param tags table An array of tag names.\nfunction SceneObject:setTags(tags)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n Tools.validateArrayOfStrings(tags)\n self:setFieldValue(\"Tags\", tags)\nend\n\n--- Add a tag to the object.\n--- @param tag string The tag to add.\nfunction SceneObject:addTag(tag)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(type(tag) == 'string', \"tag needs to be a string\")\n local tags = self:getTags()\n table.insert(tags, tag)\n self:setTags(tags)\nend\n\n--- Remove all tags from the object.\nfunction SceneObject:clearTags()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n self:setTags({})\nend\n\n--- Take a snapshot of the object and its children, if specified.\n--- This allows restoring the object to a previous state.\n--- @param recursive boolean Whether to include child objects in the snapshot.\n--- @return number The snapshot handle.\nfunction SceneObject:takeSnapshot(recursive)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.takeSnapshot', { obj=self._handle, recursive=recursive })\nend\n\n--- Restore a snapshot, reverting any changes made after the snapshot was taken.\n--- @param snapshot number The snapshot handle to restore.\nfunction SceneObject:rollbackSnapshot(snapshot)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.rollbackSnapshot', { obj=self._handle, snapshot=snapshot })\nend\n\n--- Check if the object is enabled.\n--- @return boolean True if the object is enabled, false otherwise.\nfunction SceneObject:isEnabled()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.isEnabled', { obj=self._handle })\nend\n\n--- Enable the object.\nfunction SceneObject:enable()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.enable', { obj=self._handle })\nend\n\n--- Disable the object.\nfunction SceneObject:disable()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.disable', { obj=self._handle })\nend\n\n--- Get the material assigned to the object.\n--- @return Material The material, or nil if none is assigned.\nfunction SceneObject:getMaterial()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return self:getLinkItem(\"Material\")\nend\n\n--- Set the object's material.\n--- @param mat Material The material to set or nil to clear the material\nfunction SceneObject:setMaterial(mat)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(mat == nil or (type(mat) == 'table' and mat._isMaterial), \"self is not a SceneObject\")\n self:setLinkItem(\"Material\", mat)\nend\n\n--- Get all behaviours attached to this object.\n---@deprecated 25.12 use getBehaviours.\n--- @return table A list of Behaviour objects.\nfunction SceneObject:getEntities()\n return self:getBehaviours()\nend\n\n--- Get all behaviours attached to this object.\n--- @return table A list of Behaviour objects.\nfunction SceneObject:getBehaviours()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n if not self._nodeId then\n error(\"This scene object does not have a node Id. Cannot get its behaviours.\")\n end\n return Behaviour.getByNodeId(self._nodeId)\nend\n\n--- Get a behaviour attached to this object by its script name.\n--- @param name string The script name.\n--- @return Behaviour The corresponding behaviour, or nil if not found.\nfunction SceneObject:getBehaviourByScriptName(name)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n if not self._nodeId then\n error(\"This scene object does not have a node Id. Cannot get its entities.\")\n end\n return Behaviour.getByNodeIdAndScriptName(self._nodeId, name)\nend\n\n--- Get a behaviour attached to this object by its script name.\n---@deprecated\n--- @param name string The script name.\n--- @return Behaviour The corresponding behaviour, or nil if not found.\nfunction SceneObject:getEntityByScriptName(name)\n return self:getBehaviourByScriptName(name)\nend\n\n--- Get a behaviour attached to this object by its script name.\n--- @param name string The script name.\n--- @return Behaviour The corresponding behaviour, or nil if not found.\nfunction SceneObject:behaviour(name)\n return self:getBehaviourByScriptName(name)\nend\n\n--- Get a behaviour attached to this object by its script name.\n--- @param name string The script name.\n--- @return Behaviour The corresponding behaviour, or nil if not found.\nfunction SceneObject:entity(name)\n return self:getBehaviourByScriptName(name)\nend\n\n--- Get the parent of the object.\n--- @return SceneObject The parent object, or nil if none exists.\nfunction SceneObject:getParent()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return handle.createObject(_internal.sendMessage('sceneobject.getParent', { obj=self._handle }))\nend\n\n--- Get the children of the object.\n--- @return table A table of child objects.\nfunction SceneObject:getChildren()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n local objects = _internal.sendMessage('sceneobject.getChildren', { obj=self._handle })\n for key, value in pairs(objects) do\n objects[key] = handle.createObject(value)\n end\n return objects\nend\n\n--- Enable or disable automatic recreation of the scene object between play and stop.\n---\n--- When `value` is true, the object will be automatically recreated every time\n--- the scene starts or stops. This is useful for objects that need to reset\n--- physics, state, or procedural components at each run.\n---\n--- @param value boolean Enable (`true`) or disable (`false`) automatic recreation.\nfunction SceneObject:setAutoRecreate(value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.setAutoRecreate', { obj=self._handle, value=value })\nend\n\n--- Immediately recreate the scene object.\n---\n--- This forces the object to reset its state, physics, and components as if\n--- it was newly instantiated in the scene.\nfunction SceneObject:recreate()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.recreate', { obj=self._handle })\nend\n\nreturn SceneObject\n";
131001
+ var lua_api_sceneobject = "\nlocal _internal = require('engine/_internal');\nlocal Object = require 'engine/object'\nlocal handle = require 'engine/handle'\nlocal Transform = require 'engine/math/transform'\nlocal Vector3 = require 'engine/math/vector3'\nlocal Tools = require 'engine/tools'\nlocal Promise = require 'engine/promise'\n\n------------------------------------------------------------------\n-- SceneObject class\n------------------------------------------------------------------\n\n--- @class SceneObject\nlocal SceneObject = Class.new(Object)\nSceneObject._isSceneObject = true\nSceneObject._type = \"SceneObject\"\n\n--- Enumeration of the available types to use with SceneObject.create()\n--- @enum Type\nSceneObject.Type = {\n Audio = \"SgAudio\",\n AudioListener = \"AudioListener\",\n Billboard = \"SgBillboard\",\n Box = \"SgBox\",\n Camera = \"SgCamera\",\n CircleShadow = \"SgCircleShadow\",\n Cone = \"SgCone\",\n Cylinder = \"SgCylinder\",\n GaussianSplats = \"SgGaussianSplats\",\n Group = \"SgGroup\",\n Lightsource = \"SgLightsource\",\n Mesh = \"SgMesh\",\n Particles = \"SgParticles\",\n Plane = \"SgPlane\",\n Points = \"SgPoints\",\n PositionalAudio = \"SgPositionalAudio\",\n Prefab = \"SgPrefab\",\n Ring = \"SgRing\",\n Scene = \"Scene\",\n Sky = \"SgSky\",\n Sphere = \"SgSphere\",\n Text = \"SgText\",\n Torus = \"SgTorus\",\n Vehicle = \"SgVehicle\"\n}\n\n---@private\nfunction SceneObject:__new(handle, nodeId)\n Object.__new(self, handle)\n self._type = \"SceneObject\"\n self._isSceneObject = true\n self._nodeId = nodeId\nend\n\nfunction capitalizeFirstLetter(str)\n local firstChar = str:sub(1, 1)\n if firstChar:lower() == firstChar then\n firstChar = firstChar:upper()\n return firstChar .. str:sub(2)\n end\n return str\nend\n\n---@private\nfunction SceneObject:__customIndex(key)\n -- any private properties of scene objects start with an underscore. if so, just use rawget, otherwise\n -- get the value of the field from the engine and return it\n local value = rawget(self, key)\n if value or type(key) ~= 'string' or string.sub(key, 1, 1) == '_' then\n return value\n else\n return self:get(key)\n end\nend\n\n---@private\nfunction SceneObject:__customNewIndex(key, value)\n -- any private properties of scene objects start with an underscore. if so, just use rawset, otherwise\n -- write the value to the field in the engine\n if type(key) ~= 'string' or string.sub(key, 1, 1) == '_' then\n rawset(self, key, value)\n else\n self:set(key, value)\n end\nend\n\n--- Finds all objects with the given name.\n---\n--- The search can be restricted to a specific hierarchy by passing a `root` node.\n--- If no `root` is provided, the search covers the entire scene.\n---\n--- This function supports both static and instance usage:\n---\n--- **Static call**\n--- SceneObject.findAllByName(name root?)\n--- - Searches the entire scene unless a `root` node is provided.\n---\n--- **Instance call**\n--- obj:findAllByName(name, root?)\n--- - Uses `obj` as the root of the search unless an explicit `root`\n--- argument is passed.\n--- - Ideal when searching only within a specific hierarchy.\n---\n--- @param name string Name of the objects in the scenegraph.\n--- @param root (optional) SceneObject The root node to restrict the search to.\n--- @return table A table containing all found objects, or an empty table if none are found.\n--- @usage\n--- -- Global search\n--- local objects = SceneObject.findAllByName(\"Enemy\")\n--- for _, obj in ipairs(objects) do\n--- print(\"Found object with type:\", obj:getType())\n--- end\n---\n--- -- Scoped search under a specific parent node\n--- local level = SceneObject.findByName(\"Level1\")\n--- local objects = SceneObject.findAllByName(\"Enemy\", level)\n--- for _, obj in ipairs(objects) do\n--- print(\"Found object under parent with type:\", obj:getType())\n--- end\n---\n--- -- Instance call: search within a specific object's hierarchy\n--- local level = SceneObject.findByName(\"Level1\")\n--- local enemies = level:findAllByName(\"Enemy\")\n--- for _, obj in ipairs(enemies) do\n--- print(\"Found enemy under level:\", obj:getName())\n--- end\nfunction SceneObject.findAllByName(self, name, root)\n -- Detect if called as instance method\n if type(self) == \"table\" and self._isSceneObject then\n -- Instance call: default root to self if not provided (and self is not the class)\n if root == nil and self ~= SceneObject then\n root = self\n end\n else\n -- Static call: shift parameters\n name, root = self, name\n end\n \n __assertTrace(name, \"name is empty\")\n \n local objects = _internal.sendMessage('sceneobject.findAllByName', { \n name = name, \n root = root and root._handle or nil \n })\n \n for key, value in pairs(objects) do\n objects[key] = handle.createObject(value)\n end\n \n return objects\nend\n\n--- Finds the first occurrence of an object with the given name.\n---\n--- The search can be restricted to a specific hierarchy by passing a `root` node.\n--- If no `root` is provided, the search covers the entire scene.\n---\n--- This function supports both static and instance usage:\n---\n--- **Static call**\n--- SceneObject.findByName(name root)\n--- - Searches the entire scene unless a `root` node is provided.\n---\n--- **Instance call**\n--- obj:findByName(name root)\n--- - Uses `obj` as the root of the search unless an explicit `root`\n--- argument is passed.\n--- - Ideal when searching only within a specific hierarchy.\n---\n--- @param name string Name of the object in the scenegraph.\n--- @param root (optional) SceneObject The root node to restrict the search to.\n--- @return Object|nil The first object found with the given name, or `nil` if not found.\n--- @usage\n--- -- Global search\n--- local obj = SceneObject.findByName(\"Player\")\n--- if obj then\n--- print(\"Found object:\", obj:getName())\n--- end\n---\n--- -- Scoped search under a specific parent node\n--- local obj = SceneObject.findByName(\"Player\", parentNode)\n--- if obj then\n--- print(\"Found object under parent:\", obj:getName())\n--- end\n---\n--- -- Instance call: search within a specific object's hierarchy\n--- local player = SceneObject.findByName(\"Player\")\n--- local weapon = player:findByName(\"Weapon\")\n--- if weapon then\n--- print(\"Found weapon under player:\", weapon:getName())\n--- end\nfunction SceneObject.findByName(self, name, root)\n -- Detect if called as instance method\n if type(self) == \"table\" and self._isSceneObject then\n -- Instance call: default root to self if not provided\n if root == nil and self ~= SceneObject then\n root = self\n end\n else\n -- Static call: shift parameters\n name, root = self, name\n end\n \n __assertTrace(name, \"name is empty\")\n \n return handle.createObject(_internal.sendMessage('sceneobject.findByName', { \n name = name, \n root = root and root._handle or nil \n }))\nend\n\n---Find an object by its unique ID and return it.\n---\n--- @param id string The unique ID of the object.\n--- @return Object The found object or nil if not found.\n--- @usage\n--- local obj = SceneObject.findById(\"2135a78324fe\")\n--- if obj then\n--- print(\"Found object: \", obj:getName())\n--- end\nfunction SceneObject.findById(self, id)\n if type(self) == 'string' then\n id = self\n end\n if not id then\n return nil\n end\n return handle.createObject(_internal.sendMessage('sceneobject.findById', { id=id }))\nend\n\n--- Finds all objects that have the specified tag.\n---\n--- The search can be limited to a specific hierarchy by passing a `root` node.\n--- If no `root` is provided, the search scans the entire scene.\n---\n--- This function supports both static and instance usage:\n---\n--- **Static call**\n--- SceneObject.findByTag(tag, root)\n--- - Searches the entire scene unless a `root` node is provided.\n---\n--- **Instance call**\n--- obj:findByTag(tag, root)\n--- - Uses `obj` as the root of the search unless an explicit `root`\n--- argument is passed.\n--- - Ideal when searching only within a specific hierarchy.\n---\n--- @param tag string The tag to look for.\n--- @param root (optional) SceneObject The root node to restrict the search to.\n--- @return table A table of objects that have the given tag. May be empty if no matches are found.\n--- @usage\n--- -- Global search\n--- local enemies = SceneObject.findByTag(\"Enemy\")\n--- for _, enemy in ipairs(enemies) do\n--- print(\"Found enemy:\", enemy:getName())\n--- end\n---\n--- -- Scoped search under a specific parent node\n--- local tagged = SceneObject.findByTag(\"Interactable\", parentNode)\n--- for _, obj in ipairs(tagged) do\n--- print(\"Found interactable under parent:\", obj:getName())\n--- end\n---\n--- -- Instance call: search within a specific object's hierarchy\n--- local level = SceneObject.findByName(\"Level1\")\n--- local enemies = level:findByTag(\"Enemy\")\n--- for _, enemy in ipairs(enemies) do\n--- print(\"Found enemy under level:\", enemy:getName())\n--- end\nfunction SceneObject.findByTag(self, tag, root)\n -- Detect if called as instance method\n if type(self) == \"table\" and self._isSceneObject then\n -- Instance call: default root to self if not provided (and self is not the class)\n if root == nil and self ~= SceneObject then\n root = self\n end\n else\n -- Static call: shift parameters\n tag, root = self, tag\n end\n \n __assertTrace(tag, \"tag is empty\")\n \n local objects = _internal.sendMessage('sceneobject.findByTag', { \n tag = tag, \n root = root and root._handle or nil \n })\n \n for key, value in pairs(objects) do\n objects[key] = handle.createObject(value)\n end\n \n return objects\nend\n\n---Create a new object in the scenegraph. This is a generic function, for specialized versions, see the respective Sg<name> classes like SgBox.\n--- @param objectType string Type of object.\n--- @param options table The options to use. For information about the possible options, see the specialized functions below.\n--- @param parent Object the parent object to add the new one to or nil to put it at root level\n--- @return Promise a promise resolving to a SceneObject\n--- @usage\n--- local promise = SceneObject.create(SceneObject.Type.Sphere, { radius = 1 }, nil)\n--- promise:next(function(obj)\n--- obj:setActive(true)\n--- end)\nfunction SceneObject.create(self, objectType, options, parent)\n if (type(self) == \"string\") then\n objectType, options, parent = self, objectType, options\n end\n\n __assertTrace(type(objectType) == 'string', 'objectType must be a string')\n __assertTrace(parent == nil or (type(parent) == 'table' and parent._isSceneObject), 'parent must be a SceneObject')\n\n local p = _internal.sendMessage('sceneobject.create', {\n type = objectType,\n options = options,\n parent = parent and parent._handle or nil\n })\n return Promise:new(p)\nend\n\n--- Get the ID of the scene object.\n--- @return string The unique ID of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Cube\")\n--- print(\"Object ID:\", obj:getId())\nfunction SceneObject:getId()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return self._nodeId\nend\n\n--- Get the name of the scene object.\n--- @return string The user-defined name of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Cube\")\n--- print(\"Object name:\", obj:getName())\nfunction SceneObject:getName()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.getName', { obj=self._handle })\nend\n\n--- Return the display name of the scene object.\n--- In case the object has no user-given name, a name will be generated\n--- in the format \"type <id>\"\n--- @return string Displayname\n--- @usage\n--- local obj = SceneObject.findByName(\"Enemy\")\n--- print(\"Display name:\", obj:getDisplayName())\nfunction SceneObject:getDisplayName()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.getDisplayName', { obj=self._handle })\nend\n\n--- Return the type of the object\n--- @return string Type\n--- @usage\n--- local obj = SceneObject.findByName(\"Wall\")\n--- print(\"Object type:\", obj:getType())\nfunction SceneObject:getType()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.getType', { obj=self._handle })\nend\n\n--- Set a field of the scene object to a new value.\n---\n--- @param name string The name of the field.\n--- @param value any The new value to set.\n--- @usage\n--- local obj = SceneObject.findByName(\"Light\")\n--- obj:set(\"Brightness\", \"High\")\nfunction SceneObject:set(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value })\nend\n\n--- Get a field value from the scene object.\n---\n--- @param name string The name of the field.\n--- @return string The value of the field.\n--- @usage\n--- local obj = SceneObject.findByName(\"Lamp\")\n--- local intensity = obj:get(\"Intensity\")\n--- print(\"Intensity: \" .. intensity)\nfunction SceneObject:get(name)\n\n -- This is a replacement for the old get() function without parameters that would just return a node reference\n -- It needs to stay in place as long as there are still scripts that depend on it.\n if name == nil then\n return self\n end\n\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n local field = _internal.sendMessage('sceneobject.get', { obj=self._handle, name=name })\n if field == nil then\n return nil\n end\n return toType(field.value, field.type)\nend\n\n--- Set a field of the scene object to a new value.\n---@deprecated 25.11 Use ``set`` which is the new shorter version\n--- @param name string The name of the field.\n--- @param value any The new value to set.\n--- @usage\n--- local obj = SceneObject.findByName(\"Light\")\n--- obj:setFieldValue(\"Brightness\", \"High\")\nfunction SceneObject:setFieldValue(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value })\nend\n\n--- Get a field value from the scene object.\n---@deprecated 25.11 Use ``get`` which is the new shorter version\n--- @param name string The name of the field.\n--- @return string The value of the field.\n--- @usage\n--- local obj = SceneObject.findByName(\"Lamp\")\n--- local intensity = obj:getFieldValue(\"Intensity\")\n--- print(\"Intensity: \" .. intensity)\nfunction SceneObject:getFieldValue(name)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n local field = _internal.sendMessage('sceneobject.get', { obj=self._handle, name=name })\n if field == nil then\n return nil\n end\n return toType(field.value, field.type)\nend\n\n--- Animate a field from its current value to a new value.\n---\n--- @param name string The name of the field\n--- @param value any The new value to animate to.\n--- @param duration number The duration in milliseconds for the animation to take\n--- @param ease string The type of easing to use. Possible values: 'linear', 'easeInOutQuad'\n--- @return number animation id. Can be used to cancel animation using cancelAnimation(id)\nfunction SceneObject:animateFieldValue(name, value, duration, ease)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(not (type(value) == 'table'), \"Cannot animate objects!\");\n name = capitalizeFirstLetter(name)\n return _internal.sendMessage('sceneobject.animateFieldValue', { obj=self._handle, name=name, value=value, duration=duration, ease=ease })\nend\n\n--- Cancel a running animation\n---\n--- @param name string The name of the field\nfunction SceneObject:cancelAnimation(id)\n _internal.sendMessage('sceneobject.cancelAnimation', { obj=self._handle, id=id })\nend\n\n--- Set a field of the scene object to a new color value.\n---@deprecated 25.09 Use ``set`` which handle the correct type automatically.\n--- @param name string The name of the field.\n--- @param value Color The new color value.\n--- @usage\n--- local obj = SceneObject.findByName(\"Cube\")\n--- obj:setFieldColorValue(\"Color\", Color.new(1, 0, 0, 1)) -- Sets the object to red.\nfunction SceneObject:setFieldColorValue(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(value._isColor, \"value is not a Color\")\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value:toData() })\nend\n\n--- Set a field of the scene object to a new number value.\n---@deprecated 25.09 Use ``set`` which handle the correct type automatically.\n--- @param name string The name of the field.\n--- @param value number The new number value.\nfunction SceneObject:setFieldNumberValue(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(type(value) == \"number\", \"value must be a number\")\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value })\nend\n\n---Set a field of the scene object to a new enum value\n--- @param name string\n--- @param value number\nfunction SceneObject:setFieldEnumValue(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value })\nend\n\n---Set a field of the scene object to a new boolean value\n---@deprecated 25.09 Use ``set`` which handle the correct type automatically.\n--- @param name string\n--- @param value boolean\nfunction SceneObject:setFieldBooleanValue(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n if value then value = true else value = false end\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value })\nend\n\n--- Get the layers of the scene object\n--- @return table A table containing layer indices\nfunction SceneObject:getLayers()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n local internal_layers = self:getFieldValue(\"Layers\")\n __assertTrace(type(internal_layers) == \"table\", \"Expected 'Layers' to be a table\")\n\n -- convert internal 0-based indices to frontend 1-based indices\n local layers = {}\n for _, layer_idx in ipairs(internal_layers) do\n table.insert(layers, layer_idx + 1)\n end\n\n return layers\nend\n\n--- Set the layers of the scene object.\n--- Layers control rendering visibility and interaction with other objects.\n---\n--- @param layers table A table containing layer indices.\n--- @usage\n--- local obj = SceneObject.findByName(\"Character\")\n--- obj:setLayers({1, 3, 5}) -- Assigns the object to layers 1, 3, and 5.\nfunction SceneObject:setLayers(layers)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(type(layers) == \"table\", \"Layers must be provided as a table\")\n self:setFieldValue(\"Layers\", layers)\nend\n\n--- Get the value of a scene object's field as color value.\n---@deprecated 25.09 Use ``get`` which always returns the correct type.\n--- @return Color value of the field\n--- @usage\n--- local obj = SceneObject.findByName(\"Lamp\")\n--- local color = obj:getFieldColorValue(\"Color\")\n--- print(\"Color: \" .. color)\nfunction SceneObject:getFieldColorValue(name)\n return self:getFieldValue(name)\nend\n\n--- Get the value of a scene object's field as number value.\n---@deprecated 25.09 Use ``get`` which always returns the correct type.\n--- @return number value of the field\nfunction SceneObject:getFieldNumberValue(name)\n return self:getFieldValue(name)\nend\n\n--- Get the value of a scene object's field as boolean value.\n---@deprecated 25.09 Use ``get`` which always returns the correct type.\n--- @return boolean value of the field\nfunction SceneObject:getFieldBooleanValue(name)\n return self:getFieldValue(name)\nend\n\n--- Get the value of a scene object's field as string value.\n---@deprecated 25.09 Use ``get`` which always returns the correct type.\n--- @return string value of the field\nfunction SceneObject:getFieldEnumValue(name)\n return self:getFieldValue(name)\nend\n\n--- Get the value of a scene object's field as string value\n--- @return string value of the field\nfunction SceneObject:getFieldEnumOptions(name)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.getFieldOptions', { obj=self._handle, name=name })\nend\n\n--- Set a field of the scene object to a new boolean value.\n---@deprecated 25.09 Use ``set`` which handle the correct type automatically.\n--- @param name string The name of the field.\n--- @param value boolean The new boolean value.\n--- @usage\n--- local obj = SceneObject.findByName(\"Door\")\n--- obj:setFieldBooleanValue(\"PhysicsEnabled\", true)\nfunction SceneObject:setFieldBooleanValue(name, value)\n self:setFieldValue(name, not not value)\nend\n\n---Get a link of the scene object to a new value\n---@deprecated 25.11 Use ``get`` which handles items now too\n--- @param name string name of the link field\n--- @return number handle of the linked item or nil if not set\nfunction SceneObject:getLinkItem(name)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n return handle.createObject(_internal.sendMessage('sceneobject.getLinkItem', { obj=self._handle, name=name }))\nend\n\n---Set a link from the scene object\n---@deprecated 25.11 Use ``set`` which handles items now too\n--- @param name string name of the link field\n--- @param item string value can be either an item ID or an Item object\nfunction SceneObject:setLinkItem(name, item)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(not item or (item and item._handle and item._isItem), \"item has to be an object of Item class or nil!\");\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.setLinkItem', { obj=self._handle, name=name, item=item })\nend\n\n--- Set the position of the scene object.\n--- Sets the object's local position to the given coordinates.\n---\n--- @param x number Position along the X-axis.\n--- @param y number Position along the Y-axis.\n--- @param z number Position along the Z-axis.\n--- @usage\n--- local obj = SceneObject.findByName(\"Player\")\n--- if obj then\n--- obj:setPosition(0, 5, 0) -- Places the object at (0, 5, 0)\n--- end\nfunction SceneObject:setPosition(x, y, z)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n\n _internal.sendMessage('sceneobject.setPosition', {\n obj = self._handle,\n x = x,\n y = y,\n z = z\n })\nend\n\n--- Translate (move) the scene object by an offset.\n--- Adds the given offset to the object's current local position.\n---\n--- @param x number Offset along the X-axis.\n--- @param y number Offset along the Y-axis.\n--- @param z number Offset along the Z-axis.\n--- @usage\n--- local obj = SceneObject.findByName(\"Player\")\n--- if obj then\n--- obj:translate(0, 1, 0) -- Moves the object up by 1 unit from its current position\n--- end\nfunction SceneObject:translate(x, y, z)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.translate', {\n obj = self._handle,\n x = x,\n y = y,\n z = z\n })\nend\n\n--- Rotate the scene object.\n--- Rotation is applied in degrees and follows Euler angles (XYZ order).\n---\n--- @param x number Rotation around the X-axis in degrees.\n--- @param y number Rotation around the Y-axis in degrees.\n--- @param z number Rotation around the Z-axis in degrees.\n--- @usage\n--- local obj = SceneObject.findByName(\"Cube\")\n--- if obj then\n--- obj:rotate(45, 0, 0) -- Rotates 45 degrees around the X-axis\n--- end\nfunction SceneObject:rotate(x, y, z)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.rotate', { obj=self._handle, x=x, y=y, z=z })\nend\n\n--- Scale the scene object.\n---\n--- @param x number Scale factor along the X-axis.\n--- @param y number Scale factor along the Y-axis.\n--- @param z number Scale factor along the Z-axis.\n--- @usage\n--- local obj = SceneObject.findByName(\"Tree\")\n--- if obj then\n--- obj:scale(2, 2, 2) -- Doubles the size of the object\n--- end\nfunction SceneObject:scale(x, y, z)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.scale', { obj=self._handle, x=x, y=y, z=z })\nend\n\n--- Get the local transform of a scene object.\n--- The transform contains position, rotation, and scale relative to the object's parent.\n---\n--- @param cacheLocal boolean if set to true, the transform will be cached locally for the future for performance reasons. This should only be used if no other script changes the transform\n--- @return Transform The object's local transform.\n--- @usage\n--- local obj = SceneObject.findByName(\"Character\")\n--- local transform = obj:getTransform()\n--- print(\"Local position:\", transform.position)\nfunction SceneObject:getTransform(cacheLocal)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n if self._transform == nil or not cacheLocal then\n self._transform = Transform.fromData(_internal.sendMessage('sceneobject.getTransform', { obj=self._handle }))\n end\n return self._transform:clone()\nend\n\n--- Apply a function to modify the object's transform, then update it.\n--- This allows making changes to the transform while ensuring they are properly applied back.\n---\n--- @param func function A function that takes a Transform object as its only argument.\n--- @param cacheLocal boolean if set to true, the transform will be cached locally for the future for performance reasons. This should only be used if no other script changes the transform\n--- @usage\n--- local obj = SceneObject.findByName(\"Cube\")\n--- obj:withTransform(function(transform)\n--- transform.position.x = transform.position.x + 1 -- Move object 1 unit to the right\n--- end)\nfunction SceneObject:withTransform(func, cacheLocal)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(type(func) == 'function', \"argument func is not a function\")\n local transform = self:getTransform(cacheLocal)\n func(transform)\n self:setTransform(transform)\nend\n\n--- Get the world transform of a scene object.\n--- The world transform includes absolute position, rotation, and scale in the scene.\n---\n--- @return Transform The object's world transform.\n--- @usage\n--- local obj = SceneObject.findByName(\"Light\")\n--- local worldTransform = obj:getWorldTransform()\n--- print(\"World position:\", worldTransform.position)\nfunction SceneObject:getWorldTransform()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Transform.fromData(_internal.sendMessage('sceneobject.getWorldTransform', { obj=self._handle }))\nend\n\n--- Get the forward direction vector in world space.\n--- Returns the local -Z axis transformed by the object's rotation.\n---\n--- @return Vector3 The forward direction in world space.\nfunction SceneObject:getForward()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Vector3.fromData(_internal.sendMessage('sceneobject.getForward', { obj=self._handle }))\nend\n\n--- Get the right direction vector in world space.\n--- Returns the local +X axis transformed by the object's rotation.\n---\n--- @return Vector3 The right direction in world space.\nfunction SceneObject:getRight()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Vector3.fromData(_internal.sendMessage('sceneobject.getRight', { obj=self._handle }))\nend\n\n--- Get the up direction vector in world space.\n--- Returns the local +Y axis transformed by the object's rotation.\n---\n--- @return Vector3 The up direction in world space.\nfunction SceneObject:getUp()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Vector3.fromData(_internal.sendMessage('sceneobject.getUp', { obj=self._handle }))\nend\n\n--- Set the local transform of a scene object.\n--- Updates the object's position, rotation, and scale relative to its parent.\n---\n--- @param transform Transform The new local transform to apply.\n--- @usage\n--- local obj = SceneObject.findByName(\"Door\")\n--- local newTransform = Transform.new()\n--- newTransform.position = Vector3.new(0, 2, 0) -- Move the object up 2 units\n--- obj:setTransform(newTransform)\nfunction SceneObject:setTransform(transform)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(transform and transform._isTransform, \"transform has to be an object of type Transform\")\n self._transform = transform\n _internal.sendMessage('sceneobject.setTransform', { obj=self._handle, transform=self._transform:toData() })\nend\n\n--- Set the physics position of the scene object.\n---\n--- This directly updates the position of the object inside the physics engine.\n--- The position is expressed in world coordinates.\n--- Only works for objects added to the physics engine\n--- @param position Vector3 New world position of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Box\")\n--- obj:setPhysicsPosition(Vector3.new(0, 5, 0))\nfunction SceneObject:setPhysicsPosition(position)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(position and position._isVector3, \"position has to be an object of type Vector3\")\n _internal.sendMessage('sceneobject.setPhysicsPosition', { obj=self._handle, position=position:toData() })\nend\n\n--- Set the physics rotation of the scene object.\n---\n--- This directly updates the rotation of the object inside the physics engine.\n--- The rotation is expressed in world space.\n--- Only works for objects added to the physics engine\n--- @param rotation Quaternion New world rotation of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Box\")\n--- obj:setPhysicsRotation(Quaternion.fromEuler(0, math.pi, 0))\nfunction SceneObject:setPhysicsRotation(rotation)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(rotation and rotation._isQuaternion, \"rotation has to be an object of type Quaternion\")\n _internal.sendMessage('sceneobject.setPhysicsRotation', { obj=self._handle, rotation=rotation:toData() })\nend\n\nfunction SceneObject:setPhysicsRotation(rotation)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(rotation and rotation._isQuaternion, \"rotation has to be an object of type Quaternion\")\n _internal.sendMessage('sceneobject.setPhysicsRotation', { obj=self._handle, rotation=rotation:toData() })\nend\n\n--- Get the linear velocity of the scene object.\n--- This function returns the object's velocity in world coordinates.\n--- Only works for objects added to the physics engine with `watchLinearVelocity` enabled.\n---\n--- @return Vector3 The linear velocity of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Ball\")\n--- local velocity = obj:getLinearVelocity()\n--- print(\"Velocity:\", velocity)\nfunction SceneObject:getLinearVelocity()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Vector3.fromData(_internal.sendMessage('sceneobject.getLinearVelocity', { obj=self._handle }))\nend\n\n--- Set the linear velocity of the scene object.\n--- This function assigns a new velocity to the object, but only works if the object is in the physics engine.\n---\n--- @param velocity Vector3 The new linear velocity to apply.\n--- @usage\n--- local obj = SceneObject.findByName(\"Car\")\n--- obj:setLinearVelocity(Vector3.new(10, 0, 0)) -- Move object at speed 10 in the X direction.\nfunction SceneObject:setLinearVelocity(velocity)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(velocity and velocity._isVector3, \"velocity must be an object of type Vector3\")\n local data = velocity:toData()\n _internal.sendMessage('sceneobject.setLinearVelocity', { obj=self._handle, velocity=data })\nend\n\n--- Get the angular velocity of the scene object.\n--- Only works for objects added to the physics engine with `watchAngularVelocity` enabled.\n---\n--- @return Vector3 The angular velocity of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Wheel\")\n--- local angVel = obj:getAngularVelocity()\n--- print(\"Angular velocity:\", angVel)\nfunction SceneObject:getAngularVelocity()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Vector3.fromData(_internal.sendMessage('sceneobject.getAngularVelocity', { obj=self._handle }))\nend\n\n--- Set the angular velocity of the scene object.\n---\n--- @param velocity Vector3 The new angular velocity to apply.\n--- @usage\n--- local obj = SceneObject.findByName(\"Propeller\")\n--- obj:setAngularVelocity(Vector3.new(0, 10, 0)) -- Rotate around Y-axis.\nfunction SceneObject:setAngularVelocity(velocity)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(velocity and velocity._isVector3, \"velocity must be an object of type Vector3\")\n local data = velocity:toData()\n _internal.sendMessage('sceneobject.setAngularVelocity', { obj=self._handle, velocity=data })\nend\n\n--- Apply an impulse to the scene object.\n---\n--- An **impulse** is a sudden force applied to an object that immediately\n--- changes its velocity. This is different from a continuous force, which\n--- is applied over time.\n---\n--- This function only works on objects that are physics-enabled.\n---\n--- @param impulse Vector3 The force impulse to apply.\n--- @param relPos Vector3 The relative position where the impulse is applied. Default is (0, 0, 0)\nfunction SceneObject:applyImpulse(impulse, relPos)\n relPos = relPos or Vector3.new(0, 0, 0)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(impulse and impulse._isVector3, \"impulse must be an object of type Vector3\")\n __assertTrace(relPos and relPos._isVector3, \"relPos must be an object of type Vector3\")\n local impulseData = impulse:toData()\n local relPosData = relPos:toData()\n _internal.sendMessage('sceneobject.applyImpulse', { obj=self._handle, impulse=impulseData, relPos=relPosData })\nend\n\n--- Apply an impulse to the scene object that respects constraints.\n---\n--- Only works on physics-enabled objects.\n---\n--- @param impulse Vector3 The force impulse to apply.\n--- @param relPos Vector3 The relative position where the impulse is applied. Default is (0, 0, 0)\nfunction SceneObject:applyPushImpulse(impulse, relPos)\n relPos = relPos or Vector3.new(0, 0, 0)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(impulse and impulse._isVector3, \"impulse must be an object of type Vector3\")\n __assertTrace(relPos and relPos._isVector3, \"relPos must be an object of type Vector3\")\n _internal.sendMessage('sceneobject.applyPushImpulse', { obj=self._handle, impulse=impulse:toData(), relPos=relPos:toData() })\nend\n\n--- Apply an impulse to the center of the object.\n---\n--- Applies a sudden velocity change at the object's center. Only works on physics-enabled objects.\n---\n--- @param impulse Vector3 The force impulse to apply.\n--- @usage\n--- local obj = SceneObject.findByName(\"Box\")\n--- obj:applyCentralImpulse(Vector3.new(0, 5, 0)) -- Apply upward impulse.\nfunction SceneObject:applyCentralImpulse(impulse)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(impulse and impulse._isVector3, \"impulse must be an object of type Vector3\")\n _internal.sendMessage('sceneobject.applyCentralImpulse', { obj=self._handle, impulse=impulse:toData() })\nend\n\n--- Apply a central impulse that respects constraints.\n---\n--- Only works on physics-enabled objects and respects any joint or constraint applied.\n---\n--- @param impulse Vector3 The force impulse to apply.\n--- @usage\n--- local obj = SceneObject.findByName(\"Box\")\n--- obj:applyCentralPushImpulse(Vector3.new(0, 5, 0)) -- Apply upward impulse.\nfunction SceneObject:applyCentralPushImpulse(impulse)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(impulse and impulse._isVector3, \"impulse must be an object of type Vector3\")\n _internal.sendMessage('sceneobject.applyCentralPushImpulse', { obj=self._handle, impulse=impulse:toData() })\nend\n\n--- Apply a force to the scene object.\n---\n--- Unlike impulses, **forces** are applied over time, influencing the object's acceleration.\n--- Only works on physics-enabled objects.\n---\n--- @param force Vector3 The force vector to apply.\n--- @param relPos Vector3 The relative position where the force is applied. Default is (0, 0, 0)\nfunction SceneObject:applyForce(force, relPos)\n relPos = relPos or Vector3.new(0, 0, 0)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(force and force._isVector3, \"force must be an object of type Vector3\")\n __assertTrace(relPos and relPos._isVector3, \"relPos must be an object of type Vector3\")\n _internal.sendMessage('sceneobject.applyForce', { obj=self._handle, force=force:toData(), relPos=relPos:toData() })\nend\n\n--- Clear any forces on a scene object.\n--- Only works on physics-enabled objects.\n--- @usage\n--- local obj = SceneObject.findByName(\"Ball\")\n--- obj:clearForces()\nfunction SceneObject:clearForces()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.clearForces', { obj=self._handle })\nend\n\n--- Delete the scene object from the scene.\n---\n--- @usage\n--- local obj = scene:findByName(\"TemporaryObject\")\n--- if obj then\n--- obj:delete()\n--- end\nfunction SceneObject:delete()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.delete', { obj=self._handle })\n if self._handle then\n handle.invalidate(self._handle)\n end\n self._handle = nil\nend\n\n--- Clone a scene object to a new position and return it.\n---\n--- The position can be provided as:\n--- 1. Separate `x, y, z` coordinates.\n--- 2. A `Vector3` containing the position.\n---\n--- If no position is provided, the clone will appear in the same location as the original.\n---\n--- @param x number | Vector3 The X-coordinate of the new position, or a `Vector3` containing `x, y, z`. If a `Vector3` is passed, `y` and `z` should be omitted.\n--- @param y number (optional) The Y-coordinate of the new position. Omit this if passing a `Vector3`.\n--- @param z number (optional) The Z-coordinate of the new position. Omit this if passing a `Vector3`.\n--- @param parent SceneObject (optional) The parent object to attach the new object to.\n--- @return Promise A promise resolving to a SceneObject.\n---\n--- @usage\n--- local obj = scene:findByName(\"Tree\")\n--- obj:clone(5, 0, 2):next(function(clone)\n--- clone:setActive(true) -- Activate the cloned object\n--- end)\n---\n--- local pos = Vector3.new(3, 1, 0)\n--- obj:clone(pos):next(function(clone)\n--- clone:setFieldValue(\"Name\", \"ClonedTree\") -- Rename the cloned object\n--- end)\n---\n--- local parentObj = scene:findByName(\"Group\")\n--- obj:clone(pos, parentObj):next(function(clone)\n--- clone:setFieldValue(\"Name\", \"AnotherClonedTree\") -- Rename the cloned object\n--- end)\nfunction SceneObject:clone(x, y, z, parent)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n\n -- Handle case where first argument is a Vector3 and second argument is the parent\n if type(x) == \"table\" and x._isVector3 then\n x, y, z, parent = x.x, x.y, x.z, y -- Shift arguments\n end\n\n local p = _internal.sendMessage('sceneobject.clone', {\n obj = self._handle,\n x = x,\n y = y,\n z = z,\n parent = parent and parent._handle or nil\n })\n\n return Promise:new(p)\nend\n\n--- Create a new object of same type from this one. If no options are specified this will be basically a clone.\n--- If an options table is passed, this will specify all fields with their values that should be changed.\n--- At the very least, this should contain a new Transform, otherwise the new object will be at the exact location\n--- of the old one.\n--- @param options table the table of fields to set on the new object\n--- @param parent SceneObject (optional) The parent object to attach the new object to.\n--- @return Promise A promise resolving to a SceneObject.\n---\n--- @usage\n--- local obj = SceneObject.findByName(\"Tree\")\n--- local parentObj = SceneObject.findByName(\"Group\")\n--- local clone = await(obj:createFromThis({\n--- transform = Transform.new(Vector3.new(4, 6, 8)),\n--- active = true,\n--- name = \"Cloned tree\"\n--- }, parentObj)\nfunction SceneObject:createFromThis(options, parent)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(parent == nil or parent._isSceneObject, \"parent is not nil or a SceneObject\")\n\n local p = _internal.sendMessage('sceneobject.createFromThis', {\n obj = self._handle,\n options = options,\n parent = parent and parent._handle or nil\n })\n\n return Promise:new(p)\nend\n\n--- Move the scene object to a different parent. This parent can not be a child of the scene object\n--- @param parent SceneObject (optional) The parent object to attach the new object to. if nil, the object will be moved to the scenegraph root\nfunction SceneObject:moveToParent(parent)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(not parent or parent._isSceneObject, \"parent is not a SceneObject\")\n _internal.sendMessage('sceneobject.moveToParent', { obj = self._handle, parent = parent and parent._handle or nil })\nend\n\n--- Set the active state of the scene object.\n--- @param value boolean True to activate, false to deactivate.\nfunction SceneObject:setActive(value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n self:setFieldValue(\"Active\", value)\nend\n\n--- Get the active state of the scene object.\n--- @return boolean True if the object is active, false otherwise.\nfunction SceneObject:getActive()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return self:getFieldBooleanValue(\"Active\")\nend\n\n--- Check if the object has a specific tag.\n--- @param tagName string The tag name to check.\n--- @return boolean True if the object has the tag, false otherwise.\nfunction SceneObject:hasTag(tagName)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n local tags = self:getFieldValue(\"Tags\")\n for _, value in pairs(tags) do\n if value == tagName then\n return true\n end\n end\n return false\nend\n\n--- Get a list of all tags assigned to the object.\n--- @return table An array of tag names.\nfunction SceneObject:getTags()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return self:getFieldValue(\"Tags\")\nend\n\n--- Assign a set of tags to the object.\n--- @param tags table An array of tag names.\nfunction SceneObject:setTags(tags)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n Tools.validateArrayOfStrings(tags)\n self:setFieldValue(\"Tags\", tags)\nend\n\n--- Add a tag to the object.\n--- @param tag string The tag to add.\nfunction SceneObject:addTag(tag)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(type(tag) == 'string', \"tag needs to be a string\")\n local tags = self:getTags()\n table.insert(tags, tag)\n self:setTags(tags)\nend\n\n--- Remove all tags from the object.\nfunction SceneObject:clearTags()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n self:setTags({})\nend\n\n--- Take a snapshot of the object and its children, if specified.\n--- This allows restoring the object to a previous state.\n--- @param recursive boolean Whether to include child objects in the snapshot.\n--- @return number The snapshot handle.\nfunction SceneObject:takeSnapshot(recursive)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.takeSnapshot', { obj=self._handle, recursive=recursive })\nend\n\n--- Restore a snapshot, reverting any changes made after the snapshot was taken.\n--- @param snapshot number The snapshot handle to restore.\nfunction SceneObject:rollbackSnapshot(snapshot)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.rollbackSnapshot', { obj=self._handle, snapshot=snapshot })\nend\n\n--- Check if the object is enabled.\n--- @return boolean True if the object is enabled, false otherwise.\nfunction SceneObject:isEnabled()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.isEnabled', { obj=self._handle })\nend\n\n--- Enable the object.\nfunction SceneObject:enable()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.enable', { obj=self._handle })\nend\n\n--- Disable the object.\nfunction SceneObject:disable()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.disable', { obj=self._handle })\nend\n\n--- Get the material assigned to the object.\n--- @return Material The material, or nil if none is assigned.\nfunction SceneObject:getMaterial()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return self:getLinkItem(\"Material\")\nend\n\n--- Set the object's material.\n--- @param mat Material The material to set or nil to clear the material\nfunction SceneObject:setMaterial(mat)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(mat == nil or (type(mat) == 'table' and mat._isMaterial), \"self is not a SceneObject\")\n self:setLinkItem(\"Material\", mat)\nend\n\n--- Get all behaviours attached to this object.\n---@deprecated 25.12 use getBehaviours.\n--- @return table A list of Behaviour objects.\nfunction SceneObject:getEntities()\n return self:getBehaviours()\nend\n\n--- Get all behaviours attached to this object.\n--- @return table A list of Behaviour objects.\nfunction SceneObject:getBehaviours()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n if not self._nodeId then\n error(\"This scene object does not have a node Id. Cannot get its behaviours.\")\n end\n return Behaviour.getByNodeId(self._nodeId)\nend\n\n--- Get a behaviour attached to this object by its script name.\n--- @param name string The script name.\n--- @return Behaviour The corresponding behaviour, or nil if not found.\nfunction SceneObject:getBehaviourByScriptName(name)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n if not self._nodeId then\n error(\"This scene object does not have a node Id. Cannot get its entities.\")\n end\n return Behaviour.getByNodeIdAndScriptName(self._nodeId, name)\nend\n\n--- Get a behaviour attached to this object by its script name.\n---@deprecated\n--- @param name string The script name.\n--- @return Behaviour The corresponding behaviour, or nil if not found.\nfunction SceneObject:getEntityByScriptName(name)\n return self:getBehaviourByScriptName(name)\nend\n\n--- Get a behaviour attached to this object by its script name.\n--- @param name string The script name.\n--- @return Behaviour The corresponding behaviour, or nil if not found.\nfunction SceneObject:behaviour(name)\n return self:getBehaviourByScriptName(name)\nend\n\n--- Get a behaviour attached to this object by its script name.\n--- @param name string The script name.\n--- @return Behaviour The corresponding behaviour, or nil if not found.\nfunction SceneObject:entity(name)\n return self:getBehaviourByScriptName(name)\nend\n\n--- Get the parent of the object.\n--- @return SceneObject The parent object, or nil if none exists.\nfunction SceneObject:getParent()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return handle.createObject(_internal.sendMessage('sceneobject.getParent', { obj=self._handle }))\nend\n\n--- Get the children of the object.\n--- @return table A table of child objects.\nfunction SceneObject:getChildren()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n local objects = _internal.sendMessage('sceneobject.getChildren', { obj=self._handle })\n for key, value in pairs(objects) do\n objects[key] = handle.createObject(value)\n end\n return objects\nend\n\n--- Enable or disable automatic recreation of the scene object between play and stop.\n---\n--- When `value` is true, the object will be automatically recreated every time\n--- the scene starts or stops. This is useful for objects that need to reset\n--- physics, state, or procedural components at each run.\n---\n--- @param value boolean Enable (`true`) or disable (`false`) automatic recreation.\nfunction SceneObject:setAutoRecreate(value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.setAutoRecreate', { obj=self._handle, value=value })\nend\n\n--- Immediately recreate the scene object.\n---\n--- This forces the object to reset its state, physics, and components as if\n--- it was newly instantiated in the scene.\nfunction SceneObject:recreate()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.recreate', { obj=self._handle })\nend\n\nreturn SceneObject\n";
131002
131002
 
131003
131003
  var lua_api_sceneobjects_audio = "\nlocal _internal = require('engine/_internal')\nlocal SceneObject = require('engine/sceneobject')\n\n------------------------------------------------------------------\n-- SgAudio class\n------------------------------------------------------------------\n\n--- @class SgAudio\nlocal SgAudio = Class.new(SceneObject)\n\n--- @private\nfunction SgAudio:__new(handle, nodeId)\n SceneObject.__new(self, handle, nodeId)\n self._type = \"SgAudio\"\n self._isSgAudio = true\nend\n\n--- Create a new audio object in the scenegraph.\n---@param options table A table of options to fill the parameters\n---@param parent SceneObject The parent scene object to add this to\n---@return Promise a promise which will resolve to the created object\nfunction SgAudio.create(self, options, parent)\n -- check if static function was called as method. if so, shift the parameters\n if not (type(self) == 'table' and type(self.new) == 'function') then\n parent, options = options, self\n end\n return SceneObject.create(\"SgAudio\", options, parent)\nend\n\n--- playback the audio\n---@param force boolean Force the sound to replay even if it is already playing\nfunction SgAudio:play(force)\n assert(self._isSgAudio, \"self is not an SgAudio object\")\n _internal.sendMessage('sgaudio.play', { obj=self._handle, force=force })\nend\n\n--- stop the audio\nfunction SgAudio:stop()\n assert(self._isSgAudio, \"self is not an SgAudio object\")\n _internal.sendMessage('sgaudio.stop', { obj=self._handle })\nend\n\n--- Checks if the audio is playing\n---@return boolean true, if sound is playing, false otherwise\nfunction SgAudio:isPlaying()\n assert(self._isSgAudio, \"self is not an SgAudio object\")\n return _internal.sendMessage('sgaudio.isPlaying', { obj=self._handle })\nend\n\nfunction SgAudio:setPlaybackRate(value)\n assert(self._isSgAudio, \"self is not an SgAudio object\")\n self:setFieldNumberValue(\"PlaybackRate\", value)\nend\n\nfunction SgAudio:getPlaybackRate()\n assert(self._isSgAudio, \"self is not an SgAudio object\")\n return self:getFieldNumberValue(\"PlaybackRate\");\nend\n\nfunction SgAudio:getAudioCollectionEntry()\n assert(self._isSgAudio, \"self is not an SgAudio object\")\n return self:getFieldValue(\"AudioCollectionEntry\")\nend\n\nfunction SgAudio:setAudioCollectionEntry(name)\n assert(self._isSgAudio, \"self is not an SgAudio object\")\n self:setFieldValue(\"AudioCollectionEntry\", name)\nend\n\nreturn SgAudio\n";
131004
131004
 
@@ -131042,7 +131042,7 @@
131042
131042
 
131043
131043
  var lua_api_sceneobjects_scene = "\nlocal SceneObject = require('engine/sceneobject')\n\n------------------------------------------------------------------\n-- SgScene class\n------------------------------------------------------------------\n\n--- @class SgScene\nlocal SgScene = Class.new(SceneObject)\n\n--- @private\nfunction SgScene:__new(handle, nodeId)\n SceneObject.__new(self, handle, nodeId)\n self._type = \"SgScene\"\n self._isSgScene = true\nend\n\n--- Create a new scene object in the scenegraph.\n---@param options table A table of options to fill the parameters\n---@param parent SceneObject The parent scene object to add this to\n---@return Promise a promise which will resolve to the created object\nfunction SgScene.create(self, options, parent)\n -- check if static function was called as method. if so, shift the parameters\n if not (type(self) == 'table' and type(self.new) == 'function') then\n parent, options = options, self\n end\n return SceneObject.create(\"SgScene\", options, parent)\nend\n\nreturn SgScene\n";
131044
131044
 
131045
- var lua_api_sceneobjects_scriptedmesh = "\nlocal _internal = require('engine/_internal')\nlocal SceneObject = require('engine/sceneobject')\n\n------------------------------------------------------------------\n-- SgScriptedMesh class\n------------------------------------------------------------------\n\n--- @class SgScriptedMesh\nlocal SgScriptedMesh = Class.new(SceneObject)\n\n--- @private\nfunction SgScriptedMesh:__new(handle, nodeId)\n SceneObject.__new(self, handle, nodeId)\n self._type = \"SgScriptedMesh\"\n self._isSgScriptedMesh = true\n self._offset = 1\nend\n\n--- Create a new scripted mesh object in the scenegraph.\n---@param options table A table of options to fill the parameters\n---@param parent SceneObject The parent scene object to add this to\n---@return Promise a promise which will resolve to the created object\n---@usage\n--- -- Options can be omitted. This example shows the defaults,\n--- -- only specify the ones you want different.\n--- SgScriptedMesh.create({\n--- active = true,\n--- name = \"\",\n--- transform = Transform.new(),\n--- layers = {0},\n--- tags = {},\n--- receiveShadow = false,\n--- castShadow = false,\n--- materials = nil,\n--- indexed = false\n--- })\nfunction SgScriptedMesh.create(self, options, parent)\n -- check if static function was called as method. if so, shift the parameters\n if not (type(self) == 'table' and type(self.new) == 'function') then\n parent, options = options, self\n end\n return SceneObject.create(\"SgScriptedMesh\", options, parent)\nend\n\nfunction SgScriptedMesh:setPositions(data, offset)\n self:setAttributeData(\"position\", data, offset or 0);\nend\n\nfunction SgScriptedMesh:setColors(data, offset)\n self:setAttributeData(\"color\", data, offset or 0);\nend\n\nfunction SgScriptedMesh:setNormals(data, offset)\n self:setAttributeData(\"normal\", data, offset or 0);\nend\n\nfunction SgScriptedMesh:setUvs(data, offset)\n self:setAttributeData(\"uv\", data, offset or 0);\nend\n\nfunction SgScriptedMesh:alloc(size)\n local havePositions = self:get(\"HavePositions\")\n if havePositions then\n self._positions = {}\n self._positions[size*3] = 0\n end\n\n local haveColors = self:get(\"HaveColors\")\n if haveColors then\n self._colors = {}\n self._colors[size*3] = 0\n end\n\n local haveNormals = self:get(\"HaveNormals\")\n if haveNormals then\n self._normals = {}\n self._normals[size*3] = 0\n end\n\n local haveUvs = self:get(\"HaveUVs\")\n if haveUvs then\n self._uvs = {}\n self._uvs[size*2] = 0\n end\nend\n\nfunction SgScriptedMesh:setAttributeData(type, data, offset)\n assert(self._isSgScriptedMesh, \"self is not an SgScriptedMesh\")\n _internal.sendMessage('sgscriptedmesh.setAttributeData', { obj=self._handle, type=type, data=data, offset=offset or 0 })\nend\n\nfunction SgScriptedMesh:update(computeBounds)\n if self._positions then\n self:setAttributeData(\"position\", self._positions)\n end\n if self._colors then\n self:setAttributeData(\"color\", self._colors)\n end\n if self._normals then\n self:setAttributeData(\"normal\", self._normals)\n end\n if self._uvs then\n self:setAttributeData(\"uv\", self._uvs)\n end\n\n if computeBounds then\n self:computeBoundingBox()\n self:computeBoundingSphere()\n end\nend\n\nfunction SgScriptedMesh:setVertex(offset, pos, uv, col, normals)\n local offs3 = (offset-1)*3\n local offs2 = (offset-1)*2\n\n if pos and self._positions then\n self._positions[offs3+1] = pos[1]\n self._positions[offs3+2] = pos[2]\n self._positions[offs3+3] = pos[3]\n end\n\n if col and self._colors then\n self._colors[offs3+1] = col[1]\n self._colors[offs3+2] = col[2]\n self._colors[offs3+3] = col[3]\n end\n\n if normals and self._normals then\n self._normals[offs3+1] = normals[1]\n self._normals[offs3+2] = normals[2]\n self._normals[offs3+3] = normals[3]\n end\n\n if uv and self._uvs then\n self._uvs[offs2+1] = uv[1]\n self._uvs[offs2+2] = uv[2]\n end\n\n return offset+1\nend\n\nfunction SgScriptedMesh:rewind()\n self._offset = 1\nend\n\nfunction SgScriptedMesh:setOffset(offset)\n self._offset = offset\nend\n\nfunction SgScriptedMesh:addVertex(pos, uv, col, normals)\n self._offset = self:setVertex(self._offset, pos, uv, col, normals)\nend\n\nfunction SgScriptedMesh:computeBoundingBox()\n assert(self._isSgScriptedMesh, \"self is not an SgScriptedMesh\")\n _internal.sendMessage('sgscriptedmesh.computeBoundingBox', { obj=self._handle })\nend\n\nfunction SgScriptedMesh:computeBoundingSphere()\n assert(self._isSgScriptedMesh, \"self is not an SgScriptedMesh\")\n _internal.sendMessage('sgscriptedmesh.computeBoundingSphere', { obj=self._handle })\nend\n\nfunction SgScriptedMesh:computeTangents()\n assert(self._isSgScriptedMesh, \"self is not an SgScriptedMesh\")\n _internal.sendMessage('sgscriptedmesh.computeTangents', { obj=self._handle })\nend\n\nfunction SgScriptedMesh:computeVertexNormals()\n assert(self._isSgScriptedMesh, \"self is not an SgScriptedMesh\")\n _internal.sendMessage('sgscriptedmesh.computeVertexNormals', { obj=self._handle })\nend\n\nreturn SgScriptedMesh\n";
131045
+ var lua_api_sceneobjects_scriptedmesh = "\nlocal _internal = require('engine/_internal')\nlocal SceneObject = require('engine/sceneobject')\n\n------------------------------------------------------------------\n-- SgScriptedMesh class\n------------------------------------------------------------------\n\n--- @class SgScriptedMesh\nlocal SgScriptedMesh = Class.new(SceneObject)\n\n--- @private\nfunction SgScriptedMesh:__new(handle, nodeId)\n SceneObject.__new(self, handle, nodeId)\n self._type = \"SgScriptedMesh\"\n self._isSgScriptedMesh = true\n self._offset = 1\nend\n\n--- Create a new scripted mesh object in the scenegraph.\n---@param options table A table of options to fill the parameters\n---@param parent SceneObject The parent scene object to add this to\n---@return Promise a promise which will resolve to the created object\n---@usage\n--- -- Options can be omitted. This example shows the defaults,\n--- -- only specify the ones you want different.\n--- SgScriptedMesh.create({\n--- active = true,\n--- name = \"\",\n--- transform = Transform.new(),\n--- layers = {0},\n--- tags = {},\n--- receiveShadow = false,\n--- castShadow = false,\n--- materials = nil,\n--- indexed = false,\n--- havePositions = true,\n--- haveNormals = false,\n--- haveColors = false,\n--- haveUVs = false\n--- })\nfunction SgScriptedMesh.create(self, options, parent)\n -- check if static function was called as method. if so, shift the parameters\n if not (type(self) == 'table' and type(self.new) == 'function') then\n parent, options = options, self\n end\n return SceneObject.create(\"SgScriptedMesh\", options, parent)\nend\n\n--- Set position data of the vertex. Any number of values can be written at once.\n---@param data table Array of position data.\n---@usage\n--- mesh:setPositions({{1, 2, 3}, {2, 3, 4}}, 0)\nfunction SgScriptedMesh:setPositions(data, offset)\n self:setAttributeData(\"position\", data, offset or 0);\nend\n\n--- Set color data of the vertex. Any number of values can be written at once.\n---@param data table Array of color data.\n---@usage\n--- mesh:setColors({{1, 0, 1}, {1, 1, 1}}, 0)\nfunction SgScriptedMesh:setColors(data, offset)\n self:setAttributeData(\"color\", data, offset or 0);\nend\n\n--- Set normal data of the vertex. Any number of values can be written at once.\n---@param data table Array of normal data.\n---@usage\n--- mesh:setNormals({{1, 0, 1}, {1, 1, 1}}, 0)\nfunction SgScriptedMesh:setNormals(data, offset)\n self:setAttributeData(\"normal\", data, offset or 0);\nend\n\n--- Set UV data of the vertex. Any number of values can be written at once.\n---@param data table Array of UV data.\n---@usage\n--- mesh:setUvs({{1, 0}, {1, 1}}, 0)\nfunction SgScriptedMesh:setUvs(data, offset)\n self:setAttributeData(\"uv\", data, offset or 0);\nend\n\n--- Allocate space for the vertex data.\n---@param size number Vertex count to allocate. This will allocate enough space to fit the requested positions, colors, normals and UVs.\nfunction SgScriptedMesh:alloc(size)\n local havePositions = self:get(\"HavePositions\")\n if havePositions then\n self._positions = {}\n self._positions[size*3] = 0\n end\n\n local haveColors = self:get(\"HaveColors\")\n if haveColors then\n self._colors = {}\n self._colors[size*3] = 0\n end\n\n local haveNormals = self:get(\"HaveNormals\")\n if haveNormals then\n self._normals = {}\n self._normals[size*3] = 0\n end\n\n local haveUvs = self:get(\"HaveUVs\")\n if haveUvs then\n self._uvs = {}\n self._uvs[size*2] = 0\n end\nend\n\n--- Set attribute data. This function is used to set any type of attribute (positions, normals, colors or UVs) on the\n--- mesh. You can use the specialized functions setPositions, setNormals, setColors, setUvs instead.\n--- After doing all changes, call update() to actually write the changes to the engine.\n---@param type string Type of attribute. Has to be one of \"position\", \"normal\", \"color\", or \"uv\"\n---@param data table Array of data to write.\n---@param offset number Offset within the vertex data to write the new entries to.\n---@usage\n--- mesh:setAttributeData(\"position\", {{ 1, 2, 3}, {2, 3, 4}}, 0)\nfunction SgScriptedMesh:setAttributeData(type, data, offset)\n assert(self._isSgScriptedMesh, \"self is not an SgScriptedMesh\")\n _internal.sendMessage('sgscriptedmesh.setAttributeData', { obj=self._handle, type=type, data=data, offset=offset or 0 })\nend\n\n--- Writes all changed vertex data to the engine and updates the mesh\n---@param computeBounds boolean If set to true, bounding box and sphere will be recalculated.\nfunction SgScriptedMesh:update(computeBounds)\n if self._positions then\n self:setAttributeData(\"position\", self._positions)\n end\n if self._colors then\n self:setAttributeData(\"color\", self._colors)\n end\n if self._normals then\n self:setAttributeData(\"normal\", self._normals)\n end\n if self._uvs then\n self:setAttributeData(\"uv\", self._uvs)\n end\n\n if computeBounds then\n self:computeBoundingBox()\n self:computeBoundingSphere()\n end\nend\n\n--- Set a specific vertex in the mesh. Write all attributes at once.\n---@param offset number Vertex offset in the mesh.\n---@param pos table position of the vertex as array of 3 number values. Can be nil if you don't want to set it\n---@param uv table UV coords of the vertex as array of 2 number values. Can be nil if you don't want to set it\n---@param col table color of the vertex as array of 3 number values. Can be nil if you don't want to set it\n---@param normal table normal of the vertex as array of 3 number values. Can be nil if you don't want to set it\nfunction SgScriptedMesh:setVertex(offset, pos, uv, col, normal)\n local offs3 = (offset-1)*3\n local offs2 = (offset-1)*2\n\n if pos and self._positions then\n self._positions[offs3+1] = pos[1]\n self._positions[offs3+2] = pos[2]\n self._positions[offs3+3] = pos[3]\n end\n\n if col and self._colors then\n self._colors[offs3+1] = col[1]\n self._colors[offs3+2] = col[2]\n self._colors[offs3+3] = col[3]\n end\n\n if normal and self._normals then\n self._normals[offs3+1] = normal[1]\n self._normals[offs3+2] = normal[2]\n self._normals[offs3+3] = normal[3]\n end\n\n if uv and self._uvs then\n self._uvs[offs2+1] = uv[1]\n self._uvs[offs2+2] = uv[2]\n end\n\n return offset+1\nend\n\n--- Rewind the internal offset to the first vertex. This is used together with the addVertex function that\n--- increases the internal offset by 1 with each call.\nfunction SgScriptedMesh:rewind()\n self._offset = 1\nend\n\n--- Set the internal offset to a specific value\n---@param offset number offset of the vertex. First vertex has offset 1\nfunction SgScriptedMesh:setOffset(offset)\n self._offset = offset\nend\n\n--- Add a new vertex at the internal offset value. This function will increase the offset by one after writing the data\n---@param pos table position of the vertex as array of 3 number values. Can be nil if you don't want to set it\n---@param uv table UV coords of the vertex as array of 2 number values. Can be nil if you don't want to set it\n---@param col table color of the vertex as array of 3 number values. Can be nil if you don't want to set it\n---@param normal table normal of the vertex as array of 3 number values. Can be nil if you don't want to set it\nfunction SgScriptedMesh:addVertex(pos, uv, col, normals)\n self._offset = self:setVertex(self._offset, pos, uv, col, normals)\nend\n\n--- Compute the bounding box of the mesh after updating the vertex data. update() should be called before calling this.\nfunction SgScriptedMesh:computeBoundingBox()\n assert(self._isSgScriptedMesh, \"self is not an SgScriptedMesh\")\n _internal.sendMessage('sgscriptedmesh.computeBoundingBox', { obj=self._handle })\nend\n\n--- Compute the bounding sphere of the mesh after updating the vertex data. update() should be called before calling this.\nfunction SgScriptedMesh:computeBoundingSphere()\n assert(self._isSgScriptedMesh, \"self is not an SgScriptedMesh\")\n _internal.sendMessage('sgscriptedmesh.computeBoundingSphere', { obj=self._handle })\nend\n\n--- Compute the vertex tangents of the mesh after updating the vertex data. update() should be called before calling this.\nfunction SgScriptedMesh:computeTangents()\n assert(self._isSgScriptedMesh, \"self is not an SgScriptedMesh\")\n _internal.sendMessage('sgscriptedmesh.computeTangents', { obj=self._handle })\nend\n\n--- Compute the vertex normals of the mesh after updating the vertex data. update() should be called before calling this.\nfunction SgScriptedMesh:computeVertexNormals()\n assert(self._isSgScriptedMesh, \"self is not an SgScriptedMesh\")\n _internal.sendMessage('sgscriptedmesh.computeVertexNormals', { obj=self._handle })\nend\n\nreturn SgScriptedMesh\n";
131046
131046
 
131047
131047
  var lua_api_sceneobjects_sky = "\nlocal SceneObject = require('engine/sceneobject')\n\n------------------------------------------------------------------\n-- SgSky class\n------------------------------------------------------------------\n\n--- @class SgSky\nlocal SgSky = Class.new(SceneObject)\n\n--- @private\nfunction SgSky:__new(handle, nodeId)\n SceneObject.__new(self, handle, nodeId)\n self._type = \"SgSky\"\n self._isSgSky = true\nend\n\n--- Create a new sky object in the scenegraph.\n---@param options table A table of options to fill the parameters\n---@param parent SceneObject The parent scene object to add this to\n---@return Promise a promise which will resolve to the created object\n---@usage\n--- -- Options can be omitted. This example shows the defaults,\n--- -- only specify the ones you want different.\n--- SgSky.create({\n--- active = true,\n--- name = \"\",\n--- transform = Transform.new(),\n--- layers = {0},\n--- tags = {},\n--- receiveShadow = false,\n--- castShadow = false,\n--- size = 1000,\n--- turbidity = 10,\n--- rayleigh = 2,\n--- mieCoefficient = 0.005,\n--- mieDirectionalG = 0.8,\n--- inclination = 0.49\n--- azimuth = 0.25\n--- })\nfunction SgSky.create(self, options, parent)\n -- check if static function was called as method. if so, shift the parameters\n if not (type(self) == 'table' and type(self.new) == 'function') then\n parent, options = options, self\n end\n return SceneObject.create(\"SgSky\", options, parent)\nend\n\nreturn SgSky\n";
131048
131048
 
@@ -133363,7 +133363,7 @@
133363
133363
  }
133364
133364
  obj.setFieldValue(params.name, linkItem);
133365
133365
  }
133366
- translate(params) {
133366
+ setPosition(params) {
133367
133367
  const obj = this.runtime.getObject(params.obj);
133368
133368
  if (!obj)
133369
133369
  return;
@@ -133382,6 +133382,27 @@
133382
133382
  }
133383
133383
  }
133384
133384
  }
133385
+ translate(params) {
133386
+ const obj = this.runtime.getObject(params.obj);
133387
+ if (!obj)
133388
+ return;
133389
+ if (obj instanceof SgItem) {
133390
+ const transform = obj.getField("Transform");
133391
+ if (!transform)
133392
+ return;
133393
+ const current = transform.value;
133394
+ const newPosition = {
133395
+ x: current.position.x + params.x,
133396
+ y: current.position.y + params.y,
133397
+ z: current.position.z + params.z
133398
+ };
133399
+ transform.value = {
133400
+ position: newPosition,
133401
+ rotation: current.rotation,
133402
+ scale: current.scale
133403
+ };
133404
+ }
133405
+ }
133385
133406
  rotate(params) {
133386
133407
  const obj = this.runtime.getObject(params.obj);
133387
133408
  if (!obj)
package/dist/player.zip CHANGED
Binary file
@@ -23,6 +23,7 @@ export declare class RtSceneObject extends RtBase {
23
23
  setFieldEnumValue(params: RuntimeParams): void;
24
24
  getLinkItem(params: RuntimeParams): any;
25
25
  setLinkItem(params: RuntimeParams): void;
26
+ setPosition(params: RuntimeParams): void;
26
27
  translate(params: RuntimeParams): void;
27
28
  rotate(params: RuntimeParams): void;
28
29
  scale(params: RuntimeParams): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@luminocity/lemonate-engine",
3
- "version": "26.3.0",
3
+ "version": "26.3.1",
4
4
  "repository": "https://codeberg.org/Luminocity/lemonate-engine",
5
5
  "homepage": "https://lemonate.io",
6
6
  "description": "The Lemonate Engine is the game engine that powers lemonate.io",