{"id":15722575,"url":"https://github.com/astrochili/defold-operator","last_synced_at":"2025-08-31T14:34:24.360Z","repository":{"id":37584093,"uuid":"497610831","full_name":"astrochili/defold-operator","owner":"astrochili","description":"Camera movement controller extension for Defold","archived":false,"fork":false,"pushed_at":"2025-02-02T20:53:03.000Z","size":398,"stargazers_count":52,"open_issues_count":8,"forks_count":4,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-08-23T09:14:04.023Z","etag":null,"topics":["3d","camera","defold"],"latest_commit_sha":null,"homepage":"","language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/astrochili.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-05-29T14:13:59.000Z","updated_at":"2025-07-31T16:57:30.000Z","dependencies_parsed_at":"2024-10-24T16:50:51.899Z","dependency_job_id":"4bff7dea-2331-496f-b68d-b4d2d4e35b78","html_url":"https://github.com/astrochili/defold-operator","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/astrochili/defold-operator","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/astrochili%2Fdefold-operator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/astrochili%2Fdefold-operator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/astrochili%2Fdefold-operator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/astrochili%2Fdefold-operator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/astrochili","download_url":"https://codeload.github.com/astrochili/defold-operator/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/astrochili%2Fdefold-operator/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":272991813,"owners_count":25027498,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-31T02:00:09.071Z","response_time":79,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["3d","camera","defold"],"created_at":"2024-10-03T22:08:27.418Z","updated_at":"2025-08-31T14:34:24.328Z","avatar_url":"https://github.com/astrochili.png","language":"Lua","readme":"![logo](https://user-images.githubusercontent.com/4752473/173253156-043d31ac-fe48-43bb-b91a-1272feec80c2.jpg)\n\n# Defold Operator\n\n[![Release](https://img.shields.io/github/v/release/astrochili/defold-operator.svg?include_prereleases=\u0026sort=semver\u0026color=blue)](https://github.com/astrochili/defold-operator/releases) [![License](https://img.shields.io/badge/License-MIT-blue)](https://github.com/astrochili/defold-operator/blob/master/LICENSE)\n[![Website](https://img.shields.io/badge/website-gray.svg?\u0026logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxOCIgaGVpZ2h0PSIxNiIgZmlsbD0ibm9uZSIgdmlld0JveD0iMCAwIDE4IDE2Ij48Y2lyY2xlIGN4PSIzLjY2IiBjeT0iMTQuNzUiIHI9IjEuMjUiIGZpbGw9InVybCgjYSkiLz48Y2lyY2xlIGN4PSI4LjY2IiBjeT0iMTQuNzUiIHI9IjEuMjUiIGZpbGw9InVybCgjYikiLz48Y2lyY2xlIGN4PSIxMy42NSIgY3k9IjE0Ljc1IiByPSIxLjI1IiBmaWxsPSJ1cmwoI2MpIi8+PHBhdGggZmlsbD0idXJsKCNkKSIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNNy42MyAxLjQ4Yy41LS43IDEuNTUtLjcgMi4wNSAwbDYuMjIgOC44MWMuNTguODMtLjAxIDEuOTctMS4wMyAxLjk3SDIuNDRhMS4yNSAxLjI1IDAgMCAxLTEuMDItMS45N2w2LjIxLTguODFaIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48ZGVmcz48bGluZWFyR3JhZGllbnQgaWQ9ImEiIHgxPSIyLjQxIiB4Mj0iMi40MSIgeTE9IjEzLjUiIHkyPSIxNiIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIHN0b3AtY29sb3I9IiNGRDhENDIiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNGOTU0MUYiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iYiIgeDE9IjcuNDEiIHgyPSI3LjQxIiB5MT0iMTMuNSIgeTI9IjE2IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agc3RvcC1jb2xvcj0iI0ZEOEQ0MiIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI0Y5NTQxRiIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJjIiB4MT0iMTIuNCIgeDI9IjEyLjQiIHkxPSIxMy41IiB5Mj0iMTYiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBzdG9wLWNvbG9yPSIjRkQ4RDQyIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjRjk1NDFGIi8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImQiIHgxPSIuMDMiIHgyPSIuMDMiIHkxPSIuMDMiIHkyPSIxMi4yNiIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIHN0b3AtY29sb3I9IiNGRkU2NUUiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNGRkM4MzAiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48L3N2Zz4=)](https://astronachos.com/)\n[![Mastodon](https://img.shields.io/badge/mastodon-gray?\u0026logo=mastodon)](https://mastodon.gamedev.place/@astronachos)\n[![Twitter](https://img.shields.io/badge/twitter-gray?\u0026logo=twitter)](https://twitter.com/astronachos)\n[![Telegram](https://img.shields.io/badge/telegram-gray?\u0026logo=telegram)](https://t.me/astronachos)\n[![Buy me a coffee](https://img.shields.io/badge/buy_me_a_coffee-gray?\u0026logo=buy%20me%20a%20coffee)](https://buymeacoffee.com/astrochili)\n\n📼 Also in this series:\n- 👖 [Kinematic Walker](https://github.com/astrochili/defold-kinematic-walker)\n- 🏗️ [TrenchFold](https://github.com/astrochili/defold-trenchfold)\n- 🎄 [Illumination](https://github.com/astrochili/defold-illumination)\n- 🚧 [Blockout Textures](https://github.com/astrochili/blockout-textures)\n\n## Overview\n\nThis is a tweakable camera movement controller suitable for 3D games on the [Defold](https://defold.com). It can help you smoothly control your character’s camera or make cinematic motion paths using checkpoints.\n\nShowing your player the level by flying around and finish the flight by smooth attachment to the character — sounds like a classic use case.\n\nThis extension is not about rendering so you can use any render script that you want.\n\n🎮 [Play HTML5 demo](https://astronachos.com/defold/operator).\n\n💬 [Discuss on the forum](https://forum.defold.com/t/operator-camera-movement-controller-for-3d-games/71094/3).\n\n## Features\n\n- [x] Camera rotation and zooming by mouse controls.\n- [x] External manual control by messages.\n- [x] Following an object with avoiding obstacles.\n- [x] Smart alignment to the ground slope.\n- [x] Smooth movement along checkpoints using a bezier path.\n- [x] Switching between multiple cameras.\n- [ ] Request by [adding an issue or contribute](https://github.com/astrochili/defold-operator/issues).\n\n## Install\n\nAdd links to the zip-archive of the latest versions of [defold-operator](https://github.com/astrochili/defold-operator/releases) to your Defold project as [dependencies](http://www.defold.com/manuals/libraries/).\n\nIf you plan to lock the cursor pointer, add a link to the latest version of [defold-pointer-lock](https://github.com/indiesoftby/defold-pointer-lock).\n\n## Quick Start\n\nAdd `operator.collection` to your scene and configure its script properties in the editor.\n\n**Character Following**\n\n1. Post the [`follow_point`](#follow_point) message to follow the game object.\n2. Use mouse controls or post [`manual_control`](#manual_control) messages.\n\n**Cinematic Flight**\n\n1. Add `checkpoint.go` instances to your scene and configure their script properties in the editor.\n2. Post the [`follow_sequence`](#follow_sequence) message with an array of checkpoints to follow the sequence.\n\n## Troubleshooting\n\n### Cursor Locking\n\nOperator doesn't provide cursor locking functionality. To lock the cursor use the [window.set_mouse_lock()](https://defold.com/ref/beta/window/#window.set_mouse_lock:flag) method or look at the [defold-pointer-lock](https://github.com/indiesoftby/defold-pointer-lock) extension.\n\nTo get more control and avoid camera rotation jumps, disable the [internal control](#internal_control-1) when the cursor is not locked and enable it when the cursor is locked.\n\n### Jittering\n\nIf the operator is following a moving object with a **physical body**, you may notice a jittering effect.\n\nThe operator doesn't integrate into the hierarchy of the folowing object, so it requires to know the actual position of the object. For this purpose, make sure that the project settings correspond to the recommended settings for the game with 3D physics.\n\n```ini\n[physics]\ntype = 3D\nuse_fixed_timestep = 1\nmax_fixed_timesteps = 1\n\n[display]\nupdate_frequency = 60\n\n[engine]\nfixed_update_frequency = 60\n```\n\n## Operator Properties\n\n### is_active\n\nActivates the operator during initialization.\n\n### internal_control\n\nEnables internal control with a mouse input during initialization.\n\n### camera_fov\n\nVertical field of view in degrees. The value passed to the camera component during initialization.\n\n### camera_near_z\n\nNear z position to render. The value passed to the camera component during initialization.\n\n### camera_far_z\n\nFar z position to render. The value passed to the camera component during initialization.\n\n### look_horizontal\n\nEnables horizontal camera rotation.\n\n### look_vertical\n\nEnables vertical camera rotation.\n\n### inverted_horizontal\n\nInverts horizontal camera rotation.\n\n### inverted_vertical\n\nInverts vertical camera rotation.\n\n### minimum\n\nA vector representing lower bounds of the rotation and zoom distance limits.\n\n- `x` - minimum vertical rotation in degrees.\n- `y` - minimum horizontal rotation in degrees.\n- `z` - minimum zoom distance in units.\n\n### maximum\n\nA vector representing upper bounds of the rotation and zoom distance limits.\n\n- `x` - maximum vertical rotation in degrees.\n- `y` - maximum horizontal rotation in degrees.\n- `z` - maximum zoom distance in units.\n\n### sensitivity\n\nInput sensitivity for the camera rotation. The value is an angle in degrees to rotate by 1 input value.\n\n- `0.0` - no reaction.\n- `0.7` - degrees = 70% of input.\n\n### smoothness\n\nHow much time in seconds needed to turn the current look to the desired look after input.\n\n- `0` - look immediately and sharp.\n- `0.1` - look smoothly.\n- `1.2` - look like a sloth.\n\nThe first-person look usually uses sharper rotation than the third-person look.\n\n### zoom\n\nAn initial distance between the camera and the operator anchor point.\n\n- `0` - use for the first-person look to rotate the camera inside the object.\n- `n` - use for the third-person look to rotate the camera around the object.\n\n### zoom_step\n\nThe camera distance change in units per a zoom input value.\n\n### zoom_smoothness\n\nHow much time in seconds needed to change the current zoom to the desired zoom after input.\n\n- `0` - zoom immediately and sharp.\n- `0.3` - zoom smoothly (recommended).\n- `1.2` - zoom like a sloth.\n\n### collision_check\n\nChecks a path from the camera to the operator anchor for the collisions and moves it closer if necessary.\n\n### collision_distance\n\nMinimum distance between the camera and the collision surface if `collision_check` is turned on.\n\n### ground_align_factor\n\nHow much align the camera to the ground normal.\n\n- `0` - no alignment.\n- `0.5` - half alignment (recommended).\n- `1.0` - full alignment (not recommended).\n\n### ground_align_smoothness\n\nHow much time in seconds needed to change the current ground alignment to the desired ground alignment after the ground normal update.\n\n- `0` - align immediately and sharp (not recommended).\n- `0.5` - align smoothly.\n- `2.3` - align like a sloth.\n\nDon't use low values to avoid sharp shakes due to the ground geometry.\n\n### follow_object_rotation\n\nFollow the rotation while following the object. Useful when you want to control the object rather than the camera. For example, a car.\n\n## Checkpoint Properties\n\nThere is two ways to create checkpoint:\n\n- Place `checkpoint.go` instance to your scene and configure its script properties.\n- Manually create table with parameters in the code.\n\nA point table have additional parameters `position` and `look`, which are the equivalents of `checkpoint.go` position, rotation.\n\n- `object` (optional, default is `nil`) - move the operator in the local object coordinates. Use this to attach it to something moving, like a character.\n- `position` (optional, default is `vmath.vector()`) - the operator anchor position.\n- `look` (optional, default is the previous point `look`) - the camera rotation.\n- `zoom` (optional, default is the previous point `zoom`) - the camera zoom distance.\n- `speed` (optional, default is the previous point `speed`) - the speed at which the operator should move at this point. Measured in units per second.\n- `inout` (optional, default is `false`) - use in-out easing on speed changing along tha path to this point. The speed between points changes linearly so in-out is useful when moving from `0` to `0` speed.\n- `bezier` (optional, default is `true`) - use bezier smoothness at this point.\n\n```lua\nlocal point = {\n    -- global space\n    object = nil\n\n    -- global position\n    position = vmath.vector3(14, 2, 7)\n\n    -- look down to the left.\n    look = vmath.vector3(-45, 90, 0)\n\n    -- 3 units from the camera to the anchor\n    zoom = 3\n\n    -- 5 units/sec speed at this point\n    speed = 5\n\n    -- speed linear changing to this point\n    inout = false\n\n    -- use bezier smooth at this point\n    bezier = true\n}\n```\n\n## Activation Messages\n\n### activate\n\nActivates the operator. Acquires input and camera focuses, posts `use_camera_projection` to the render. Automatically deactivates a previous operator.\n\n```lua\nmsg.post(operator_url, hash 'activate')\n```\n\n### deactivate\n\nDeactivates the operator. Releases input and camera focuses.\n\n```lua\nmsg.post(operator_url, hash 'deactivate')\n```\n\n## Follow Messages\n\nThe operator can follow the object or follow checkpoints to make kind of cinematic camera movement.\n\nLocal space coordinates and rotations are respected so you can switch between two moving objects without problems.\n\n### follow_sequence\n\nFollow the sequence of points.\n\n```lua\nlocal sequence = {\n    -- manually created point\n    checkpoint_1,\n\n    -- checkpoint instance url\n    msg.url('/checkpoint_2'),\n\n    -- follow the object\n    object_point\n}\n\nmsg.post(operator_url, hash 'follow_sequence', sequence)\n```\n\nSender will become `motion_observer` during the motion to receivce [motion messages](#motion_point).\n\n### follow_point\n\nThis is equilent to the `follow_sequence` message with one `point`.\n\nIf you want just to attach the camera to the character — that's your case.\n\n```lua\n-- Create a point\nlocal point = {\n    object = character_url,\n    position = vmath.vector3(0, 1.5, 0)\n}\n\n-- Follow the point\nmsg.post(operator_url, hash 'follow_point', point)\n```\n\n### unfollow\n\nStop following any sequence or object.\n\n```lua\nmsg.post(operator_url, hash 'unfollow')\n```\n\n## Ground Alignment Messages\n\n### ground_normal\n\nUpdate the ground normal to proper camera alignment. This is usually done with raycasting down from the followed object.\n\n```lua\n-- raycast from the object to the ground\nlocal ground = physics.raycast(position, position + vmath.vector3(0, -1, 0), { hash 'default' })\n\n-- nil will be interpreted as vmath.vector3(0, 1, 0).\nlocal normal = ground and ground.normal or nil\n\nmsg.post(operator_url, hash 'ground_normal', { normal = normal })\n```\n\n## Control Messages\n\n### internal_control\n\nEnables or disables internal control with a mouse input.\n\n```lua\nmsg.post(operator_url, hash 'internal_control', { is_inabled = true })\n```\n\n### manual_control\n\nManually control the look and zoom by input values. This can be useful on devices without a mouse or if you prefer to control everything by yourself.\n\n```lua\nlocal input = {\n    horizontal = dx,\n    vertical = dy,\n    zoom = dz\n}\n\nmsg.post(operator_url, hash 'manual_control', input)\n```\n\nYou can reference to default internal control values:\n-  Mouse moving actions send `action.dx` and `action.dy` values.\n-  Mouse wheel actions send `dz` as `-1` or `1` values.\n\n## Outgoing Messages\n\n### operator_attached\n\n- `operator` - the `url` of the operator.\n\nSends to the followed object when the operator attached.\n\n### operator_detached\n\n- `operator` - the `url` of the operator.\n\nSends to the followed object when the operator detached.\n\n### motion_point\n\n- `object` - the `url` of the object if exists.\n- `checkpoint`- the `url` of the checkpoint if exists.\n\nSends to `motion_observer` when the operator has reached the checkpoint.\n\n### motion_finished\n\n- `object` - the `url` of the object if exists.\n- `checkpoint`- the `url` of the checkpoint if exists.\n\nSends to `motion_observer` when the current operator motion has finished.\n\n### motion_interrupted\n\nSends to `motion_observer` when the current operator motion has interrupted by any other commands.\n\n## Operator Module\n\nThe operator module gives you access to additional shared preferences. Add the following requirement to use it.\n\n```lua\nlocal operator = require 'operator.operator'\n```\n\n### operator.get_active_operator()\n\nReturns the current active operator `url`.\n\n```lua\nlocal active_operator_url = operator.get_active_operator()\n```\n\n### operator.set_debug(is_debug)\n\nEnables camera and checkpoint meshes, active bezier drawing.\n\n```lua\noperator.set_debug(true)\n```\n\n### operator.is_debug()\n\nReturns current debug mode state as `bool`.\n\n```lua\nlocal is_debug = operator.is_debug()\n```\n\n### operator.camera_collisions_groups\n\nCollision groups that are tested by raycast from the operator anchor to the camera to avoid obstacles.\n\n```lua\noperator.camera_collisions_groups = { hash 'default' }\n```\n\n### operator.flight_bezier_samples_count\n\nCount of bezier segments. Used by uniform position calculation. `16` is usually enough, but `32` is more beautiful.\n\n```lua\noperator.flight_bezier_samples_count = 32\n```\n\n### operator.flight_look_easing\n\nEasing function to update the camera rotation between motion points.\n\n```lua\n-- set a ready to use easing function\noperator.flight_look_easing = operator.EASING_INOUT_QUAD\n\n-- or use your own\nlocal easing_linear = function(x) return x end\noperator.flight_look_easing = easing_linear\n```\n\nIncluded easing functions:\n\n- `operator.EASING_INOUT_SINE`\n- `operator.EASING_INOUT_CUBIC`\n- `operator.EASING_INOUT_QUINT`\n- `operator.EASING_INOUT_CIRC`\n- `operator.EASING_INOUT_QUAD`\n- `operator.EASING_INOUT_EXPO`\n\n## Bezier Module\n\nThe `bezier.lua` module is used to move the camera, but you can also use it for your own purposes. It supports vector and integer points.\n\nAdd the following requirement to use it.\n\n```lua\nlocal bezier = require 'operator.bezier'\n```\n\n### bezier.new(points, samples_count, length_func, lerp_func)\n\n- `points` - an array of vector or integer points. 3 points for quadratic bezier or 4 points for cubic bezier.\n- `samples_count` (optional, default is `32`) - count of bezier segments for uniform position calculation.\n- `length_func` (optional, default function supports vectors and integers) - length function to override.\n- `lerp_func` (optional, default function supports vectors and integers) - lerp function to override.\n\nReturns a bezier instance with pre-calculated data.\n\n```lua\n-- quadratic bezier with default count of samples\nlocal points = { p1, p2, p3 }\nlocal bezier_path = bezier.new(points)\n\n-- cubic bezier with 16 samples\nlocal points = { p1, p2, p3, p4 }\nlocal bezier_path = bezier.new(points, 16)\n```\n\n### bezier:position(time)\n\n- `time` - a float value from 0 to 1 representing progress.\n\nReturns a classic position along the bezier.\n\n### bezier:uniform_position(time)\n\n- `time` - a float value from 0 to 1 representing progress.\n\nReturns an uniform position along the bezier. The accuracy depends of `samples_count`.\n\n## Credits\n\n- [Pointer Lock](https://github.com/indiesoftby/defold-pointer-lock) by Artsiom Trubchyk ([@aglitchman](https://github.com/aglitchman)) used in the demo.","funding_links":["https://buymeacoffee.com/astrochili"],"categories":["Libraries"],"sub_categories":["Programming Language"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fastrochili%2Fdefold-operator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fastrochili%2Fdefold-operator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fastrochili%2Fdefold-operator/lists"}