Please be gentle: Noob needs help

General discussion about LÖVE, Lua, game development, puns, and unicorns.
Post Reply
Randalierpirat
Prole
Posts: 7
Joined: Mon Jul 16, 2012 12:49 pm

Please be gentle: Noob needs help

Post by Randalierpirat »

Hi everyone!
I hope someone is nice enough to help me; or rather hold my hand:
So basically I have no experience in game development or coding, except a brief session with Gamemaker a few years ago ( which doesn't :) ). Nevertheless I always wanted to try and hoped to teach myself some basics via the tutorials. But now I must confess that I was unable to find the answers I needed though I tried to implement some tips from very related problems, alas, to no avail.
What I want to do is very simple I think, yet I seem to fail at its execution, so I would like someone to basically tell me exactly what to do in for dummies terms.
And what would that be? you might ask. I have a static background, in front of that a hero who flies. I am happy (thanks hamsterwheel tutorial). But it looks rather undynamic so I thought to myself a few clouds going from right to left would give the implementation of movement. But I can't get it to work even after multiple tries T.T . I searched the forums for code to copy but it wouln't work. The wiki was no help for me either, as were the tutorial videos since I seem to be to dumb to implement the examples to my own usage. I know I need something along the lines of loops and math.random to create my scrolling effect, but as of now I'm pretty much done for.
So please excuse this wall of text and tell me how to make a scroller of multiple clouds randomly loop out of a picture of one cloud.
I'd appreciate it a lot. Thanks in advance!
And please excuse any errors since english is not my mothertongue.
User avatar
Robin
The Omniscient
Posts: 6506
Joined: Fri Feb 20, 2009 4:29 pm
Location: The Netherlands
Contact:

Re: Please be gentle: Noob needs help

Post by Robin »

Sure we can help you. It would be easier if you can upload what you already have, then we can build on that.
Help us help you: attach a .love.
Randalierpirat
Prole
Posts: 7
Joined: Mon Jul 16, 2012 12:49 pm

Re: Please be gentle: Noob needs help

Post by Randalierpirat »

Sure no problem, but I don't think this will help you much since I tend to delete things that don't work and start anew. Hence why there is nothing to see in terms of loop. But here you go!

Code: Select all

-- Ovierun 
-- Hamsterball Movement stuff
-- load and key functions

function love.load()
	Ovie = love.graphics.newImage("Ovie.png")
	Background = love.graphics.newImage("Background.png")	
	x = 25	
	y = 25
	speed = 200
	height = love.graphics.getHeight( )
	width = love.graphics.getWidth( )
	Cloud = love.graphics.newImage("box.png")
	Clouds = {}
end
-- Ovie movement
function love.update(dt)
	if love.keyboard.isDown("right") then 
	 x = x + (speed * dt) 
 	elseif love. keyboard. isDown ("left") then
 	x = x - (speed * dt)
	end

	 if love.keyboard.isDown ("up") then
 	y = y - (speed * dt) 
	 elseif love.keyboard.isDown ("down") then
 	y = y + (speed * dt) 
	end
--Cloud loop 
--??????
end

function love.draw()
	 love.graphics.draw (Background)
	 love.graphics.draw (Ovie, x, y)
	 love.graphics.draw (Cloud, height/2, width/2)--positional test
end
User avatar
Robin
The Omniscient
Posts: 6506
Joined: Fri Feb 20, 2009 4:29 pm
Location: The Netherlands
Contact:

Re: Please be gentle: Noob needs help

Post by Robin »

After the line that says Clouds = {}, I would do the following:

Code: Select all

cloudspeed = 50
for i = 1, 5 do -- or something like 10 or whatever
    Clouds[i] = {x = math.random(0, width), y = math.random(0, height)}
end
In love.update:

Code: Select all

for i, cloud in ipairs(Clouds) do
    cloud.x = cloud.x + cloudspeed * dt
end
In love.draw:

Code: Select all

for i, cloud in ipairs(Clouds) do
    love.graphics.draw(Cloud, cloud.x, cloud.y)
end
Try it out :)
Help us help you: attach a .love.
Randalierpirat
Prole
Posts: 7
Joined: Mon Jul 16, 2012 12:49 pm

Re: Please be gentle: Noob needs help

Post by Randalierpirat »

OMG Thank you so much! I was really frustrated with this since I planned on this to be the easy part. I will tweek it a little just to get a grasp of what is what and then onward to the real Problems. :)
Again: Thank you.
Santos
Party member
Posts: 384
Joined: Sat Oct 22, 2011 7:37 am

Re: Please be gentle: Noob needs help

Post by Santos »

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.
cloud.love
(9.46 KiB) Downloaded 178 times

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! :D

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. :D

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.
yayclouds.love
(9.74 KiB) Downloaded 357 times

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! :D

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.
differentclouds.love
(36.13 KiB) Downloaded 181 times
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! :rofl: I hope you get something from this anyway! :ultraglee:)
Last edited by Santos on Mon Jul 16, 2012 6:36 pm, edited 1 time in total.
Randalierpirat
Prole
Posts: 7
Joined: Mon Jul 16, 2012 12:49 pm

Re: Please be gentle: Noob needs help

Post by Randalierpirat »

Definitely. You take a lot of guesswork and random mistakes off of my shoulders. Great tutorial.
Edit:grammar
User avatar
tavuntu
Citizen
Posts: 65
Joined: Mon Dec 24, 2012 6:56 am
Contact:

Thanks a lot

Post by tavuntu »

I was looking for something like this (I want to make a spaceship which shots an indeterminated number of bullets that disapear once they are out of the screen and apear when the user press certain button), nice! :)
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot] and 3 guests