{"id":15722399,"url":"https://github.com/defold/tutorial-movement","last_synced_at":"2026-03-01T21:39:12.699Z","repository":{"id":146435541,"uuid":"131978274","full_name":"defold/tutorial-movement","owner":"defold","description":null,"archived":false,"fork":false,"pushed_at":"2025-08-15T13:03:02.000Z","size":735,"stargazers_count":22,"open_issues_count":0,"forks_count":7,"subscribers_count":16,"default_branch":"master","last_synced_at":"2025-10-20T06:52:05.672Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/defold.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2018-05-03T10:16:30.000Z","updated_at":"2025-08-15T13:03:06.000Z","dependencies_parsed_at":null,"dependency_job_id":"df909595-2828-4eeb-9f50-8760087b9438","html_url":"https://github.com/defold/tutorial-movement","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/defold/tutorial-movement","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/defold%2Ftutorial-movement","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/defold%2Ftutorial-movement/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/defold%2Ftutorial-movement/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/defold%2Ftutorial-movement/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/defold","download_url":"https://codeload.github.com/defold/tutorial-movement/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/defold%2Ftutorial-movement/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29984731,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-01T21:06:37.093Z","status":"ssl_error","status_checked_at":"2026-03-01T21:05:45.052Z","response_time":124,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":[],"created_at":"2024-10-03T22:07:21.053Z","updated_at":"2026-03-01T21:39:12.666Z","avatar_url":"https://github.com/defold.png","language":"Lua","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Movement tutorial\n\nThis is a tutorial for beginners. It walks you through the steps of creating a player controlled space ship that moves in a natural manner when you give keyboard input. After having completed this tutorial you will know the answers to the following questions:\n\n* What are vectors?\n* How can you use vectors to represent positions, velocity and acceleration?\n* How do you use this to create a game embryo that you can experiment with and develop further?\n\nIt is assumed that you have basic understanding of physics concepts such as velocity and acceleration. You will also need some basic understanding of Lua programming.\n\nThis project is prepared in advance for you so there is no setup to bother about. [Run the game](defold://project.build) to get an overview of what you have to work with:\n\n- It include graphics: an animated spaceship and a background.\n- Input is set up for arrow keys and mouse clicks.\n- There is a \"spaceship\" game object that has a script attached to it.\n- The script has code in place to react to player input. Initially, it only prints messages to the console as a reaction to input.\n\nWith the game running, try pressing the arrow buttons, or click in the game window, then check the editor console for input results. Notice that different text is printed in the console depending on which button you pressed:\n\n\u003cimg src=\"doc/input.png\" srcset=\"doc/input@2x.png 2x\"\u003e\n\n## Making the spaceship move\n\nBefore digging into any details, let's first make a simple experiment and give the spaceship motion.\n\nOpen [\"spaceship.script\"](defold://open?path=/main/spaceship.script) and scroll down to the beginning of the function `on_input()` where you find this code:\n\n```lua\nfunction on_input(self, action_id, action)\n    if action_id == hash(\"up\") then\n        print(\"UP!\")\n    elseif...\n```\n\nRemove the line with the statement `print(\"UP!\")` and edit the code so the beginning of the function instead looks like this:\n\n```lua\nfunction on_input(self, action_id, action)\n    if action_id == hash(\"up\") then\n        local p = go.get_position()\n        p.y = p.y + 1\n        go.set_position(p)\n    elseif...\n```\n\n[Run the game](defold://project.build) again and press the \u003ckbd\u003eup\u003c/kbd\u003e arrow key and see how the spaceship moves up. The code is very simple, but let's look at it line by line to get a proper idea of what's going on:\n\n```lua\nif action_id == hash(\"up\") then\n```\n\nThe input bindings that are set up in the project (in the file [\"/input/game.input_binding\"](defold://open?path=/input/game.input_binding)) binds each of the arrow keys to action named \"up\", \"down\", \"left\" and \"right\". The game runs at 60 frames per second and each frame you push \u003ckbd\u003eup\u003c/kbd\u003e, the [hashed](https://en.wikipedia.org/wiki/Hash_function) \"up\" action name is sent to the `on_input()` function. Holding the button down will thus make the code between the `if` statement and the first `elseif` execute 60 times each second.\n\n```lua\nlocal p = go.get_position()\n```\n\nThe function `go.get_position()` gets the position of a game object. Since the function is called without any arguments, the position of the *current* game object is returned. This code belongs to the spaceship game object so the position of the spaceship is returned.\n\nThe position is assigned to a local variable called `p` so it is possible to manipulate it. The position object is a `vector3`, which is a *vector* that holds three values.\n\n```lua\np.y = p.y + 1\n```\n\nThe `vector3` object in `p` describes a point in 3D space, consisting of an X coordinate, an Y coordinate and a Z coordinate. Since pressing the \u003ckbd\u003eup\u003c/kbd\u003e button should move the ship in the positive direction of the Y axis, the `y` component of the position is increased by 1.\n\n```lua\ngo.set_position(p)\n```\n\nFinally, the new changed position value is written back to the current game object.\n\nBefore moving on, try changing the value added to `p.y` from 1 to 5 and [run the game again](defold://project.build). Notice how the ship now moves much faster.\n\nFinally, add a line below `go.set_position(p)` to print the value of `p`:\n\n```lua\nfunction on_input(self, action_id, action)\n    if action_id == hash(\"up\") then\n        local p = go.get_position()\n        p.y = p.y + 5\n        go.set_position(p)\n        print(p)\n    elseif...\n```\n\n[Run the game again](defold://project.build) and see how the engine print the value of the position vector each frame. Notice that the second value of the vector changes as the spaceship moves:\n\n```text\n...\nDEBUG:SCRIPT: vmath.vector3(640, 460, 0)\nDEBUG:SCRIPT: vmath.vector3(640, 465, 0)\nDEBUG:SCRIPT: vmath.vector3(640, 470, 0)\nDEBUG:SCRIPT: vmath.vector3(640, 475, 0)\n...\n```\n\n## Vectors\n\nA vector is a mathematical entity that has a _direction_ and a _magnitude_ (length). A vector describes a specific point in a vector space. In practice, a vector consists of a set of numbers that give the coordinates to the point. In a two dimensional space (a plane), two numbers are necessary to describe vectors: one value for the X axis and one for the Y axis:\n\n\u003cimg src=\"doc/2d-vector.png\" srcset=\"doc/2d-vector@2x.png 2x\"\u003e\n\nIn a three dimensional space, you need three numbers: one for the X axis, one for the Y axis and one for the Z axis:\n\n\u003cimg src=\"doc/3d-vector.png\" srcset=\"doc/3d-vector@2x.png 2x\"\u003e\n\nThe magnitude, or length, of a vector *v* is calculated using the Pythagorean theorum:\n\n\u003cimg src=\"doc/magnitude.png\" srcset=\"doc/magnitude@2x.png 2x\"\u003e\n\nA vector with magnitude 1 is called a *normalized vector*.\n\nEven though Defold has a toolset tailored for 2D games, the engine is truly a 3D engine. All game objects and components are positioned in 3D space with positions expressed as `vector3` objects. When you view your game in 2D, the X and Y value determine the position of an object along the \"width\" and \"height\" axis, and the Z position determines the position along the \"depth\" axis. The Z position allows you to control the visibility of overlapping objects: a sprite with a Z value of 1 will appear in front of a sprite at Z position 0. By default, Defold uses a coordinate system allowing Z values between -1 and 1:\n\n\u003cimg src=\"doc/z-order.png\" srcset=\"doc/z-order@2x.png 2x\"\u003e\n\nThe Defold Lua library [`vmath`](https://defold.com/ref/vmath) contains functions to create and manipulate [`vector3`](https://defold.com/ref/vmath/#vmath.vector3) objects:\n\n```lua\n-- create a new vector3 at X position 100 and Y position 350.\nlocal position = vmath.vector3(100, 350, 0)\n\n-- set the position of game object \"player\" to the new vector.\ngo.set_position(position, \"player\")\n```\n\nVectors in higher dimensions than 3 are also possible. Defold uses `vector4` objects with four components to encode colors. The first three components give the amount of red, green, and blue, and the last component give the amount of translucency, also called \"alpha\".\n\nIn everyday life you are used to do arithmetic with scalar values, real numbers that describe points on the number line. We use scalars to mean many different things. The number 12 could mean a number of meters, kilograms, pounds, seconds, meters per second, volts or dollars. The same is true for vectors. You have already seen how vectors can be used to describe a position of an object. They are also very good for describing an object's motion through space.\n\nTo describe motion on a computer screen (a 2D plane) you need two values: The speed along the X axis and the speed along the Y axis. You can very well use two separate scalar values and add the speed values to the X and Y positions separately:\n\n```lua\nposition_x = position_x + speed_x * elapsed_seconds\nposition_y = position_y + speed_y * elapsed_seconds\n```\n\nThis is roughly what you did when you previously made the spaceship move upwards, and there is nothing wrong calculating motion like this. Vectors, however, allow you to express motion clearer and more concise. Since a vector describe a *direction* and a *magnitude* they are an intuitive fit for motion: the direction of the vector equals the direction of motion, and the magnitude describes the amount of motion:\n\n```lua\nposition = position + speed * elapsed_seconds\n```\n\nAs long as the `position` and `speed` values are expressed as vectors in the same space you can add and subtract them, and scale them by multiplying them with scalar values. These operations are a central part of *vector algebra*.\n\n## Vector algebra\n\nVector algebra defines mathematical operations on vectors. Beginning with the simplest, negation, addition and subtraction.\n\nNegation\n: Negating a vector *v*, denoted by -*v*, negates each component of the vector. This makes a vector that points in the opposite direction of the original vector, with the same magnitude:\n\n  \u003cimg src=\"doc/vector-neg.png\" srcset=\"doc/vector-neg@2x.png 2x\"\u003e\n\nAddition\n: Adding vector *u* to vector *v*, denoted by *u* + *v*, adds each component of *u* to *v*. The result is a new vector:\n\n  \u003cimg src=\"doc/vector-add-cs.png\" srcset=\"doc/vector-add-cs@2x.png 2x\"\u003e\n\n  Vectors are often drawn displaced from the coordinate system which brings clarity to the operations:\n\n  \u003cimg src=\"doc/vector-add.png\" srcset=\"doc/vector-add@2x.png 2x\"\u003e\n\nSubtraction\n: Subtracting vector *v* from vector *u*, denoted by *u* - *v*, is equal to adding the negation of *v* to *u*. So *u* - *v* = *u* + (-*v*):\n\n  \u003cimg src=\"doc/vector-sub.png\" srcset=\"doc/vector-sub@2x.png 2x\"\u003e\n\nMultiplication with scalar\n: Multiplying a vector *v* with a real number *r* produces a new vector with the magnitude scaled: the vector is streched out by a factor *r*. Multiplying with a negative *r* flips the orientation 180 degrees:\n  \n  \u003cimg src=\"doc/vector-mul.png\" srcset=\"doc/vector-mul@2x.png 2x\"\u003e\n\nThese were the basic operations on vectors that you will use all the time. In addition, there are two special operations that come in handy if you, for instance, want to check if two vectors are parallel or at right angles of each other:\n\nDot product\n: The dot product of two vectors *u* and *v*, denoted by *u ∙ v*, is a scalar value. It is defined as:\n\n  \u003cimg src=\"doc/dot.png\" srcset=\"doc/dot@2x.png 2x\"\u003e\n\n  - *‖u‖* is the magnitude of vector *u*.\n  - *‖v‖* is the magnitude of vector *v*.\n  - *θ* is the angle between the vectors.\n\n  \u003cimg src=\"doc/vector-dot.png\" srcset=\"doc/vector-dot@2x.png 2x\"\u003e\n\n  If the vectors are orthogonal (the angle between them is 90 degrees), then the dot product is zero.\n\nCross product\n: The cross product of two vectors *u* and *v*, denoted by *u* × *v*, is a vector that is perpendicular to both *u* and *v*:\n\n  \u003cimg src=\"doc/vector-cross.png\" srcset=\"doc/vector-cross@2x.png 2x\"\u003e\n\n  The resulting vector is a zero vector if:\n\n  - Either one or both of the input vectors are zero vectors, (*u* = 0 or *v* = 0)\n  - The two input vectors are parallel (θ = 0°)\n  - The two input vectors are antiparallel (θ = 180°)\n\n## Creating movement with vectors\n\nUsing vector algebra, you can now rewrite the spaceship's movement in a straightforward way.\n\nOpen [\"spaceship.script\"](defold://open?path=/main/spaceship.script) and modify the `init()`, `update()` and `on_input()` functions:\n\n```lua\nfunction init(self)\n    msg.post(\".\", \"acquire_input_focus\")\n    self.input = vmath.vector3()                -- [1]\nend\n```\n1. Create a new zero `vector3` for storing the input direction. It is placed it in the current script instance (`self`) so it can be used throughout the lifetime of the spaceship game object.\n\n```lua\nfunction update(self, dt)\n    local movement = self.input * 3             -- [1]\n    local p = go.get_position()                 -- [2]\n    go.set_position(p + movement)               -- [3]\n    self.input = vmath.vector3()                -- [4]\nend\n```\n1. Calculate a movement vector based on the player's input vector.\n2. Retrieve the position of the current game object (the spaceship). The position is a `vector3`.\n2. Set the position of the current game object to `p` plus the movement vector.\n3. Zero the input vector. The `on_input()` function is called each frame before `update()` and has the responsibility to set the input vector.\n\n```lua\nfunction on_input(self, action_id, action)\n    if action_id == hash(\"up\") then\n        self.input.y = 1                     -- [1]\n    elseif action_id == hash(\"down\") then\n        self.input.y = -1                    -- [1]\n    elseif action_id == hash(\"left\") then\n        self.input.x = -1                    -- [1]\n    elseif action_id == hash(\"right\") then\n        self.input.x = 1                     -- [1]\n    elseif action_id == hash(\"click\") and action.pressed then\n        print(\"CLICK!\")\n    end\nend\n```\n1. Set the x or y component of the input vector depending on player input. If the player presses `up` and `left` at the same time, the function will be called twice and both components are set, resulting in a diagonal input direction.\n\nThere are two issues with this code:\n\nFirst, the input vector has length 1 if you move vertically or horizontally, but diagonally the length is 1.4142 (square root of 2) so diagonal movement is faster. You probably don't want that.\n\nSecond, the units of velocity is expressed in pixels/frame, no matter the frame length. It's set to 3 pixels of movement each frame (or about 4.2 diagonally). To make the ship go faster, change the 3 to a higher value. If you want it to go slower, decrease the value. It would be better if you could express velocity in pixels/second.\n\nThe first problem is easy to fix, just normalize the input vector so the input length is always 1:\n\n```lua\nfunction update(self, dt)\n    if vmath.length_sqr(self.input) \u003e 1 then        -- [1]\n        self.input = vmath.normalize(self.input)\n    end\n    local movement = self.input * 3\n    local p = go.get_position()\n    go.set_position(p + movement)\n    self.input = vmath.vector3()\nend\n```\n1. If the squared length of the input vector is larger than 1, normalize the vector so it is of magnitude 1. Compare against square length since it's faster than comparing against length.\n\nThe second problem requires the use of a time step value.\n\n## Time step\n\nEach frame the Defold engine calls the `update()` function of each script. A Defold game usually runs at 60 frames per second, so each frame is 0.016666 seconds long. That is the time elapsed between each call to `update()`. A velocity vector with a magnitude of 3 will then represent a speed of 3 * 60 = 180 pixels per second (with the regular render script), *as long as there really are 60 frames each second*. What would happen if there, for whatever reason, is a hitch in the framerate? With the current code movement will be uneven and unpredictable.\n\nWorking with pixels per second allows you to use variable framerate properly, you would also be able to measure your game with a stopwatch and reason about distances and timings in a better way.\n\nDefold provides a time step argument value to the `update()` function. The argument is usually called `dt` (for \"delta time\") and its value is the number of *seconds* that elapsed since the last frame. If you scale velocity against `dt` you will get proper units:\n\n```lua\nfunction update(self, dt)\n    if vmath.length_sqr(self.input) \u003e 1 then\n        self.input = vmath.normalize(self.input)\n    end\n    local movement = self.input * 150 * dt              -- [1]  \n    local p = go.get_position()\n    go.set_position(p + movement)\n    self.input = vmath.vector3()\nend\n```\n1. The velocity is now 150 pixels per second. The screen is 1280 pixels wide so it should take the ship 8.53 seconds to fly across. You can check that with a stopwatch.\n\n[Run the game again](defold://project.build) and try the movement code. At this stage it works but it's stiff and not very dynamic. To give a sense of weight to the spaceship a good way is to have the player's input control movement by altering acceleration instead of the velocity.\n\n## Acceleration\n\nIn the above code, velocity was set to a constant value, meaning that the resulting movement, or translation, of the velocity acting over the time step (`dt`) could be calculated by multiplying the velocity with the time step: *movement* = *velocity* * *dt*, or the orange area in the following diagram:\n\n\u003cimg src=\"doc/integration-constant.png\" srcset=\"doc/integration-constant@2x.png 2x\"\u003e\n\nAcceleration defines how fast something changes speed and direction. The acceleration is acting over the frame time step (`dt`) and then added to the velocity. The velocity acts over the frame and the resulting movement is added to the position. Since velocity changes over time the movement has to be calculated as the area under a curve. In mathematics, this is called [integration over time](http://en.wikipedia.org/wiki/Integral).\n\n\u003cimg src=\"doc/integration.png\" srcset=\"doc/integration@2x.png 2x\"\u003e\n\nWith a small enough time step a good geometric approximation of the area can be calculated by assuming that the acceleration acting between *v0* and *v1* is constant, meaning that the velocity changes linearly between the two points. By that assumption *v1* can be calculated as *v0* + *acceleration* * *dt* and the resulting movement becomes:\n\n\u003cimg src=\"doc/movement.png\" srcset=\"doc/movement@2x.png 2x\"\u003e\n\nYou can now write the final code for `init()` and `update()` (the code for `on_input()` is kept as is):\n\n```lua\nfunction init(self)\n    msg.post(\".\", \"acquire_input_focus\")\n    self.velocity = vmath.vector3()             -- [1]\n    self.input = vmath.vector3()\nend\n\nfunction update(self, dt)\n    if vmath.length_sqr(self.input) \u003e 1 then\n        self.input = vmath.normalize(self.input)\n    end\n    \n    local acceleration = self.input * 200       -- [2]\n    \n    local dv = acceleration * dt                -- [3]\n    local v0 = self.velocity                    -- [4]\n    local v1 = self.velocity + dv               -- [5]\n    local movement = (v0 + v1) * dt * 0.5       -- [6]\n\n    local p = go.get_position()\n    go.set_position(p + movement)               -- [7]\n\n    self.velocity = v1                          -- [8]\n    self.input = vmath.vector3()\nend\n```\n1. Create a vector for storing velocity over time.\n2. Acceleration is set to 200 pixels per second in the direction of player input.\n3. Calculate change of velocity this time step.\n4. v0 is set to the velocity from the previous time step.\n5. v1 is v0 plus the change of velocity this time step.\n6. Calculate how much the ship shall move this time step.\n7. Apply the change in position.\n8. Store the v1 velocity so it can be used in next time step.\n\nNow it's time to [take your new heavy spaceship for a spin](defold://project.build).\n\nCongratulations! You have completed the tutorial. But don't stop here. to continue experimenting with the code.\n\nHere are some ideas what you can try:\n\n1. Put a cap on the velocity.\n2. Make the spaceship bounce off the edges of the screen.\n3. Allow mouse clicks to dictate the input direction.\n\nCheck out [the documentation pages](https://defold.com/learn) for more examples, tutorials, manuals and API docs.\n\nIf you run into trouble, help is available in [our forum](https://forum.defold.com).\n\nHappy Defolding!\n\n----\n\nThis project is released under the Creative Commons CC0 1.0 Universal license.\n\nYou’re free to use these assets in any project, personal or commercial. There’s no need to ask permission before using these. Giving attribution is not required, but is greatly appreciated!\n[Full license text](https://creativecommons.org/publicdomain/zero/1.0)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdefold%2Ftutorial-movement","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdefold%2Ftutorial-movement","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdefold%2Ftutorial-movement/lists"}