├── .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 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 12 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 13 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 14 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 15 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 16 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 17 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 18 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 19 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 20 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 21 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 22 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 23 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 24 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 25 | 0,0,0,0,0,0,0,0,0,33,34,33,34,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 26 | 0,0,0,0,0,0,0,0,0,47,48,47,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 27 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 28 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 29 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,33,34,33,34,0,0,0,0,0,0,0,0,0,0,0, 30 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,48,47,48,0,0,0,0,0,0,0,0,0,0,0, 31 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,48,33,34,0,0,0,0,0,0,0,0,0,0,0, 32 | 29,30,29,30,29,30,29,30,29,30,29,30,29,30,29,30,29,30,29,30,29,30,29,30,29,33,34,33,34,30,29,30,29,30,29,30,29,30,29,30, 33 | 43,44,43,44,43,44,43,44,43,44,43,44,43,44,43,44,43,44,43,44,43,44,43,44,43,47,47,47,47,44,43,44,43,44,43,44,43,44,43,44 34 | 35 | 36 | 37 | 38 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 39 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 40 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 41 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 42 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 43 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 44 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 45 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 46 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 47 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 48 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 49 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 50 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 51 | 0,0,0,0,0,0,0,0,1,2,3,12,13,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 52 | 0,0,0,0,0,0,0,0,15,16,17,26,27,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 53 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 54 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 55 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,10,11,13,14,0,0,0,0,0,0,0,0,0,0, 56 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,16,24,25,27,28,0,0,0,0,0,0,0,0,0,0, 57 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 58 | 2,3,4,5,6,7,8,9,10,11,12,13,2,3,4,5,6,7,8,9,10,11,12,13,2,0,0,0,0,7,8,9,10,11,12,13,2,3,4,5, 59 | 16,17,18,19,20,21,22,23,24,25,26,27,16,17,18,19,20,21,22,23,24,25,26,27,16,0,0,0,0,21,22,23,24,25,26,27,16,17,18,19, 60 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 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 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 12 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 13 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 14 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 15 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 16 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 17 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 18 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 19 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 20 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 21 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 22 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 23 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 24 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 25 | 0,0,0,0,0,0,0,0,0,33,34,33,34,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 26 | 0,0,0,0,0,0,0,0,0,47,48,47,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 27 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 28 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 29 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,33,34,33,34,0,0,0,0,0,0,0,0,0,0,0, 30 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,48,47,48,0,0,0,0,0,0,0,0,0,0,0, 31 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,48,33,34,0,0,0,0,0,0,0,0,0,0,0, 32 | 29,30,29,30,29,30,29,30,29,30,29,30,29,30,29,30,29,30,29,30,29,30,29,30,29,33,34,33,34,30,29,30,29,30,29,30,29,30,29,30, 33 | 43,44,43,44,43,44,43,44,43,44,43,44,43,44,43,44,43,44,43,44,43,44,43,44,43,47,47,47,47,44,43,44,43,44,43,44,43,44,43,44 34 | 35 | 36 | 37 | 38 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 39 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 40 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 41 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 42 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 43 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 44 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 45 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 46 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 47 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 48 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 49 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 50 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 51 | 0,0,0,0,0,0,0,0,1,2,3,12,13,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 52 | 0,0,0,0,0,0,0,0,15,16,17,26,27,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 53 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 54 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 55 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,10,11,13,14,0,0,0,0,0,0,0,0,0,0, 56 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,16,24,25,27,28,0,0,0,0,0,0,0,0,0,0, 57 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 58 | 2,3,4,5,6,7,8,9,10,11,12,13,2,3,4,5,6,7,8,9,10,11,12,13,2,0,0,0,0,7,8,9,10,11,12,13,2,3,4,5, 59 | 16,17,18,19,20,21,22,23,24,25,26,27,16,17,18,19,20,21,22,23,24,25,26,27,16,0,0,0,0,21,22,23,24,25,26,27,16,17,18,19, 60 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 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 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 12 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 13 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 14 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 15 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 16 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 17 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 18 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 19 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 20 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 21 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 22 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 23 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 24 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 25 | 0,0,0,0,0,0,0,0,0,33,34,33,34,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 26 | 0,0,0,0,0,0,0,0,0,47,48,47,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 27 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 28 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 29 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,33,34,33,34,0,0,0,0,0,0,0,0,0,0,0, 30 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,48,47,48,0,0,0,0,0,0,0,0,0,0,0, 31 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,48,33,34,0,0,0,0,0,0,0,0,0,0,0, 32 | 29,30,29,30,29,30,29,30,29,30,29,30,29,30,29,30,29,30,29,30,29,30,29,30,29,33,34,33,34,30,29,30,29,30,29,30,29,30,29,30, 33 | 43,44,43,44,43,44,43,44,43,44,43,44,43,44,43,44,43,44,43,44,43,44,43,44,43,47,47,47,47,44,43,44,43,44,43,44,43,44,43,44 34 | 35 | 36 | 37 | 38 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 39 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 40 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 41 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 42 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 43 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 44 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 45 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 46 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 47 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 48 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 49 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 50 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 51 | 0,0,0,0,0,0,0,0,1,2,3,12,13,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 52 | 0,0,0,0,0,0,0,0,15,16,17,26,27,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 53 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 54 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 55 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,10,11,13,14,0,0,0,0,0,0,0,0,0,0, 56 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,16,24,25,27,28,0,0,0,0,0,0,0,0,0,0, 57 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 58 | 2,3,4,5,6,7,8,9,10,11,12,13,2,3,4,5,6,7,8,9,10,11,12,13,2,0,0,0,0,7,8,9,10,11,12,13,2,3,4,5, 59 | 16,17,18,19,20,21,22,23,24,25,26,27,16,17,18,19,20,21,22,23,24,25,26,27,16,0,0,0,0,21,22,23,24,25,26,27,16,17,18,19, 60 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 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 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 12 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 13 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 14 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 15 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 16 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 17 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 18 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 19 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 20 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 21 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 22 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 23 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 24 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 25 | 0,0,0,0,0,0,0,0,0,33,34,33,34,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 26 | 0,0,0,0,0,0,0,0,0,47,48,47,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 27 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 28 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 29 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,33,34,33,34,0,0,0,0,0,0,0,0,0,0,0, 30 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,48,47,48,0,0,0,0,0,0,0,0,0,0,0, 31 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,48,33,34,0,0,0,0,0,0,0,0,0,0,0, 32 | 29,30,29,30,29,30,29,30,29,30,29,30,29,30,29,30,29,30,29,30,29,30,29,30,29,33,34,33,34,30,29,30,29,30,29,30,29,30,29,30, 33 | 43,44,43,44,43,44,43,44,43,44,43,44,43,44,43,44,43,44,43,44,43,44,43,44,43,47,47,47,47,44,43,44,43,44,43,44,43,44,43,44 34 | 35 | 36 | 37 | 38 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 39 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 40 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 41 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 42 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 43 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 44 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 45 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 46 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 47 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 48 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 49 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 50 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 51 | 0,0,0,0,0,0,0,0,1,2,3,12,13,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 52 | 0,0,0,0,0,0,0,0,15,16,17,26,27,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 53 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 54 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 55 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,10,11,13,14,0,0,0,0,0,0,0,0,0,0, 56 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,16,24,25,27,28,0,0,0,0,0,0,0,0,0,0, 57 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 58 | 2,3,4,5,6,7,8,9,10,11,12,13,2,3,4,5,6,7,8,9,10,11,12,13,2,0,0,0,0,7,8,9,10,11,12,13,2,3,4,5, 59 | 16,17,18,19,20,21,22,23,24,25,26,27,16,17,18,19,20,21,22,23,24,25,26,27,16,0,0,0,0,21,22,23,24,25,26,27,16,17,18,19, 60 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 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 | --------------------------------------------------------------------------------