http://love2d.org/w/api.php?action=feedcontributions&user=Erasio&feedformat=atomLOVE - User contributions [en]2024-03-29T11:04:05ZUser contributionsMediaWiki 1.31.12http://love2d.org/w/index.php?title=Joystick:getHatCount&diff=25635Joystick:getHatCount2020-12-20T21:47:52Z<p>Erasio: </p>
<hr />
<div>{{newin|[[0.9.0]]|090|type=function|text=It has been moved from [[love.joystick.getNumHats]]}}<br />
Gets the number of [https://en.wikipedia.org/wiki/Joystick#Hat_switch hats] on the joystick.<br />
== Function ==<br />
=== Synopsis ===<br />
<source lang="lua"><br />
hats = Joystick:getHatCount( )<br />
</source><br />
=== Arguments ===<br />
None.<br />
=== Returns ===<br />
{{param|number|hats|How many hats the joystick has.}}<br />
== See Also ==<br />
* [[parent::Joystick]]<br />
* [[Joystick:getHat]]<br />
[[Category:Functions]]<br />
{{#set:Description=Gets the number of hats on the joystick.}}<br />
== Other Languages ==<br />
{{i18n|Joystick:getHatCount}}</div>Erasiohttp://love2d.org/w/index.php?title=JoystickHat&diff=25634JoystickHat2020-12-20T21:46:51Z<p>Erasio: </p>
<hr />
<div>{{newin|[[0.5.0]]|050|type=enum}}<br />
[https://en.wikipedia.org/wiki/Joystick#Hat_switch Joystick hat] positions.<br />
== Constants ==<br />
;c: Centered<br />
;d: Down<br />
;l: Left<br />
;ld: Left+Down<br />
;lu: Left+Up<br />
;r: Right<br />
;rd: Right+Down<br />
;ru: Right+Up<br />
;u: Up<br />
== See Also ==<br />
* [[parent::love.joystick]]<br />
* [[parent::Joystick]]<br />
[[Category:Enums]]<br />
{{#set:Description=Joystick hat positions.}}<br />
== Other Languages ==<br />
{{i18n|JoystickHat}}</div>Erasiohttp://love2d.org/w/index.php?title=HooECS&diff=20734HooECS2017-10-21T08:28:12Z<p>Erasio: </p>
<hr />
<div>{{#set:Name=HooECS}}<br />
{{#set:LOVE Version=Any}}<br />
{{#set:Description= It's a full-featured Entity-Component-System framework for making games with lua}}<br />
{{#set:Keyword=Framework}}<br />
<br />
'''[https://github.com/Hooodini/HooECS Github Repository]'''<br />
<br />
'''[https://love2d.org/forums/viewtopic.php?f=5&t=84471 Forum Thread]'''<br />
<br />
HooECS is an Entity Component System framework for game development with lua. Originally written for the LÖVE 2D game engine, it is actually compatible with pretty much any game that uses lua! It is inspired by [Richard Lords Introduction](http://www.richardlord.net/blog/what-is-an-entity-framework) to ECS. If you don't have any idea what this entity component stuff is all about, click that link and give it a read! It's totally worth it! <br />
<br />
This is a fork that's been improved upon.<br />
<br />
The original software was written by Arne Beer and Rafael Epplee under the name [lovetoys](https://love2d.org/wiki/Lovetoys).<br />
For the foreseeable future. All lovetoys projects will stay 100% compatible with HooECS. The unit tests of lovetoys are merely expanded upon. <br />
<br />
No core behavior is changed!<br />
<br />
[[Category:Libraries]]</div>Erasiohttp://love2d.org/w/index.php?title=HooECS&diff=20733HooECS2017-10-21T08:27:10Z<p>Erasio: Created page with "{{#set:Name=HooECS}} {{#set:LOVE Version=Any}} {{#set:Description= It's a full-featured Entity-Component-System framework for making games with lua}} {{#set:Keyword=Framework}..."</p>
<hr />
<div>{{#set:Name=HooECS}}<br />
{{#set:LOVE Version=Any}}<br />
{{#set:Description= It's a full-featured Entity-Component-System framework for making games with lua}}<br />
{{#set:Keyword=Framework}}<br />
<br />
[Github Repository](https://github.com/Hooodini/HooECS)<br />
[Forum Thread](https://love2d.org/forums/viewtopic.php?f=5&t=84471)<br />
<br />
HooECS is an Entity Component System framework for game development with lua. Originally written for the LÖVE 2D game engine, it is actually compatible with pretty much any game that uses lua! It is inspired by [Richard Lords Introduction](http://www.richardlord.net/blog/what-is-an-entity-framework) to ECS. If you don't have any idea what this entity component stuff is all about, click that link and give it a read! It's totally worth it! <br />
<br />
This is a fork that's been improved upon.<br />
The original software was written by Arne Beer and Rafael Epplee under the name [lovetoys](https://love2d.org/wiki/Lovetoys).<br />
For the foreseeable future. All lovetoys projects will stay 100% compatible with HooECS. The unit tests of lovetoys are merely expanded upon. <br />
No core behavior is changed!<br />
<br />
[[Category:Libraries]]</div>Erasiohttp://love2d.org/w/index.php?title=Tutorial:PhysicsDrawing&diff=20437Tutorial:PhysicsDrawing2017-08-30T14:15:35Z<p>Erasio: Created page with "This is not so much a tutorial but an explained code snippet that helps with physics debugging. == Drawing Physics Collisions == When developing with Box2D (aka love.physics..."</p>
<hr />
<div>This is not so much a tutorial but an explained code snippet that helps with physics debugging.<br />
<br />
== Drawing Physics Collisions ==<br />
<br />
When developing with Box2D (aka love.physics) you will likely get to a point where the collisions don't quite match up with what you expect them to be due to some minor mistake.<br />
<br />
At the end of this guide there's a complete snippet that will draw all collisions at the correct location. If you just want the code feel free to jump down and just copy & paste it. But we will go over what the code does bit by bit.<br />
<br />
love.physics works via a few different sets of objects. Most notably. "World", "Body", "Shape" and "Fixture". <br />
<br />
* "World" is the physics world. You can have multiple ones but only objects within the same world will collide.<br />
<br />
* "Body" is the location of your object within the physics world. It defines which world this object belongs to, if it can move and the mass of the object (which will be calculated from the size of your shape by default). It also holds a reference to the fixture.<br />
<br />
* "Shape" is the collision itself. If two shapes collide, the physics system will handle it. It also holds a reference to the fixture.<br />
<br />
* "Fixture" is how we attach a shape to a body. It defines a few additional properties how collisions will be handled (such as friction, collision channels and a few more). It also holds a reference to the body and the shape. <br />
<br />
In order to draw all shapes we need to start at the world. It handles all collisions. So it's gotta contain a list of things. Specifically. It contains a list of bodies which we can get in the following way:<br />
<br />
<source lang="lua"><br />
listOfBodies = World:getBodyList()<br />
</source><br />
<br />
We don't need a list of bodies though. We just wanna do something for every body. So we make a for loop out of it:<br />
<br />
<source lang="lua"><br />
for _, body in pairs(World:getBodyList()) do<br />
-- ToDo<br />
end<br />
</source><br />
<br />
A body can have multiple shapes which are attached with multiple fixtures. So we have to iterate over the list of fixtures as well:<br />
<br />
<source lang="lua"><br />
for _, body in pairs(World:getBodyList()) do<br />
for _, fixture in pairs(body:getFixtureList()) do<br />
-- ToDo<br />
end<br />
end<br />
</source><br />
<br />
A fixture however can only have one shape. So now we already have a reference to all collision fixtures (and therefore shapes) there are within this loop!<br />
<br />
We can simply store a reference to the shape by doing<br />
<br />
<source lang="lua"><br />
for _, body in pairs(World:getBodyList()) do<br />
for _, fixture in pairs(body:getFixtureList()) do<br />
local shape = fixture:getShape()<br />
<br />
-- ToDo Draw collision<br />
end<br />
end<br />
</source><br />
<br />
So next up is the drawing. There are multiple types of shapes. To be specific we have<br />
<br />
* CircleShape<br />
<br />
* PolygonShape<br />
<br />
* EdgeShape<br />
<br />
* ChainShape<br />
<br />
== Drawing circle shapes == <br />
<br />
So let's first look at the circle. We will draw it with the following code:<br />
<br />
<source lang="lua"><br />
local cx, cy = body:getWorldPoints(shape:getPoint())<br />
love.graphics.circle("fill", cx, cy, shape:getRadius())<br />
</source><br />
<br />
cx & cy stand for "CircleX" and "CircleY". The position we want to draw it at.<br />
<br />
body:getWorldPoints() will take any amount of points and convert them into world space (a point being two numbers. One for x and one for y). In other words. It will do "x + body:getX()", "y + body:getY()" for all points provided. <br />
<br />
Then we simply draw the circle at the position we just calculated with the radius of the shape. I choose "fill" as first parameter. You can also use "line". "fill" means it'll be drawn as circle. "fill" will draw it as a ring.<br />
<br />
== Drawing a polygon shapes ==<br />
<br />
Next up is the PolygonShape. A polygon is a list of points with up to 8 vertices (aka 16 numbers representing x and y. e.g. (x1, y1, x2, y1, ..., xn, yn) )<br />
<br />
We can abuse a little bit of functionality of lua to make this a super small snippet of code. We can get all the points by calling "shape:getPoints()" (once we verified it's a polygon shape).<br />
<br />
This will return up to 16 individual numbers. However. If we put this directly into another function without following parameter. It will simply put the next number as next parameter.<br />
<br />
<source lang="lua"><br />
add = function(a, b)<br />
print( a + b )<br />
end<br />
<br />
f = function()<br />
return 1, 2<br />
end<br />
<br />
add(2, 2) --> Will print 4<br />
<br />
add(f()) --> Will print 3. We returned multiple parameters which are simply considered as following parameters. So a -> 1, b -> 2.<br />
<br />
add(f(), 0) --> Will print 1. We returned multiple parameters. However those are directly followed by another value. So only the first return value is used and the others will be discarded. a -> 1, b -> 0<br />
</source><br />
<br />
Knowing this we can do the following:<br />
<br />
<source lang="lua"><br />
love.graphics.polygon("fill", body:getWorldPoints(shape:getPoints()))<br />
</source><br />
<br />
Since there is no following parameter it will simply put as many points in the function as the shape has. We don't have to handle anything else!<br />
<br />
== Drawing edge and chain Shapes ==<br />
<br />
Lastly we have edge and chain shapes. They are essentially the same. Internally they are handled a bit differently. But for our case we can assume an EdgeShape to be the same as a ChainShape with two points. A ChainShape can have two or more points (this is the only shape without a limit).<br />
<br />
However. Both are made up of lines that may or may not loop around. And both have the function "shape:getPoints()" as well! So we do not have to treat them separately.<br />
<br />
We pretty much do the same as we did for the polygon. But instead of drawing a polygon we draw a line.<br />
<br />
<source lang="lua"><br />
love.graphics.line(body:getWorldPoints(shape:getPoints()))<br />
</source><br />
<br />
== Final code ==<br />
<br />
This leaves us with the final code snippet that will draw all of our collisions at the correct location on the screen!<br />
<br />
Happy debugging! <br />
<br />
<source lang="lua"><br />
for _, body in pairs(world:getBodyList()) do<br />
for _, fixture in pairs(body:getFixtureList()) do<br />
local shape = fixture:getShape()<br />
<br />
if shape:typeOf("CircleShape") then<br />
local cx, cy = body:getWorldPoints(shape:getPoint())<br />
love.graphics.circle("fill", cx, cy, shape:getRadius())<br />
elseif shape:typeOf("PolygonShape") then<br />
love.graphics.polygon("fill", body:getWorldPoints(shape:getPoints()))<br />
else<br />
love.graphics.line(body:getWorldPoints(shape:getPoints()))<br />
end<br />
end<br />
end<br />
</source></div>Erasiohttp://love2d.org/w/index.php?title=Tutorial:Animation&diff=20376Tutorial:Animation2017-08-13T13:29:49Z<p>Erasio: Fixed typo, reworded "dt" explenation and stressed this is tutorial code. Not production ready.</p>
<hr />
<div>Before we begin. This is a tutorial for semi advanced users. You are expected to know about tables, loops and the basics of drawing in Löve2D. And of course how to run a Löve2D game.<br />
<br />
Alright. Let's get to it shall we?<br />
<br />
I'm going to cover the basics of sprite based animation. This means you have a series of images which are displayed one after another. If you intent to create your own spritesheet make sure to leave at least 1px of pure transparency between the individual sprites. Otherwise you might see artifacts from the next or previous image and nobody wants that!<br />
<br />
== Loading animations ==<br />
<br />
For this tutorial we'll be using a unnamed old hero. Generously made available for everyone by GrafxKid on [https://opengameart.org/content/classic-hero OpenGameArt.org]. Download the image below and place it next to your main.lua in the folder.<br />
<br />
[[File:oldHero.png|200px|thumb|center|oldHero.png]]<br />
<br />
To be able to create multiple animations we want a reusable function that provides us with a table that we can use for everything related to the animation.<br />
We start this by defining a new function, storing our image in a variable and creating a new local table (the return value will be our table. We don't want some random animation table be globally defined or worse yet overwrite other data!)<br />
<br />
The following parameters are going to be needed:<br />
<br />
* image <br />
An image object created with love.graphics.newImage(filepath)<br />
* width <br />
The width of each individual sprite. We are going to assume all sprites have the same size.<br />
* height <br />
The height of each individual sprite.<br />
* duration <br />
How long the animation takes before it loops back to the first frame.<br />
<br />
<source lang="lua"><br />
function newAnimation(image, width, height, duration)<br />
local animation = {}<br />
animation.spriteSheet = image;<br />
<br />
return animation<br />
end<br />
</source><br />
<br />
You can try to just draw this directly ("love.graphics.draw(animation.spriteSheet)"). However. Then we'd just have all the images drawn next to each other. Certainly not what we want. Which is where [[Quad|Quads]] come in very handy! <br />
<br />
They define a part of the image which will be drawn instead of the whole image. Exactly what we need!<br />
<br />
Now we could define each quad individually. But that's going to be very annoying if we have several animations and hundreds of sprites! So instead we will load it within a loop that detects how many sprites are within an image. For this setup there must not be empty spaces and the image must contain exactly one animation.<br />
<br />
<source lang="lua"><br />
function newAnimation(image, width, height, duration)<br />
local animation = {}<br />
animation.spriteSheet = image;<br />
animation.quads = {};<br />
<br />
for y = 0, image:getHeight() - height, height do<br />
for x = 0, image:getWidth() - width, width do<br />
table.insert(animation.quads, love.graphics.newQuad(x, y, width, height, image:getDimensions()))<br />
end<br />
end<br />
<br />
return animation<br />
end<br />
</source><br />
<br />
The 3 parameters to the for loop mean the following:<br />
<br />
1. The starting value for y or x.<br />
<br />
2. The maximum value (in this case the total width or height of the reference image)<br />
<br />
3. How much should be added per step. In our case the size of the sprite.<br />
<br />
With this we get the location on the sprite sheet from y and x!<br />
<br />
You might wonder why we do "image:getHeight() - height" instead of just testing against the height of the image. This is because we want to make sure another sprite still fits on the sprite sheet. The sheet itself might not have the exact size we want it to. If it were one pixel too large. We do not want to add another quad (which would render as nothing) but instead ignore it. <br />
<br />
Now we have 6 Quads ranging from index 1 - 6 in the table. Awesome!<br />
<br />
But we have the issue that we have essentially 6 images that we can draw individually. But we need to draw them one after another over time.<br />
Also we don't just want to play this animation. We might want to change the speed at which it plays.<br />
<br />
To cover this we add two more variables to our animation table.<br />
Duration (which we expect as parameter) and currentTime which is used to measure how much time has passed.<br />
<br />
<source lang="lua"><br />
function newAnimation(image, width, height, duration)<br />
local animation = {}<br />
animation.spriteSheet = image;<br />
animation.quads = {};<br />
<br />
for y = 0, image:getHeight() - height, height do<br />
for x = 0, image:getWidth() - width, width do<br />
table.insert(animation.quads, love.graphics.newQuad(x, y, width, height, image:getDimensions()))<br />
end<br />
end<br />
<br />
animation.duration = duration or 1<br />
animation.currentTime = 0<br />
<br />
return animation<br />
end<br />
</source><br />
<br />
Which concludes our animation creation function already!<br />
<br />
== Updating the animation == <br />
<br />
Next up we need to load our animation table (call our newly created function) and update our current time.<br />
<br />
<source lang="lua"><br />
function love.load()<br />
animation = newAnimation(love.graphics.newImage("oldHero.png"), 16, 18, 1)<br />
end<br />
<br />
function love.update(dt)<br />
animation.currentTime = animation.currentTime + dt<br />
if animation.currentTime >= animation.duration then<br />
animation.currentTime = animation.currentTime - animation.duration<br />
end<br />
end<br />
</source><br />
<br />
The demo sprite sheet has a sprite size of 16 pixels wide and 18 pixels high. We intent to play all images over the course of 1 second.<br />
<br />
In love.update we simply add dt (delta time aka the time since the last frame) to our current time. We now count upwards continuously!<br />
<br />
But we will use the current time to determine which frame should be shown. As such we will want it to be between 0 and the value of "duration". The if simply checks if "currentTime" is more than "duration" in which case we subtract "duration". You could just set "currentTime" to 0 instead of subtracting "duration". However this will result in fractions of a second being lost every time the animation finishes and therefore slow down the animation playtime ever so slightly. Which can easily be avoided and should be avoided!<br />
<br />
Now for the really interesting part!<br />
<br />
== Drawing the animation == <br />
<br />
How do we draw this?<br />
<br />
Well. We have the duration and current time. With this info we can calculate a percentage! How much of the animation has passed so far?<br />
<br />
If you've followed this tutorial correctly so far ''"currentTime / duration"'' will provide you with a number between 0 and 1. Which represents the percentage. 0.25 means 25% of the animation has passed.<br />
<br />
Considering this we can search for the correct image to use! Since we already have a number between 0 and 1 we can simply multiply this percentage with our total amount of images and get a number between 0 and 6!<br />
<br />
''currentTime / duration * #quads''<br />
<br />
However. If we try to get this from our table we will run into the issue that this is not a whole number. But our images are stored with whole numbers! So attempting to get the image at index "4.75" will give us nothing. Bummer! <br />
<br />
Fear not. The solution is not too difficult.<br />
<br />
"currentTime" will be a number between 0 and just below "duration" (because we reduce "currentTime" if it is larger or ''equal'' "duration"). Which would result in a number between 0 and 5.<br />
<br />
To transform this value from our decimal point value to a whole number between 1 and 6 we do the following:<br />
<br />
<source lang="lua"><br />
math.floor(currentTime / duration * #quads) + 1<br />
</source><br />
<br />
"math.floor" provides us with the next lower number. Which means in our case a number between 0 and 5. We add one pushing it to a number between 1 and 6. All the sprites we have!<br />
<br />
Lövely!<br />
<br />
Alright. So all that's left is to draw the appropriate quad!<br />
<br />
This simply requires us to provide "love.graphics.draw" with the image reference (our spriteSheet) and the quad we want to use. Simple enough!<br />
<br />
<source lang="lua"><br />
local spriteNum = math.floor(animation.currentTime / animation.duration * #animation.quads) + 1<br />
love.graphics.draw(animation.spriteSheet, animation.quads[spriteNum])<br />
</source><br />
<br />
And we are done! You should have a walking dude in the top left corner of your window when you execute this code!<br />
<br />
You can change where and how it is drawn by providing more parameters to the [[love.graphics.draw|draw function]].<br />
<br />
'''Disclaimer: This code is not game ready! It is meant to explain the basic logic behind animations. If you are searching for game ready code take a look at the list of [[Libraries]] available!'''<br />
<br />
== Task ==<br />
<br />
To improve this code. Try to rewrite the update and draw function to be able to handle multiple animations.<br />
<br />
You can load the same animation multiple times and store them in a special table to get started right away!<br />
<br />
As hint as to how it could be done: You could put the drawing logic in the animation table as well. And build it so you can call "animation:draw(x, y, r, sx, sy, [...])" to draw it on the screen. <br />
<br />
Good luck and have fun!<br />
<br />
== Full Sourcecode ==<br />
<br />
<source lang="lua"><br />
function love.load()<br />
animation = newAnimation(love.graphics.newImage("oldHero.png"), 16, 18, 1)<br />
end<br />
<br />
function love.update(dt)<br />
animation.currentTime = animation.currentTime + dt<br />
if animation.currentTime >= animation.duration then<br />
animation.currentTime = animation.currentTime - animation.duration<br />
end<br />
end<br />
<br />
function love.draw()<br />
local spriteNum = math.floor(animation.currentTime / animation.duration * #animation.quads) + 1<br />
love.graphics.draw(animation.spriteSheet, animation.quads[spriteNum], 0, 0, 0, 4)<br />
end<br />
<br />
function newAnimation(image, width, height, duration)<br />
local animation = {}<br />
animation.spriteSheet = image;<br />
animation.quads = {};<br />
<br />
for y = 0, image:getHeight() - height, height do<br />
for x = 0, image:getWidth() - width, width do<br />
table.insert(animation.quads, love.graphics.newQuad(x, y, width, height, image:getDimensions()))<br />
end<br />
end<br />
<br />
animation.duration = duration or 1<br />
animation.currentTime = 0<br />
<br />
return animation<br />
end<br />
</source><br />
<br />
== Other Languages ==<br />
{{i18n|Tutorial:Animation}}</div>Erasiohttp://love2d.org/w/index.php?title=Tutorial:Animation&diff=20375Tutorial:Animation2017-08-13T11:56:00Z<p>Erasio: </p>
<hr />
<div>Before we begin. This is a tutorial for semi advanced users. You are expected to know about tables, loops and the basics of drawing in Löve2D. And of course how to run a Löve2D game.<br />
<br />
Alright. Let's get to it shall we?<br />
<br />
I'm going to cover the basics of sprite based animation. This means you have a series of images which are displayed one after another. If you intent to create your own spritesheet make sure to leave at least 1px of pure transparency between the individual sprites. Otherwise you might see artifacts from the next or previous image and nobody wants that!<br />
<br />
== Loading animations ==<br />
<br />
For this tutorial we'll be using a unnamed old hero. Generously made available for everyone by GrafxKid on [https://opengameart.org/content/classic-hero OpenGameArt.org]. Download the image below and place it next to your main.lua in the folder.<br />
<br />
[[File:oldHero.png|200px|thumb|center|oldHero.png]]<br />
<br />
To be able to create multiple animations we want a reusable function that provides us with a table that we can use for everything related to the animation.<br />
We start this by defining a new function, storing our image in a variable and creating a new local table (the return value will be our table. We don't want some random animation table be globally defined or worse yet overwrite other data!)<br />
<br />
The following parameters are going to be needed:<br />
<br />
* image <br />
An image object created with love.graphics.newImage(filepath)<br />
* width <br />
The width of each individual sprite. We are going to assume all sprites have the same size.<br />
* height <br />
The height of each individual sprite.<br />
* duration <br />
How long the animation takes before it loops back to the first frame.<br />
<br />
<source lang="lua"><br />
function newAnimation(image, width, height, duration)<br />
local animation = {}<br />
animation.spriteSheet = image;<br />
<br />
return animation<br />
end<br />
</source><br />
<br />
You can try to just draw this directly ("love.graphics.draw(animation.spriteSheet)"). However. Then we'd just have all the images drawn next to each other. Certainly not what we want. Which is where [[Quad|Quads]] come in very handy! <br />
<br />
They define a part of the image which will be drawn instead of the whole image. Exactly what we need!<br />
<br />
Now we could define each quad individually. But that's going to be very annoying if we have several animations and hundreds of sprites! So instead we will load it within a loop that detects how many sprites are within an image. For this setup there must not be empty spaces and the image must contain exactly one animation.<br />
<br />
<source lang="lua"><br />
function newAnimation(image, width, height, duration)<br />
local animation = {}<br />
animation.spriteSheet = image;<br />
animation.quads = {};<br />
<br />
for y = 0, image:getHeight() - height, height do<br />
for x = 0, image:getWidth() - width, width do<br />
table.insert(animation.quads, love.graphics.newQuad(x, y, width, height, image:getDimensions()))<br />
end<br />
end<br />
<br />
return animation<br />
end<br />
</source><br />
<br />
The 3 parameters to the for loop mean the following:<br />
<br />
1. The starting value for y or x.<br />
<br />
2. The maximum value (in this case the total width or height of the reference image)<br />
<br />
3. How much should be added per step. In our case the size of the sprite.<br />
<br />
With this we get the location on the sprite sheet from y and x!<br />
<br />
You might wonder why we do "image:getHeight() - height" instead of just testing against the height of the image. This is because we want to make sure another sprite still fits on the sprite sheet. The sheet itself might not have the exact size we want it to. If it were one pixel too large. We do not want to add another quad (which would render as nothing) but instead ignore it. <br />
<br />
Now we have 6 Quads ranging from index 1 - 6 in the table. Awesome!<br />
<br />
But we have the issue that we have essentially 6 images that we can draw individually. But we need to draw them one after another over time.<br />
Also we don't just want to play this animation. We might want to change the speed at which it plays.<br />
<br />
To cover this we add two more variables to our animation table.<br />
Duration (which we expect as parameter) and currentTime which is used to measure how much time has passed.<br />
<br />
<source lang="lua"><br />
function newAnimation(image, width, height, duration)<br />
local animation = {}<br />
animation.spriteSheet = image;<br />
animation.quads = {};<br />
<br />
for y = 0, image:getHeight() - height, height do<br />
for x = 0, image:getWidth() - width, width do<br />
table.insert(animation.quads, love.graphics.newQuad(x, y, width, height, image:getDimensions()))<br />
end<br />
end<br />
<br />
animation.duration = duration or 1<br />
animation.currentTime = 0<br />
<br />
return animation<br />
end<br />
</source><br />
<br />
Which concludes our animation creation function already!<br />
<br />
== Updating the animation == <br />
<br />
Next up we need to load our animation table (call our newly created function) and update our current time.<br />
<br />
<source lang="lua"><br />
function love.load()<br />
animation = newAnimation(love.graphics.newImage("oldHero.png"), 16, 18, 1)<br />
end<br />
<br />
function love.update(dt)<br />
animation.currentTime = animation.currentTime + dt<br />
if animation.currentTime >= animation.duration then<br />
animation.currentTime = animation.currentTime - animation.duration<br />
end<br />
end<br />
</source><br />
<br />
The demo sprite sheet has a sprite size of 16 pixels wide and 18 pixels high. We intent to play all images over the course of 1 second.<br />
<br />
In love.update we simply add dt (delta time aka the time since the last frame) to our current time. We now count upwards continuously!<br />
<br />
But we will use the current time to determine which frame should be shown. As such we will want it to be between 0 and the value of "duration". The if simply checks if "currentTime" is more than "duration" in which case we subtract "duration". <br />
<br />
"dt" will never be an exact number. So currentTime will also always be a number with a lot of decimal points. To not get inaccurate we subtract the duration from the current time rather than setting it to 0. <br />
<br />
Now for the really interesting part!<br />
<br />
== Drawing the animation == <br />
<br />
How do we draw this?<br />
<br />
Well. We have the duration and current time. With this info we can calculate a percentage! How much of the animation has passed so far?<br />
<br />
If you've followed this tutorial correctly so far ''"currentTime / duration"'' will provide you with a number between 0 and 1. Which represents the percentage. 0.25 means 25% of the animation has passed.<br />
<br />
With that. We can search for the correct image to use! Since we already have a number betwee 0 and 1 we can simply multiply this percentage with our total amount of images and get a number between 0 and 6!<br />
<br />
''currentTime / duration * #quads''<br />
<br />
However. If we try to get this from our table we will run into the issue that this is not a whole number. But our images are stored with whole numbers! So attempting to get the image at index "4.75" will give us nothing. Bummer! <br />
<br />
Fear not. The solution is not too difficult.<br />
<br />
"currentTime" will be a number between 0 and just below "duration" (because we reduce "currentTime" if it is larger or ''equal'' "duration"). Which would result in a number between 0 and 5.<br />
<br />
To transform this value from our decimal point value to a whole number between 1 and 6 we do the following:<br />
<br />
<source lang="lua"><br />
math.floor(currentTime / duration * #quads) + 1<br />
</source><br />
<br />
"math.floor" provides us with the next lower number. Which means in our case a number between 0 and 5. We add one pushing it to a number between 1 and 6. All the sprites we have!<br />
<br />
Lövely!<br />
<br />
Alright. So all that's left is to draw the appropriate quad!<br />
<br />
This simply requires us to provide "love.graphics.draw" with the image reference (our spriteSheet) and the quad we want to use. Simple enough!<br />
<br />
<source lang="lua"><br />
local spriteNum = math.floor(animation.currentTime / animation.duration * #animation.quads) + 1<br />
love.graphics.draw(animation.spriteSheet, animation.quads[spriteNum])<br />
</source><br />
<br />
And we are done! You should have a walking dude in the top left corner of your window when you execute this code!<br />
<br />
You can change where and how it is drawn by providing more parameters to the [[love.graphics.draw|draw function]].<br />
<br />
== Task ==<br />
<br />
To improve this code. Try to rewrite the update and draw function to be able to handle multiple animations.<br />
<br />
You can load the same animation multiple times and store them in a special table to get started right away!<br />
<br />
Good luck and have fun!<br />
<br />
== Full Sourcecode ==<br />
<br />
<source lang="lua"><br />
function love.load()<br />
animation = newAnimation(love.graphics.newImage("oldHero.png"), 16, 18, 1)<br />
end<br />
<br />
function love.update(dt)<br />
animation.currentTime = animation.currentTime + dt<br />
if animation.currentTime >= animation.duration then<br />
animation.currentTime = animation.currentTime - animation.duration<br />
end<br />
end<br />
<br />
function love.draw()<br />
local spriteNum = math.floor(animation.currentTime / animation.duration * #animation.quads) + 1<br />
love.graphics.draw(animation.spriteSheet, animation.quads[spriteNum], 0, 0, 0, 4)<br />
end<br />
<br />
function newAnimation(image, width, height, duration)<br />
local animation = {}<br />
animation.spriteSheet = image;<br />
animation.quads = {};<br />
<br />
for y = 0, image:getHeight() - height, height do<br />
for x = 0, image:getWidth() - width, width do<br />
table.insert(animation.quads, love.graphics.newQuad(x, y, width, height, image:getDimensions()))<br />
end<br />
end<br />
<br />
animation.duration = duration or 1<br />
animation.currentTime = 0<br />
<br />
return animation<br />
end<br />
</source><br />
<br />
== Other Languages ==<br />
{{i18n|Tutorial:Animation}}</div>Erasiohttp://love2d.org/w/index.php?title=File:oldHero.png&diff=20374File:oldHero.png2017-08-13T11:35:38Z<p>Erasio: Erasio uploaded a new version of File:oldHero.png</p>
<hr />
<div>Spritesheet for animation tutorial.<br />
<br />
Public Domain License<br />
https://opengameart.org/content/classic-hero</div>Erasiohttp://love2d.org/w/index.php?title=File:oldHero.png&diff=20373File:oldHero.png2017-08-13T10:31:06Z<p>Erasio: Spritesheet for animation tutorial.
Public Domain License
https://opengameart.org/content/classic-hero</p>
<hr />
<div>Spritesheet for animation tutorial.<br />
<br />
Public Domain License<br />
https://opengameart.org/content/classic-hero</div>Erasiohttp://love2d.org/w/index.php?title=Tutorial:Animation&diff=20366Tutorial:Animation2017-08-12T23:54:54Z<p>Erasio: Created page with "Before we begin. This is a tutorial for semi advanced users. You are expected to know about tables, loops and the basics of drawing in Löve2D. And of course how to run a Löv..."</p>
<hr />
<div>Before we begin. This is a tutorial for semi advanced users. You are expected to know about tables, loops and the basics of drawing in Löve2D. And of course how to run a Löve2D game.<br />
<br />
<br />
== Explenation ==<br />
<br />
<br />
Alright. Let's get to it shall we?<br />
<br />
I'm going to cover the basics of sprite based animation. This means you have a series of images which are displayed one after another. If you intent to create your own spritesheet make sure to leave at least 1px of pure transparency between the individual sprites. Otherwise you might see artifacts from the next or previous image.<br />
<br />
For this tutorial we'll be using Tim. The protagonist from the game ''Braid'' because the artist generously made the sprite sheet available! Specifically the walk cycle which you can see below. Download the image and place it next to your main.lua in the folder.<br />
<br />
[[File:braid.png|200px|thumb|center|braid.png]]<br />
<br />
So. Now we have 27 individual images. First we wanna load the image.<br />
<br />
<source lang="lua"><br />
function love.load()<br />
animation = {}<br />
animation.spriteSheet = love.graphics.newImage("braid.png");<br />
end<br />
</source><br />
<br />
Encapsulated within the variable "animation" because we'll add a few more values to that and don't want them to be accessible globally as they are only relevant to this animation.<br />
<br />
You can try to just draw this directly ("love.graphics.draw(animation.spriteSheet)"). However. Then we'd just have all the images drawn next to each other. Certainly not what we want. Which is where [[Quads|Quad]] come in very handy! <br />
<br />
They define a part of the image which will be drawn instead of the whole image. Exactly what we need!<br />
<br />
<source lang="lua"><br />
function love.load()<br />
animation = {}<br />
animation.spriteSheet = love.graphics.newImage("braid.png");<br />
animation.quads = {<br />
love.graphics.newQuad( 0, 0, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 130, 0, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 260, 0, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 390, 0, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 520, 0, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 650, 0, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 780, 0, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 0, 150, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 130, 150, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 260, 150, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 390, 150, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 520, 150, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 650, 150, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 780, 150, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 0, 300, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 130, 300, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 260, 300, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 390, 300, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 520, 300, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 650, 300, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 780, 300, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 0, 450, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 130, 450, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 260, 450, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 390, 450, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 520, 450, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 650, 450, 130, 150, animation.spriteSheet:getDimensions())<br />
}<br />
end<br />
</source><br />
<br />
Now we have 27 Quads ranging from index 1 - 27 in the table "quads". Awesome!<br />
<br />
But we have the issue that we have essentially 27 images that we can draw individually. But we need to draw them one after another over time.<br />
Also we don't just want to play this animation. We might want to change the speed at which it plays.<br />
<br />
To cover this we start by defining a variable which contains the duration.<br />
We also need to measure the time this animation has been running so far. Which requires another variable.<br />
<br />
<source lang="lua"><br />
function love.load()<br />
animation = {}<br />
animation.spriteSheet = love.graphics.newImage("braid.png");<br />
animation.quads = {<br />
-- To keep this shorter I don't repeat the quad loading. All of them should still be in here!<br />
}<br />
<br />
animation.duration = 2<br />
animation.currentTime = 0<br />
end<br />
</source><br />
<br />
And our load part is done!<br />
<br />
Next up we need to update our current time.<br />
<br />
<source lang="lua"><br />
function love.update(dt)<br />
animation.currentTime = animation.currentTime + dt<br />
if animation.currentTime >= animation.duration then<br />
animation.currentTime = animation.currentTime - animation.duration<br />
end<br />
end<br />
</source><br />
<br />
First we simply add dt (delta time aka the time since the last frame) to our current time. We now count upwards continuously!<br />
<br />
But we will use the current time to determine which frame should be shown. As such we will want it to be between 0 and the value of "duration". The if simply checks if "currentTime" is more than duration in which case we subtract the duration. "dt" will never be an exact number. So currentTime will also always be a number with a lot of decimal points. To not get inaccurate we subtract the duration from the current time rather than setting it to 0. <br />
<br />
Now for the really interesting part!<br />
<br />
How do we draw this?<br />
<br />
Well. We have the duration and current time. With this info we can calculate a percentage! How much of the animation has passed so far?<br />
<br />
If you've followed this tutorial correctly so far ''"currentTime / duration"'' will provide you with a number between 0 and 1. Which represents the percentage. 0.25 means 25% of the animation has passed.<br />
<br />
With that. We can search for the correct image to use! Since we already have a number betwee 0 and 1 we can simply multiply this percentage with our total amount of images and get a number between 0 and 27!<br />
<br />
''currentTime / duration * #quads''<br />
<br />
However. If we try to get this from our table we will run into the issue that this is not a whole number. But our images are stored with whole numbers! So attempting to get the image at index "6.75" will give us nothing. Bummer! <br />
<br />
Fear not. The solution is not too difficult.<br />
<br />
"currentTime" will be a number between 0 and just below "duration" (because we reduce "currentTime" if it is larger or ''equal'' "duration")<br />
<br />
To transform this value from our decimal point value to a whole number we do the following:<br />
<br />
<source lang="lua"><br />
math.floor(currentTime / duration * #quads) + 1<br />
</source><br />
<br />
"math.floor" provides us with the next lower number. Which means in our case a number between 0 and 26. We add one pushing it to a number between 1 and 27. All the sprites we have!<br />
<br />
Lövely!<br />
<br />
Alright. So all that's left is to draw the appropriate quad!<br />
<br />
This simply requires us to provide "love.graphics.draw" with the image reference (our spriteSheet) and the quad we want to use. Simple enough!<br />
<br />
<source lang="lua"><br />
local spriteNum = math.floor(animation.currentTime / animation.duration * #animation.quads) + 1<br />
love.graphics.draw(animation.spriteSheet, animation.quads[spriteNum])<br />
</source><br />
<br />
And we are done! You should have a walking Tim in the top left corner of your window when you execute this code!<br />
<br />
<br />
== Training ==<br />
<br />
<br />
This is not a setup that should be used for the development of games. You want a function to handle all of what we just did and which simply returns the animation table. Including loading the quads automatically rather than individually defining them.<br />
<br />
This can be done by creating a function which takes the parameters:<br />
* SpriteSheet (an image object created with "love.graphics.newImage(filepath)"<br />
* Duration (a number)<br />
* Total Quads (a number)<br />
* SpriteWidth (a number)<br />
* SpriteHeight (a number)<br />
<br />
You can then get the size of the image with "SpriteSheet:getDimensions()". Until you reached the total amount of quads, you look if the image is large enough to fit another sprite ("current location on the spriteSheet" + "spriteWidth"), if so, create a quad with this location and test the next position. Otherwise check the next row. (the first and second parameter of "love.graphics.newQuad" are the location on the image. First x (sideways) and then y (upwards))<br />
<br />
With only "animation" tables. You can then start a table containing only animation tables (''table.insert(animations, newAnimation)'') and iterate over all your animation objects and all of them, instead of just one like we did here.<br />
<br />
All of this works just fine with our current sprite sheet. <br />
<br />
This part will be up to you though. Good luck and have fun!<br />
<br />
== Sourcecode ==<br />
<br />
<br />
Full sourcecode of this tutorial (excluding the task at the end):<br />
<br />
<source lang="lua"><br />
function love.load()<br />
animation = {}<br />
animation.spriteSheet = love.graphics.newImage("braid.png");<br />
animation.quads = {<br />
love.graphics.newQuad( 0, 0, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 130, 0, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 260, 0, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 390, 0, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 520, 0, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 650, 0, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 780, 0, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 0, 150, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 130, 150, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 260, 150, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 390, 150, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 520, 150, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 650, 150, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 780, 150, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 0, 300, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 130, 300, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 260, 300, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 390, 300, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 520, 300, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 650, 300, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 780, 300, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 0, 450, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 130, 450, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 260, 450, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 390, 450, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 520, 450, 130, 150, animation.spriteSheet:getDimensions()),<br />
love.graphics.newQuad( 650, 450, 130, 150, animation.spriteSheet:getDimensions())<br />
}<br />
<br />
animation.duration = 2<br />
animation.currentTime = 0<br />
end<br />
<br />
function love.update(dt)<br />
animation.currentTime = animation.currentTime + dt<br />
if animation.currentTime >= animation.duration then<br />
animation.currentTime = animation.currentTime - animation.duration<br />
end<br />
end<br />
<br />
function love.draw()<br />
local spriteNum = math.ceil(animation.currentTime / animation.duration * #animation.quads)<br />
love.graphics.draw(animation.spriteSheet, animation.quads[spriteNum])<br />
end<br />
</source></div>Erasiohttp://love2d.org/w/index.php?title=File:braid.png&diff=20365File:braid.png2017-08-12T23:52:56Z<p>Erasio: Sprite sheet for the animation tutorial.</p>
<hr />
<div>Sprite sheet for the animation tutorial.</div>Erasio