├── .gitattributes
├── Episode 1
├── README.md
├── assets
│ ├── Licence.md
│ ├── background.png
│ ├── bit.ttf
│ ├── coin.png
│ ├── enemy
│ │ ├── run
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ ├── 4.png
│ │ │ └── Untitled-1.psd
│ │ └── walk
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ ├── 4.png
│ │ │ └── Untitled-1.psd
│ ├── heart.png
│ ├── player
│ │ ├── air
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ └── 4.png
│ │ ├── idle
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ └── 4.png
│ │ ├── jump
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ └── 4.png
│ │ └── run
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ ├── 4.png
│ │ │ ├── 5.png
│ │ │ └── 6.png
│ ├── sfx
│ │ ├── player_get_coin.ogg
│ │ ├── player_hit.ogg
│ │ └── player_jump.ogg
│ ├── spikes.png
│ ├── square.png
│ ├── stone.png
│ └── tiles.png
├── conf.lua
├── main.lua
├── map
│ ├── 1.lua
│ └── tmx
│ │ └── 1.tmx
├── run.bat
└── sti
│ ├── graphics.lua
│ ├── init.lua
│ ├── plugins
│ ├── box2d.lua
│ └── bump.lua
│ └── utils.lua
├── Episode 2
├── README.md
├── assets
│ ├── background.png
│ ├── bit.ttf
│ ├── coin.png
│ ├── enemy
│ │ ├── run
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ ├── 4.png
│ │ │ └── Untitled-1.psd
│ │ └── walk
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ ├── 4.png
│ │ │ └── Untitled-1.psd
│ ├── heart.png
│ ├── player
│ │ ├── air
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ └── 4.png
│ │ ├── idle
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ └── 4.png
│ │ ├── jump
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ └── 4.png
│ │ └── run
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ ├── 4.png
│ │ │ ├── 5.png
│ │ │ └── 6.png
│ ├── sfx
│ │ ├── player_get_coin.ogg
│ │ ├── player_hit.ogg
│ │ └── player_jump.ogg
│ ├── spikes.png
│ ├── square.png
│ ├── stone.png
│ └── tiles.png
├── conf.lua
├── main.lua
├── map
│ ├── 1.lua
│ └── tmx
│ │ └── 1.tmx
├── player.lua
├── run.bat
└── sti
│ ├── graphics.lua
│ ├── init.lua
│ ├── plugins
│ ├── box2d.lua
│ └── bump.lua
│ └── utils.lua
├── Episode 3
├── README.md
├── assets
│ ├── background.png
│ ├── bit.ttf
│ ├── coin.png
│ ├── enemy
│ │ ├── run
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ ├── 4.png
│ │ │ └── Untitled-1.psd
│ │ └── walk
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ ├── 4.png
│ │ │ └── Untitled-1.psd
│ ├── heart.png
│ ├── player
│ │ ├── air
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ └── 4.png
│ │ ├── idle
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ └── 4.png
│ │ ├── jump
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ └── 4.png
│ │ └── run
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ ├── 4.png
│ │ │ ├── 5.png
│ │ │ └── 6.png
│ ├── sfx
│ │ ├── player_get_coin.ogg
│ │ ├── player_hit.ogg
│ │ └── player_jump.ogg
│ ├── spikes.png
│ ├── square.png
│ ├── stone.png
│ └── tiles.png
├── conf.lua
├── main.lua
├── map
│ ├── 1.lua
│ └── tmx
│ │ └── 1.tmx
├── player.lua
├── run.bat
└── sti
│ ├── graphics.lua
│ ├── init.lua
│ ├── plugins
│ ├── box2d.lua
│ └── bump.lua
│ └── utils.lua
├── Episode 4
├── README.md
├── assets
│ ├── background.png
│ ├── bit.ttf
│ ├── coin.png
│ ├── enemy
│ │ ├── run
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ ├── 4.png
│ │ │ └── Untitled-1.psd
│ │ └── walk
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ ├── 4.png
│ │ │ └── Untitled-1.psd
│ ├── heart.png
│ ├── player
│ │ ├── air
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ └── 4.png
│ │ ├── idle
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ └── 4.png
│ │ ├── jump
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ └── 4.png
│ │ └── run
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ ├── 4.png
│ │ │ ├── 5.png
│ │ │ └── 6.png
│ ├── sfx
│ │ ├── player_get_coin.ogg
│ │ ├── player_hit.ogg
│ │ └── player_jump.ogg
│ ├── spikes.png
│ ├── square.png
│ ├── stone.png
│ └── tiles.png
├── coin.lua
├── conf.lua
├── gui.lua
├── main.lua
├── map
│ ├── 1.lua
│ └── tmx
│ │ └── 1.tmx
├── player.lua
├── run.bat
└── sti
│ ├── graphics.lua
│ ├── init.lua
│ ├── plugins
│ ├── box2d.lua
│ └── bump.lua
│ └── utils.lua
├── Episode 5
├── README.md
├── assets
│ ├── background.png
│ ├── bit.ttf
│ ├── coin.png
│ ├── enemy
│ │ ├── run
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ ├── 4.png
│ │ │ └── Untitled-1.psd
│ │ └── walk
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ ├── 4.png
│ │ │ └── Untitled-1.psd
│ ├── heart.png
│ ├── player
│ │ ├── air
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ └── 4.png
│ │ ├── idle
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ └── 4.png
│ │ ├── jump
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ └── 4.png
│ │ └── run
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ ├── 4.png
│ │ │ ├── 5.png
│ │ │ └── 6.png
│ ├── sfx
│ │ ├── player_get_coin.ogg
│ │ ├── player_hit.ogg
│ │ └── player_jump.ogg
│ ├── spike.png
│ ├── square.png
│ ├── stone.png
│ └── tiles.png
├── camera.lua
├── coin.lua
├── conf.lua
├── gui.lua
├── main.lua
├── map
│ ├── 1.lua
│ └── tmx
│ │ └── 1.tmx
├── player.lua
├── run.bat
├── spike.lua
├── sti
│ ├── graphics.lua
│ ├── init.lua
│ ├── plugins
│ │ ├── box2d.lua
│ │ └── bump.lua
│ └── utils.lua
└── stone.lua
├── Episode 6
├── .gitattributes
├── README.md
├── assets
│ ├── background.png
│ ├── bit.ttf
│ ├── coin.png
│ ├── enemy
│ │ ├── run
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ ├── 4.png
│ │ │ └── Untitled-1.psd
│ │ └── walk
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ ├── 4.png
│ │ │ └── Untitled-1.psd
│ ├── heart.png
│ ├── player
│ │ ├── air
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ └── 4.png
│ │ ├── idle
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ └── 4.png
│ │ ├── jump
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ └── 4.png
│ │ └── run
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ ├── 4.png
│ │ │ ├── 5.png
│ │ │ └── 6.png
│ ├── sfx
│ │ ├── player_get_coin.ogg
│ │ ├── player_hit.ogg
│ │ └── player_jump.ogg
│ ├── spike.png
│ ├── square.png
│ ├── stone.png
│ └── tiles.png
├── camera.lua
├── coin.lua
├── conf.lua
├── enemy.lua
├── gui.lua
├── main.lua
├── map.lua
├── map
│ ├── 1.lua
│ ├── 2.lua
│ └── tmx
│ │ ├── 1.tmx
│ │ └── 2.tmx
├── player.lua
├── run.bat
├── spike.lua
├── sti
│ ├── graphics.lua
│ ├── init.lua
│ ├── plugins
│ │ ├── box2d.lua
│ │ └── bump.lua
│ └── utils.lua
└── stone.lua
└── README.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/Episode 1/README.md:
--------------------------------------------------------------------------------
1 | Full source code and assets from the tutorial series.
2 | Note: The code and assets have different licences
3 |
4 | Code: WTFPL (Do whatever you want, no restrictions)
5 |
6 | Art: Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License (BY-NC-ND) (https://creativecommons.org/licenses/by-nc-nd/4.0/)
7 |
--------------------------------------------------------------------------------
/Episode 1/assets/Licence.md:
--------------------------------------------------------------------------------
1 | BY-NC-ND (https://creativecommons.org/licenses/by-nc-nd/4.0/)
2 |
--------------------------------------------------------------------------------
/Episode 1/assets/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/background.png
--------------------------------------------------------------------------------
/Episode 1/assets/bit.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/bit.ttf
--------------------------------------------------------------------------------
/Episode 1/assets/coin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/coin.png
--------------------------------------------------------------------------------
/Episode 1/assets/enemy/run/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/enemy/run/1.png
--------------------------------------------------------------------------------
/Episode 1/assets/enemy/run/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/enemy/run/2.png
--------------------------------------------------------------------------------
/Episode 1/assets/enemy/run/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/enemy/run/3.png
--------------------------------------------------------------------------------
/Episode 1/assets/enemy/run/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/enemy/run/4.png
--------------------------------------------------------------------------------
/Episode 1/assets/enemy/run/Untitled-1.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/enemy/run/Untitled-1.psd
--------------------------------------------------------------------------------
/Episode 1/assets/enemy/walk/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/enemy/walk/1.png
--------------------------------------------------------------------------------
/Episode 1/assets/enemy/walk/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/enemy/walk/2.png
--------------------------------------------------------------------------------
/Episode 1/assets/enemy/walk/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/enemy/walk/3.png
--------------------------------------------------------------------------------
/Episode 1/assets/enemy/walk/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/enemy/walk/4.png
--------------------------------------------------------------------------------
/Episode 1/assets/enemy/walk/Untitled-1.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/enemy/walk/Untitled-1.psd
--------------------------------------------------------------------------------
/Episode 1/assets/heart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/heart.png
--------------------------------------------------------------------------------
/Episode 1/assets/player/air/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/player/air/1.png
--------------------------------------------------------------------------------
/Episode 1/assets/player/air/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/player/air/2.png
--------------------------------------------------------------------------------
/Episode 1/assets/player/air/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/player/air/3.png
--------------------------------------------------------------------------------
/Episode 1/assets/player/air/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/player/air/4.png
--------------------------------------------------------------------------------
/Episode 1/assets/player/idle/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/player/idle/1.png
--------------------------------------------------------------------------------
/Episode 1/assets/player/idle/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/player/idle/2.png
--------------------------------------------------------------------------------
/Episode 1/assets/player/idle/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/player/idle/3.png
--------------------------------------------------------------------------------
/Episode 1/assets/player/idle/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/player/idle/4.png
--------------------------------------------------------------------------------
/Episode 1/assets/player/jump/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/player/jump/1.png
--------------------------------------------------------------------------------
/Episode 1/assets/player/jump/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/player/jump/2.png
--------------------------------------------------------------------------------
/Episode 1/assets/player/jump/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/player/jump/3.png
--------------------------------------------------------------------------------
/Episode 1/assets/player/jump/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/player/jump/4.png
--------------------------------------------------------------------------------
/Episode 1/assets/player/run/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/player/run/1.png
--------------------------------------------------------------------------------
/Episode 1/assets/player/run/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/player/run/2.png
--------------------------------------------------------------------------------
/Episode 1/assets/player/run/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/player/run/3.png
--------------------------------------------------------------------------------
/Episode 1/assets/player/run/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/player/run/4.png
--------------------------------------------------------------------------------
/Episode 1/assets/player/run/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/player/run/5.png
--------------------------------------------------------------------------------
/Episode 1/assets/player/run/6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/player/run/6.png
--------------------------------------------------------------------------------
/Episode 1/assets/sfx/player_get_coin.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/sfx/player_get_coin.ogg
--------------------------------------------------------------------------------
/Episode 1/assets/sfx/player_hit.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/sfx/player_hit.ogg
--------------------------------------------------------------------------------
/Episode 1/assets/sfx/player_jump.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/sfx/player_jump.ogg
--------------------------------------------------------------------------------
/Episode 1/assets/spikes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/spikes.png
--------------------------------------------------------------------------------
/Episode 1/assets/square.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/square.png
--------------------------------------------------------------------------------
/Episode 1/assets/stone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/stone.png
--------------------------------------------------------------------------------
/Episode 1/assets/tiles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 1/assets/tiles.png
--------------------------------------------------------------------------------
/Episode 1/conf.lua:
--------------------------------------------------------------------------------
1 |
2 | function love.conf(t)
3 | t.title = "Platformer" -- The title of the window the game is in (string)
4 | t.version = "11.3" -- The LÖVE version this game was made for (string)
5 | t.console = true -- Attach a console (boolean, Windows only)
6 | t.window.width = 1280 -- The window width (number)
7 | t.window.height = 720 -- The window height (number)
8 | t.window.vsync = 0
9 | end
10 |
--------------------------------------------------------------------------------
/Episode 1/main.lua:
--------------------------------------------------------------------------------
1 | local STI = require("sti")
2 |
3 | function love.load()
4 | Map = STI("map/1.lua", {"box2d"}) -- Load the our map file using the library STI
5 | World = love.physics.newWorld(0,0) -- Creates the physics-world (Box2d)
6 | Map:box2d_init(World) --STI loads our solid layer and creates it into the physics-world.
7 | Map.layers.solid.visible = false -- Hides the solid layer from drawing.
8 | background = love.graphics.newImage("assets/background.png") -- Load the background image
9 | end
10 |
11 | function love.update(dt)
12 | World:update(dt) --Updates the physical world so that objects can move.
13 | end
14 |
15 | function love.draw()
16 | love.graphics.draw(background) -- Draws the background image at 0,0
17 | Map:draw(0, 0, 2, 2) -- Draws the Map at 0,0 with a scale of 200%
18 | love.graphics.push() -- "Saves" the current state
19 | love.graphics.scale(2,2) --Scales everything to 200%
20 | --Anything drawn here will have 200% scale
21 | love.graphics.pop() -- Returns to the saved state, everything is back to 100% scale
22 | end
23 |
--------------------------------------------------------------------------------
/Episode 1/map/tmx/1.tmx:
--------------------------------------------------------------------------------
1 |
2 |
81 |
--------------------------------------------------------------------------------
/Episode 1/run.bat:
--------------------------------------------------------------------------------
1 | @ECHO
2 |
3 | start "" "C:\Program Files\LOVE\lovec" .
--------------------------------------------------------------------------------
/Episode 1/sti/graphics.lua:
--------------------------------------------------------------------------------
1 | local lg = _G.love.graphics
2 | local graphics = { isCreated = lg and true or false }
3 |
4 | function graphics.newSpriteBatch(...)
5 | if graphics.isCreated then
6 | return lg.newSpriteBatch(...)
7 | end
8 | end
9 |
10 | function graphics.newCanvas(...)
11 | if graphics.isCreated then
12 | return lg.newCanvas(...)
13 | end
14 | end
15 |
16 | function graphics.newImage(...)
17 | if graphics.isCreated then
18 | return lg.newImage(...)
19 | end
20 | end
21 |
22 | function graphics.newQuad(...)
23 | if graphics.isCreated then
24 | return lg.newQuad(...)
25 | end
26 | end
27 |
28 | function graphics.getCanvas(...)
29 | if graphics.isCreated then
30 | return lg.getCanvas(...)
31 | end
32 | end
33 |
34 | function graphics.setCanvas(...)
35 | if graphics.isCreated then
36 | return lg.setCanvas(...)
37 | end
38 | end
39 |
40 | function graphics.clear(...)
41 | if graphics.isCreated then
42 | return lg.clear(...)
43 | end
44 | end
45 |
46 | function graphics.push(...)
47 | if graphics.isCreated then
48 | return lg.push(...)
49 | end
50 | end
51 |
52 | function graphics.origin(...)
53 | if graphics.isCreated then
54 | return lg.origin(...)
55 | end
56 | end
57 |
58 | function graphics.scale(...)
59 | if graphics.isCreated then
60 | return lg.scale(...)
61 | end
62 | end
63 |
64 | function graphics.translate(...)
65 | if graphics.isCreated then
66 | return lg.translate(...)
67 | end
68 | end
69 |
70 | function graphics.pop(...)
71 | if graphics.isCreated then
72 | return lg.pop(...)
73 | end
74 | end
75 |
76 | function graphics.draw(...)
77 | if graphics.isCreated then
78 | return lg.draw(...)
79 | end
80 | end
81 |
82 | function graphics.rectangle(...)
83 | if graphics.isCreated then
84 | return lg.rectangle(...)
85 | end
86 | end
87 |
88 | function graphics.getColor(...)
89 | if graphics.isCreated then
90 | return lg.getColor(...)
91 | end
92 | end
93 |
94 | function graphics.setColor(...)
95 | if graphics.isCreated then
96 | return lg.setColor(...)
97 | end
98 | end
99 |
100 | function graphics.line(...)
101 | if graphics.isCreated then
102 | return lg.line(...)
103 | end
104 | end
105 |
106 | function graphics.polygon(...)
107 | if graphics.isCreated then
108 | return lg.polygon(...)
109 | end
110 | end
111 |
112 | function graphics.points(...)
113 | if graphics.isCreated then
114 | return lg.points(...)
115 | end
116 | end
117 |
118 | function graphics.getWidth()
119 | if graphics.isCreated then
120 | return lg.getWidth()
121 | end
122 | return 0
123 | end
124 |
125 | function graphics.getHeight()
126 | if graphics.isCreated then
127 | return lg.getHeight()
128 | end
129 | return 0
130 | end
131 |
132 | return graphics
133 |
--------------------------------------------------------------------------------
/Episode 1/sti/plugins/bump.lua:
--------------------------------------------------------------------------------
1 | --- Bump.lua plugin for STI
2 | -- @module bump.lua
3 | -- @author David Serrano (BobbyJones|FrenchFryLord)
4 | -- @copyright 2019
5 | -- @license MIT/X11
6 |
7 | local lg = require((...):gsub('plugins.bump', 'graphics'))
8 |
9 | return {
10 | bump_LICENSE = "MIT/X11",
11 | bump_URL = "https://github.com/karai17/Simple-Tiled-Implementation",
12 | bump_VERSION = "3.1.7.1",
13 | bump_DESCRIPTION = "Bump hooks for STI.",
14 |
15 | --- Adds each collidable tile to the Bump world.
16 | -- @param world The Bump world to add objects to.
17 | -- @return collidables table containing the handles to the objects in the Bump world.
18 | bump_init = function(map, world)
19 | local collidables = {}
20 |
21 | for _, tileset in ipairs(map.tilesets) do
22 | for _, tile in ipairs(tileset.tiles) do
23 | local gid = tileset.firstgid + tile.id
24 |
25 | if map.tileInstances[gid] then
26 | for _, instance in ipairs(map.tileInstances[gid]) do
27 | -- Every object in every instance of a tile
28 | if tile.objectGroup then
29 | for _, object in ipairs(tile.objectGroup.objects) do
30 | if object.properties.collidable == true then
31 | local t = {
32 | name = object.name,
33 | type = object.type,
34 | x = instance.x + map.offsetx + object.x,
35 | y = instance.y + map.offsety + object.y,
36 | width = object.width,
37 | height = object.height,
38 | layer = instance.layer,
39 | properties = object.properties
40 |
41 | }
42 |
43 | world:add(t, t.x, t.y, t.width, t.height)
44 | table.insert(collidables, t)
45 | end
46 | end
47 | end
48 |
49 | -- Every instance of a tile
50 | if tile.properties and tile.properties.collidable == true then
51 | local t = {
52 | x = instance.x + map.offsetx,
53 | y = instance.y + map.offsety,
54 | width = map.tilewidth,
55 | height = map.tileheight,
56 | layer = instance.layer,
57 | properties = tile.properties
58 | }
59 |
60 | world:add(t, t.x, t.y, t.width, t.height)
61 | table.insert(collidables, t)
62 | end
63 | end
64 | end
65 | end
66 | end
67 |
68 | for _, layer in ipairs(map.layers) do
69 | -- Entire layer
70 | if layer.properties.collidable == true then
71 | if layer.type == "tilelayer" then
72 | for y, tiles in ipairs(layer.data) do
73 | for x, tile in pairs(tiles) do
74 |
75 | if tile.objectGroup then
76 | for _, object in ipairs(tile.objectGroup.objects) do
77 | if object.properties.collidable == true then
78 | local t = {
79 | name = object.name,
80 | type = object.type,
81 | x = ((x-1) * map.tilewidth + tile.offset.x + map.offsetx) + object.x,
82 | y = ((y-1) * map.tileheight + tile.offset.y + map.offsety) + object.y,
83 | width = object.width,
84 | height = object.height,
85 | layer = layer,
86 | properties = object.properties
87 | }
88 |
89 | world:add(t, t.x, t.y, t.width, t.height)
90 | table.insert(collidables, t)
91 | end
92 | end
93 | end
94 |
95 |
96 | local t = {
97 | x = (x-1) * map.tilewidth + tile.offset.x + map.offsetx,
98 | y = (y-1) * map.tileheight + tile.offset.y + map.offsety,
99 | width = tile.width,
100 | height = tile.height,
101 | layer = layer,
102 | properties = tile.properties
103 | }
104 |
105 | world:add(t, t.x, t.y, t.width, t.height)
106 | table.insert(collidables, t)
107 | end
108 | end
109 | elseif layer.type == "imagelayer" then
110 | world:add(layer, layer.x, layer.y, layer.width, layer.height)
111 | table.insert(collidables, layer)
112 | end
113 | end
114 |
115 | -- individual collidable objects in a layer that is not "collidable"
116 | -- or whole collidable objects layer
117 | if layer.type == "objectgroup" then
118 | for _, obj in ipairs(layer.objects) do
119 | if layer.properties.collidable == true or obj.properties.collidable == true then
120 | if obj.shape == "rectangle" then
121 | local t = {
122 | name = obj.name,
123 | type = obj.type,
124 | x = obj.x + map.offsetx,
125 | y = obj.y + map.offsety,
126 | width = obj.width,
127 | height = obj.height,
128 | layer = layer,
129 | properties = obj.properties
130 | }
131 |
132 | if obj.gid then
133 | t.y = t.y - obj.height
134 | end
135 |
136 | world:add(t, t.x, t.y, t.width, t.height)
137 | table.insert(collidables, t)
138 | end -- TODO implement other object shapes?
139 | end
140 | end
141 | end
142 | end
143 |
144 | map.bump_world = world
145 | map.bump_collidables = collidables
146 | end,
147 |
148 | --- Remove layer
149 | -- @param index to layer to be removed
150 | bump_removeLayer = function(map, index)
151 | local layer = assert(map.layers[index], "Layer not found: " .. index)
152 | local collidables = map.bump_collidables
153 |
154 | -- Remove collision objects
155 | for i = #collidables, 1, -1 do
156 | local obj = collidables[i]
157 |
158 | if obj.layer == layer
159 | and (
160 | layer.properties.collidable == true
161 | or obj.properties.collidable == true
162 | ) then
163 | map.bump_world:remove(obj)
164 | table.remove(collidables, i)
165 | end
166 | end
167 | end,
168 |
169 | --- Draw bump collisions world.
170 | -- @param world bump world holding the tiles geometry
171 | -- @param tx Translate on X
172 | -- @param ty Translate on Y
173 | -- @param sx Scale on X
174 | -- @param sy Scale on Y
175 | bump_draw = function(map, tx, ty, sx, sy)
176 | lg.push()
177 | lg.scale(sx or 1, sy or sx or 1)
178 | lg.translate(math.floor(tx or 0), math.floor(ty or 0))
179 |
180 | local items = map.bump_world:getItems()
181 | for _, item in ipairs(items) do
182 | lg.rectangle("line", map.bump_world:getRect(item))
183 | end
184 |
185 | lg.pop()
186 | end
187 | }
188 |
189 | --- Custom Properties in Tiled are used to tell this plugin what to do.
190 | -- @table Properties
191 | -- @field collidable set to true, can be used on any Layer, Tile, or Object
192 |
--------------------------------------------------------------------------------
/Episode 1/sti/utils.lua:
--------------------------------------------------------------------------------
1 | -- Some utility functions that shouldn't be exposed.
2 | local utils = {}
3 |
4 | -- https://github.com/stevedonovan/Penlight/blob/master/lua/pl/path.lua#L286
5 | function utils.format_path(path)
6 | local np_gen1,np_gen2 = '[^SEP]+SEP%.%.SEP?','SEP+%.?SEP'
7 | local np_pat1, np_pat2 = np_gen1:gsub('SEP','/'), np_gen2:gsub('SEP','/')
8 | local k
9 |
10 | repeat -- /./ -> /
11 | path,k = path:gsub(np_pat2,'/',1)
12 | until k == 0
13 |
14 | repeat -- A/../ -> (empty)
15 | path,k = path:gsub(np_pat1,'',1)
16 | until k == 0
17 |
18 | if path == '' then path = '.' end
19 |
20 | return path
21 | end
22 |
23 | -- Compensation for scale/rotation shift
24 | function utils.compensate(tile, tileX, tileY, tileW, tileH)
25 | local compx = 0
26 | local compy = 0
27 |
28 | if tile.sx < 0 then compx = tileW end
29 | if tile.sy < 0 then compy = tileH end
30 |
31 | if tile.r > 0 then
32 | tileX = tileX + tileH - compy
33 | tileY = tileY + tileH + compx - tileW
34 | elseif tile.r < 0 then
35 | tileX = tileX + compy
36 | tileY = tileY - compx + tileH
37 | else
38 | tileX = tileX + compx
39 | tileY = tileY + compy
40 | end
41 |
42 | return tileX, tileY
43 | end
44 |
45 | -- Cache images in main STI module
46 | function utils.cache_image(sti, path, image)
47 | image = image or love.graphics.newImage(path)
48 | image:setFilter("nearest", "nearest")
49 | sti.cache[path] = image
50 | end
51 |
52 | -- We just don't know.
53 | function utils.get_tiles(imageW, tileW, margin, spacing)
54 | imageW = imageW - margin
55 | local n = 0
56 |
57 | while imageW >= tileW do
58 | imageW = imageW - tileW
59 | if n ~= 0 then imageW = imageW - spacing end
60 | if imageW >= 0 then n = n + 1 end
61 | end
62 |
63 | return n
64 | end
65 |
66 | -- Decompress tile layer data
67 | function utils.get_decompressed_data(data)
68 | local ffi = require "ffi"
69 | local d = {}
70 | local decoded = ffi.cast("uint32_t*", data)
71 |
72 | for i = 0, data:len() / ffi.sizeof("uint32_t") do
73 | table.insert(d, tonumber(decoded[i]))
74 | end
75 |
76 | return d
77 | end
78 |
79 | -- Convert a Tiled ellipse object to a LOVE polygon
80 | function utils.convert_ellipse_to_polygon(x, y, w, h, max_segments)
81 | local ceil = math.ceil
82 | local cos = math.cos
83 | local sin = math.sin
84 |
85 | local function calc_segments(segments)
86 | local function vdist(a, b)
87 | local c = {
88 | x = a.x - b.x,
89 | y = a.y - b.y,
90 | }
91 |
92 | return c.x * c.x + c.y * c.y
93 | end
94 |
95 | segments = segments or 64
96 | local vertices = {}
97 |
98 | local v = { 1, 2, ceil(segments/4-1), ceil(segments/4) }
99 |
100 | local m
101 | if love and love.physics then
102 | m = love.physics.getMeter()
103 | else
104 | m = 32
105 | end
106 |
107 | for _, i in ipairs(v) do
108 | local angle = (i / segments) * math.pi * 2
109 | local px = x + w / 2 + cos(angle) * w / 2
110 | local py = y + h / 2 + sin(angle) * h / 2
111 |
112 | table.insert(vertices, { x = px / m, y = py / m })
113 | end
114 |
115 | local dist1 = vdist(vertices[1], vertices[2])
116 | local dist2 = vdist(vertices[3], vertices[4])
117 |
118 | -- Box2D threshold
119 | if dist1 < 0.0025 or dist2 < 0.0025 then
120 | return calc_segments(segments-2)
121 | end
122 |
123 | return segments
124 | end
125 |
126 | local segments = calc_segments(max_segments)
127 | local vertices = {}
128 |
129 | table.insert(vertices, { x = x + w / 2, y = y + h / 2 })
130 |
131 | for i = 0, segments do
132 | local angle = (i / segments) * math.pi * 2
133 | local px = x + w / 2 + cos(angle) * w / 2
134 | local py = y + h / 2 + sin(angle) * h / 2
135 |
136 | table.insert(vertices, { x = px, y = py })
137 | end
138 |
139 | return vertices
140 | end
141 |
142 | function utils.rotate_vertex(map, vertex, x, y, cos, sin, oy)
143 | if map.orientation == "isometric" then
144 | x, y = utils.convert_isometric_to_screen(map, x, y)
145 | vertex.x, vertex.y = utils.convert_isometric_to_screen(map, vertex.x, vertex.y)
146 | end
147 |
148 | vertex.x = vertex.x - x
149 | vertex.y = vertex.y - y
150 |
151 | return
152 | x + cos * vertex.x - sin * vertex.y,
153 | y + sin * vertex.x + cos * vertex.y - (oy or 0)
154 | end
155 |
156 | --- Project isometric position to cartesian position
157 | function utils.convert_isometric_to_screen(map, x, y)
158 | local mapW = map.width
159 | local tileW = map.tilewidth
160 | local tileH = map.tileheight
161 | local tileX = x / tileH
162 | local tileY = y / tileH
163 | local offsetX = mapW * tileW / 2
164 |
165 | return
166 | (tileX - tileY) * tileW / 2 + offsetX,
167 | (tileX + tileY) * tileH / 2
168 | end
169 |
170 | function utils.hex_to_color(hex)
171 | if hex:sub(1, 1) == "#" then
172 | hex = hex:sub(2)
173 | end
174 |
175 | return {
176 | r = tonumber(hex:sub(1, 2), 16) / 255,
177 | g = tonumber(hex:sub(3, 4), 16) / 255,
178 | b = tonumber(hex:sub(5, 6), 16) / 255
179 | }
180 | end
181 |
182 | function utils.pixel_function(_, _, r, g, b, a)
183 | local mask = utils._TC
184 |
185 | if r == mask.r and
186 | g == mask.g and
187 | b == mask.b then
188 | return r, g, b, 0
189 | end
190 |
191 | return r, g, b, a
192 | end
193 |
194 | function utils.fix_transparent_color(tileset, path)
195 | local image_data = love.image.newImageData(path)
196 | tileset.image = love.graphics.newImage(image_data)
197 |
198 | if tileset.transparentcolor then
199 | utils._TC = utils.hex_to_color(tileset.transparentcolor)
200 |
201 | image_data:mapPixel(utils.pixel_function)
202 | tileset.image = love.graphics.newImage(image_data)
203 | end
204 | end
205 |
206 | function utils.deepCopy(t)
207 | local copy = {}
208 | for k,v in pairs(t) do
209 | if type(v) == "table" then
210 | v = utils.deepCopy(v)
211 | end
212 | copy[k] = v
213 | end
214 | return copy
215 | end
216 |
217 | return utils
218 |
--------------------------------------------------------------------------------
/Episode 2/README.md:
--------------------------------------------------------------------------------
1 | Full source code and assets from the tutorial series.
2 | Note: The code and assets have different licences
3 |
4 | Code: WTFPL (Do whatever you want, no restrictions)
5 |
6 | Art: Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License (BY-NC-ND) (https://creativecommons.org/licenses/by-nc-nd/4.0/)
7 |
--------------------------------------------------------------------------------
/Episode 2/assets/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/background.png
--------------------------------------------------------------------------------
/Episode 2/assets/bit.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/bit.ttf
--------------------------------------------------------------------------------
/Episode 2/assets/coin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/coin.png
--------------------------------------------------------------------------------
/Episode 2/assets/enemy/run/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/enemy/run/1.png
--------------------------------------------------------------------------------
/Episode 2/assets/enemy/run/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/enemy/run/2.png
--------------------------------------------------------------------------------
/Episode 2/assets/enemy/run/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/enemy/run/3.png
--------------------------------------------------------------------------------
/Episode 2/assets/enemy/run/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/enemy/run/4.png
--------------------------------------------------------------------------------
/Episode 2/assets/enemy/run/Untitled-1.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/enemy/run/Untitled-1.psd
--------------------------------------------------------------------------------
/Episode 2/assets/enemy/walk/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/enemy/walk/1.png
--------------------------------------------------------------------------------
/Episode 2/assets/enemy/walk/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/enemy/walk/2.png
--------------------------------------------------------------------------------
/Episode 2/assets/enemy/walk/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/enemy/walk/3.png
--------------------------------------------------------------------------------
/Episode 2/assets/enemy/walk/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/enemy/walk/4.png
--------------------------------------------------------------------------------
/Episode 2/assets/enemy/walk/Untitled-1.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/enemy/walk/Untitled-1.psd
--------------------------------------------------------------------------------
/Episode 2/assets/heart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/heart.png
--------------------------------------------------------------------------------
/Episode 2/assets/player/air/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/player/air/1.png
--------------------------------------------------------------------------------
/Episode 2/assets/player/air/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/player/air/2.png
--------------------------------------------------------------------------------
/Episode 2/assets/player/air/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/player/air/3.png
--------------------------------------------------------------------------------
/Episode 2/assets/player/air/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/player/air/4.png
--------------------------------------------------------------------------------
/Episode 2/assets/player/idle/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/player/idle/1.png
--------------------------------------------------------------------------------
/Episode 2/assets/player/idle/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/player/idle/2.png
--------------------------------------------------------------------------------
/Episode 2/assets/player/idle/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/player/idle/3.png
--------------------------------------------------------------------------------
/Episode 2/assets/player/idle/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/player/idle/4.png
--------------------------------------------------------------------------------
/Episode 2/assets/player/jump/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/player/jump/1.png
--------------------------------------------------------------------------------
/Episode 2/assets/player/jump/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/player/jump/2.png
--------------------------------------------------------------------------------
/Episode 2/assets/player/jump/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/player/jump/3.png
--------------------------------------------------------------------------------
/Episode 2/assets/player/jump/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/player/jump/4.png
--------------------------------------------------------------------------------
/Episode 2/assets/player/run/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/player/run/1.png
--------------------------------------------------------------------------------
/Episode 2/assets/player/run/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/player/run/2.png
--------------------------------------------------------------------------------
/Episode 2/assets/player/run/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/player/run/3.png
--------------------------------------------------------------------------------
/Episode 2/assets/player/run/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/player/run/4.png
--------------------------------------------------------------------------------
/Episode 2/assets/player/run/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/player/run/5.png
--------------------------------------------------------------------------------
/Episode 2/assets/player/run/6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/player/run/6.png
--------------------------------------------------------------------------------
/Episode 2/assets/sfx/player_get_coin.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/sfx/player_get_coin.ogg
--------------------------------------------------------------------------------
/Episode 2/assets/sfx/player_hit.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/sfx/player_hit.ogg
--------------------------------------------------------------------------------
/Episode 2/assets/sfx/player_jump.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/sfx/player_jump.ogg
--------------------------------------------------------------------------------
/Episode 2/assets/spikes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/spikes.png
--------------------------------------------------------------------------------
/Episode 2/assets/square.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/square.png
--------------------------------------------------------------------------------
/Episode 2/assets/stone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/stone.png
--------------------------------------------------------------------------------
/Episode 2/assets/tiles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 2/assets/tiles.png
--------------------------------------------------------------------------------
/Episode 2/conf.lua:
--------------------------------------------------------------------------------
1 |
2 | function love.conf(t)
3 | t.title = "Platformer" -- The title of the window the game is in (string)
4 | t.version = "11.3" -- The LÖVE version this game was made for (string)
5 | t.console = true -- Attach a console (boolean, Windows only)
6 | t.window.width = 1280 -- The window width (number)
7 | t.window.height = 720 -- The window height (number)
8 | t.window.vsync = 0
9 | end
10 |
--------------------------------------------------------------------------------
/Episode 2/main.lua:
--------------------------------------------------------------------------------
1 | local STI = require("sti")
2 | require("player")
3 |
4 | function love.load()
5 | Map = STI("map/1.lua", {"box2d"})
6 | World = love.physics.newWorld(0,0)
7 | World:setCallbacks(beginContact, endContact)
8 | Map:box2d_init(World)
9 | Map.layers.solid.visible = false
10 | background = love.graphics.newImage("assets/background.png")
11 | Player:load()
12 | end
13 |
14 | function love.update(dt)
15 | World:update(dt)
16 | Player:update(dt)
17 | end
18 |
19 | function love.draw()
20 | love.graphics.draw(background)
21 | Map:draw(0, 0, 2, 2)
22 | love.graphics.push()
23 | love.graphics.scale(2,2)
24 |
25 | Player:draw()
26 |
27 | love.graphics.pop()
28 |
29 | end
30 |
31 | function love.keypressed(key)
32 | Player:jump(key)
33 | end
34 |
35 | function beginContact(a, b, collision)
36 | Player:beginContact(a, b, collision)
37 | end
38 |
39 | function endContact(a, b, collision)
40 | Player:endContact(a, b, collision)
41 | end
42 |
--------------------------------------------------------------------------------
/Episode 2/map/tmx/1.tmx:
--------------------------------------------------------------------------------
1 |
2 |
81 |
--------------------------------------------------------------------------------
/Episode 2/player.lua:
--------------------------------------------------------------------------------
1 |
2 |
3 | Player = {}
4 |
5 | function Player:load()
6 | self.x = 100
7 | self.y = 0
8 | self.width = 20
9 | self.height = 60
10 | self.xVel = 0
11 | self.yVel = 0
12 | self.maxSpeed = 200
13 | self.acceleration = 4000
14 | self.friction = 3500
15 | self.gravity = 1500
16 | self.jumpAmount = -500
17 |
18 | self.grounded = false
19 |
20 | self.physics = {}
21 | self.physics.body = love.physics.newBody(World, self.x, self.y, "dynamic")
22 | self.physics.body:setFixedRotation(true)
23 | self.physics.shape = love.physics.newRectangleShape(self.width, self.height)
24 | self.physics.fixture = love.physics.newFixture(self.physics.body, self.physics.shape)
25 | end
26 |
27 | function Player:update(dt)
28 | self:syncPhysics()
29 | self:move(dt)
30 | self:applyGravity(dt)
31 | end
32 |
33 | function Player:applyGravity(dt)
34 | if not self.grounded then
35 | self.yVel = self.yVel + self.gravity * dt
36 | end
37 | end
38 |
39 | function Player:move(dt)
40 | if love.keyboard.isDown("d", "right") then
41 | if self.xVel < self.maxSpeed then
42 | if self.xVel + self.acceleration * dt < self.maxSpeed then
43 | self.xVel = self.xVel + self.acceleration * dt
44 | else
45 | self.xVel = self.maxSpeed
46 | end
47 | end
48 | elseif love.keyboard.isDown("a", "left") then
49 | if self.xVel > -self.maxSpeed then
50 | if self.xVel - self.acceleration * dt > -self.maxSpeed then
51 | self.xVel = self.xVel - self.acceleration * dt
52 | else
53 | self.xVel = -self.maxSpeed
54 | end
55 | end
56 | else
57 | self:applyFriction(dt)
58 | end
59 | end
60 |
61 | function Player:applyFriction(dt)
62 | if self.xVel > 0 then
63 | if self.xVel - self.friction * dt > 0 then
64 | self.xVel = self.xVel - self.friction * dt
65 | else
66 | self.xVel = 0
67 | end
68 | elseif self.xVel < 0 then
69 | if self.xVel + self.friction * dt < 0 then
70 | self.xVel = self.xVel + self.friction * dt
71 | else
72 | self.xVel = 0
73 | end
74 | end
75 | end
76 |
77 | function Player:syncPhysics()
78 | self.x, self.y = self.physics.body:getPosition()
79 | self.physics.body:setLinearVelocity(self.xVel, self.yVel)
80 | end
81 |
82 | function Player:beginContact(a, b, collision)
83 | if self.grounded == true then return end
84 | local nx, ny = collision:getNormal()
85 | if a == self.physics.fixture then
86 | if ny > 0 then
87 | self:land(collision)
88 | end
89 | elseif b == self.physics.fixture then
90 | if ny < 0 then
91 | self:land(collision)
92 | end
93 | end
94 | end
95 |
96 | function Player:land(collision)
97 | self.currentGroundCollision = collision
98 | self.yVel = 0
99 | self.grounded = true
100 | end
101 |
102 | function Player:jump(key)
103 | if (key == "w" or key == "up") and self.grounded then
104 | self.yVel = self.jumpAmount
105 | self.grounded = false
106 | end
107 | end
108 |
109 | function Player:endContact(a, b, collision)
110 | if a == self.physics.fixture or b == self.physics.fixture then
111 | if self.currentGroundCollision == collision then
112 | self.grounded = false
113 | end
114 | end
115 | end
116 |
117 | function Player:draw()
118 | love.graphics.rectangle("fill", self.x - self.width / 2, self.y - self.height / 2, self.width, self.height)
119 | end
120 |
--------------------------------------------------------------------------------
/Episode 2/run.bat:
--------------------------------------------------------------------------------
1 | @ECHO
2 |
3 | start "" "C:\Program Files\LOVE\lovec" .
--------------------------------------------------------------------------------
/Episode 2/sti/graphics.lua:
--------------------------------------------------------------------------------
1 | local lg = _G.love.graphics
2 | local graphics = { isCreated = lg and true or false }
3 |
4 | function graphics.newSpriteBatch(...)
5 | if graphics.isCreated then
6 | return lg.newSpriteBatch(...)
7 | end
8 | end
9 |
10 | function graphics.newCanvas(...)
11 | if graphics.isCreated then
12 | return lg.newCanvas(...)
13 | end
14 | end
15 |
16 | function graphics.newImage(...)
17 | if graphics.isCreated then
18 | return lg.newImage(...)
19 | end
20 | end
21 |
22 | function graphics.newQuad(...)
23 | if graphics.isCreated then
24 | return lg.newQuad(...)
25 | end
26 | end
27 |
28 | function graphics.getCanvas(...)
29 | if graphics.isCreated then
30 | return lg.getCanvas(...)
31 | end
32 | end
33 |
34 | function graphics.setCanvas(...)
35 | if graphics.isCreated then
36 | return lg.setCanvas(...)
37 | end
38 | end
39 |
40 | function graphics.clear(...)
41 | if graphics.isCreated then
42 | return lg.clear(...)
43 | end
44 | end
45 |
46 | function graphics.push(...)
47 | if graphics.isCreated then
48 | return lg.push(...)
49 | end
50 | end
51 |
52 | function graphics.origin(...)
53 | if graphics.isCreated then
54 | return lg.origin(...)
55 | end
56 | end
57 |
58 | function graphics.scale(...)
59 | if graphics.isCreated then
60 | return lg.scale(...)
61 | end
62 | end
63 |
64 | function graphics.translate(...)
65 | if graphics.isCreated then
66 | return lg.translate(...)
67 | end
68 | end
69 |
70 | function graphics.pop(...)
71 | if graphics.isCreated then
72 | return lg.pop(...)
73 | end
74 | end
75 |
76 | function graphics.draw(...)
77 | if graphics.isCreated then
78 | return lg.draw(...)
79 | end
80 | end
81 |
82 | function graphics.rectangle(...)
83 | if graphics.isCreated then
84 | return lg.rectangle(...)
85 | end
86 | end
87 |
88 | function graphics.getColor(...)
89 | if graphics.isCreated then
90 | return lg.getColor(...)
91 | end
92 | end
93 |
94 | function graphics.setColor(...)
95 | if graphics.isCreated then
96 | return lg.setColor(...)
97 | end
98 | end
99 |
100 | function graphics.line(...)
101 | if graphics.isCreated then
102 | return lg.line(...)
103 | end
104 | end
105 |
106 | function graphics.polygon(...)
107 | if graphics.isCreated then
108 | return lg.polygon(...)
109 | end
110 | end
111 |
112 | function graphics.points(...)
113 | if graphics.isCreated then
114 | return lg.points(...)
115 | end
116 | end
117 |
118 | function graphics.getWidth()
119 | if graphics.isCreated then
120 | return lg.getWidth()
121 | end
122 | return 0
123 | end
124 |
125 | function graphics.getHeight()
126 | if graphics.isCreated then
127 | return lg.getHeight()
128 | end
129 | return 0
130 | end
131 |
132 | return graphics
133 |
--------------------------------------------------------------------------------
/Episode 2/sti/plugins/bump.lua:
--------------------------------------------------------------------------------
1 | --- Bump.lua plugin for STI
2 | -- @module bump.lua
3 | -- @author David Serrano (BobbyJones|FrenchFryLord)
4 | -- @copyright 2019
5 | -- @license MIT/X11
6 |
7 | local lg = require((...):gsub('plugins.bump', 'graphics'))
8 |
9 | return {
10 | bump_LICENSE = "MIT/X11",
11 | bump_URL = "https://github.com/karai17/Simple-Tiled-Implementation",
12 | bump_VERSION = "3.1.7.1",
13 | bump_DESCRIPTION = "Bump hooks for STI.",
14 |
15 | --- Adds each collidable tile to the Bump world.
16 | -- @param world The Bump world to add objects to.
17 | -- @return collidables table containing the handles to the objects in the Bump world.
18 | bump_init = function(map, world)
19 | local collidables = {}
20 |
21 | for _, tileset in ipairs(map.tilesets) do
22 | for _, tile in ipairs(tileset.tiles) do
23 | local gid = tileset.firstgid + tile.id
24 |
25 | if map.tileInstances[gid] then
26 | for _, instance in ipairs(map.tileInstances[gid]) do
27 | -- Every object in every instance of a tile
28 | if tile.objectGroup then
29 | for _, object in ipairs(tile.objectGroup.objects) do
30 | if object.properties.collidable == true then
31 | local t = {
32 | name = object.name,
33 | type = object.type,
34 | x = instance.x + map.offsetx + object.x,
35 | y = instance.y + map.offsety + object.y,
36 | width = object.width,
37 | height = object.height,
38 | layer = instance.layer,
39 | properties = object.properties
40 |
41 | }
42 |
43 | world:add(t, t.x, t.y, t.width, t.height)
44 | table.insert(collidables, t)
45 | end
46 | end
47 | end
48 |
49 | -- Every instance of a tile
50 | if tile.properties and tile.properties.collidable == true then
51 | local t = {
52 | x = instance.x + map.offsetx,
53 | y = instance.y + map.offsety,
54 | width = map.tilewidth,
55 | height = map.tileheight,
56 | layer = instance.layer,
57 | properties = tile.properties
58 | }
59 |
60 | world:add(t, t.x, t.y, t.width, t.height)
61 | table.insert(collidables, t)
62 | end
63 | end
64 | end
65 | end
66 | end
67 |
68 | for _, layer in ipairs(map.layers) do
69 | -- Entire layer
70 | if layer.properties.collidable == true then
71 | if layer.type == "tilelayer" then
72 | for y, tiles in ipairs(layer.data) do
73 | for x, tile in pairs(tiles) do
74 |
75 | if tile.objectGroup then
76 | for _, object in ipairs(tile.objectGroup.objects) do
77 | if object.properties.collidable == true then
78 | local t = {
79 | name = object.name,
80 | type = object.type,
81 | x = ((x-1) * map.tilewidth + tile.offset.x + map.offsetx) + object.x,
82 | y = ((y-1) * map.tileheight + tile.offset.y + map.offsety) + object.y,
83 | width = object.width,
84 | height = object.height,
85 | layer = layer,
86 | properties = object.properties
87 | }
88 |
89 | world:add(t, t.x, t.y, t.width, t.height)
90 | table.insert(collidables, t)
91 | end
92 | end
93 | end
94 |
95 |
96 | local t = {
97 | x = (x-1) * map.tilewidth + tile.offset.x + map.offsetx,
98 | y = (y-1) * map.tileheight + tile.offset.y + map.offsety,
99 | width = tile.width,
100 | height = tile.height,
101 | layer = layer,
102 | properties = tile.properties
103 | }
104 |
105 | world:add(t, t.x, t.y, t.width, t.height)
106 | table.insert(collidables, t)
107 | end
108 | end
109 | elseif layer.type == "imagelayer" then
110 | world:add(layer, layer.x, layer.y, layer.width, layer.height)
111 | table.insert(collidables, layer)
112 | end
113 | end
114 |
115 | -- individual collidable objects in a layer that is not "collidable"
116 | -- or whole collidable objects layer
117 | if layer.type == "objectgroup" then
118 | for _, obj in ipairs(layer.objects) do
119 | if layer.properties.collidable == true or obj.properties.collidable == true then
120 | if obj.shape == "rectangle" then
121 | local t = {
122 | name = obj.name,
123 | type = obj.type,
124 | x = obj.x + map.offsetx,
125 | y = obj.y + map.offsety,
126 | width = obj.width,
127 | height = obj.height,
128 | layer = layer,
129 | properties = obj.properties
130 | }
131 |
132 | if obj.gid then
133 | t.y = t.y - obj.height
134 | end
135 |
136 | world:add(t, t.x, t.y, t.width, t.height)
137 | table.insert(collidables, t)
138 | end -- TODO implement other object shapes?
139 | end
140 | end
141 | end
142 | end
143 |
144 | map.bump_world = world
145 | map.bump_collidables = collidables
146 | end,
147 |
148 | --- Remove layer
149 | -- @param index to layer to be removed
150 | bump_removeLayer = function(map, index)
151 | local layer = assert(map.layers[index], "Layer not found: " .. index)
152 | local collidables = map.bump_collidables
153 |
154 | -- Remove collision objects
155 | for i = #collidables, 1, -1 do
156 | local obj = collidables[i]
157 |
158 | if obj.layer == layer
159 | and (
160 | layer.properties.collidable == true
161 | or obj.properties.collidable == true
162 | ) then
163 | map.bump_world:remove(obj)
164 | table.remove(collidables, i)
165 | end
166 | end
167 | end,
168 |
169 | --- Draw bump collisions world.
170 | -- @param world bump world holding the tiles geometry
171 | -- @param tx Translate on X
172 | -- @param ty Translate on Y
173 | -- @param sx Scale on X
174 | -- @param sy Scale on Y
175 | bump_draw = function(map, tx, ty, sx, sy)
176 | lg.push()
177 | lg.scale(sx or 1, sy or sx or 1)
178 | lg.translate(math.floor(tx or 0), math.floor(ty or 0))
179 |
180 | local items = map.bump_world:getItems()
181 | for _, item in ipairs(items) do
182 | lg.rectangle("line", map.bump_world:getRect(item))
183 | end
184 |
185 | lg.pop()
186 | end
187 | }
188 |
189 | --- Custom Properties in Tiled are used to tell this plugin what to do.
190 | -- @table Properties
191 | -- @field collidable set to true, can be used on any Layer, Tile, or Object
192 |
--------------------------------------------------------------------------------
/Episode 2/sti/utils.lua:
--------------------------------------------------------------------------------
1 | -- Some utility functions that shouldn't be exposed.
2 | local utils = {}
3 |
4 | -- https://github.com/stevedonovan/Penlight/blob/master/lua/pl/path.lua#L286
5 | function utils.format_path(path)
6 | local np_gen1,np_gen2 = '[^SEP]+SEP%.%.SEP?','SEP+%.?SEP'
7 | local np_pat1, np_pat2 = np_gen1:gsub('SEP','/'), np_gen2:gsub('SEP','/')
8 | local k
9 |
10 | repeat -- /./ -> /
11 | path,k = path:gsub(np_pat2,'/',1)
12 | until k == 0
13 |
14 | repeat -- A/../ -> (empty)
15 | path,k = path:gsub(np_pat1,'',1)
16 | until k == 0
17 |
18 | if path == '' then path = '.' end
19 |
20 | return path
21 | end
22 |
23 | -- Compensation for scale/rotation shift
24 | function utils.compensate(tile, tileX, tileY, tileW, tileH)
25 | local compx = 0
26 | local compy = 0
27 |
28 | if tile.sx < 0 then compx = tileW end
29 | if tile.sy < 0 then compy = tileH end
30 |
31 | if tile.r > 0 then
32 | tileX = tileX + tileH - compy
33 | tileY = tileY + tileH + compx - tileW
34 | elseif tile.r < 0 then
35 | tileX = tileX + compy
36 | tileY = tileY - compx + tileH
37 | else
38 | tileX = tileX + compx
39 | tileY = tileY + compy
40 | end
41 |
42 | return tileX, tileY
43 | end
44 |
45 | -- Cache images in main STI module
46 | function utils.cache_image(sti, path, image)
47 | image = image or love.graphics.newImage(path)
48 | image:setFilter("nearest", "nearest")
49 | sti.cache[path] = image
50 | end
51 |
52 | -- We just don't know.
53 | function utils.get_tiles(imageW, tileW, margin, spacing)
54 | imageW = imageW - margin
55 | local n = 0
56 |
57 | while imageW >= tileW do
58 | imageW = imageW - tileW
59 | if n ~= 0 then imageW = imageW - spacing end
60 | if imageW >= 0 then n = n + 1 end
61 | end
62 |
63 | return n
64 | end
65 |
66 | -- Decompress tile layer data
67 | function utils.get_decompressed_data(data)
68 | local ffi = require "ffi"
69 | local d = {}
70 | local decoded = ffi.cast("uint32_t*", data)
71 |
72 | for i = 0, data:len() / ffi.sizeof("uint32_t") do
73 | table.insert(d, tonumber(decoded[i]))
74 | end
75 |
76 | return d
77 | end
78 |
79 | -- Convert a Tiled ellipse object to a LOVE polygon
80 | function utils.convert_ellipse_to_polygon(x, y, w, h, max_segments)
81 | local ceil = math.ceil
82 | local cos = math.cos
83 | local sin = math.sin
84 |
85 | local function calc_segments(segments)
86 | local function vdist(a, b)
87 | local c = {
88 | x = a.x - b.x,
89 | y = a.y - b.y,
90 | }
91 |
92 | return c.x * c.x + c.y * c.y
93 | end
94 |
95 | segments = segments or 64
96 | local vertices = {}
97 |
98 | local v = { 1, 2, ceil(segments/4-1), ceil(segments/4) }
99 |
100 | local m
101 | if love and love.physics then
102 | m = love.physics.getMeter()
103 | else
104 | m = 32
105 | end
106 |
107 | for _, i in ipairs(v) do
108 | local angle = (i / segments) * math.pi * 2
109 | local px = x + w / 2 + cos(angle) * w / 2
110 | local py = y + h / 2 + sin(angle) * h / 2
111 |
112 | table.insert(vertices, { x = px / m, y = py / m })
113 | end
114 |
115 | local dist1 = vdist(vertices[1], vertices[2])
116 | local dist2 = vdist(vertices[3], vertices[4])
117 |
118 | -- Box2D threshold
119 | if dist1 < 0.0025 or dist2 < 0.0025 then
120 | return calc_segments(segments-2)
121 | end
122 |
123 | return segments
124 | end
125 |
126 | local segments = calc_segments(max_segments)
127 | local vertices = {}
128 |
129 | table.insert(vertices, { x = x + w / 2, y = y + h / 2 })
130 |
131 | for i = 0, segments do
132 | local angle = (i / segments) * math.pi * 2
133 | local px = x + w / 2 + cos(angle) * w / 2
134 | local py = y + h / 2 + sin(angle) * h / 2
135 |
136 | table.insert(vertices, { x = px, y = py })
137 | end
138 |
139 | return vertices
140 | end
141 |
142 | function utils.rotate_vertex(map, vertex, x, y, cos, sin, oy)
143 | if map.orientation == "isometric" then
144 | x, y = utils.convert_isometric_to_screen(map, x, y)
145 | vertex.x, vertex.y = utils.convert_isometric_to_screen(map, vertex.x, vertex.y)
146 | end
147 |
148 | vertex.x = vertex.x - x
149 | vertex.y = vertex.y - y
150 |
151 | return
152 | x + cos * vertex.x - sin * vertex.y,
153 | y + sin * vertex.x + cos * vertex.y - (oy or 0)
154 | end
155 |
156 | --- Project isometric position to cartesian position
157 | function utils.convert_isometric_to_screen(map, x, y)
158 | local mapW = map.width
159 | local tileW = map.tilewidth
160 | local tileH = map.tileheight
161 | local tileX = x / tileH
162 | local tileY = y / tileH
163 | local offsetX = mapW * tileW / 2
164 |
165 | return
166 | (tileX - tileY) * tileW / 2 + offsetX,
167 | (tileX + tileY) * tileH / 2
168 | end
169 |
170 | function utils.hex_to_color(hex)
171 | if hex:sub(1, 1) == "#" then
172 | hex = hex:sub(2)
173 | end
174 |
175 | return {
176 | r = tonumber(hex:sub(1, 2), 16) / 255,
177 | g = tonumber(hex:sub(3, 4), 16) / 255,
178 | b = tonumber(hex:sub(5, 6), 16) / 255
179 | }
180 | end
181 |
182 | function utils.pixel_function(_, _, r, g, b, a)
183 | local mask = utils._TC
184 |
185 | if r == mask.r and
186 | g == mask.g and
187 | b == mask.b then
188 | return r, g, b, 0
189 | end
190 |
191 | return r, g, b, a
192 | end
193 |
194 | function utils.fix_transparent_color(tileset, path)
195 | local image_data = love.image.newImageData(path)
196 | tileset.image = love.graphics.newImage(image_data)
197 |
198 | if tileset.transparentcolor then
199 | utils._TC = utils.hex_to_color(tileset.transparentcolor)
200 |
201 | image_data:mapPixel(utils.pixel_function)
202 | tileset.image = love.graphics.newImage(image_data)
203 | end
204 | end
205 |
206 | function utils.deepCopy(t)
207 | local copy = {}
208 | for k,v in pairs(t) do
209 | if type(v) == "table" then
210 | v = utils.deepCopy(v)
211 | end
212 | copy[k] = v
213 | end
214 | return copy
215 | end
216 |
217 | return utils
218 |
--------------------------------------------------------------------------------
/Episode 3/README.md:
--------------------------------------------------------------------------------
1 | Full source code and assets from the tutorial series.
2 | Note: The code and assets have different licences
3 |
4 | Code: WTFPL (Do whatever you want, no restrictions)
5 |
6 | Art: Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License (BY-NC-ND) (https://creativecommons.org/licenses/by-nc-nd/4.0/)
7 |
--------------------------------------------------------------------------------
/Episode 3/assets/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/background.png
--------------------------------------------------------------------------------
/Episode 3/assets/bit.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/bit.ttf
--------------------------------------------------------------------------------
/Episode 3/assets/coin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/coin.png
--------------------------------------------------------------------------------
/Episode 3/assets/enemy/run/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/enemy/run/1.png
--------------------------------------------------------------------------------
/Episode 3/assets/enemy/run/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/enemy/run/2.png
--------------------------------------------------------------------------------
/Episode 3/assets/enemy/run/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/enemy/run/3.png
--------------------------------------------------------------------------------
/Episode 3/assets/enemy/run/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/enemy/run/4.png
--------------------------------------------------------------------------------
/Episode 3/assets/enemy/run/Untitled-1.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/enemy/run/Untitled-1.psd
--------------------------------------------------------------------------------
/Episode 3/assets/enemy/walk/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/enemy/walk/1.png
--------------------------------------------------------------------------------
/Episode 3/assets/enemy/walk/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/enemy/walk/2.png
--------------------------------------------------------------------------------
/Episode 3/assets/enemy/walk/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/enemy/walk/3.png
--------------------------------------------------------------------------------
/Episode 3/assets/enemy/walk/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/enemy/walk/4.png
--------------------------------------------------------------------------------
/Episode 3/assets/enemy/walk/Untitled-1.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/enemy/walk/Untitled-1.psd
--------------------------------------------------------------------------------
/Episode 3/assets/heart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/heart.png
--------------------------------------------------------------------------------
/Episode 3/assets/player/air/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/player/air/1.png
--------------------------------------------------------------------------------
/Episode 3/assets/player/air/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/player/air/2.png
--------------------------------------------------------------------------------
/Episode 3/assets/player/air/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/player/air/3.png
--------------------------------------------------------------------------------
/Episode 3/assets/player/air/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/player/air/4.png
--------------------------------------------------------------------------------
/Episode 3/assets/player/idle/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/player/idle/1.png
--------------------------------------------------------------------------------
/Episode 3/assets/player/idle/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/player/idle/2.png
--------------------------------------------------------------------------------
/Episode 3/assets/player/idle/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/player/idle/3.png
--------------------------------------------------------------------------------
/Episode 3/assets/player/idle/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/player/idle/4.png
--------------------------------------------------------------------------------
/Episode 3/assets/player/jump/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/player/jump/1.png
--------------------------------------------------------------------------------
/Episode 3/assets/player/jump/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/player/jump/2.png
--------------------------------------------------------------------------------
/Episode 3/assets/player/jump/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/player/jump/3.png
--------------------------------------------------------------------------------
/Episode 3/assets/player/jump/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/player/jump/4.png
--------------------------------------------------------------------------------
/Episode 3/assets/player/run/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/player/run/1.png
--------------------------------------------------------------------------------
/Episode 3/assets/player/run/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/player/run/2.png
--------------------------------------------------------------------------------
/Episode 3/assets/player/run/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/player/run/3.png
--------------------------------------------------------------------------------
/Episode 3/assets/player/run/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/player/run/4.png
--------------------------------------------------------------------------------
/Episode 3/assets/player/run/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/player/run/5.png
--------------------------------------------------------------------------------
/Episode 3/assets/player/run/6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/player/run/6.png
--------------------------------------------------------------------------------
/Episode 3/assets/sfx/player_get_coin.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/sfx/player_get_coin.ogg
--------------------------------------------------------------------------------
/Episode 3/assets/sfx/player_hit.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/sfx/player_hit.ogg
--------------------------------------------------------------------------------
/Episode 3/assets/sfx/player_jump.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/sfx/player_jump.ogg
--------------------------------------------------------------------------------
/Episode 3/assets/spikes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/spikes.png
--------------------------------------------------------------------------------
/Episode 3/assets/square.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/square.png
--------------------------------------------------------------------------------
/Episode 3/assets/stone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/stone.png
--------------------------------------------------------------------------------
/Episode 3/assets/tiles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 3/assets/tiles.png
--------------------------------------------------------------------------------
/Episode 3/conf.lua:
--------------------------------------------------------------------------------
1 |
2 | function love.conf(t)
3 | t.title = "Platformer" -- The title of the window the game is in (string)
4 | t.version = "11.3" -- The LÖVE version this game was made for (string)
5 | t.console = true -- Attach a console (boolean, Windows only)
6 | t.window.width = 1280 -- The window width (number)
7 | t.window.height = 720 -- The window height (number)
8 | t.window.vsync = 0
9 | end
10 |
--------------------------------------------------------------------------------
/Episode 3/main.lua:
--------------------------------------------------------------------------------
1 | local STI = require("sti")
2 | require("player")
3 | love.graphics.setDefaultFilter("nearest", "nearest")
4 |
5 | function love.load()
6 | Map = STI("map/1.lua", {"box2d"})
7 | World = love.physics.newWorld(0,0)
8 | World:setCallbacks(beginContact, endContact)
9 | Map:box2d_init(World)
10 | Map.layers.solid.visible = false
11 | background = love.graphics.newImage("assets/background.png")
12 | Player:load()
13 | end
14 |
15 | function love.update(dt)
16 | World:update(dt)
17 | Player:update(dt)
18 | end
19 |
20 | function love.draw()
21 | love.graphics.draw(background)
22 | Map:draw(0, 0, 2, 2)
23 | love.graphics.push()
24 | love.graphics.scale(2,2)
25 |
26 | Player:draw()
27 |
28 | love.graphics.pop()
29 |
30 | end
31 |
32 | function love.keypressed(key)
33 | Player:jump(key)
34 | end
35 |
36 | function beginContact(a, b, collision)
37 | Player:beginContact(a, b, collision)
38 | end
39 |
40 | function endContact(a, b, collision)
41 | Player:endContact(a, b, collision)
42 | end
43 |
--------------------------------------------------------------------------------
/Episode 3/map/tmx/1.tmx:
--------------------------------------------------------------------------------
1 |
2 |
81 |
--------------------------------------------------------------------------------
/Episode 3/player.lua:
--------------------------------------------------------------------------------
1 |
2 |
3 | Player = {}
4 |
5 | function Player:load()
6 | self.x = 100
7 | self.y = 0
8 | self.width = 20
9 | self.height = 60
10 | self.xVel = 0
11 | self.yVel = 0
12 | self.maxSpeed = 200
13 | self.acceleration = 4000
14 | self.friction = 3500
15 | self.gravity = 1500
16 | self.jumpAmount = -500
17 |
18 | self.graceTime = 0
19 | self.graceDuration = 0.1
20 |
21 | self.grounded = false
22 | self.hasDoubleJump = true
23 |
24 | self.direction = "right"
25 | self.state = "idle"
26 |
27 | self:loadAssets()
28 |
29 | self.physics = {}
30 | self.physics.body = love.physics.newBody(World, self.x, self.y, "dynamic")
31 | self.physics.body:setFixedRotation(true)
32 | self.physics.shape = love.physics.newRectangleShape(self.width, self.height)
33 | self.physics.fixture = love.physics.newFixture(self.physics.body, self.physics.shape)
34 | end
35 |
36 | function Player:loadAssets()
37 | self.animation = {timer = 0, rate = 0.1}
38 |
39 | self.animation.run = {total = 6, current = 1, img = {}}
40 | for i=1, self.animation.run.total do
41 | self.animation.run.img[i] = love.graphics.newImage("assets/player/run/"..i..".png")
42 | end
43 |
44 | self.animation.idle = {total = 4, current = 1, img = {}}
45 | for i=1, self.animation.idle.total do
46 | self.animation.idle.img[i] = love.graphics.newImage("assets/player/idle/"..i..".png")
47 | end
48 |
49 | self.animation.air = {total = 4, current = 1, img = {}}
50 | for i=1, self.animation.air.total do
51 | self.animation.air.img[i] = love.graphics.newImage("assets/player/air/"..i..".png")
52 | end
53 |
54 | self.animation.draw = self.animation.idle.img[1]
55 | self.animation.width = self.animation.draw:getWidth()
56 | self.animation.height = self.animation.draw:getHeight()
57 | end
58 |
59 | function Player:update(dt)
60 | self:setState()
61 | self:setDirection()
62 | self:animate(dt)
63 | self:decreaseGraceTime(dt)
64 | self:syncPhysics()
65 | self:move(dt)
66 | self:applyGravity(dt)
67 | end
68 |
69 | function Player:setState()
70 | if not self.grounded then
71 | self.state = "air"
72 | elseif self.xVel == 0 then
73 | self.state = "idle"
74 | else
75 | self.state = "run"
76 | end
77 | end
78 |
79 | function Player:setDirection()
80 | if self.xVel < 0 then
81 | self.direction = "left"
82 | elseif self.xVel > 0 then
83 | self.direction = "right"
84 | end
85 | end
86 |
87 | function Player:animate(dt)
88 | self.animation.timer = self.animation.timer + dt
89 | if self.animation.timer > self.animation.rate then
90 | self.animation.timer = 0
91 | self:setNewFrame()
92 | end
93 | end
94 |
95 | function Player:setNewFrame()
96 | local anim = self.animation[self.state]
97 | if anim.current < anim.total then
98 | anim.current = anim.current + 1
99 | else
100 | anim.current = 1
101 | end
102 | self.animation.draw = anim.img[anim.current]
103 | end
104 |
105 | function Player:decreaseGraceTime(dt)
106 | if not self.grounded then
107 | self.graceTime = self.graceTime - dt
108 | end
109 | end
110 |
111 | function Player:applyGravity(dt)
112 | if not self.grounded then
113 | self.yVel = self.yVel + self.gravity * dt
114 | end
115 | end
116 |
117 | function Player:move(dt)
118 | if love.keyboard.isDown("d", "right") then
119 | self.xVel = math.min(self.xVel + self.acceleration * dt, self.maxSpeed)
120 | elseif love.keyboard.isDown("a", "left") then
121 | self.xVel = math.max(self.xVel - self.acceleration * dt, -self.maxSpeed)
122 | else
123 | self:applyFriction(dt)
124 | end
125 | end
126 |
127 | function Player:applyFriction(dt)
128 | if self.xVel > 0 then
129 | if self.xVel - self.friction * dt > 0 then
130 | self.xVel = self.xVel - self.friction * dt
131 | else
132 | self.xVel = 0
133 | end
134 | elseif self.xVel < 0 then
135 | if self.xVel + self.friction * dt < 0 then
136 | self.xVel = self.xVel + self.friction * dt
137 | else
138 | self.xVel = 0
139 | end
140 | end
141 | end
142 |
143 | function Player:syncPhysics()
144 | self.x, self.y = self.physics.body:getPosition()
145 | self.physics.body:setLinearVelocity(self.xVel, self.yVel)
146 | end
147 |
148 | function Player:beginContact(a, b, collision)
149 | if self.grounded == true then return end
150 | local nx, ny = collision:getNormal()
151 | if a == self.physics.fixture then
152 | if ny > 0 then
153 | self:land(collision)
154 | elseif ny < 0 then
155 | self.yVel = 0
156 | end
157 | elseif b == self.physics.fixture then
158 | if ny < 0 then
159 | self:land(collision)
160 | elseif ny > 0 then
161 | self.yVel = 0
162 | end
163 | end
164 | end
165 |
166 | function Player:land(collision)
167 | self.currentGroundCollision = collision
168 | self.yVel = 0
169 | self.grounded = true
170 | self.hasDoubleJump = true
171 | self.graceTime = self.graceDuration
172 | end
173 |
174 | function Player:jump(key)
175 | if (key == "w" or key == "up") then
176 | if self.grounded or self.graceTime > 0 then
177 | self.yVel = self.jumpAmount
178 | self.graceTime = 0
179 | elseif self.hasDoubleJump then
180 | self.hasDoubleJump = false
181 | self.yVel = self.jumpAmount * 0.8
182 | end
183 | end
184 | end
185 |
186 | function Player:endContact(a, b, collision)
187 | if a == self.physics.fixture or b == self.physics.fixture then
188 | if self.currentGroundCollision == collision then
189 | self.grounded = false
190 | end
191 | end
192 | end
193 |
194 | function Player:draw()
195 | local scaleX = 1
196 | if self.direction == "left" then
197 | scaleX = -1
198 | end
199 | love.graphics.draw(self.animation.draw, self.x, self.y, 0, scaleX, 1, self.animation.width / 2, self.animation.height / 2)
200 | end
201 |
--------------------------------------------------------------------------------
/Episode 3/run.bat:
--------------------------------------------------------------------------------
1 | @ECHO
2 |
3 | start "" "C:\Program Files\LOVE\lovec" .
--------------------------------------------------------------------------------
/Episode 3/sti/graphics.lua:
--------------------------------------------------------------------------------
1 | local lg = _G.love.graphics
2 | local graphics = { isCreated = lg and true or false }
3 |
4 | function graphics.newSpriteBatch(...)
5 | if graphics.isCreated then
6 | return lg.newSpriteBatch(...)
7 | end
8 | end
9 |
10 | function graphics.newCanvas(...)
11 | if graphics.isCreated then
12 | return lg.newCanvas(...)
13 | end
14 | end
15 |
16 | function graphics.newImage(...)
17 | if graphics.isCreated then
18 | return lg.newImage(...)
19 | end
20 | end
21 |
22 | function graphics.newQuad(...)
23 | if graphics.isCreated then
24 | return lg.newQuad(...)
25 | end
26 | end
27 |
28 | function graphics.getCanvas(...)
29 | if graphics.isCreated then
30 | return lg.getCanvas(...)
31 | end
32 | end
33 |
34 | function graphics.setCanvas(...)
35 | if graphics.isCreated then
36 | return lg.setCanvas(...)
37 | end
38 | end
39 |
40 | function graphics.clear(...)
41 | if graphics.isCreated then
42 | return lg.clear(...)
43 | end
44 | end
45 |
46 | function graphics.push(...)
47 | if graphics.isCreated then
48 | return lg.push(...)
49 | end
50 | end
51 |
52 | function graphics.origin(...)
53 | if graphics.isCreated then
54 | return lg.origin(...)
55 | end
56 | end
57 |
58 | function graphics.scale(...)
59 | if graphics.isCreated then
60 | return lg.scale(...)
61 | end
62 | end
63 |
64 | function graphics.translate(...)
65 | if graphics.isCreated then
66 | return lg.translate(...)
67 | end
68 | end
69 |
70 | function graphics.pop(...)
71 | if graphics.isCreated then
72 | return lg.pop(...)
73 | end
74 | end
75 |
76 | function graphics.draw(...)
77 | if graphics.isCreated then
78 | return lg.draw(...)
79 | end
80 | end
81 |
82 | function graphics.rectangle(...)
83 | if graphics.isCreated then
84 | return lg.rectangle(...)
85 | end
86 | end
87 |
88 | function graphics.getColor(...)
89 | if graphics.isCreated then
90 | return lg.getColor(...)
91 | end
92 | end
93 |
94 | function graphics.setColor(...)
95 | if graphics.isCreated then
96 | return lg.setColor(...)
97 | end
98 | end
99 |
100 | function graphics.line(...)
101 | if graphics.isCreated then
102 | return lg.line(...)
103 | end
104 | end
105 |
106 | function graphics.polygon(...)
107 | if graphics.isCreated then
108 | return lg.polygon(...)
109 | end
110 | end
111 |
112 | function graphics.points(...)
113 | if graphics.isCreated then
114 | return lg.points(...)
115 | end
116 | end
117 |
118 | function graphics.getWidth()
119 | if graphics.isCreated then
120 | return lg.getWidth()
121 | end
122 | return 0
123 | end
124 |
125 | function graphics.getHeight()
126 | if graphics.isCreated then
127 | return lg.getHeight()
128 | end
129 | return 0
130 | end
131 |
132 | return graphics
133 |
--------------------------------------------------------------------------------
/Episode 3/sti/plugins/bump.lua:
--------------------------------------------------------------------------------
1 | --- Bump.lua plugin for STI
2 | -- @module bump.lua
3 | -- @author David Serrano (BobbyJones|FrenchFryLord)
4 | -- @copyright 2019
5 | -- @license MIT/X11
6 |
7 | local lg = require((...):gsub('plugins.bump', 'graphics'))
8 |
9 | return {
10 | bump_LICENSE = "MIT/X11",
11 | bump_URL = "https://github.com/karai17/Simple-Tiled-Implementation",
12 | bump_VERSION = "3.1.7.1",
13 | bump_DESCRIPTION = "Bump hooks for STI.",
14 |
15 | --- Adds each collidable tile to the Bump world.
16 | -- @param world The Bump world to add objects to.
17 | -- @return collidables table containing the handles to the objects in the Bump world.
18 | bump_init = function(map, world)
19 | local collidables = {}
20 |
21 | for _, tileset in ipairs(map.tilesets) do
22 | for _, tile in ipairs(tileset.tiles) do
23 | local gid = tileset.firstgid + tile.id
24 |
25 | if map.tileInstances[gid] then
26 | for _, instance in ipairs(map.tileInstances[gid]) do
27 | -- Every object in every instance of a tile
28 | if tile.objectGroup then
29 | for _, object in ipairs(tile.objectGroup.objects) do
30 | if object.properties.collidable == true then
31 | local t = {
32 | name = object.name,
33 | type = object.type,
34 | x = instance.x + map.offsetx + object.x,
35 | y = instance.y + map.offsety + object.y,
36 | width = object.width,
37 | height = object.height,
38 | layer = instance.layer,
39 | properties = object.properties
40 |
41 | }
42 |
43 | world:add(t, t.x, t.y, t.width, t.height)
44 | table.insert(collidables, t)
45 | end
46 | end
47 | end
48 |
49 | -- Every instance of a tile
50 | if tile.properties and tile.properties.collidable == true then
51 | local t = {
52 | x = instance.x + map.offsetx,
53 | y = instance.y + map.offsety,
54 | width = map.tilewidth,
55 | height = map.tileheight,
56 | layer = instance.layer,
57 | properties = tile.properties
58 | }
59 |
60 | world:add(t, t.x, t.y, t.width, t.height)
61 | table.insert(collidables, t)
62 | end
63 | end
64 | end
65 | end
66 | end
67 |
68 | for _, layer in ipairs(map.layers) do
69 | -- Entire layer
70 | if layer.properties.collidable == true then
71 | if layer.type == "tilelayer" then
72 | for y, tiles in ipairs(layer.data) do
73 | for x, tile in pairs(tiles) do
74 |
75 | if tile.objectGroup then
76 | for _, object in ipairs(tile.objectGroup.objects) do
77 | if object.properties.collidable == true then
78 | local t = {
79 | name = object.name,
80 | type = object.type,
81 | x = ((x-1) * map.tilewidth + tile.offset.x + map.offsetx) + object.x,
82 | y = ((y-1) * map.tileheight + tile.offset.y + map.offsety) + object.y,
83 | width = object.width,
84 | height = object.height,
85 | layer = layer,
86 | properties = object.properties
87 | }
88 |
89 | world:add(t, t.x, t.y, t.width, t.height)
90 | table.insert(collidables, t)
91 | end
92 | end
93 | end
94 |
95 |
96 | local t = {
97 | x = (x-1) * map.tilewidth + tile.offset.x + map.offsetx,
98 | y = (y-1) * map.tileheight + tile.offset.y + map.offsety,
99 | width = tile.width,
100 | height = tile.height,
101 | layer = layer,
102 | properties = tile.properties
103 | }
104 |
105 | world:add(t, t.x, t.y, t.width, t.height)
106 | table.insert(collidables, t)
107 | end
108 | end
109 | elseif layer.type == "imagelayer" then
110 | world:add(layer, layer.x, layer.y, layer.width, layer.height)
111 | table.insert(collidables, layer)
112 | end
113 | end
114 |
115 | -- individual collidable objects in a layer that is not "collidable"
116 | -- or whole collidable objects layer
117 | if layer.type == "objectgroup" then
118 | for _, obj in ipairs(layer.objects) do
119 | if layer.properties.collidable == true or obj.properties.collidable == true then
120 | if obj.shape == "rectangle" then
121 | local t = {
122 | name = obj.name,
123 | type = obj.type,
124 | x = obj.x + map.offsetx,
125 | y = obj.y + map.offsety,
126 | width = obj.width,
127 | height = obj.height,
128 | layer = layer,
129 | properties = obj.properties
130 | }
131 |
132 | if obj.gid then
133 | t.y = t.y - obj.height
134 | end
135 |
136 | world:add(t, t.x, t.y, t.width, t.height)
137 | table.insert(collidables, t)
138 | end -- TODO implement other object shapes?
139 | end
140 | end
141 | end
142 | end
143 |
144 | map.bump_world = world
145 | map.bump_collidables = collidables
146 | end,
147 |
148 | --- Remove layer
149 | -- @param index to layer to be removed
150 | bump_removeLayer = function(map, index)
151 | local layer = assert(map.layers[index], "Layer not found: " .. index)
152 | local collidables = map.bump_collidables
153 |
154 | -- Remove collision objects
155 | for i = #collidables, 1, -1 do
156 | local obj = collidables[i]
157 |
158 | if obj.layer == layer
159 | and (
160 | layer.properties.collidable == true
161 | or obj.properties.collidable == true
162 | ) then
163 | map.bump_world:remove(obj)
164 | table.remove(collidables, i)
165 | end
166 | end
167 | end,
168 |
169 | --- Draw bump collisions world.
170 | -- @param world bump world holding the tiles geometry
171 | -- @param tx Translate on X
172 | -- @param ty Translate on Y
173 | -- @param sx Scale on X
174 | -- @param sy Scale on Y
175 | bump_draw = function(map, tx, ty, sx, sy)
176 | lg.push()
177 | lg.scale(sx or 1, sy or sx or 1)
178 | lg.translate(math.floor(tx or 0), math.floor(ty or 0))
179 |
180 | local items = map.bump_world:getItems()
181 | for _, item in ipairs(items) do
182 | lg.rectangle("line", map.bump_world:getRect(item))
183 | end
184 |
185 | lg.pop()
186 | end
187 | }
188 |
189 | --- Custom Properties in Tiled are used to tell this plugin what to do.
190 | -- @table Properties
191 | -- @field collidable set to true, can be used on any Layer, Tile, or Object
192 |
--------------------------------------------------------------------------------
/Episode 3/sti/utils.lua:
--------------------------------------------------------------------------------
1 | -- Some utility functions that shouldn't be exposed.
2 | local utils = {}
3 |
4 | -- https://github.com/stevedonovan/Penlight/blob/master/lua/pl/path.lua#L286
5 | function utils.format_path(path)
6 | local np_gen1,np_gen2 = '[^SEP]+SEP%.%.SEP?','SEP+%.?SEP'
7 | local np_pat1, np_pat2 = np_gen1:gsub('SEP','/'), np_gen2:gsub('SEP','/')
8 | local k
9 |
10 | repeat -- /./ -> /
11 | path,k = path:gsub(np_pat2,'/',1)
12 | until k == 0
13 |
14 | repeat -- A/../ -> (empty)
15 | path,k = path:gsub(np_pat1,'',1)
16 | until k == 0
17 |
18 | if path == '' then path = '.' end
19 |
20 | return path
21 | end
22 |
23 | -- Compensation for scale/rotation shift
24 | function utils.compensate(tile, tileX, tileY, tileW, tileH)
25 | local compx = 0
26 | local compy = 0
27 |
28 | if tile.sx < 0 then compx = tileW end
29 | if tile.sy < 0 then compy = tileH end
30 |
31 | if tile.r > 0 then
32 | tileX = tileX + tileH - compy
33 | tileY = tileY + tileH + compx - tileW
34 | elseif tile.r < 0 then
35 | tileX = tileX + compy
36 | tileY = tileY - compx + tileH
37 | else
38 | tileX = tileX + compx
39 | tileY = tileY + compy
40 | end
41 |
42 | return tileX, tileY
43 | end
44 |
45 | -- Cache images in main STI module
46 | function utils.cache_image(sti, path, image)
47 | image = image or love.graphics.newImage(path)
48 | image:setFilter("nearest", "nearest")
49 | sti.cache[path] = image
50 | end
51 |
52 | -- We just don't know.
53 | function utils.get_tiles(imageW, tileW, margin, spacing)
54 | imageW = imageW - margin
55 | local n = 0
56 |
57 | while imageW >= tileW do
58 | imageW = imageW - tileW
59 | if n ~= 0 then imageW = imageW - spacing end
60 | if imageW >= 0 then n = n + 1 end
61 | end
62 |
63 | return n
64 | end
65 |
66 | -- Decompress tile layer data
67 | function utils.get_decompressed_data(data)
68 | local ffi = require "ffi"
69 | local d = {}
70 | local decoded = ffi.cast("uint32_t*", data)
71 |
72 | for i = 0, data:len() / ffi.sizeof("uint32_t") do
73 | table.insert(d, tonumber(decoded[i]))
74 | end
75 |
76 | return d
77 | end
78 |
79 | -- Convert a Tiled ellipse object to a LOVE polygon
80 | function utils.convert_ellipse_to_polygon(x, y, w, h, max_segments)
81 | local ceil = math.ceil
82 | local cos = math.cos
83 | local sin = math.sin
84 |
85 | local function calc_segments(segments)
86 | local function vdist(a, b)
87 | local c = {
88 | x = a.x - b.x,
89 | y = a.y - b.y,
90 | }
91 |
92 | return c.x * c.x + c.y * c.y
93 | end
94 |
95 | segments = segments or 64
96 | local vertices = {}
97 |
98 | local v = { 1, 2, ceil(segments/4-1), ceil(segments/4) }
99 |
100 | local m
101 | if love and love.physics then
102 | m = love.physics.getMeter()
103 | else
104 | m = 32
105 | end
106 |
107 | for _, i in ipairs(v) do
108 | local angle = (i / segments) * math.pi * 2
109 | local px = x + w / 2 + cos(angle) * w / 2
110 | local py = y + h / 2 + sin(angle) * h / 2
111 |
112 | table.insert(vertices, { x = px / m, y = py / m })
113 | end
114 |
115 | local dist1 = vdist(vertices[1], vertices[2])
116 | local dist2 = vdist(vertices[3], vertices[4])
117 |
118 | -- Box2D threshold
119 | if dist1 < 0.0025 or dist2 < 0.0025 then
120 | return calc_segments(segments-2)
121 | end
122 |
123 | return segments
124 | end
125 |
126 | local segments = calc_segments(max_segments)
127 | local vertices = {}
128 |
129 | table.insert(vertices, { x = x + w / 2, y = y + h / 2 })
130 |
131 | for i = 0, segments do
132 | local angle = (i / segments) * math.pi * 2
133 | local px = x + w / 2 + cos(angle) * w / 2
134 | local py = y + h / 2 + sin(angle) * h / 2
135 |
136 | table.insert(vertices, { x = px, y = py })
137 | end
138 |
139 | return vertices
140 | end
141 |
142 | function utils.rotate_vertex(map, vertex, x, y, cos, sin, oy)
143 | if map.orientation == "isometric" then
144 | x, y = utils.convert_isometric_to_screen(map, x, y)
145 | vertex.x, vertex.y = utils.convert_isometric_to_screen(map, vertex.x, vertex.y)
146 | end
147 |
148 | vertex.x = vertex.x - x
149 | vertex.y = vertex.y - y
150 |
151 | return
152 | x + cos * vertex.x - sin * vertex.y,
153 | y + sin * vertex.x + cos * vertex.y - (oy or 0)
154 | end
155 |
156 | --- Project isometric position to cartesian position
157 | function utils.convert_isometric_to_screen(map, x, y)
158 | local mapW = map.width
159 | local tileW = map.tilewidth
160 | local tileH = map.tileheight
161 | local tileX = x / tileH
162 | local tileY = y / tileH
163 | local offsetX = mapW * tileW / 2
164 |
165 | return
166 | (tileX - tileY) * tileW / 2 + offsetX,
167 | (tileX + tileY) * tileH / 2
168 | end
169 |
170 | function utils.hex_to_color(hex)
171 | if hex:sub(1, 1) == "#" then
172 | hex = hex:sub(2)
173 | end
174 |
175 | return {
176 | r = tonumber(hex:sub(1, 2), 16) / 255,
177 | g = tonumber(hex:sub(3, 4), 16) / 255,
178 | b = tonumber(hex:sub(5, 6), 16) / 255
179 | }
180 | end
181 |
182 | function utils.pixel_function(_, _, r, g, b, a)
183 | local mask = utils._TC
184 |
185 | if r == mask.r and
186 | g == mask.g and
187 | b == mask.b then
188 | return r, g, b, 0
189 | end
190 |
191 | return r, g, b, a
192 | end
193 |
194 | function utils.fix_transparent_color(tileset, path)
195 | local image_data = love.image.newImageData(path)
196 | tileset.image = love.graphics.newImage(image_data)
197 |
198 | if tileset.transparentcolor then
199 | utils._TC = utils.hex_to_color(tileset.transparentcolor)
200 |
201 | image_data:mapPixel(utils.pixel_function)
202 | tileset.image = love.graphics.newImage(image_data)
203 | end
204 | end
205 |
206 | function utils.deepCopy(t)
207 | local copy = {}
208 | for k,v in pairs(t) do
209 | if type(v) == "table" then
210 | v = utils.deepCopy(v)
211 | end
212 | copy[k] = v
213 | end
214 | return copy
215 | end
216 |
217 | return utils
218 |
--------------------------------------------------------------------------------
/Episode 4/README.md:
--------------------------------------------------------------------------------
1 | Full source code and assets from the tutorial series. - Youtube link: https://www.youtube.com/playlist?list=PL1A1gsSe2tMxngwl-CU1MCX7kmbLf4P5O
2 | Note: The code and assets have different licences
3 |
4 | Code: WTFPL (Do whatever you want, no restrictions)
5 | Art: BY-NC-ND (https://creativecommons.org/licenses/by-nc-nd/4.0/) More restricted.
6 |
--------------------------------------------------------------------------------
/Episode 4/assets/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/background.png
--------------------------------------------------------------------------------
/Episode 4/assets/bit.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/bit.ttf
--------------------------------------------------------------------------------
/Episode 4/assets/coin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/coin.png
--------------------------------------------------------------------------------
/Episode 4/assets/enemy/run/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/enemy/run/1.png
--------------------------------------------------------------------------------
/Episode 4/assets/enemy/run/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/enemy/run/2.png
--------------------------------------------------------------------------------
/Episode 4/assets/enemy/run/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/enemy/run/3.png
--------------------------------------------------------------------------------
/Episode 4/assets/enemy/run/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/enemy/run/4.png
--------------------------------------------------------------------------------
/Episode 4/assets/enemy/run/Untitled-1.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/enemy/run/Untitled-1.psd
--------------------------------------------------------------------------------
/Episode 4/assets/enemy/walk/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/enemy/walk/1.png
--------------------------------------------------------------------------------
/Episode 4/assets/enemy/walk/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/enemy/walk/2.png
--------------------------------------------------------------------------------
/Episode 4/assets/enemy/walk/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/enemy/walk/3.png
--------------------------------------------------------------------------------
/Episode 4/assets/enemy/walk/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/enemy/walk/4.png
--------------------------------------------------------------------------------
/Episode 4/assets/enemy/walk/Untitled-1.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/enemy/walk/Untitled-1.psd
--------------------------------------------------------------------------------
/Episode 4/assets/heart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/heart.png
--------------------------------------------------------------------------------
/Episode 4/assets/player/air/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/player/air/1.png
--------------------------------------------------------------------------------
/Episode 4/assets/player/air/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/player/air/2.png
--------------------------------------------------------------------------------
/Episode 4/assets/player/air/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/player/air/3.png
--------------------------------------------------------------------------------
/Episode 4/assets/player/air/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/player/air/4.png
--------------------------------------------------------------------------------
/Episode 4/assets/player/idle/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/player/idle/1.png
--------------------------------------------------------------------------------
/Episode 4/assets/player/idle/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/player/idle/2.png
--------------------------------------------------------------------------------
/Episode 4/assets/player/idle/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/player/idle/3.png
--------------------------------------------------------------------------------
/Episode 4/assets/player/idle/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/player/idle/4.png
--------------------------------------------------------------------------------
/Episode 4/assets/player/jump/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/player/jump/1.png
--------------------------------------------------------------------------------
/Episode 4/assets/player/jump/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/player/jump/2.png
--------------------------------------------------------------------------------
/Episode 4/assets/player/jump/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/player/jump/3.png
--------------------------------------------------------------------------------
/Episode 4/assets/player/jump/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/player/jump/4.png
--------------------------------------------------------------------------------
/Episode 4/assets/player/run/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/player/run/1.png
--------------------------------------------------------------------------------
/Episode 4/assets/player/run/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/player/run/2.png
--------------------------------------------------------------------------------
/Episode 4/assets/player/run/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/player/run/3.png
--------------------------------------------------------------------------------
/Episode 4/assets/player/run/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/player/run/4.png
--------------------------------------------------------------------------------
/Episode 4/assets/player/run/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/player/run/5.png
--------------------------------------------------------------------------------
/Episode 4/assets/player/run/6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/player/run/6.png
--------------------------------------------------------------------------------
/Episode 4/assets/sfx/player_get_coin.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/sfx/player_get_coin.ogg
--------------------------------------------------------------------------------
/Episode 4/assets/sfx/player_hit.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/sfx/player_hit.ogg
--------------------------------------------------------------------------------
/Episode 4/assets/sfx/player_jump.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/sfx/player_jump.ogg
--------------------------------------------------------------------------------
/Episode 4/assets/spikes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/spikes.png
--------------------------------------------------------------------------------
/Episode 4/assets/square.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/square.png
--------------------------------------------------------------------------------
/Episode 4/assets/stone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/stone.png
--------------------------------------------------------------------------------
/Episode 4/assets/tiles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 4/assets/tiles.png
--------------------------------------------------------------------------------
/Episode 4/coin.lua:
--------------------------------------------------------------------------------
1 |
2 |
3 | Coin = {}
4 | Coin.__index = Coin
5 | ActiveCoins = {}
6 |
7 | function Coin.new(x,y)
8 | local instance = setmetatable({}, Coin)
9 | instance.x = x
10 | instance.y = y
11 | instance.img = love.graphics.newImage("assets/coin.png")
12 | instance.width = instance.img:getWidth()
13 | instance.height = instance.img:getHeight()
14 | instance.scaleX = 1
15 | instance.randomTimeOffset = math.random(0, 100)
16 | instance.toBeRemoved = false
17 |
18 | instance.physics = {}
19 | instance.physics.body = love.physics.newBody(World, instance.x, instance.y, "static")
20 | instance.physics.shape = love.physics.newRectangleShape(instance.width, instance.height)
21 | instance.physics.fixture = love.physics.newFixture(instance.physics.body, instance.physics.shape)
22 | instance.physics.fixture:setSensor(true)
23 | table.insert(ActiveCoins, instance)
24 | end
25 |
26 | function Coin:remove()
27 | for i,instance in ipairs(ActiveCoins) do
28 | if instance == self then
29 | Player:incrementCoins()
30 | print(Player.coins)
31 | self.physics.body:destroy()
32 | table.remove(ActiveCoins, i)
33 | end
34 | end
35 | end
36 |
37 | function Coin:update(dt)
38 | self:spin(dt)
39 | self:checkRemove()
40 | end
41 |
42 | function Coin:checkRemove()
43 | if self.toBeRemoved then
44 | self:remove()
45 | end
46 | end
47 |
48 | function Coin:spin(dt)
49 | self.scaleX = math.sin(love.timer.getTime() * 2 + self.randomTimeOffset)
50 | end
51 |
52 | function Coin:draw()
53 | love.graphics.draw(self.img, self.x, self.y, 0, self.scaleX, 1, self.width / 2, self.height / 2)
54 | end
55 |
56 | function Coin.updateAll(dt)
57 | for i,instance in ipairs(ActiveCoins) do
58 | instance:update(dt)
59 | end
60 | end
61 |
62 | function Coin.drawAll()
63 | for i,instance in ipairs(ActiveCoins) do
64 | instance:draw()
65 | end
66 | end
67 |
68 | function Coin.beginContact(a, b, collision)
69 | for i,instance in ipairs(ActiveCoins) do
70 | if a == instance.physics.fixture or b == instance.physics.fixture then
71 | if a == Player.physics.fixture or b == Player.physics.fixture then
72 | instance.toBeRemoved = true
73 | return true
74 | end
75 | end
76 | end
77 | end
78 |
--------------------------------------------------------------------------------
/Episode 4/conf.lua:
--------------------------------------------------------------------------------
1 |
2 | function love.conf(t)
3 | t.title = "Platformer" -- The title of the window the game is in (string)
4 | t.version = "11.3" -- The LÖVE version this game was made for (string)
5 | t.console = true -- Attach a console (boolean, Windows only)
6 | t.window.width = 1280 -- The window width (number)
7 | t.window.height = 720 -- The window height (number)
8 | t.window.vsync = 0
9 | end
10 |
--------------------------------------------------------------------------------
/Episode 4/gui.lua:
--------------------------------------------------------------------------------
1 |
2 |
3 | GUI = {}
4 |
5 | function GUI:load()
6 | self.coins = {}
7 | self.coins.img = love.graphics.newImage("assets/coin.png")
8 | self.coins.width = self.coins.img:getWidth()
9 | self.coins.height = self.coins.img:getHeight()
10 | self.coins.scale = 3
11 | self.coins.x = 50
12 | self.coins.y = 50
13 | self.font = love.graphics.newFont("assets/bit.ttf", 36)
14 | end
15 |
16 | function GUI:update(dt)
17 |
18 | end
19 |
20 | function GUI:draw()
21 | self:displayCoins()
22 | self:displayCoinText()
23 | end
24 |
25 | function GUI:displayCoins()
26 | love.graphics.setColor(0,0,0,0.5)
27 | love.graphics.draw(self.coins.img, self.coins.x + 2, self.coins.y + 2, 0, self.coins.scale, self.coins.scale)
28 | love.graphics.setColor(1,1,1,1)
29 | love.graphics.draw(self.coins.img, self.coins.x, self.coins.y, 0, self.coins.scale, self.coins.scale)
30 | end
31 |
32 | function GUI:displayCoinText()
33 | love.graphics.setFont(self.font)
34 | local x = self.coins.x + self.coins.width * self.coins.scale
35 | local y = self.coins.y + self.coins.height / 2 * self.coins.scale - self.font:getHeight() / 2
36 | love.graphics.setColor(0,0,0,0.5)
37 | love.graphics.print(" : "..Player.coins, x + 2, y + 2)
38 | love.graphics.setColor(1,1,1,1)
39 | love.graphics.print(" : "..Player.coins, x, y)
40 | end
41 |
--------------------------------------------------------------------------------
/Episode 4/main.lua:
--------------------------------------------------------------------------------
1 | local STI = require("sti")
2 | require("player")
3 | require("coin")
4 | require("gui")
5 | love.graphics.setDefaultFilter("nearest", "nearest")
6 |
7 | function love.load()
8 | Map = STI("map/1.lua", {"box2d"})
9 | World = love.physics.newWorld(0,0)
10 | World:setCallbacks(beginContact, endContact)
11 | Map:box2d_init(World)
12 | Map.layers.solid.visible = false
13 | background = love.graphics.newImage("assets/background.png")
14 | GUI:load()
15 | Player:load()
16 | Coin.new(300, 200)
17 | Coin.new(400, 200)
18 | Coin.new(500, 100)
19 | end
20 |
21 | function love.update(dt)
22 | World:update(dt)
23 | Player:update(dt)
24 | Coin.updateAll(dt)
25 | GUI:update(dt)
26 | end
27 |
28 | function love.draw()
29 | love.graphics.draw(background)
30 | Map:draw(0, 0, 2, 2)
31 | love.graphics.push()
32 | love.graphics.scale(2,2)
33 |
34 | Player:draw()
35 | Coin.drawAll()
36 |
37 | love.graphics.pop()
38 | GUI:draw()
39 | end
40 |
41 | function love.keypressed(key)
42 | Player:jump(key)
43 | end
44 |
45 | function beginContact(a, b, collision)
46 | if Coin.beginContact(a, b, collision) then return end
47 | Player:beginContact(a, b, collision)
48 | end
49 |
50 | function endContact(a, b, collision)
51 | Player:endContact(a, b, collision)
52 | end
53 |
--------------------------------------------------------------------------------
/Episode 4/map/tmx/1.tmx:
--------------------------------------------------------------------------------
1 |
2 |
81 |
--------------------------------------------------------------------------------
/Episode 4/player.lua:
--------------------------------------------------------------------------------
1 |
2 |
3 | Player = {}
4 |
5 | function Player:load()
6 | self.x = 100
7 | self.y = 0
8 | self.width = 20
9 | self.height = 60
10 | self.xVel = 0
11 | self.yVel = 0
12 | self.maxSpeed = 200
13 | self.acceleration = 4000
14 | self.friction = 3500
15 | self.gravity = 1500
16 | self.jumpAmount = -500
17 | self.coins = 0
18 |
19 | self.graceTime = 0
20 | self.graceDuration = 0.1
21 |
22 | self.grounded = false
23 | self.hasDoubleJump = true
24 |
25 | self.direction = "right"
26 | self.state = "idle"
27 |
28 | self:loadAssets()
29 |
30 | self.physics = {}
31 | self.physics.body = love.physics.newBody(World, self.x, self.y, "dynamic")
32 | self.physics.body:setFixedRotation(true)
33 | self.physics.shape = love.physics.newRectangleShape(self.width, self.height)
34 | self.physics.fixture = love.physics.newFixture(self.physics.body, self.physics.shape)
35 | end
36 |
37 | function Player:loadAssets()
38 | self.animation = {timer = 0, rate = 0.1}
39 |
40 | self.animation.run = {total = 6, current = 1, img = {}}
41 | for i=1, self.animation.run.total do
42 | self.animation.run.img[i] = love.graphics.newImage("assets/player/run/"..i..".png")
43 | end
44 |
45 | self.animation.idle = {total = 4, current = 1, img = {}}
46 | for i=1, self.animation.idle.total do
47 | self.animation.idle.img[i] = love.graphics.newImage("assets/player/idle/"..i..".png")
48 | end
49 |
50 | self.animation.air = {total = 4, current = 1, img = {}}
51 | for i=1, self.animation.air.total do
52 | self.animation.air.img[i] = love.graphics.newImage("assets/player/air/"..i..".png")
53 | end
54 |
55 | self.animation.draw = self.animation.idle.img[1]
56 | self.animation.width = self.animation.draw:getWidth()
57 | self.animation.height = self.animation.draw:getHeight()
58 | end
59 |
60 | function Player:incrementCoins()
61 | self.coins = self.coins + 1
62 | end
63 |
64 | function Player:update(dt)
65 | self:setState()
66 | self:setDirection()
67 | self:animate(dt)
68 | self:decreaseGraceTime(dt)
69 | self:syncPhysics()
70 | self:move(dt)
71 | self:applyGravity(dt)
72 | end
73 |
74 | function Player:setState()
75 | if not self.grounded then
76 | self.state = "air"
77 | elseif self.xVel == 0 then
78 | self.state = "idle"
79 | else
80 | self.state = "run"
81 | end
82 | end
83 |
84 | function Player:setDirection()
85 | if self.xVel < 0 then
86 | self.direction = "left"
87 | elseif self.xVel > 0 then
88 | self.direction = "right"
89 | end
90 | end
91 |
92 | function Player:animate(dt)
93 | self.animation.timer = self.animation.timer + dt
94 | if self.animation.timer > self.animation.rate then
95 | self.animation.timer = 0
96 | self:setNewFrame()
97 | end
98 | end
99 |
100 | function Player:setNewFrame()
101 | local anim = self.animation[self.state]
102 | if anim.current < anim.total then
103 | anim.current = anim.current + 1
104 | else
105 | anim.current = 1
106 | end
107 | self.animation.draw = anim.img[anim.current]
108 | end
109 |
110 | function Player:decreaseGraceTime(dt)
111 | if not self.grounded then
112 | self.graceTime = self.graceTime - dt
113 | end
114 | end
115 |
116 | function Player:applyGravity(dt)
117 | if not self.grounded then
118 | self.yVel = self.yVel + self.gravity * dt
119 | end
120 | end
121 |
122 | function Player:move(dt)
123 | if love.keyboard.isDown("d", "right") then
124 | self.xVel = math.min(self.xVel + self.acceleration * dt, self.maxSpeed)
125 | elseif love.keyboard.isDown("a", "left") then
126 | self.xVel = math.max(self.xVel - self.acceleration * dt, -self.maxSpeed)
127 | else
128 | self:applyFriction(dt)
129 | end
130 | end
131 |
132 | function Player:applyFriction(dt)
133 | if self.xVel > 0 then
134 | self.xVel = math.max(self.xVel - self.friction * dt, 0)
135 | elseif self.xVel < 0 then
136 | self.xVel = math.min(self.xVel + self.friction * dt, 0)
137 | end
138 | end
139 |
140 | function Player:syncPhysics()
141 | self.x, self.y = self.physics.body:getPosition()
142 | self.physics.body:setLinearVelocity(self.xVel, self.yVel)
143 | end
144 |
145 | function Player:beginContact(a, b, collision)
146 | if self.grounded == true then return end
147 | local nx, ny = collision:getNormal()
148 | if a == self.physics.fixture then
149 | if ny > 0 then
150 | self:land(collision)
151 | elseif ny < 0 then
152 | self.yVel = 0
153 | end
154 | elseif b == self.physics.fixture then
155 | if ny < 0 then
156 | self:land(collision)
157 | elseif ny > 0 then
158 | self.yVel = 0
159 | end
160 | end
161 | end
162 |
163 | function Player:land(collision)
164 | self.currentGroundCollision = collision
165 | self.yVel = 0
166 | self.grounded = true
167 | self.hasDoubleJump = true
168 | self.graceTime = self.graceDuration
169 | end
170 |
171 | function Player:jump(key)
172 | if (key == "w" or key == "up") then
173 | if self.grounded or self.graceTime > 0 then
174 | self.yVel = self.jumpAmount
175 | self.graceTime = 0
176 | elseif self.hasDoubleJump then
177 | self.hasDoubleJump = false
178 | self.yVel = self.jumpAmount * 0.8
179 | end
180 | end
181 | end
182 |
183 | function Player:endContact(a, b, collision)
184 | if a == self.physics.fixture or b == self.physics.fixture then
185 | if self.currentGroundCollision == collision then
186 | self.grounded = false
187 | end
188 | end
189 | end
190 |
191 | function Player:draw()
192 | local scaleX = 1
193 | if self.direction == "left" then
194 | scaleX = -1
195 | end
196 | love.graphics.draw(self.animation.draw, self.x, self.y, 0, scaleX, 1, self.animation.width / 2, self.animation.height / 2)
197 | end
198 |
--------------------------------------------------------------------------------
/Episode 4/run.bat:
--------------------------------------------------------------------------------
1 | @ECHO
2 |
3 | start "" "C:\Program Files\LOVE\lovec" .
--------------------------------------------------------------------------------
/Episode 4/sti/graphics.lua:
--------------------------------------------------------------------------------
1 | local lg = _G.love.graphics
2 | local graphics = { isCreated = lg and true or false }
3 |
4 | function graphics.newSpriteBatch(...)
5 | if graphics.isCreated then
6 | return lg.newSpriteBatch(...)
7 | end
8 | end
9 |
10 | function graphics.newCanvas(...)
11 | if graphics.isCreated then
12 | return lg.newCanvas(...)
13 | end
14 | end
15 |
16 | function graphics.newImage(...)
17 | if graphics.isCreated then
18 | return lg.newImage(...)
19 | end
20 | end
21 |
22 | function graphics.newQuad(...)
23 | if graphics.isCreated then
24 | return lg.newQuad(...)
25 | end
26 | end
27 |
28 | function graphics.getCanvas(...)
29 | if graphics.isCreated then
30 | return lg.getCanvas(...)
31 | end
32 | end
33 |
34 | function graphics.setCanvas(...)
35 | if graphics.isCreated then
36 | return lg.setCanvas(...)
37 | end
38 | end
39 |
40 | function graphics.clear(...)
41 | if graphics.isCreated then
42 | return lg.clear(...)
43 | end
44 | end
45 |
46 | function graphics.push(...)
47 | if graphics.isCreated then
48 | return lg.push(...)
49 | end
50 | end
51 |
52 | function graphics.origin(...)
53 | if graphics.isCreated then
54 | return lg.origin(...)
55 | end
56 | end
57 |
58 | function graphics.scale(...)
59 | if graphics.isCreated then
60 | return lg.scale(...)
61 | end
62 | end
63 |
64 | function graphics.translate(...)
65 | if graphics.isCreated then
66 | return lg.translate(...)
67 | end
68 | end
69 |
70 | function graphics.pop(...)
71 | if graphics.isCreated then
72 | return lg.pop(...)
73 | end
74 | end
75 |
76 | function graphics.draw(...)
77 | if graphics.isCreated then
78 | return lg.draw(...)
79 | end
80 | end
81 |
82 | function graphics.rectangle(...)
83 | if graphics.isCreated then
84 | return lg.rectangle(...)
85 | end
86 | end
87 |
88 | function graphics.getColor(...)
89 | if graphics.isCreated then
90 | return lg.getColor(...)
91 | end
92 | end
93 |
94 | function graphics.setColor(...)
95 | if graphics.isCreated then
96 | return lg.setColor(...)
97 | end
98 | end
99 |
100 | function graphics.line(...)
101 | if graphics.isCreated then
102 | return lg.line(...)
103 | end
104 | end
105 |
106 | function graphics.polygon(...)
107 | if graphics.isCreated then
108 | return lg.polygon(...)
109 | end
110 | end
111 |
112 | function graphics.points(...)
113 | if graphics.isCreated then
114 | return lg.points(...)
115 | end
116 | end
117 |
118 | function graphics.getWidth()
119 | if graphics.isCreated then
120 | return lg.getWidth()
121 | end
122 | return 0
123 | end
124 |
125 | function graphics.getHeight()
126 | if graphics.isCreated then
127 | return lg.getHeight()
128 | end
129 | return 0
130 | end
131 |
132 | return graphics
133 |
--------------------------------------------------------------------------------
/Episode 4/sti/utils.lua:
--------------------------------------------------------------------------------
1 | -- Some utility functions that shouldn't be exposed.
2 | local utils = {}
3 |
4 | -- https://github.com/stevedonovan/Penlight/blob/master/lua/pl/path.lua#L286
5 | function utils.format_path(path)
6 | local np_gen1,np_gen2 = '[^SEP]+SEP%.%.SEP?','SEP+%.?SEP'
7 | local np_pat1, np_pat2 = np_gen1:gsub('SEP','/'), np_gen2:gsub('SEP','/')
8 | local k
9 |
10 | repeat -- /./ -> /
11 | path,k = path:gsub(np_pat2,'/',1)
12 | until k == 0
13 |
14 | repeat -- A/../ -> (empty)
15 | path,k = path:gsub(np_pat1,'',1)
16 | until k == 0
17 |
18 | if path == '' then path = '.' end
19 |
20 | return path
21 | end
22 |
23 | -- Compensation for scale/rotation shift
24 | function utils.compensate(tile, tileX, tileY, tileW, tileH)
25 | local compx = 0
26 | local compy = 0
27 |
28 | if tile.sx < 0 then compx = tileW end
29 | if tile.sy < 0 then compy = tileH end
30 |
31 | if tile.r > 0 then
32 | tileX = tileX + tileH - compy
33 | tileY = tileY + tileH + compx - tileW
34 | elseif tile.r < 0 then
35 | tileX = tileX + compy
36 | tileY = tileY - compx + tileH
37 | else
38 | tileX = tileX + compx
39 | tileY = tileY + compy
40 | end
41 |
42 | return tileX, tileY
43 | end
44 |
45 | -- Cache images in main STI module
46 | function utils.cache_image(sti, path, image)
47 | image = image or love.graphics.newImage(path)
48 | image:setFilter("nearest", "nearest")
49 | sti.cache[path] = image
50 | end
51 |
52 | -- We just don't know.
53 | function utils.get_tiles(imageW, tileW, margin, spacing)
54 | imageW = imageW - margin
55 | local n = 0
56 |
57 | while imageW >= tileW do
58 | imageW = imageW - tileW
59 | if n ~= 0 then imageW = imageW - spacing end
60 | if imageW >= 0 then n = n + 1 end
61 | end
62 |
63 | return n
64 | end
65 |
66 | -- Decompress tile layer data
67 | function utils.get_decompressed_data(data)
68 | local ffi = require "ffi"
69 | local d = {}
70 | local decoded = ffi.cast("uint32_t*", data)
71 |
72 | for i = 0, data:len() / ffi.sizeof("uint32_t") do
73 | table.insert(d, tonumber(decoded[i]))
74 | end
75 |
76 | return d
77 | end
78 |
79 | -- Convert a Tiled ellipse object to a LOVE polygon
80 | function utils.convert_ellipse_to_polygon(x, y, w, h, max_segments)
81 | local ceil = math.ceil
82 | local cos = math.cos
83 | local sin = math.sin
84 |
85 | local function calc_segments(segments)
86 | local function vdist(a, b)
87 | local c = {
88 | x = a.x - b.x,
89 | y = a.y - b.y,
90 | }
91 |
92 | return c.x * c.x + c.y * c.y
93 | end
94 |
95 | segments = segments or 64
96 | local vertices = {}
97 |
98 | local v = { 1, 2, ceil(segments/4-1), ceil(segments/4) }
99 |
100 | local m
101 | if love and love.physics then
102 | m = love.physics.getMeter()
103 | else
104 | m = 32
105 | end
106 |
107 | for _, i in ipairs(v) do
108 | local angle = (i / segments) * math.pi * 2
109 | local px = x + w / 2 + cos(angle) * w / 2
110 | local py = y + h / 2 + sin(angle) * h / 2
111 |
112 | table.insert(vertices, { x = px / m, y = py / m })
113 | end
114 |
115 | local dist1 = vdist(vertices[1], vertices[2])
116 | local dist2 = vdist(vertices[3], vertices[4])
117 |
118 | -- Box2D threshold
119 | if dist1 < 0.0025 or dist2 < 0.0025 then
120 | return calc_segments(segments-2)
121 | end
122 |
123 | return segments
124 | end
125 |
126 | local segments = calc_segments(max_segments)
127 | local vertices = {}
128 |
129 | table.insert(vertices, { x = x + w / 2, y = y + h / 2 })
130 |
131 | for i = 0, segments do
132 | local angle = (i / segments) * math.pi * 2
133 | local px = x + w / 2 + cos(angle) * w / 2
134 | local py = y + h / 2 + sin(angle) * h / 2
135 |
136 | table.insert(vertices, { x = px, y = py })
137 | end
138 |
139 | return vertices
140 | end
141 |
142 | function utils.rotate_vertex(map, vertex, x, y, cos, sin, oy)
143 | if map.orientation == "isometric" then
144 | x, y = utils.convert_isometric_to_screen(map, x, y)
145 | vertex.x, vertex.y = utils.convert_isometric_to_screen(map, vertex.x, vertex.y)
146 | end
147 |
148 | vertex.x = vertex.x - x
149 | vertex.y = vertex.y - y
150 |
151 | return
152 | x + cos * vertex.x - sin * vertex.y,
153 | y + sin * vertex.x + cos * vertex.y - (oy or 0)
154 | end
155 |
156 | --- Project isometric position to cartesian position
157 | function utils.convert_isometric_to_screen(map, x, y)
158 | local mapW = map.width
159 | local tileW = map.tilewidth
160 | local tileH = map.tileheight
161 | local tileX = x / tileH
162 | local tileY = y / tileH
163 | local offsetX = mapW * tileW / 2
164 |
165 | return
166 | (tileX - tileY) * tileW / 2 + offsetX,
167 | (tileX + tileY) * tileH / 2
168 | end
169 |
170 | function utils.hex_to_color(hex)
171 | if hex:sub(1, 1) == "#" then
172 | hex = hex:sub(2)
173 | end
174 |
175 | return {
176 | r = tonumber(hex:sub(1, 2), 16) / 255,
177 | g = tonumber(hex:sub(3, 4), 16) / 255,
178 | b = tonumber(hex:sub(5, 6), 16) / 255
179 | }
180 | end
181 |
182 | function utils.pixel_function(_, _, r, g, b, a)
183 | local mask = utils._TC
184 |
185 | if r == mask.r and
186 | g == mask.g and
187 | b == mask.b then
188 | return r, g, b, 0
189 | end
190 |
191 | return r, g, b, a
192 | end
193 |
194 | function utils.fix_transparent_color(tileset, path)
195 | local image_data = love.image.newImageData(path)
196 | tileset.image = love.graphics.newImage(image_data)
197 |
198 | if tileset.transparentcolor then
199 | utils._TC = utils.hex_to_color(tileset.transparentcolor)
200 |
201 | image_data:mapPixel(utils.pixel_function)
202 | tileset.image = love.graphics.newImage(image_data)
203 | end
204 | end
205 |
206 | function utils.deepCopy(t)
207 | local copy = {}
208 | for k,v in pairs(t) do
209 | if type(v) == "table" then
210 | v = utils.deepCopy(v)
211 | end
212 | copy[k] = v
213 | end
214 | return copy
215 | end
216 |
217 | return utils
218 |
--------------------------------------------------------------------------------
/Episode 5/README.md:
--------------------------------------------------------------------------------
1 | Full source code and assets from the tutorial series. - Youtube link: https://www.youtube.com/playlist?list=PL1A1gsSe2tMxngwl-CU1MCX7kmbLf4P5O
2 | Note: The code and assets have different licences
3 |
4 | Code: WTFPL (Do whatever you want, no restrictions)
5 | Art: BY-NC-ND (https://creativecommons.org/licenses/by-nc-nd/4.0/) More restricted.
6 |
--------------------------------------------------------------------------------
/Episode 5/assets/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/background.png
--------------------------------------------------------------------------------
/Episode 5/assets/bit.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/bit.ttf
--------------------------------------------------------------------------------
/Episode 5/assets/coin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/coin.png
--------------------------------------------------------------------------------
/Episode 5/assets/enemy/run/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/enemy/run/1.png
--------------------------------------------------------------------------------
/Episode 5/assets/enemy/run/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/enemy/run/2.png
--------------------------------------------------------------------------------
/Episode 5/assets/enemy/run/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/enemy/run/3.png
--------------------------------------------------------------------------------
/Episode 5/assets/enemy/run/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/enemy/run/4.png
--------------------------------------------------------------------------------
/Episode 5/assets/enemy/run/Untitled-1.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/enemy/run/Untitled-1.psd
--------------------------------------------------------------------------------
/Episode 5/assets/enemy/walk/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/enemy/walk/1.png
--------------------------------------------------------------------------------
/Episode 5/assets/enemy/walk/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/enemy/walk/2.png
--------------------------------------------------------------------------------
/Episode 5/assets/enemy/walk/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/enemy/walk/3.png
--------------------------------------------------------------------------------
/Episode 5/assets/enemy/walk/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/enemy/walk/4.png
--------------------------------------------------------------------------------
/Episode 5/assets/enemy/walk/Untitled-1.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/enemy/walk/Untitled-1.psd
--------------------------------------------------------------------------------
/Episode 5/assets/heart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/heart.png
--------------------------------------------------------------------------------
/Episode 5/assets/player/air/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/player/air/1.png
--------------------------------------------------------------------------------
/Episode 5/assets/player/air/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/player/air/2.png
--------------------------------------------------------------------------------
/Episode 5/assets/player/air/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/player/air/3.png
--------------------------------------------------------------------------------
/Episode 5/assets/player/air/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/player/air/4.png
--------------------------------------------------------------------------------
/Episode 5/assets/player/idle/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/player/idle/1.png
--------------------------------------------------------------------------------
/Episode 5/assets/player/idle/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/player/idle/2.png
--------------------------------------------------------------------------------
/Episode 5/assets/player/idle/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/player/idle/3.png
--------------------------------------------------------------------------------
/Episode 5/assets/player/idle/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/player/idle/4.png
--------------------------------------------------------------------------------
/Episode 5/assets/player/jump/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/player/jump/1.png
--------------------------------------------------------------------------------
/Episode 5/assets/player/jump/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/player/jump/2.png
--------------------------------------------------------------------------------
/Episode 5/assets/player/jump/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/player/jump/3.png
--------------------------------------------------------------------------------
/Episode 5/assets/player/jump/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/player/jump/4.png
--------------------------------------------------------------------------------
/Episode 5/assets/player/run/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/player/run/1.png
--------------------------------------------------------------------------------
/Episode 5/assets/player/run/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/player/run/2.png
--------------------------------------------------------------------------------
/Episode 5/assets/player/run/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/player/run/3.png
--------------------------------------------------------------------------------
/Episode 5/assets/player/run/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/player/run/4.png
--------------------------------------------------------------------------------
/Episode 5/assets/player/run/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/player/run/5.png
--------------------------------------------------------------------------------
/Episode 5/assets/player/run/6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/player/run/6.png
--------------------------------------------------------------------------------
/Episode 5/assets/sfx/player_get_coin.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/sfx/player_get_coin.ogg
--------------------------------------------------------------------------------
/Episode 5/assets/sfx/player_hit.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/sfx/player_hit.ogg
--------------------------------------------------------------------------------
/Episode 5/assets/sfx/player_jump.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/sfx/player_jump.ogg
--------------------------------------------------------------------------------
/Episode 5/assets/spike.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/spike.png
--------------------------------------------------------------------------------
/Episode 5/assets/square.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/square.png
--------------------------------------------------------------------------------
/Episode 5/assets/stone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/stone.png
--------------------------------------------------------------------------------
/Episode 5/assets/tiles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 5/assets/tiles.png
--------------------------------------------------------------------------------
/Episode 5/camera.lua:
--------------------------------------------------------------------------------
1 |
2 |
3 | local Camera = {
4 | x = 0,
5 | y = 0,
6 | scale = 2,
7 | }
8 |
9 | function Camera:apply()
10 | love.graphics.push()
11 | love.graphics.scale(self.scale,self.scale)
12 | love.graphics.translate(-self.x, -self.y)
13 | end
14 |
15 | function Camera:clear()
16 | love.graphics.pop()
17 | end
18 |
19 | function Camera:setPosition(x, y)
20 | self.x = x - love.graphics.getWidth() / 2 / self.scale
21 | self.y = y
22 | local RS = self.x + love.graphics.getWidth() / 2
23 |
24 | if self.x < 0 then
25 | self.x = 0
26 | elseif RS > MapWidth then
27 | self.x = MapWidth - love.graphics.getWidth() / 2
28 | end
29 | end
30 |
31 | return Camera
32 |
--------------------------------------------------------------------------------
/Episode 5/coin.lua:
--------------------------------------------------------------------------------
1 |
2 |
3 | local Coin = {}
4 | Coin.__index = Coin
5 | local ActiveCoins = {}
6 | local Player = require("player")
7 |
8 | function Coin.new(x,y)
9 | local instance = setmetatable({}, Coin)
10 | instance.x = x
11 | instance.y = y
12 | instance.img = love.graphics.newImage("assets/coin.png")
13 | instance.width = instance.img:getWidth()
14 | instance.height = instance.img:getHeight()
15 | instance.scaleX = 1
16 | instance.randomTimeOffset = math.random(0, 100)
17 | instance.toBeRemoved = false
18 |
19 | instance.physics = {}
20 | instance.physics.body = love.physics.newBody(World, instance.x, instance.y, "static")
21 | instance.physics.shape = love.physics.newRectangleShape(instance.width, instance.height)
22 | instance.physics.fixture = love.physics.newFixture(instance.physics.body, instance.physics.shape)
23 | instance.physics.fixture:setSensor(true)
24 | table.insert(ActiveCoins, instance)
25 | end
26 |
27 | function Coin:remove()
28 | for i,instance in ipairs(ActiveCoins) do
29 | if instance == self then
30 | Player:incrementCoins()
31 | print(Player.coins)
32 | self.physics.body:destroy()
33 | table.remove(ActiveCoins, i)
34 | end
35 | end
36 | end
37 |
38 | function Coin:update(dt)
39 | self:spin(dt)
40 | self:checkRemove()
41 | end
42 |
43 | function Coin:checkRemove()
44 | if self.toBeRemoved then
45 | self:remove()
46 | end
47 | end
48 |
49 | function Coin:spin(dt)
50 | self.scaleX = math.sin(love.timer.getTime() * 2 + self.randomTimeOffset)
51 | end
52 |
53 | function Coin:draw()
54 | love.graphics.draw(self.img, self.x, self.y, 0, self.scaleX, 1, self.width / 2, self.height / 2)
55 | end
56 |
57 | function Coin.updateAll(dt)
58 | for i,instance in ipairs(ActiveCoins) do
59 | instance:update(dt)
60 | end
61 | end
62 |
63 | function Coin.drawAll()
64 | for i,instance in ipairs(ActiveCoins) do
65 | instance:draw()
66 | end
67 | end
68 |
69 | function Coin.beginContact(a, b, collision)
70 | for i,instance in ipairs(ActiveCoins) do
71 | if a == instance.physics.fixture or b == instance.physics.fixture then
72 | if a == Player.physics.fixture or b == Player.physics.fixture then
73 | instance.toBeRemoved = true
74 | return true
75 | end
76 | end
77 | end
78 | end
79 |
80 | return Coin
81 |
--------------------------------------------------------------------------------
/Episode 5/conf.lua:
--------------------------------------------------------------------------------
1 |
2 | function love.conf(t)
3 | t.title = "Platformer" -- The title of the window the game is in (string)
4 | t.version = "11.3" -- The LÖVE version this game was made for (string)
5 | t.console = true -- Attach a console (boolean, Windows only)
6 | t.window.width = 1280 -- The window width (number)
7 | t.window.height = 720 -- The window height (number)
8 | t.window.vsync = 0
9 | end
10 |
--------------------------------------------------------------------------------
/Episode 5/gui.lua:
--------------------------------------------------------------------------------
1 |
2 |
3 | local GUI = {}
4 | local Player = require("player")
5 |
6 | function GUI:load()
7 | self.coins = {}
8 | self.coins.img = love.graphics.newImage("assets/coin.png")
9 | self.coins.width = self.coins.img:getWidth()
10 | self.coins.height = self.coins.img:getHeight()
11 | self.coins.scale = 3
12 | self.coins.x = love.graphics.getWidth() - 200
13 | self.coins.y = 50
14 |
15 | self.hearts = {}
16 | self.hearts.img = love.graphics.newImage("assets/heart.png")
17 | self.hearts.width = self.hearts.img:getWidth()
18 | self.hearts.height = self.hearts.img:getHeight()
19 | self.hearts.x = 0
20 | self.hearts.y = 30
21 | self.hearts.scale = 3
22 | self.hearts.spacing = self.hearts.width * self.hearts.scale + 30
23 |
24 | self.font = love.graphics.newFont("assets/bit.ttf", 36)
25 | end
26 |
27 | function GUI:update(dt)
28 |
29 | end
30 |
31 | function GUI:draw()
32 | self:displayCoins()
33 | self:displayCoinText()
34 | self:displayHearts()
35 | end
36 |
37 | function GUI:displayHearts()
38 | for i=1,Player.health.current do
39 | local x = self.hearts.x + self.hearts.spacing * i
40 | love.graphics.setColor(0,0,0,0.5)
41 | love.graphics.draw(self.hearts.img, x + 2, self.hearts.y + 2, 0, self.hearts.scale, self.hearts.scale)
42 | love.graphics.setColor(1,1,1,1)
43 | love.graphics.draw(self.hearts.img, x, self.hearts.y, 0, self.hearts.scale, self.hearts.scale)
44 | end
45 | end
46 |
47 | function GUI:displayCoins()
48 | love.graphics.setColor(0,0,0,0.5)
49 | love.graphics.draw(self.coins.img, self.coins.x + 2, self.coins.y + 2, 0, self.coins.scale, self.coins.scale)
50 | love.graphics.setColor(1,1,1,1)
51 | love.graphics.draw(self.coins.img, self.coins.x, self.coins.y, 0, self.coins.scale, self.coins.scale)
52 | end
53 |
54 | function GUI:displayCoinText()
55 | love.graphics.setFont(self.font)
56 | local x = self.coins.x + self.coins.width * self.coins.scale
57 | local y = self.coins.y + self.coins.height / 2 * self.coins.scale - self.font:getHeight() / 2
58 | love.graphics.setColor(0,0,0,0.5)
59 | love.graphics.print(" : "..Player.coins, x + 2, y + 2)
60 | love.graphics.setColor(1,1,1,1)
61 | love.graphics.print(" : "..Player.coins, x, y)
62 | end
63 |
64 | return GUI
65 |
--------------------------------------------------------------------------------
/Episode 5/main.lua:
--------------------------------------------------------------------------------
1 | love.graphics.setDefaultFilter("nearest", "nearest")
2 | local STI = require("sti")
3 | local Player = require("player")
4 | local Coin = require("coin")
5 | local GUI = require("gui")
6 | local Spike = require("spike")
7 | local Stone = require("stone")
8 | local Camera = require("camera")
9 |
10 | function love.load()
11 | Map = STI("map/1.lua", {"box2d"})
12 | World = love.physics.newWorld(0,2000)
13 | World:setCallbacks(beginContact, endContact)
14 | Map:box2d_init(World)
15 | Map.layers.solid.visible = false
16 | MapWidth = Map.layers.ground.width * 16
17 | background = love.graphics.newImage("assets/background.png")
18 | GUI:load()
19 | Player:load()
20 | Coin.new(300, 200)
21 | Coin.new(400, 200)
22 | Coin.new(500, 100)
23 | Spike.new(430, 310)
24 | Stone.new(330, 100)
25 | end
26 |
27 | function love.update(dt)
28 | World:update(dt)
29 | Player:update(dt)
30 | Coin.updateAll(dt)
31 | Spike.updateAll(dt)
32 | Stone.updateAll(dt)
33 | GUI:update(dt)
34 | Camera:setPosition(Player.x, 0)
35 | end
36 |
37 | function love.draw()
38 | love.graphics.draw(background)
39 | Map:draw(-Camera.x, -Camera.y, Camera.scale, Camera.scale)
40 |
41 | Camera:apply()
42 | Player:draw()
43 | Coin.drawAll()
44 | Spike.drawAll()
45 | Stone.drawAll()
46 | Camera:clear()
47 |
48 | GUI:draw()
49 | end
50 |
51 | function love.keypressed(key)
52 | Player:jump(key)
53 | end
54 |
55 | function beginContact(a, b, collision)
56 | if Coin.beginContact(a, b, collision) then return end
57 | if Spike.beginContact(a, b, collision) then return end
58 | Player:beginContact(a, b, collision)
59 | end
60 |
61 | function endContact(a, b, collision)
62 | Player:endContact(a, b, collision)
63 | end
64 |
--------------------------------------------------------------------------------
/Episode 5/run.bat:
--------------------------------------------------------------------------------
1 | @ECHO
2 |
3 | start "" "C:\Program Files\LOVE\lovec" .
--------------------------------------------------------------------------------
/Episode 5/spike.lua:
--------------------------------------------------------------------------------
1 |
2 |
3 | local Spike = {img = love.graphics.newImage("assets/spike.png")}
4 | Spike.__index = Spike
5 |
6 | Spike.width = Spike.img:getWidth()
7 | Spike.height = Spike.img:getHeight()
8 |
9 | local ActiveSpikes = {}
10 | local Player = require("player")
11 |
12 | function Spike.new(x,y)
13 | local instance = setmetatable({}, Spike)
14 | instance.x = x
15 | instance.y = y
16 |
17 | instance.damage = 1
18 |
19 | instance.physics = {}
20 | instance.physics.body = love.physics.newBody(World, instance.x, instance.y, "static")
21 | instance.physics.shape = love.physics.newRectangleShape(instance.width, instance.height)
22 | instance.physics.fixture = love.physics.newFixture(instance.physics.body, instance.physics.shape)
23 | instance.physics.fixture:setSensor(true)
24 | table.insert(ActiveSpikes, instance)
25 | end
26 |
27 | function Spike:update(dt)
28 |
29 | end
30 |
31 | function Spike:draw()
32 | love.graphics.draw(self.img, self.x, self.y, 0, 1, 1, self.width / 2, self.height / 2)
33 | end
34 |
35 | function Spike.updateAll(dt)
36 | for i,instance in ipairs(ActiveSpikes) do
37 | instance:update(dt)
38 | end
39 | end
40 |
41 | function Spike.drawAll()
42 | for i,instance in ipairs(ActiveSpikes) do
43 | instance:draw()
44 | end
45 | end
46 |
47 | function Spike.beginContact(a, b, collision)
48 | for i,instance in ipairs(ActiveSpikes) do
49 | if a == instance.physics.fixture or b == instance.physics.fixture then
50 | if a == Player.physics.fixture or b == Player.physics.fixture then
51 | Player:takeDamage(instance.damage)
52 | return true
53 | end
54 | end
55 | end
56 | end
57 |
58 | return Spike
59 |
--------------------------------------------------------------------------------
/Episode 5/sti/graphics.lua:
--------------------------------------------------------------------------------
1 | local lg = _G.love.graphics
2 | local graphics = { isCreated = lg and true or false }
3 |
4 | function graphics.newSpriteBatch(...)
5 | if graphics.isCreated then
6 | return lg.newSpriteBatch(...)
7 | end
8 | end
9 |
10 | function graphics.newCanvas(...)
11 | if graphics.isCreated then
12 | return lg.newCanvas(...)
13 | end
14 | end
15 |
16 | function graphics.newImage(...)
17 | if graphics.isCreated then
18 | return lg.newImage(...)
19 | end
20 | end
21 |
22 | function graphics.newQuad(...)
23 | if graphics.isCreated then
24 | return lg.newQuad(...)
25 | end
26 | end
27 |
28 | function graphics.getCanvas(...)
29 | if graphics.isCreated then
30 | return lg.getCanvas(...)
31 | end
32 | end
33 |
34 | function graphics.setCanvas(...)
35 | if graphics.isCreated then
36 | return lg.setCanvas(...)
37 | end
38 | end
39 |
40 | function graphics.clear(...)
41 | if graphics.isCreated then
42 | return lg.clear(...)
43 | end
44 | end
45 |
46 | function graphics.push(...)
47 | if graphics.isCreated then
48 | return lg.push(...)
49 | end
50 | end
51 |
52 | function graphics.origin(...)
53 | if graphics.isCreated then
54 | return lg.origin(...)
55 | end
56 | end
57 |
58 | function graphics.scale(...)
59 | if graphics.isCreated then
60 | return lg.scale(...)
61 | end
62 | end
63 |
64 | function graphics.translate(...)
65 | if graphics.isCreated then
66 | return lg.translate(...)
67 | end
68 | end
69 |
70 | function graphics.pop(...)
71 | if graphics.isCreated then
72 | return lg.pop(...)
73 | end
74 | end
75 |
76 | function graphics.draw(...)
77 | if graphics.isCreated then
78 | return lg.draw(...)
79 | end
80 | end
81 |
82 | function graphics.rectangle(...)
83 | if graphics.isCreated then
84 | return lg.rectangle(...)
85 | end
86 | end
87 |
88 | function graphics.getColor(...)
89 | if graphics.isCreated then
90 | return lg.getColor(...)
91 | end
92 | end
93 |
94 | function graphics.setColor(...)
95 | if graphics.isCreated then
96 | return lg.setColor(...)
97 | end
98 | end
99 |
100 | function graphics.line(...)
101 | if graphics.isCreated then
102 | return lg.line(...)
103 | end
104 | end
105 |
106 | function graphics.polygon(...)
107 | if graphics.isCreated then
108 | return lg.polygon(...)
109 | end
110 | end
111 |
112 | function graphics.points(...)
113 | if graphics.isCreated then
114 | return lg.points(...)
115 | end
116 | end
117 |
118 | function graphics.getWidth()
119 | if graphics.isCreated then
120 | return lg.getWidth()
121 | end
122 | return 0
123 | end
124 |
125 | function graphics.getHeight()
126 | if graphics.isCreated then
127 | return lg.getHeight()
128 | end
129 | return 0
130 | end
131 |
132 | return graphics
133 |
--------------------------------------------------------------------------------
/Episode 5/sti/utils.lua:
--------------------------------------------------------------------------------
1 | -- Some utility functions that shouldn't be exposed.
2 | local utils = {}
3 |
4 | -- https://github.com/stevedonovan/Penlight/blob/master/lua/pl/path.lua#L286
5 | function utils.format_path(path)
6 | local np_gen1,np_gen2 = '[^SEP]+SEP%.%.SEP?','SEP+%.?SEP'
7 | local np_pat1, np_pat2 = np_gen1:gsub('SEP','/'), np_gen2:gsub('SEP','/')
8 | local k
9 |
10 | repeat -- /./ -> /
11 | path,k = path:gsub(np_pat2,'/',1)
12 | until k == 0
13 |
14 | repeat -- A/../ -> (empty)
15 | path,k = path:gsub(np_pat1,'',1)
16 | until k == 0
17 |
18 | if path == '' then path = '.' end
19 |
20 | return path
21 | end
22 |
23 | -- Compensation for scale/rotation shift
24 | function utils.compensate(tile, tileX, tileY, tileW, tileH)
25 | local compx = 0
26 | local compy = 0
27 |
28 | if tile.sx < 0 then compx = tileW end
29 | if tile.sy < 0 then compy = tileH end
30 |
31 | if tile.r > 0 then
32 | tileX = tileX + tileH - compy
33 | tileY = tileY + tileH + compx - tileW
34 | elseif tile.r < 0 then
35 | tileX = tileX + compy
36 | tileY = tileY - compx + tileH
37 | else
38 | tileX = tileX + compx
39 | tileY = tileY + compy
40 | end
41 |
42 | return tileX, tileY
43 | end
44 |
45 | -- Cache images in main STI module
46 | function utils.cache_image(sti, path, image)
47 | image = image or love.graphics.newImage(path)
48 | image:setFilter("nearest", "nearest")
49 | sti.cache[path] = image
50 | end
51 |
52 | -- We just don't know.
53 | function utils.get_tiles(imageW, tileW, margin, spacing)
54 | imageW = imageW - margin
55 | local n = 0
56 |
57 | while imageW >= tileW do
58 | imageW = imageW - tileW
59 | if n ~= 0 then imageW = imageW - spacing end
60 | if imageW >= 0 then n = n + 1 end
61 | end
62 |
63 | return n
64 | end
65 |
66 | -- Decompress tile layer data
67 | function utils.get_decompressed_data(data)
68 | local ffi = require "ffi"
69 | local d = {}
70 | local decoded = ffi.cast("uint32_t*", data)
71 |
72 | for i = 0, data:len() / ffi.sizeof("uint32_t") do
73 | table.insert(d, tonumber(decoded[i]))
74 | end
75 |
76 | return d
77 | end
78 |
79 | -- Convert a Tiled ellipse object to a LOVE polygon
80 | function utils.convert_ellipse_to_polygon(x, y, w, h, max_segments)
81 | local ceil = math.ceil
82 | local cos = math.cos
83 | local sin = math.sin
84 |
85 | local function calc_segments(segments)
86 | local function vdist(a, b)
87 | local c = {
88 | x = a.x - b.x,
89 | y = a.y - b.y,
90 | }
91 |
92 | return c.x * c.x + c.y * c.y
93 | end
94 |
95 | segments = segments or 64
96 | local vertices = {}
97 |
98 | local v = { 1, 2, ceil(segments/4-1), ceil(segments/4) }
99 |
100 | local m
101 | if love and love.physics then
102 | m = love.physics.getMeter()
103 | else
104 | m = 32
105 | end
106 |
107 | for _, i in ipairs(v) do
108 | local angle = (i / segments) * math.pi * 2
109 | local px = x + w / 2 + cos(angle) * w / 2
110 | local py = y + h / 2 + sin(angle) * h / 2
111 |
112 | table.insert(vertices, { x = px / m, y = py / m })
113 | end
114 |
115 | local dist1 = vdist(vertices[1], vertices[2])
116 | local dist2 = vdist(vertices[3], vertices[4])
117 |
118 | -- Box2D threshold
119 | if dist1 < 0.0025 or dist2 < 0.0025 then
120 | return calc_segments(segments-2)
121 | end
122 |
123 | return segments
124 | end
125 |
126 | local segments = calc_segments(max_segments)
127 | local vertices = {}
128 |
129 | table.insert(vertices, { x = x + w / 2, y = y + h / 2 })
130 |
131 | for i = 0, segments do
132 | local angle = (i / segments) * math.pi * 2
133 | local px = x + w / 2 + cos(angle) * w / 2
134 | local py = y + h / 2 + sin(angle) * h / 2
135 |
136 | table.insert(vertices, { x = px, y = py })
137 | end
138 |
139 | return vertices
140 | end
141 |
142 | function utils.rotate_vertex(map, vertex, x, y, cos, sin, oy)
143 | if map.orientation == "isometric" then
144 | x, y = utils.convert_isometric_to_screen(map, x, y)
145 | vertex.x, vertex.y = utils.convert_isometric_to_screen(map, vertex.x, vertex.y)
146 | end
147 |
148 | vertex.x = vertex.x - x
149 | vertex.y = vertex.y - y
150 |
151 | return
152 | x + cos * vertex.x - sin * vertex.y,
153 | y + sin * vertex.x + cos * vertex.y - (oy or 0)
154 | end
155 |
156 | --- Project isometric position to cartesian position
157 | function utils.convert_isometric_to_screen(map, x, y)
158 | local mapW = map.width
159 | local tileW = map.tilewidth
160 | local tileH = map.tileheight
161 | local tileX = x / tileH
162 | local tileY = y / tileH
163 | local offsetX = mapW * tileW / 2
164 |
165 | return
166 | (tileX - tileY) * tileW / 2 + offsetX,
167 | (tileX + tileY) * tileH / 2
168 | end
169 |
170 | function utils.hex_to_color(hex)
171 | if hex:sub(1, 1) == "#" then
172 | hex = hex:sub(2)
173 | end
174 |
175 | return {
176 | r = tonumber(hex:sub(1, 2), 16) / 255,
177 | g = tonumber(hex:sub(3, 4), 16) / 255,
178 | b = tonumber(hex:sub(5, 6), 16) / 255
179 | }
180 | end
181 |
182 | function utils.pixel_function(_, _, r, g, b, a)
183 | local mask = utils._TC
184 |
185 | if r == mask.r and
186 | g == mask.g and
187 | b == mask.b then
188 | return r, g, b, 0
189 | end
190 |
191 | return r, g, b, a
192 | end
193 |
194 | function utils.fix_transparent_color(tileset, path)
195 | local image_data = love.image.newImageData(path)
196 | tileset.image = love.graphics.newImage(image_data)
197 |
198 | if tileset.transparentcolor then
199 | utils._TC = utils.hex_to_color(tileset.transparentcolor)
200 |
201 | image_data:mapPixel(utils.pixel_function)
202 | tileset.image = love.graphics.newImage(image_data)
203 | end
204 | end
205 |
206 | function utils.deepCopy(t)
207 | local copy = {}
208 | for k,v in pairs(t) do
209 | if type(v) == "table" then
210 | v = utils.deepCopy(v)
211 | end
212 | copy[k] = v
213 | end
214 | return copy
215 | end
216 |
217 | return utils
218 |
--------------------------------------------------------------------------------
/Episode 5/stone.lua:
--------------------------------------------------------------------------------
1 |
2 |
3 | local Stone = {img = love.graphics.newImage("assets/stone.png")}
4 | Stone.__index = Stone
5 |
6 | Stone.width = Stone.img:getWidth()
7 | Stone.height = Stone.img:getHeight()
8 |
9 | local ActiveStones = {}
10 |
11 | function Stone.new(x,y)
12 | local instance = setmetatable({}, Stone)
13 | instance.x = x
14 | instance.y = y
15 | instance.r = 0
16 |
17 | instance.physics = {}
18 | instance.physics.body = love.physics.newBody(World, instance.x, instance.y, "dynamic")
19 | instance.physics.shape = love.physics.newRectangleShape(instance.width, instance.height)
20 | instance.physics.fixture = love.physics.newFixture(instance.physics.body, instance.physics.shape)
21 | instance.physics.body:setMass(25)
22 | table.insert(ActiveStones, instance)
23 | end
24 |
25 | function Stone:update(dt)
26 | self:syncPhysics()
27 | end
28 |
29 | function Stone:syncPhysics()
30 | self.x, self.y = self.physics.body:getPosition()
31 | self.r = self.physics.body:getAngle()
32 | end
33 |
34 | function Stone:draw()
35 | love.graphics.draw(self.img, self.x, self.y, self.r, self.scaleX, 1, self.width / 2, self.height / 2)
36 | end
37 |
38 | function Stone.updateAll(dt)
39 | for i,instance in ipairs(ActiveStones) do
40 | instance:update(dt)
41 | end
42 | end
43 |
44 | function Stone.drawAll()
45 | for i,instance in ipairs(ActiveStones) do
46 | instance:draw()
47 | end
48 | end
49 |
50 | return Stone
51 |
--------------------------------------------------------------------------------
/Episode 6/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/Episode 6/README.md:
--------------------------------------------------------------------------------
1 | Full source code and assets from the tutorial series. - Youtube link: https://www.youtube.com/playlist?list=PL1A1gsSe2tMxngwl-CU1MCX7kmbLf4P5O
2 | Note: The code and assets have different licences
3 |
4 | Code: WTFPL (Do whatever you want, no restrictions)
5 | Art: BY-NC-ND (https://creativecommons.org/licenses/by-nc-nd/4.0/) More restricted.
6 |
--------------------------------------------------------------------------------
/Episode 6/assets/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/background.png
--------------------------------------------------------------------------------
/Episode 6/assets/bit.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/bit.ttf
--------------------------------------------------------------------------------
/Episode 6/assets/coin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/coin.png
--------------------------------------------------------------------------------
/Episode 6/assets/enemy/run/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/enemy/run/1.png
--------------------------------------------------------------------------------
/Episode 6/assets/enemy/run/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/enemy/run/2.png
--------------------------------------------------------------------------------
/Episode 6/assets/enemy/run/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/enemy/run/3.png
--------------------------------------------------------------------------------
/Episode 6/assets/enemy/run/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/enemy/run/4.png
--------------------------------------------------------------------------------
/Episode 6/assets/enemy/run/Untitled-1.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/enemy/run/Untitled-1.psd
--------------------------------------------------------------------------------
/Episode 6/assets/enemy/walk/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/enemy/walk/1.png
--------------------------------------------------------------------------------
/Episode 6/assets/enemy/walk/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/enemy/walk/2.png
--------------------------------------------------------------------------------
/Episode 6/assets/enemy/walk/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/enemy/walk/3.png
--------------------------------------------------------------------------------
/Episode 6/assets/enemy/walk/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/enemy/walk/4.png
--------------------------------------------------------------------------------
/Episode 6/assets/enemy/walk/Untitled-1.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/enemy/walk/Untitled-1.psd
--------------------------------------------------------------------------------
/Episode 6/assets/heart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/heart.png
--------------------------------------------------------------------------------
/Episode 6/assets/player/air/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/player/air/1.png
--------------------------------------------------------------------------------
/Episode 6/assets/player/air/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/player/air/2.png
--------------------------------------------------------------------------------
/Episode 6/assets/player/air/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/player/air/3.png
--------------------------------------------------------------------------------
/Episode 6/assets/player/air/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/player/air/4.png
--------------------------------------------------------------------------------
/Episode 6/assets/player/idle/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/player/idle/1.png
--------------------------------------------------------------------------------
/Episode 6/assets/player/idle/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/player/idle/2.png
--------------------------------------------------------------------------------
/Episode 6/assets/player/idle/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/player/idle/3.png
--------------------------------------------------------------------------------
/Episode 6/assets/player/idle/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/player/idle/4.png
--------------------------------------------------------------------------------
/Episode 6/assets/player/jump/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/player/jump/1.png
--------------------------------------------------------------------------------
/Episode 6/assets/player/jump/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/player/jump/2.png
--------------------------------------------------------------------------------
/Episode 6/assets/player/jump/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/player/jump/3.png
--------------------------------------------------------------------------------
/Episode 6/assets/player/jump/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/player/jump/4.png
--------------------------------------------------------------------------------
/Episode 6/assets/player/run/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/player/run/1.png
--------------------------------------------------------------------------------
/Episode 6/assets/player/run/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/player/run/2.png
--------------------------------------------------------------------------------
/Episode 6/assets/player/run/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/player/run/3.png
--------------------------------------------------------------------------------
/Episode 6/assets/player/run/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/player/run/4.png
--------------------------------------------------------------------------------
/Episode 6/assets/player/run/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/player/run/5.png
--------------------------------------------------------------------------------
/Episode 6/assets/player/run/6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/player/run/6.png
--------------------------------------------------------------------------------
/Episode 6/assets/sfx/player_get_coin.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/sfx/player_get_coin.ogg
--------------------------------------------------------------------------------
/Episode 6/assets/sfx/player_hit.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/sfx/player_hit.ogg
--------------------------------------------------------------------------------
/Episode 6/assets/sfx/player_jump.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/sfx/player_jump.ogg
--------------------------------------------------------------------------------
/Episode 6/assets/spike.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/spike.png
--------------------------------------------------------------------------------
/Episode 6/assets/square.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/square.png
--------------------------------------------------------------------------------
/Episode 6/assets/stone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/stone.png
--------------------------------------------------------------------------------
/Episode 6/assets/tiles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jeepzor/Platformer-tutorial/ca25e157c32c0b522be398f3d2cfd0ce9fc6990f/Episode 6/assets/tiles.png
--------------------------------------------------------------------------------
/Episode 6/camera.lua:
--------------------------------------------------------------------------------
1 |
2 |
3 | local Camera = {
4 | x = 0,
5 | y = 0,
6 | scale = 2,
7 | }
8 |
9 | function Camera:apply()
10 | love.graphics.push()
11 | love.graphics.scale(self.scale,self.scale)
12 | love.graphics.translate(-self.x, -self.y)
13 | end
14 |
15 | function Camera:clear()
16 | love.graphics.pop()
17 | end
18 |
19 | function Camera:setPosition(x, y)
20 | self.x = x - love.graphics.getWidth() / 2 / self.scale
21 | self.y = y
22 | local RS = self.x + love.graphics.getWidth() / 2
23 |
24 | if self.x < 0 then
25 | self.x = 0
26 | elseif RS > MapWidth then
27 | self.x = MapWidth - love.graphics.getWidth() / 2
28 | end
29 | end
30 |
31 | return Camera
32 |
--------------------------------------------------------------------------------
/Episode 6/coin.lua:
--------------------------------------------------------------------------------
1 |
2 |
3 | local Coin = {}
4 | Coin.__index = Coin
5 | local ActiveCoins = {}
6 | local Player = require("player")
7 |
8 | function Coin.new(x,y)
9 | local instance = setmetatable({}, Coin)
10 | instance.x = x
11 | instance.y = y
12 | instance.img = love.graphics.newImage("assets/coin.png")
13 | instance.width = instance.img:getWidth()
14 | instance.height = instance.img:getHeight()
15 | instance.scaleX = 1
16 | instance.randomTimeOffset = math.random(0, 100)
17 | instance.toBeRemoved = false
18 |
19 | instance.physics = {}
20 | instance.physics.body = love.physics.newBody(World, instance.x, instance.y, "static")
21 | instance.physics.shape = love.physics.newRectangleShape(instance.width, instance.height)
22 | instance.physics.fixture = love.physics.newFixture(instance.physics.body, instance.physics.shape)
23 | instance.physics.fixture:setSensor(true)
24 | table.insert(ActiveCoins, instance)
25 | end
26 |
27 | function Coin:remove()
28 | for i,instance in ipairs(ActiveCoins) do
29 | if instance == self then
30 | Player:incrementCoins()
31 | print(Player.coins)
32 | self.physics.body:destroy()
33 | table.remove(ActiveCoins, i)
34 | end
35 | end
36 | end
37 |
38 | function Coin.removeAll()
39 | for i,v in ipairs(ActiveCoins) do
40 | v.physics.body:destroy()
41 | end
42 |
43 | ActiveCoins = {}
44 | end
45 |
46 | function Coin:update(dt)
47 | self:spin(dt)
48 | self:checkRemove()
49 | end
50 |
51 | function Coin:checkRemove()
52 | if self.toBeRemoved then
53 | self:remove()
54 | end
55 | end
56 |
57 | function Coin:spin(dt)
58 | self.scaleX = math.sin(love.timer.getTime() * 2 + self.randomTimeOffset)
59 | end
60 |
61 | function Coin:draw()
62 | love.graphics.draw(self.img, self.x, self.y, 0, self.scaleX, 1, self.width / 2, self.height / 2)
63 | end
64 |
65 | function Coin.updateAll(dt)
66 | for i,instance in ipairs(ActiveCoins) do
67 | instance:update(dt)
68 | end
69 | end
70 |
71 | function Coin.drawAll()
72 | for i,instance in ipairs(ActiveCoins) do
73 | instance:draw()
74 | end
75 | end
76 |
77 | function Coin.beginContact(a, b, collision)
78 | for i,instance in ipairs(ActiveCoins) do
79 | if a == instance.physics.fixture or b == instance.physics.fixture then
80 | if a == Player.physics.fixture or b == Player.physics.fixture then
81 | instance.toBeRemoved = true
82 | return true
83 | end
84 | end
85 | end
86 | end
87 |
88 | return Coin
89 |
--------------------------------------------------------------------------------
/Episode 6/conf.lua:
--------------------------------------------------------------------------------
1 |
2 | function love.conf(t)
3 | t.title = "Platformer" -- The title of the window the game is in (string)
4 | t.version = "11.3" -- The LÖVE version this game was made for (string)
5 | t.console = true -- Attach a console (boolean, Windows only)
6 | t.window.width = 1280 -- The window width (number)
7 | t.window.height = 720 -- The window height (number)
8 | t.window.vsync = 0
9 | end
10 |
--------------------------------------------------------------------------------
/Episode 6/enemy.lua:
--------------------------------------------------------------------------------
1 |
2 |
3 | local Enemy = {}
4 | Enemy.__index = Enemy
5 | local Player = require("player")
6 |
7 | local ActiveEnemies = {}
8 |
9 | function Enemy.removeAll()
10 | for i,v in ipairs(ActiveEnemies) do
11 | v.physics.body:destroy()
12 | end
13 |
14 | ActiveEnemies = {}
15 | end
16 |
17 | function Enemy.new(x,y)
18 | local instance = setmetatable({}, Enemy)
19 | instance.x = x
20 | instance.y = y
21 | instance.offsetY = -8
22 | instance.r = 0
23 |
24 | instance.speed = 100
25 | instance.speedMod = 1
26 | instance.xVel = instance.speed
27 |
28 | instance.rageCounter = 0
29 | instance.rageTrigger = 3
30 |
31 | instance.damage = 1
32 |
33 | instance.state = "walk"
34 |
35 | instance.animation = {timer = 0, rate = 0.1}
36 | instance.animation.run = {total = 4, current = 1, img = Enemy.runAnim}
37 | instance.animation.walk = {total = 4, current = 1, img = Enemy.walkAnim}
38 | instance.animation.draw = instance.animation.walk.img[1]
39 |
40 | instance.physics = {}
41 | instance.physics.body = love.physics.newBody(World, instance.x, instance.y, "dynamic")
42 | instance.physics.body:setFixedRotation(true)
43 | instance.physics.shape = love.physics.newRectangleShape(instance.width * 0.4, instance.height * 0.75)
44 | instance.physics.fixture = love.physics.newFixture(instance.physics.body, instance.physics.shape)
45 | instance.physics.body:setMass(25)
46 | table.insert(ActiveEnemies, instance)
47 | end
48 |
49 | function Enemy.loadAssets()
50 | Enemy.runAnim = {}
51 | for i=1,4 do
52 | Enemy.runAnim[i] = love.graphics.newImage("assets/enemy/run/"..i..".png")
53 | end
54 |
55 | Enemy.walkAnim = {}
56 | for i=1,4 do
57 | Enemy.walkAnim[i] = love.graphics.newImage("assets/enemy/walk/"..i..".png")
58 | end
59 |
60 | Enemy.width = Enemy.runAnim[1]:getWidth()
61 | Enemy.height = Enemy.runAnim[1]:getHeight()
62 | end
63 |
64 | function Enemy:update(dt)
65 | self:syncPhysics()
66 | self:animate(dt)
67 | end
68 |
69 | function Enemy:incrementRage()
70 | self.rageCounter = self.rageCounter + 1
71 | if self.rageCounter > self.rageTrigger then
72 | self.state = "run"
73 | self.speedMod = 3
74 | self.rageCounter = 0
75 | else
76 | self.state = "walk"
77 | self.speedMod = 1
78 | end
79 | end
80 |
81 | function Enemy:flipDirection()
82 | self.xVel = -self.xVel
83 | end
84 |
85 | function Enemy:animate(dt)
86 | self.animation.timer = self.animation.timer + dt
87 | if self.animation.timer > self.animation.rate then
88 | self.animation.timer = 0
89 | self:setNewFrame()
90 | end
91 | end
92 |
93 | function Enemy:setNewFrame()
94 | local anim = self.animation[self.state]
95 | if anim.current < anim.total then
96 | anim.current = anim.current + 1
97 | else
98 | anim.current = 1
99 | end
100 | self.animation.draw = anim.img[anim.current]
101 | end
102 |
103 | function Enemy:syncPhysics()
104 | self.x, self.y = self.physics.body:getPosition()
105 | self.physics.body:setLinearVelocity(self.xVel * self.speedMod, 100)
106 | end
107 |
108 | function Enemy:draw()
109 | local scaleX = 1
110 | if self.xVel < 0 then
111 | scaleX = -1
112 | end
113 | love.graphics.draw(self.animation.draw, self.x, self.y + self.offsetY, self.r, scaleX, 1, self.width / 2, self.height / 2)
114 | end
115 |
116 | function Enemy.updateAll(dt)
117 | for i,instance in ipairs(ActiveEnemies) do
118 | instance:update(dt)
119 | end
120 | end
121 |
122 | function Enemy.drawAll()
123 | for i,instance in ipairs(ActiveEnemies) do
124 | instance:draw()
125 | end
126 | end
127 |
128 | function Enemy.beginContact(a, b, collision)
129 | for i,instance in ipairs(ActiveEnemies) do
130 | if a == instance.physics.fixture or b == instance.physics.fixture then
131 | if a == Player.physics.fixture or b == Player.physics.fixture then
132 | Player:takeDamage(instance.damage)
133 | end
134 | instance:incrementRage()
135 | instance:flipDirection()
136 | end
137 | end
138 | end
139 |
140 | return Enemy
141 |
--------------------------------------------------------------------------------
/Episode 6/gui.lua:
--------------------------------------------------------------------------------
1 |
2 |
3 | local GUI = {}
4 | local Player = require("player")
5 |
6 | function GUI:load()
7 | self.coins = {}
8 | self.coins.img = love.graphics.newImage("assets/coin.png")
9 | self.coins.width = self.coins.img:getWidth()
10 | self.coins.height = self.coins.img:getHeight()
11 | self.coins.scale = 3
12 | self.coins.x = love.graphics.getWidth() - 200
13 | self.coins.y = 50
14 |
15 | self.hearts = {}
16 | self.hearts.img = love.graphics.newImage("assets/heart.png")
17 | self.hearts.width = self.hearts.img:getWidth()
18 | self.hearts.height = self.hearts.img:getHeight()
19 | self.hearts.x = 0
20 | self.hearts.y = 30
21 | self.hearts.scale = 3
22 | self.hearts.spacing = self.hearts.width * self.hearts.scale + 30
23 |
24 | self.font = love.graphics.newFont("assets/bit.ttf", 36)
25 | end
26 |
27 | function GUI:update(dt)
28 |
29 | end
30 |
31 | function GUI:draw()
32 | self:displayCoins()
33 | self:displayCoinText()
34 | self:displayHearts()
35 | end
36 |
37 | function GUI:displayHearts()
38 | for i=1,Player.health.current do
39 | local x = self.hearts.x + self.hearts.spacing * i
40 | love.graphics.setColor(0,0,0,0.5)
41 | love.graphics.draw(self.hearts.img, x + 2, self.hearts.y + 2, 0, self.hearts.scale, self.hearts.scale)
42 | love.graphics.setColor(1,1,1,1)
43 | love.graphics.draw(self.hearts.img, x, self.hearts.y, 0, self.hearts.scale, self.hearts.scale)
44 | end
45 | end
46 |
47 | function GUI:displayCoins()
48 | love.graphics.setColor(0,0,0,0.5)
49 | love.graphics.draw(self.coins.img, self.coins.x + 2, self.coins.y + 2, 0, self.coins.scale, self.coins.scale)
50 | love.graphics.setColor(1,1,1,1)
51 | love.graphics.draw(self.coins.img, self.coins.x, self.coins.y, 0, self.coins.scale, self.coins.scale)
52 | end
53 |
54 | function GUI:displayCoinText()
55 | love.graphics.setFont(self.font)
56 | local x = self.coins.x + self.coins.width * self.coins.scale
57 | local y = self.coins.y + self.coins.height / 2 * self.coins.scale - self.font:getHeight() / 2
58 | love.graphics.setColor(0,0,0,0.5)
59 | love.graphics.print(" : "..Player.coins, x + 2, y + 2)
60 | love.graphics.setColor(1,1,1,1)
61 | love.graphics.print(" : "..Player.coins, x, y)
62 | end
63 |
64 | return GUI
65 |
--------------------------------------------------------------------------------
/Episode 6/main.lua:
--------------------------------------------------------------------------------
1 | love.graphics.setDefaultFilter("nearest", "nearest")
2 | local Player = require("player")
3 | local Coin = require("coin")
4 | local GUI = require("gui")
5 | local Spike = require("spike")
6 | local Stone = require("stone")
7 | local Camera = require("camera")
8 | local Enemy = require("enemy")
9 | local Map = require("map")
10 |
11 | function love.load()
12 | Enemy.loadAssets()
13 | Map:load()
14 | background = love.graphics.newImage("assets/background.png")
15 | GUI:load()
16 | Player:load()
17 | end
18 |
19 | function love.update(dt)
20 | World:update(dt)
21 | Player:update(dt)
22 | Coin.updateAll(dt)
23 | Spike.updateAll(dt)
24 | Stone.updateAll(dt)
25 | Enemy.updateAll(dt)
26 | GUI:update(dt)
27 | Camera:setPosition(Player.x, 0)
28 | Map:update(dt)
29 | end
30 |
31 | function love.draw()
32 | love.graphics.draw(background)
33 | Map.level:draw(-Camera.x, -Camera.y, Camera.scale, Camera.scale)
34 |
35 | Camera:apply()
36 | Player:draw()
37 | Enemy.drawAll()
38 | Coin.drawAll()
39 | Spike.drawAll()
40 | Stone.drawAll()
41 | Camera:clear()
42 |
43 | GUI:draw()
44 | end
45 |
46 | function love.keypressed(key)
47 | Player:jump(key)
48 | end
49 |
50 | function beginContact(a, b, collision)
51 | if Coin.beginContact(a, b, collision) then return end
52 | if Spike.beginContact(a, b, collision) then return end
53 | Enemy.beginContact(a, b, collision)
54 | Player:beginContact(a, b, collision)
55 | end
56 |
57 | function endContact(a, b, collision)
58 | Player:endContact(a, b, collision)
59 | end
60 |
--------------------------------------------------------------------------------
/Episode 6/map.lua:
--------------------------------------------------------------------------------
1 |
2 | local Map = {}
3 | local STI = require("sti")
4 | local Coin = require("coin")
5 | local Spike = require("spike")
6 | local Stone = require("stone")
7 | local Enemy = require("enemy")
8 | local Player = require("player")
9 |
10 | function Map:load()
11 | self.currentLevel = 1
12 | World = love.physics.newWorld(0,2000)
13 | World:setCallbacks(beginContact, endContact)
14 |
15 | self:init()
16 | end
17 |
18 | function Map:init()
19 | self.level = STI("map/"..self.currentLevel..".lua", {"box2d"})
20 |
21 | self.level:box2d_init(World)
22 | self.solidLayer = self.level.layers.solid
23 | self.groundLayer = self.level.layers.ground
24 | self.entityLayer = self.level.layers.entity
25 |
26 | self.solidLayer.visible = false
27 | self.entityLayer.visible = false
28 | MapWidth = self.groundLayer.width * 16
29 |
30 | self:spawnEntities()
31 | end
32 |
33 | function Map:next()
34 | self:clean()
35 | self.currentLevel = self.currentLevel + 1
36 | self:init()
37 | Player:resetPosition()
38 | end
39 |
40 | function Map:clean()
41 | self.level:box2d_removeLayer("solid")
42 | Coin.removeAll()
43 | Enemy.removeAll()
44 | Stone.removeAll()
45 | Spike.removeAll()
46 | end
47 |
48 | function Map:update()
49 | if Player.x > MapWidth - 16 then
50 | self:next()
51 | end
52 | end
53 |
54 | function Map:spawnEntities()
55 | for i,v in ipairs(self.entityLayer.objects) do
56 | if v.type == "spikes" then
57 | Spike.new(v.x + v.width / 2, v.y + v.height / 2)
58 | elseif v.type == "stone" then
59 | Stone.new(v.x + v.width / 2, v.y + v.height / 2)
60 | elseif v.type == "enemy" then
61 | Enemy.new(v.x + v.width / 2, v.y + v.height / 2)
62 | elseif v.type == "coin" then
63 | Coin.new(v.x, v.y)
64 | end
65 | end
66 | end
67 |
68 | return Map
69 |
--------------------------------------------------------------------------------
/Episode 6/run.bat:
--------------------------------------------------------------------------------
1 | @ECHO
2 |
3 | start "" "C:\Program Files\LOVE\lovec" .
--------------------------------------------------------------------------------
/Episode 6/spike.lua:
--------------------------------------------------------------------------------
1 |
2 |
3 | local Spike = {img = love.graphics.newImage("assets/spike.png")}
4 | Spike.__index = Spike
5 |
6 | Spike.width = Spike.img:getWidth()
7 | Spike.height = Spike.img:getHeight()
8 |
9 | local ActiveSpikes = {}
10 | local Player = require("player")
11 |
12 | function Spike.removeAll()
13 | for i,v in ipairs(ActiveSpikes) do
14 | v.physics.body:destroy()
15 | end
16 |
17 | ActiveSpikes = {}
18 | end
19 |
20 | function Spike.new(x,y)
21 | local instance = setmetatable({}, Spike)
22 | instance.x = x
23 | instance.y = y
24 |
25 | instance.damage = 1
26 |
27 | instance.physics = {}
28 | instance.physics.body = love.physics.newBody(World, instance.x, instance.y, "static")
29 | instance.physics.shape = love.physics.newRectangleShape(instance.width, instance.height)
30 | instance.physics.fixture = love.physics.newFixture(instance.physics.body, instance.physics.shape)
31 | instance.physics.fixture:setSensor(true)
32 | table.insert(ActiveSpikes, instance)
33 | end
34 |
35 | function Spike:update(dt)
36 |
37 | end
38 |
39 | function Spike:draw()
40 | love.graphics.draw(self.img, self.x, self.y, 0, 1, 1, self.width / 2, self.height / 2)
41 | end
42 |
43 | function Spike.updateAll(dt)
44 | for i,instance in ipairs(ActiveSpikes) do
45 | instance:update(dt)
46 | end
47 | end
48 |
49 | function Spike.drawAll()
50 | for i,instance in ipairs(ActiveSpikes) do
51 | instance:draw()
52 | end
53 | end
54 |
55 | function Spike.beginContact(a, b, collision)
56 | for i,instance in ipairs(ActiveSpikes) do
57 | if a == instance.physics.fixture or b == instance.physics.fixture then
58 | if a == Player.physics.fixture or b == Player.physics.fixture then
59 | Player:takeDamage(instance.damage)
60 | return true
61 | end
62 | end
63 | end
64 | end
65 |
66 | return Spike
67 |
--------------------------------------------------------------------------------
/Episode 6/sti/graphics.lua:
--------------------------------------------------------------------------------
1 | local lg = _G.love.graphics
2 | local graphics = { isCreated = lg and true or false }
3 |
4 | function graphics.newSpriteBatch(...)
5 | if graphics.isCreated then
6 | return lg.newSpriteBatch(...)
7 | end
8 | end
9 |
10 | function graphics.newCanvas(...)
11 | if graphics.isCreated then
12 | return lg.newCanvas(...)
13 | end
14 | end
15 |
16 | function graphics.newImage(...)
17 | if graphics.isCreated then
18 | return lg.newImage(...)
19 | end
20 | end
21 |
22 | function graphics.newQuad(...)
23 | if graphics.isCreated then
24 | return lg.newQuad(...)
25 | end
26 | end
27 |
28 | function graphics.getCanvas(...)
29 | if graphics.isCreated then
30 | return lg.getCanvas(...)
31 | end
32 | end
33 |
34 | function graphics.setCanvas(...)
35 | if graphics.isCreated then
36 | return lg.setCanvas(...)
37 | end
38 | end
39 |
40 | function graphics.clear(...)
41 | if graphics.isCreated then
42 | return lg.clear(...)
43 | end
44 | end
45 |
46 | function graphics.push(...)
47 | if graphics.isCreated then
48 | return lg.push(...)
49 | end
50 | end
51 |
52 | function graphics.origin(...)
53 | if graphics.isCreated then
54 | return lg.origin(...)
55 | end
56 | end
57 |
58 | function graphics.scale(...)
59 | if graphics.isCreated then
60 | return lg.scale(...)
61 | end
62 | end
63 |
64 | function graphics.translate(...)
65 | if graphics.isCreated then
66 | return lg.translate(...)
67 | end
68 | end
69 |
70 | function graphics.pop(...)
71 | if graphics.isCreated then
72 | return lg.pop(...)
73 | end
74 | end
75 |
76 | function graphics.draw(...)
77 | if graphics.isCreated then
78 | return lg.draw(...)
79 | end
80 | end
81 |
82 | function graphics.rectangle(...)
83 | if graphics.isCreated then
84 | return lg.rectangle(...)
85 | end
86 | end
87 |
88 | function graphics.getColor(...)
89 | if graphics.isCreated then
90 | return lg.getColor(...)
91 | end
92 | end
93 |
94 | function graphics.setColor(...)
95 | if graphics.isCreated then
96 | return lg.setColor(...)
97 | end
98 | end
99 |
100 | function graphics.line(...)
101 | if graphics.isCreated then
102 | return lg.line(...)
103 | end
104 | end
105 |
106 | function graphics.polygon(...)
107 | if graphics.isCreated then
108 | return lg.polygon(...)
109 | end
110 | end
111 |
112 | function graphics.points(...)
113 | if graphics.isCreated then
114 | return lg.points(...)
115 | end
116 | end
117 |
118 | function graphics.getWidth()
119 | if graphics.isCreated then
120 | return lg.getWidth()
121 | end
122 | return 0
123 | end
124 |
125 | function graphics.getHeight()
126 | if graphics.isCreated then
127 | return lg.getHeight()
128 | end
129 | return 0
130 | end
131 |
132 | return graphics
133 |
--------------------------------------------------------------------------------
/Episode 6/sti/utils.lua:
--------------------------------------------------------------------------------
1 | -- Some utility functions that shouldn't be exposed.
2 | local utils = {}
3 |
4 | -- https://github.com/stevedonovan/Penlight/blob/master/lua/pl/path.lua#L286
5 | function utils.format_path(path)
6 | local np_gen1,np_gen2 = '[^SEP]+SEP%.%.SEP?','SEP+%.?SEP'
7 | local np_pat1, np_pat2 = np_gen1:gsub('SEP','/'), np_gen2:gsub('SEP','/')
8 | local k
9 |
10 | repeat -- /./ -> /
11 | path,k = path:gsub(np_pat2,'/',1)
12 | until k == 0
13 |
14 | repeat -- A/../ -> (empty)
15 | path,k = path:gsub(np_pat1,'',1)
16 | until k == 0
17 |
18 | if path == '' then path = '.' end
19 |
20 | return path
21 | end
22 |
23 | -- Compensation for scale/rotation shift
24 | function utils.compensate(tile, tileX, tileY, tileW, tileH)
25 | local compx = 0
26 | local compy = 0
27 |
28 | if tile.sx < 0 then compx = tileW end
29 | if tile.sy < 0 then compy = tileH end
30 |
31 | if tile.r > 0 then
32 | tileX = tileX + tileH - compy
33 | tileY = tileY + tileH + compx - tileW
34 | elseif tile.r < 0 then
35 | tileX = tileX + compy
36 | tileY = tileY - compx + tileH
37 | else
38 | tileX = tileX + compx
39 | tileY = tileY + compy
40 | end
41 |
42 | return tileX, tileY
43 | end
44 |
45 | -- Cache images in main STI module
46 | function utils.cache_image(sti, path, image)
47 | image = image or love.graphics.newImage(path)
48 | image:setFilter("nearest", "nearest")
49 | sti.cache[path] = image
50 | end
51 |
52 | -- We just don't know.
53 | function utils.get_tiles(imageW, tileW, margin, spacing)
54 | imageW = imageW - margin
55 | local n = 0
56 |
57 | while imageW >= tileW do
58 | imageW = imageW - tileW
59 | if n ~= 0 then imageW = imageW - spacing end
60 | if imageW >= 0 then n = n + 1 end
61 | end
62 |
63 | return n
64 | end
65 |
66 | -- Decompress tile layer data
67 | function utils.get_decompressed_data(data)
68 | local ffi = require "ffi"
69 | local d = {}
70 | local decoded = ffi.cast("uint32_t*", data)
71 |
72 | for i = 0, data:len() / ffi.sizeof("uint32_t") do
73 | table.insert(d, tonumber(decoded[i]))
74 | end
75 |
76 | return d
77 | end
78 |
79 | -- Convert a Tiled ellipse object to a LOVE polygon
80 | function utils.convert_ellipse_to_polygon(x, y, w, h, max_segments)
81 | local ceil = math.ceil
82 | local cos = math.cos
83 | local sin = math.sin
84 |
85 | local function calc_segments(segments)
86 | local function vdist(a, b)
87 | local c = {
88 | x = a.x - b.x,
89 | y = a.y - b.y,
90 | }
91 |
92 | return c.x * c.x + c.y * c.y
93 | end
94 |
95 | segments = segments or 64
96 | local vertices = {}
97 |
98 | local v = { 1, 2, ceil(segments/4-1), ceil(segments/4) }
99 |
100 | local m
101 | if love and love.physics then
102 | m = love.physics.getMeter()
103 | else
104 | m = 32
105 | end
106 |
107 | for _, i in ipairs(v) do
108 | local angle = (i / segments) * math.pi * 2
109 | local px = x + w / 2 + cos(angle) * w / 2
110 | local py = y + h / 2 + sin(angle) * h / 2
111 |
112 | table.insert(vertices, { x = px / m, y = py / m })
113 | end
114 |
115 | local dist1 = vdist(vertices[1], vertices[2])
116 | local dist2 = vdist(vertices[3], vertices[4])
117 |
118 | -- Box2D threshold
119 | if dist1 < 0.0025 or dist2 < 0.0025 then
120 | return calc_segments(segments-2)
121 | end
122 |
123 | return segments
124 | end
125 |
126 | local segments = calc_segments(max_segments)
127 | local vertices = {}
128 |
129 | table.insert(vertices, { x = x + w / 2, y = y + h / 2 })
130 |
131 | for i = 0, segments do
132 | local angle = (i / segments) * math.pi * 2
133 | local px = x + w / 2 + cos(angle) * w / 2
134 | local py = y + h / 2 + sin(angle) * h / 2
135 |
136 | table.insert(vertices, { x = px, y = py })
137 | end
138 |
139 | return vertices
140 | end
141 |
142 | function utils.rotate_vertex(map, vertex, x, y, cos, sin, oy)
143 | if map.orientation == "isometric" then
144 | x, y = utils.convert_isometric_to_screen(map, x, y)
145 | vertex.x, vertex.y = utils.convert_isometric_to_screen(map, vertex.x, vertex.y)
146 | end
147 |
148 | vertex.x = vertex.x - x
149 | vertex.y = vertex.y - y
150 |
151 | return
152 | x + cos * vertex.x - sin * vertex.y,
153 | y + sin * vertex.x + cos * vertex.y - (oy or 0)
154 | end
155 |
156 | --- Project isometric position to cartesian position
157 | function utils.convert_isometric_to_screen(map, x, y)
158 | local mapW = map.width
159 | local tileW = map.tilewidth
160 | local tileH = map.tileheight
161 | local tileX = x / tileH
162 | local tileY = y / tileH
163 | local offsetX = mapW * tileW / 2
164 |
165 | return
166 | (tileX - tileY) * tileW / 2 + offsetX,
167 | (tileX + tileY) * tileH / 2
168 | end
169 |
170 | function utils.hex_to_color(hex)
171 | if hex:sub(1, 1) == "#" then
172 | hex = hex:sub(2)
173 | end
174 |
175 | return {
176 | r = tonumber(hex:sub(1, 2), 16) / 255,
177 | g = tonumber(hex:sub(3, 4), 16) / 255,
178 | b = tonumber(hex:sub(5, 6), 16) / 255
179 | }
180 | end
181 |
182 | function utils.pixel_function(_, _, r, g, b, a)
183 | local mask = utils._TC
184 |
185 | if r == mask.r and
186 | g == mask.g and
187 | b == mask.b then
188 | return r, g, b, 0
189 | end
190 |
191 | return r, g, b, a
192 | end
193 |
194 | function utils.fix_transparent_color(tileset, path)
195 | local image_data = love.image.newImageData(path)
196 | tileset.image = love.graphics.newImage(image_data)
197 |
198 | if tileset.transparentcolor then
199 | utils._TC = utils.hex_to_color(tileset.transparentcolor)
200 |
201 | image_data:mapPixel(utils.pixel_function)
202 | tileset.image = love.graphics.newImage(image_data)
203 | end
204 | end
205 |
206 | function utils.deepCopy(t)
207 | local copy = {}
208 | for k,v in pairs(t) do
209 | if type(v) == "table" then
210 | v = utils.deepCopy(v)
211 | end
212 | copy[k] = v
213 | end
214 | return copy
215 | end
216 |
217 | return utils
218 |
--------------------------------------------------------------------------------
/Episode 6/stone.lua:
--------------------------------------------------------------------------------
1 |
2 |
3 | local Stone = {img = love.graphics.newImage("assets/stone.png")}
4 | Stone.__index = Stone
5 |
6 | Stone.width = Stone.img:getWidth()
7 | Stone.height = Stone.img:getHeight()
8 |
9 | local ActiveStones = {}
10 |
11 | function Stone.removeAll()
12 | for i,v in ipairs(ActiveStones) do
13 | v.physics.body:destroy()
14 | end
15 |
16 | ActiveStones = {}
17 | end
18 |
19 | function Stone.new(x,y)
20 | local instance = setmetatable({}, Stone)
21 | instance.x = x
22 | instance.y = y
23 | instance.r = 0
24 |
25 | instance.physics = {}
26 | instance.physics.body = love.physics.newBody(World, instance.x, instance.y, "dynamic")
27 | instance.physics.shape = love.physics.newRectangleShape(instance.width, instance.height)
28 | instance.physics.fixture = love.physics.newFixture(instance.physics.body, instance.physics.shape)
29 | instance.physics.body:setMass(25)
30 | table.insert(ActiveStones, instance)
31 | end
32 |
33 | function Stone:update(dt)
34 | self:syncPhysics()
35 | end
36 |
37 | function Stone:syncPhysics()
38 | self.x, self.y = self.physics.body:getPosition()
39 | self.r = self.physics.body:getAngle()
40 | end
41 |
42 | function Stone:draw()
43 | love.graphics.draw(self.img, self.x, self.y, self.r, 1, 1, self.width / 2, self.height / 2)
44 | end
45 |
46 | function Stone.updateAll(dt)
47 | for i,instance in ipairs(ActiveStones) do
48 | instance:update(dt)
49 | end
50 | end
51 |
52 | function Stone.drawAll()
53 | for i,instance in ipairs(ActiveStones) do
54 | instance:draw()
55 | end
56 | end
57 |
58 | return Stone
59 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Full source code and assets from the tutorial series. - Youtube link: https://www.youtube.com/playlist?list=PL1A1gsSe2tMxngwl-CU1MCX7kmbLf4P5O
2 | Note: The code and assets have different licences
3 |
4 | Code: WTFPL (Do whatever you want, no restrictions)
5 | Art: BY-NC-ND (https://creativecommons.org/licenses/by-nc-nd/4.0/) More restricted.
6 | Font: https://www.dafont.com/5x5.font
7 |
--------------------------------------------------------------------------------