Skip to content

Instantly share code, notes, and snippets.

@AGulev
Last active March 22, 2023 17:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AGulev/484dea838528f86ff93ae530e333f76a to your computer and use it in GitHub Desktop.
Save AGulev/484dea838528f86ff93ae530e333f76a to your computer and use it in GitHub Desktop.
Here is a simple manual about how to make a simple flappy bird-like game on Defold with code examples:
* A flappy bird-like game is a game where you have to control a bird that flies through gaps between pipes. The bird falls down because of gravity, but you can make it flap its wings and go up by tapping the screen or pressing a key. The game ends when the bird hits a pipe or the ground. The goal is to fly as far as possible and get a high score.
* To make a flappy bird-like game on Defold, you need to do these steps:
* Create a new project in Defold and name it “Flappy Bird”.
* Create a new collection file and name it “main.collection”. This will be the main scene of your game.
* Create a new game object file and name it “bird.go”. This will be the bird that the player controls.
* Add a sprite component to the bird game object and set the image to a bird image of your choice. You can use the built-in “hero.atlas” or import your own image.
* Add a script component to the bird game object and name it “bird.script”. This will be the code that controls the bird’s movement and collision.
* Add a collision object component to the bird game object and set the type to “Kinematic”. This will make the bird move according to the code and detect collisions with other objects. Set the shape to a box that fits the bird sprite and set the group to “bird” and the mask to “pipe”.
* Create a new game object file and name it “pipe.go”. This will be the pipe that the bird has to avoid.
* Add a sprite component to the pipe game object and set the image to a pipe image of your choice. You can use the built-in “pipe.atlas” or import your own image.
* Add a collision object component to the pipe game object and set the type to “Static”. This will make the pipe stay in place and collide with other objects. Set the shape to a box that fits the pipe sprite and set the group to “pipe” and the mask to “bird”.
* Create a new game object file and name it “spawner.go”. This will be the object that spawns the pipes at regular intervals.
* Add a script component to the spawner game object and name it “spawner.script”. This will be the code that creates and destroys the pipes and keeps track of the score.
* Add the bird, the spawner, and a camera game object to the main collection. Set the camera’s projection to “Orthographic” and adjust the size to fit the screen. Set the bird’s position to (0, 0, 0) and the spawner’s position to (200, 0, 0).
* Write the code for the bird.script. Here is an example:
`bird.script`:
```
-- The gravity constant that pulls the bird down
local GRAVITY = -1000
-- The flap impulse that pushes the bird up
local FLAP = 400
-- The maximum speed that the bird can reach
local MAX_SPEED = 500
-- The minimum and maximum angles that the bird can rotate
local MIN_ANGLE = -90
local MAX_ANGLE = 45
-- The angular speed that the bird rotates
local ANGULAR_SPEED = 5
-- The initial velocity of the bird
local velocity = vmath.vector3(0, 0, 0)
-- The initial angle of the bird
local angle = 0
-- The initial state of the bird
local state = "idle"
-- The input action that makes the bird flap
local FLAP_ACTION = hash("flap")
-- The message that indicates the bird has collided with something
local COLLISION_MESSAGE = hash("collision_response")
-- The message that indicates the game has started
local START_MESSAGE = hash("start")
-- The message that indicates the game has ended
local END_MESSAGE = hash("end")
-- The message that indicates the bird has passed a pipe
local PASS_MESSAGE = hash("pass")
-- The message that indicates the bird has scored a point
local SCORE_MESSAGE = hash("score")
-- The message that indicates the bird has died
local DIE_MESSAGE = hash("die")
-- The message that indicates the bird has reset
local RESET_MESSAGE = hash("reset")
-- The function that updates the bird's position and angle
local function update(dt)
-- Apply gravity to the velocity
velocity.y = velocity.y + GRAVITY * dt
-- Clamp the velocity to the maximum speed
velocity.y = math.min(velocity.y, MAX_SPEED)
-- Move the bird according to the velocity
local position = go.get_position()
position = position + velocity * dt
go.set_position(position)
-- Calculate the angle based on the velocity
angle = vmath.lerp(ANGULAR_SPEED * dt, angle, math.deg(math.atan2(velocity.y, MAX_SPEED)))
-- Clamp the angle to the minimum and maximum values
angle = math.max(MIN_ANGLE, math.min(MAX_ANGLE, angle))
-- Rotate the bird according to the angle
go.set_rotation(vmath.quat_rotation_z(math.rad(angle)))
end
-- The function that makes the bird flap
local function flap()
-- Set the velocity to the flap impulse
velocity.y = FLAP
-- Play a flap sound
sound.play("#flap")
end
-- The function that handles the input action
local function on_input(action_id, action)
-- If the action is the flap action and the state is not "dead"
if action_id == FLAP_ACTION and state ~= "dead" then
-- If the state is "idle"
if state == "idle" then
-- Send a start message to the spawner
msg.post("spawner#spawner", START_MESSAGE)
-- Set the state to "flap"
state = "flap"
end
-- If the action is pressed
if action.pressed then
-- Call the flap function
flap()
end
end
end
-- The function that handles the collision message
local function on_collision(message_id, message, sender)
-- If the message is the collision message and the state is not "dead"
if message_id == COLLISION_MESSAGE and state ~= "dead" then
-- Send an end message to the spawner
msg.post("spawner#spawner", END_MESSAGE)
-- Set the state to "dead"
state = "dead"
-- Play a die sound
sound.play("#die")
-- Send a die message to the bird
msg.post(".", DIE_MESSAGE)
end
end
-- The function that handles the pass message
local function on_pass(message_id, message, sender)
-- If the message is the pass message and the state is not "dead"
if message_id == PASS_MESSAGE and state ~= "dead" then
-- Send a score message to the bird
msg.post(".", SCORE_MESSAGE)
-- Play a score sound
sound.play("#score")
end
end
-- The function that handles the reset message
local function on_reset(message_id, message, sender)
-- If the message is the reset message
if message_id == RESET_MESSAGE then
-- Reset the velocity, angle, and state of the bird
velocity = vmath.vector3(0, 0, 0)
angle = 0
state = "idle"
-- Reset the position and rotation of the bird
go.set_position(vmath.vector3(0, 0, 0))
go.set_rotation(vmath.quat_rotation_z(0))
end
end
-- The function that is called every frame
function update(self, dt)
-- Call the update function
update(dt)
end
-- The function that is called when an input action is received
function on_input(self, action_id, action)
-- Call the on_input function
on_input(action_id, action)
end
-- The function that is called when a message is received
function on_message(self, message_id, message, sender)
-- Call the on_collision function
on_collision(message_id, message, sender)
-- Call the on_pass function
on_pass(message_id, message, sender)
-- Call the on_reset function
on_reset(message_id, message, sender)
end
```
Here is the code for the `spawner.script`:
```
-- The interval between each pipe spawn
local INTERVAL = 2
-- The minimum and maximum gap size between the pipes
local MIN_GAP = 100
local MAX_GAP = 200
-- The minimum and maximum pipe height
local MIN_HEIGHT = 50
local MAX_HEIGHT = 400
-- The speed that the pipes move to the left
local SPEED = -200
-- The initial state of the spawner
local state = "idle"
-- The timer that counts down to the next pipe spawn
local timer = 0
-- The message that indicates the game has started
local START_MESSAGE = hash("start")
-- The message that indicates the game has ended
local END_MESSAGE = hash("end")
-- The message that indicates the bird has passed a pipe
local PASS_MESSAGE = hash("pass")
-- The message that indicates the bird has reset
local RESET_MESSAGE = hash("reset")
-- The function that spawns a pair of pipes
local function spawn()
-- Calculate a random gap size between the pipes
local gap = math.random(MIN_GAP, MAX_GAP)
-- Calculate a random pipe height
local height = math.random(MIN_HEIGHT, MAX_HEIGHT)
-- Create a top pipe game object
local top_pipe = factory.create("#pipe_factory")
-- Set the position and scale of the top pipe
go.set_position(vmath.vector3(200, height + gap / 2, 0), top_pipe)
go.set_scale(vmath.vector3(1, -1, 1), top_pipe)
-- Set the linear velocity of the top pipe
go.set("#collisionobject", "linear_velocity", vmath.vector3(SPEED, 0, 0), top_pipe)
-- Create a bottom pipe game object
local bottom_pipe = factory.create("#pipe_factory")
-- Set the position and scale of the bottom pipe
go.set_position(vmath.vector3(200, height - gap / 2, 0), bottom_pipe)
go.set_scale(vmath.vector3(1, 1, 1), bottom_pipe)
-- Set the linear velocity of the bottom pipe
go.set("#collisionobject", "linear_velocity", vmath.vector3(SPEED, 0, 0), bottom_pipe)
-- Create a sensor game object
local sensor = factory.create("#sensor_factory")
-- Set the position and size of the sensor
go.set_position(vmath.vector3(200, height, 0), sensor)
go.set("#collisionobject", "size", vmath.vector3(10, gap, 0), sensor)
-- Set the linear velocity of the sensor
go.set("#collisionobject", "linear_velocity", vmath.vector3(SPEED, 0, 0), sensor)
end
-- The function that updates the timer and spawns pipes
local function update(dt)
-- Decrease the timer by the delta time
timer = timer - dt
-- If the timer is less than or equal to zero
if timer <= 0 then
-- Call the spawn function
spawn()
-- Reset the timer to the interval
timer = INTERVAL
end
end
-- The function that handles the start message
local function on_start(message_id, message, sender)
-- If the message is the start message and the state is "idle"
if message_id == START_MESSAGE and state == "idle" then
-- Set the state to "spawn"
state = "spawn"
-- Reset the timer to the interval
timer = INTERVAL
end
end
-- The function that handles the end message
local function on_end(message_id, message, sender)
-- If the message is the end message and the state is "spawn"
if message_id == END_MESSAGE and state == "spawn" then
-- Set the state to "idle"
state = "idle"
-- Delete all the pipe and sensor game objects
go.delete("#pipe_factory")
go.delete("#sensor_factory")
end
end
-- The function that handles the reset message
local function on_reset(message_id, message, sender)
-- If the message is the reset message
if message_id == RESET_MESSAGE then
-- Set the state to "idle"
state = "idle"
-- Delete all the pipe and sensor game objects
go.delete("#pipe_factory")
go.delete("#sensor_factory")
end
end
-- The function that is called every frame
function update(self, dt)
-- If the state is "spawn"
if state == "spawn" then
-- Call the update function
update(dt)
end
end
-- The function that is called when a message is received
function on_message(self, message_id, message, sender)
-- Call the on_start function
on_start(message_id, message, sender)
-- Call the on_end function
on_end(message_id, message, sender)
-- Call the on_reset function
on_reset(message_id, message, sender)
end
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment