Here are some things to consider:
- How to store and draw the clouds
- How the clouds will move
- Where the cloud will be created
- When the cloud will be created
And, possibly,
- What cloud will be created (if we have have multiple cloud images)
Storing and drawing a single cloud
Each cloud could be a table, having an x position, a y position, and an image. These values can then be used to draw the cloud.
Code: Select all
function love.load()
cloud = {
x = 500,
y = 200,
image = love.graphics.newImage('cloud.png')
}
end
function love.draw()
love.graphics.draw(cloud.image, cloud.x, cloud.y)
end
Moving the cloud
The clouds x position can be subtracted from in
love.update, making it move left. The speed it does this can also be stored in the cloud's table, in pixels-per-second.
Code: Select all
function love.load()
cloud = {
x = 500,
y = 200,
speed = 100,
image = love.graphics.newImage('cloud.png')
}
end
function love.update(dt)
cloud.x = cloud.x - cloud.speed * dt
end
function love.draw()
love.graphics.draw(cloud.image, cloud.x, cloud.y)
end
To start the cloud at the right of the screen, we need to know the screen width, and set the initial x position to that.
Code: Select all
function love.load()
SCREEN_WIDTH = 800
cloud = {
x = SCREEN_WIDTH,
y = 200,
speed = 100,
image = love.graphics.newImage('cloud.png')
}
end
function love.update(dt)
cloud.x = cloud.x - cloud.speed * dt
end
function love.draw()
love.graphics.draw(cloud.image, cloud.x, cloud.y)
end
The y position and speed of each cloud can be set to random numbers using
math.random, which works like this:
Code: Select all
randomNumber = math.random(minimumNumber, maximumNumber)
Code: Select all
function love.load()
SCREEN_WIDTH = 800
cloud = {
x = SCREEN_WIDTH,
y = math.random(-10, 500),
speed = math.random(75, 125),
image = love.graphics.newImage('cloud.png')
}
end
function love.update(dt)
cloud.x = cloud.x - cloud.speed * dt
end
function love.draw()
love.graphics.draw(cloud.image, cloud.x, cloud.y)
end
A cloud generator
Let's make a function which returns a cloud in a random position, at a random speed. It will be useful to create multiple clouds.
Code: Select all
function love.load()
SCREEN_WIDTH = 800
aNiceCloud = cloudGenerator()
end
function love.update(dt)
aNiceCloud.x = aNiceCloud.x - aNiceCloud.speed * dt
end
function love.draw()
love.graphics.draw(aNiceCloud.image, aNiceCloud.x, aNiceCloud.y)
end
function cloudGenerator()
local cloud = {
x = SCREEN_WIDTH,
y = math.random(-10, 500),
speed = math.random(75, 125),
image = love.graphics.newImage('cloud.png')
}
return cloud
end
(Notice the "local" before the cloud table in
cloudGenerator? That means that the cloud variable won't be accessable outside that function. It's not needed, because it's returned.)
aNiceCloud is now exactly the same as the cloud table used in the previous example! But now with the cloud generator function, it's easy to create multiple clouds...
Code: Select all
function love.load()
SCREEN_WIDTH = 800
aNiceCloud = cloudGenerator()
anotherCloud = cloudGenerator()
andAnother = cloudGenerator()
end
function love.update(dt)
aNiceCloud.x = aNiceCloud.x - aNiceCloud.speed * dt
anotherCloud.x = anotherCloud.x - anotherCloud.speed * dt
andAnother.x = andAnother.x - andAnother.speed * dt
end
function love.draw()
love.graphics.draw(aNiceCloud.image, aNiceCloud.x, aNiceCloud.y)
love.graphics.draw(anotherCloud.image, anotherCloud.x, anotherCloud.y)
love.graphics.draw(andAnother.image, andAnother.x, andAnother.y)
end
function cloudGenerator()
local cloud = {
x = SCREEN_WIDTH,
y = math.random(-10, 500),
speed = math.random(75, 125),
image = love.graphics.newImage('cloud.png')
}
return cloud
end
But, it could be easier!
Let's a make a table for all of the clouds. That way, they can all be updated and drawn at the same time.
Code: Select all
function love.load()
SCREEN_WIDTH = 800
clouds = {}
table.insert(clouds, cloudGenerator())
table.insert(clouds, cloudGenerator())
table.insert(clouds, cloudGenerator())
table.insert(clouds, cloudGenerator())
table.insert(clouds, cloudGenerator())
end
function love.update(dt)
for _, cloud in ipairs(clouds) do
cloud.x = cloud.x - cloud.speed * dt
end
end
function love.draw()
for _, cloud in ipairs(clouds) do
love.graphics.draw(cloud.image, cloud.x, cloud.y)
end
end
function cloudGenerator()
local cloud = {
x = SCREEN_WIDTH,
y = math.random(-10, 500),
speed = math.random(75, 125),
image = love.graphics.newImage('cloud.png')
}
return cloud
end
clouds = {} creates the table,
table.insert(clouds, cloudGenerator()) insert a generated cloud, and
for _, cloud in ipairs(clouds) do loops over everything in
clouds, with each element being referent to as "cloud".
Instead of having table.insert(clouds, cloudGenerator()) repeated, we could use another type of for loop (called a numeric for loop) to create them all, but you probably won't be creating all of them at the same time.
For more on for loops,
check this out.
Now, for a slight detour...
When, part 1: Getting something to happen after a certain time.
Here is one way of having something occur at a regular interval:
- Creating a variable to store the time (in seconds) at which to do something
- Subtracting "dt" from this variable every frame from love.update
- Checking to see if this variable has reached 0, and if so, doing some action
Here is an example of something happening after three seconds:
Code: Select all
function love.load()
timeToDoSomething = 3
timer = timeToDoSomething
end
function love.update(dt)
timer = timer - dt
if timer <= 0 then
-- Do something!
end
end
Or, for an example you can actually run...
Code: Select all
function love.load()
timeToDisplayText = 3
timer = timeToDisplayText
text = '...'
end
function love.update(dt)
timer = timer - dt
if timer <= 0 then
text = 'WOO!'
end
end
function love.draw()
love.graphics.print(text, 10, 10)
end
When, part 2: Getting something to happen repeatedly after a certain time.
Now, let's say you want something to happen repeatedly, instead of just once.
To do this, you can reset your timer variable. You could set the timer back to the time at which to do something, but for more accuracy, you could set the timer to whatever the value of the current timer is (which may not be
exactly, and add the time limit.
Code: Select all
function love.load()
timeToDoSomething = 3
timer = timeToDoSomething
end
function love.update(dt)
timer = timer - dt
if timer <= timeToDoSomething then
-- Do something
-- Reset the timer
timer = timer + timeToDoSomething
end
end
In the example below, a new "O" gets added every half a second.
Code: Select all
function love.load()
timeToAddAnO = 0.5
timer = timeToAddAnO
numberOfOs = 2
end
function love.update(dt)
timer = timer - dt
if timer <= 0 then
numberOfOs = numberOfOs + 1
timer = timer + timeToAddAnO
end
end
function love.draw()
love.graphics.print('W'..string.rep('O', numberOfOs)..'!', 10, 10)
end
When, part 3: Getting something to happen repeatedly after a random time.
Instead of the timer being set back to an exact number, you can use
math.random to choose a random number.
For example, for something to happen at a random interval between 1 and 10 seconds...
Code: Select all
function love.load()
timer = math.random(1, 10)
end
function love.update(dt)
timer = timer - dt
if timer <= timeToDoSomething then
-- Do something
-- Reset the timer
timer = timer + math.random(1, 10)
end
end
Here is the example from before, but with random timing:
Code: Select all
function love.load()
minRandomTime = 0.1
maxRandomTime = 2
timeToAddAnO = math.random(minRandomTime, maxRandomTime)
timer = timeToAddAnO
numberOfOs = 2
end
function love.update(dt)
timer = timer - dt
if timer <= 0 then
numberOfOs = numberOfOs + 1
timer = math.random(minRandomTime, maxRandomTime)
end
end
function love.draw()
love.graphics.print('W'..string.rep('O', numberOfOs)..'!', 10, 10)
end
Creating clouds at random times
Everytime the timer reaches 0, we can insert a new cloud into the clouds
table.
Code: Select all
function love.load()
SCREEN_WIDTH = 800
minCloudGenerationTime = 0.2
maxCloudGenerationTime = 3
cloudTimer = math.random(minCloudGenerationTime, maxCloudGenerationTime)
clouds = {}
end
function love.update(dt)
for _, cloud in ipairs(clouds) do
cloud.x = cloud.x - cloud.speed * dt
end
cloudTimer = cloudTimer - dt
if cloudTimer <= 0 then
table.insert(clouds, cloudGenerator())
cloudTimer = math.random(minCloudGenerationTime, maxCloudGenerationTime)
end
end
function love.draw()
for _, cloud in ipairs(clouds) do
love.graphics.draw(cloud.image, cloud.x, cloud.y)
end
end
function cloudGenerator()
local cloud = {
x = SCREEN_WIDTH,
y = math.random(-10, 500),
speed = math.random(75, 125),
image = love.graphics.newImage('cloud.png')
}
return cloud
end
Removing clouds which have moved off the screen
Once clouds have moved off the screen, they are still in memory and still being updated and drawn. To remove a cloud after it has gone off the screen, we can...
- Loop over the clouds table (using a reverse numeric for loop, because removing table elements while looping over the table with an
ipairs loop can be hazardous).
- Check if it is off the screen, which is when the x position
plus the width of the cloud (which is the width of the image of the cloud) is less than or equal to the x position of the right side of the screen (which is 0).
- Removing the element with
table.remove.
To test that this is working, we can print the number of clouds to see if it's what we would expect.
Code: Select all
function love.load()
SCREEN_WIDTH = 800
minCloudGenerationTime = 0.2
maxCloudGenerationTime = 3
cloudTimer = math.random(minCloudGenerationTime, maxCloudGenerationTime)
clouds = {}
end
function love.update(dt)
for _, cloud in ipairs(clouds) do
cloud.x = cloud.x - cloud.speed * dt
end
cloudTimer = cloudTimer - dt
if cloudTimer <= 0 then
table.insert(clouds, cloudGenerator())
cloudTimer = math.random(minCloudGenerationTime, maxCloudGenerationTime)
end
for i = #clouds, 1, -1 do
if clouds[i].x + clouds[i].image:getWidth() <= 0 then
table.remove(clouds, i)
end
end
end
function love.draw()
love.graphics.print('Clouds: ' .. #clouds, 10, 10)
for _, cloud in ipairs(clouds) do
love.graphics.draw(cloud.image, cloud.x, cloud.y)
end
end
function cloudGenerator()
local cloud = {
x = SCREEN_WIDTH,
y = math.random(-10, 500),
speed = math.random(75, 125),
image = love.graphics.newImage('cloud.png')
}
return cloud
end
Here is an explanation of what was added...
Code: Select all
for i = #clouds, 1, -1 do
if clouds[i].x + clouds[i].image:getWidth() <= 0 then
table.remove(clouds, i)
end
end
This is a numeric for loop. The first part of the for loop is the variable which will change, and its starting value.
The
# operator returns the number of elements in a table. So
#clouds is the number of clouds.
The second part of the for loop,
1, is the limit. After it gets past one, it will stop.
The third part, -1, means that
i will be subtracted by one every time the for loop loops.
Individual clouds can be access by their number. For example, cloud[1] will be the first cloud.
getWidth() is a method that images have in LÖVE, and, well, it gets the width of the image!
table.remove removes the table element at the position which you give it as the second argument.
Different cloud images
Instead of having just one image, you can have a table of different images, and randomly choose from that table.
Code: Select all
function love.load()
SCREEN_WIDTH = 800
minCloudGenerationTime = 0.2
maxCloudGenerationTime = 3
cloudTimer = math.random(minCloudGenerationTime, maxCloudGenerationTime)
cloudImages = {
love.graphics.newImage('cloud1.png'),
love.graphics.newImage('cloud2.png'),
love.graphics.newImage('cloud3.png')
}
clouds = {}
end
function love.update(dt)
for _, cloud in ipairs(clouds) do
cloud.x = cloud.x - cloud.speed * dt
end
cloudTimer = cloudTimer - dt
if cloudTimer <= 0 then
table.insert(clouds, cloudGenerator())
cloudTimer = math.random(minCloudGenerationTime, maxCloudGenerationTime)
end
for i = #clouds, 1, -1 do
if clouds[i].x + clouds[i].image:getWidth() <= 0 then
table.remove(clouds, i)
end
end
end
function love.draw()
love.graphics.print('Clouds: ' .. #clouds, 10, 10)
for _, cloud in ipairs(clouds) do
love.graphics.draw(cloud.image, cloud.x, cloud.y)
end
end
function cloudGenerator()
local cloud = {
x = SCREEN_WIDTH,
y = math.random(-10, 500),
speed = math.random(75, 125),
image = cloudImages[math.random(1, #cloudImages)]
}
return cloud
end
cloudImages[math.random(1, #cloudImages)]
This line selects a random number from 1 to the number of elements in cloudImages, and the uses that number as an index of
cloudImages to select a random image.
Making things easier to read with functions
You can take all the stuff for updating and drawing clouds, and put them in functions.
Code: Select all
function love.load()
SCREEN_WIDTH = 800
loadClouds()
end
function love.update(dt)
updateClouds(dt)
end
function love.draw()
drawClouds()
end
function cloudGenerator()
local cloud = {
x = SCREEN_WIDTH,
y = math.random(-10, 500),
speed = math.random(75, 125),
image = cloudImages[math.random(1, #cloudImages)]
}
return cloud
end
function loadClouds()
minCloudGenerationTime = 0.2
maxCloudGenerationTime = 3
cloudTimer = math.random(minCloudGenerationTime, maxCloudGenerationTime)
cloudImages = {
love.graphics.newImage('cloud1.png'),
love.graphics.newImage('cloud2.png'),
love.graphics.newImage('cloud3.png')
}
clouds = {}
end
function updateClouds(dt)
for _, cloud in ipairs(clouds) do
cloud.x = cloud.x - cloud.speed * dt
end
cloudTimer = cloudTimer - dt
if cloudTimer <= 0 then
table.insert(clouds, cloudGenerator())
cloudTimer = math.random(minCloudGenerationTime, maxCloudGenerationTime)
end
for i = #clouds, 1, -1 do
if clouds[i].x + clouds[i].image:getWidth() <= 0 then
table.remove(clouds, i)
end
end
end
function drawClouds()
love.graphics.print('Clouds: ' .. #clouds, 10, 10)
for _, cloud in ipairs(clouds) do
love.graphics.draw(cloud.image, cloud.x, cloud.y)
end
end
Or, to make
main.lua even easier to read, you can put all the cloud stuff in its own file with
require! Notice there is no ".lua" extention when requiring files.
main.lua
Code: Select all
require 'clouds'
function love.load()
SCREEN_WIDTH = 800
loadClouds()
end
function love.update(dt)
updateClouds(dt)
end
function love.draw()
drawClouds()
end
clouds.lua
Code: Select all
function loadClouds()
minCloudGenerationTime = 0.2
maxCloudGenerationTime = 3
cloudTimer = math.random(minCloudGenerationTime, maxCloudGenerationTime)
cloudImages = {
love.graphics.newImage('cloud1.png'),
love.graphics.newImage('cloud2.png'),
love.graphics.newImage('cloud3.png')
}
clouds = {}
end
function cloudGenerator()
local cloud = {
x = SCREEN_WIDTH,
y = math.random(-10, 500),
speed = math.random(75, 125),
image = cloudImages[math.random(1, #cloudImages)]
}
return cloud
end
function updateClouds(dt)
for _, cloud in ipairs(clouds) do
cloud.x = cloud.x - cloud.speed * dt
end
cloudTimer = cloudTimer - dt
if cloudTimer <= 0 then
table.insert(clouds, cloudGenerator())
cloudTimer = math.random(minCloudGenerationTime, maxCloudGenerationTime)
end
for i = #clouds, 1, -1 do
if clouds[i].x + clouds[i].image:getWidth() <= 0 then
table.remove(clouds, i)
end
end
end
function drawClouds()
love.graphics.print('Clouds: ' .. #clouds, 10, 10)
for _, cloud in ipairs(clouds) do
love.graphics.draw(cloud.image, cloud.x, cloud.y)
end
end
I hope this helps!
(I have been ninjaed!
I hope you get something from this anyway!
)