├── .gitignore ├── README.md ├── lovedemo ├── graphics │ └── mario.png ├── main.lua └── push.lua ├── lua_demo.lua ├── mario-0-exercise ├── fonts │ └── font.ttf ├── graphics │ └── tiles.png ├── main.lua └── push.lua ├── mario-0 ├── fonts │ └── font.ttf ├── graphics │ └── tiles.png ├── main.lua └── push.lua ├── mario-1-exercise ├── Map.lua ├── Util.lua ├── fonts │ └── font.ttf ├── graphics │ ├── mario_tiles_numbered.png │ ├── playable_left.png │ ├── playable_right.png │ ├── tiles.png │ └── tiles_2.png ├── main.lua └── push.lua ├── mario-1 ├── Map.lua ├── Util.lua ├── fonts │ └── font.ttf ├── graphics │ ├── mario_tiles_numbered.png │ ├── playable_left.png │ ├── playable_right.png │ ├── tiles.png │ └── tiles_2.png ├── main.lua └── push.lua ├── mario-10-exercise ├── Animation.lua ├── Map.lua ├── Player.lua ├── Util.lua ├── fonts │ └── font.ttf ├── graphics │ ├── mario.png │ ├── mario1.png │ ├── playable_left.png │ ├── playable_right.png │ ├── tiles.png │ └── tiles_2.png ├── main.lua └── push.lua ├── mario-10 ├── Animation.lua ├── Map.lua ├── Player.lua ├── Util.lua ├── fonts │ └── font.ttf ├── graphics │ ├── mario.png │ ├── mario1.png │ ├── playable_left.png │ ├── playable_right.png │ ├── tiles.png │ └── tiles_2.png ├── main.lua └── push.lua ├── mario-11-exercise ├── Animation.lua ├── Map.lua ├── Player.lua ├── Util.lua ├── fonts │ └── font.ttf ├── graphics │ ├── mario.png │ ├── mario1.png │ ├── playable_left.png │ ├── playable_right.png │ ├── tiles.png │ └── tiles_2.png ├── main.lua ├── music │ └── overworld.mp3 ├── push.lua └── sounds │ ├── coin.wav │ ├── hit.wav │ └── jump.wav ├── mario-11 ├── Animation.lua ├── Map.lua ├── Player.lua ├── Util.lua ├── fonts │ └── font.ttf ├── graphics │ ├── mario.png │ ├── mario1.png │ ├── playable_left.png │ ├── playable_right.png │ ├── tiles.png │ └── tiles_2.png ├── main.lua ├── music │ └── overworld.mp3 ├── push.lua └── sounds │ ├── coin.wav │ ├── hit.wav │ └── jump.wav ├── mario-2-exercise ├── Map.lua ├── Util.lua ├── fonts │ └── font.ttf ├── graphics │ ├── playable_left.png │ ├── playable_right.png │ ├── tiles.png │ └── tiles_2.png ├── main.lua └── push.lua ├── mario-2 ├── Map.lua ├── Util.lua ├── fonts │ └── font.ttf ├── graphics │ ├── playable_left.png │ ├── playable_right.png │ ├── tiles.png │ └── tiles_2.png ├── main.lua └── push.lua ├── mario-3-exercise ├── Map.lua ├── Util.lua ├── fonts │ └── font.ttf ├── graphics │ ├── playable_left.png │ ├── playable_right.png │ ├── tiles.png │ └── tiles_2.png ├── main.lua └── push.lua ├── mario-3 ├── Map.lua ├── Util.lua ├── fonts │ └── font.ttf ├── graphics │ ├── playable_left.png │ ├── playable_right.png │ ├── tiles.png │ └── tiles_2.png ├── main.lua └── push.lua ├── mario-4-exercise ├── Map.lua ├── Util.lua ├── fonts │ └── font.ttf ├── graphics │ ├── mario.png │ ├── mario1.png │ ├── playable_left.png │ ├── playable_right.png │ ├── tiles.png │ └── tiles_2.png ├── main.lua └── push.lua ├── mario-4 ├── Map.lua ├── Util.lua ├── fonts │ └── font.ttf ├── graphics │ ├── mario.png │ ├── mario1.png │ ├── playable_left.png │ ├── playable_right.png │ ├── tiles.png │ └── tiles_2.png ├── main.lua └── push.lua ├── mario-5-exercise ├── Map.lua ├── Player.lua ├── Util.lua ├── fonts │ └── font.ttf ├── graphics │ ├── mario.png │ ├── mario1.png │ ├── playable_left.png │ ├── playable_right.png │ ├── tiles.png │ └── tiles_2.png ├── main.lua └── push.lua ├── mario-5 ├── Map.lua ├── Player.lua ├── Util.lua ├── fonts │ └── font.ttf ├── graphics │ ├── mario.png │ ├── mario1.png │ ├── playable_left.png │ ├── playable_right.png │ ├── tiles.png │ └── tiles_2.png ├── main.lua └── push.lua ├── mario-6-exercise ├── Map.lua ├── Player.lua ├── Util.lua ├── fonts │ └── font.ttf ├── graphics │ ├── mario.png │ ├── mario1.png │ ├── playable_left.png │ ├── playable_right.png │ ├── tiles.png │ └── tiles_2.png ├── main.lua └── push.lua ├── mario-6 ├── Map.lua ├── Player.lua ├── Util.lua ├── fonts │ └── font.ttf ├── graphics │ ├── mario.png │ ├── mario1.png │ ├── playable_left.png │ ├── playable_right.png │ ├── tiles.png │ └── tiles_2.png ├── main.lua └── push.lua ├── mario-7-exercise ├── Animation.lua ├── Map.lua ├── Player.lua ├── Util.lua ├── fonts │ └── font.ttf ├── graphics │ ├── mario.png │ ├── mario1.png │ ├── playable_left.png │ ├── playable_right.png │ ├── tiles.png │ └── tiles_2.png ├── main.lua └── push.lua ├── mario-7 ├── Animation.lua ├── Map.lua ├── Player.lua ├── Util.lua ├── fonts │ └── font.ttf ├── graphics │ ├── mario.png │ ├── mario1.png │ ├── playable_left.png │ ├── playable_right.png │ ├── tiles.png │ └── tiles_2.png ├── main.lua └── push.lua ├── mario-8-exercise ├── Animation.lua ├── Map.lua ├── Player.lua ├── Util.lua ├── fonts │ └── font.ttf ├── graphics │ ├── mario.png │ ├── mario1.png │ ├── playable_left.png │ ├── playable_right.png │ ├── tiles.png │ └── tiles_2.png ├── main.lua └── push.lua ├── mario-8 ├── Animation.lua ├── Map.lua ├── Player.lua ├── Util.lua ├── fonts │ └── font.ttf ├── graphics │ ├── mario.png │ ├── mario1.png │ ├── playable_left.png │ ├── playable_right.png │ ├── tiles.png │ └── tiles_2.png ├── main.lua └── push.lua ├── mario-9-exercise ├── Animation.lua ├── Map.lua ├── Player.lua ├── Util.lua ├── fonts │ └── font.ttf ├── graphics │ ├── mario.png │ ├── mario1.png │ ├── playable_left.png │ ├── playable_right.png │ ├── tiles.png │ └── tiles_2.png ├── main.lua └── push.lua └── mario-9 ├── Animation.lua ├── Map.lua ├── Player.lua ├── Util.lua ├── fonts └── font.ttf ├── graphics ├── mario.png ├── mario1.png ├── playable_left.png ├── playable_right.png ├── tiles.png └── tiles_2.png ├── main.lua └── push.lua /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Falling in LÖVE with Lua 2 | ======================== 3 | 4 | Seminar Source by Colton Ogden 5 | ----------------------- 6 | 7 | Installation and Usage 8 | ---------------------- 9 | 10 | * Download LÖVE2D (comes with Lua) from https://love2d.org/#download 11 | * Run examples using instructions at https://love2d.org/wiki/Getting_Started 12 | 13 | Exercises 14 | --------- 15 | 16 | Each mario-x example contains an accompanying exercise folder with some TODOS; 17 | try and solve if you can! 18 | 19 | Google Slides 20 | ------ 21 | https://docs.google.com/presentation/d/1K3GN5827gbqQZJzKu43kXPaW2cTkJWja8WWbrv2Wnmc/edit?usp=sharing 22 | -------------------------------------------------------------------------------- /lovedemo/graphics/mario.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/lovedemo/graphics/mario.png -------------------------------------------------------------------------------- /lovedemo/main.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Simple LOVE2D Demo 3 | -- Author: Colton Ogden 4 | -- 5 | 6 | -- virtual resolution library 7 | push = require 'push' 8 | 9 | -- a sprite we will draw and its X-Y coordinates 10 | local sprite 11 | local x 12 | local y 13 | 14 | local virtualWidth = 432 15 | local virtualHeight = 243 16 | local speed = 10 17 | 18 | -- function called at start of game to load assets 19 | function love.load() 20 | love.graphics.setDefaultFilter('nearest', 'nearest') 21 | 22 | sprite = love.graphics.newImage('graphics/mario.png') 23 | x = virtualWidth / 2 - sprite:getWidth() / 2 24 | y = virtualHeight / 2 - sprite:getHeight() / 2 25 | 26 | push:setupScreen(virtualWidth, virtualHeight, 640, 480, { 27 | fullscreen = false 28 | }) 29 | end 30 | 31 | -- function called every frame with the delta (dt) since last frame 32 | function love.update(dt) 33 | 34 | end 35 | 36 | -- a callback function called whenever we press a key 37 | function love.keypressed(key) 38 | if key == 'left' then 39 | x = x - speed 40 | end 41 | if key == 'right' then 42 | x = x + speed 43 | end 44 | 45 | if key == 'up' then 46 | y = y - speed 47 | end 48 | if key == 'down' then 49 | y = y + speed 50 | end 51 | 52 | if key == 'escape' then 53 | love.event.quit() 54 | end 55 | end 56 | 57 | -- a function called each frame meant to render things to the screen 58 | function love.draw() 59 | push:apply('start') 60 | love.graphics.draw(sprite, x, y) 61 | push:apply('end') 62 | end 63 | -------------------------------------------------------------------------------- /lua_demo.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Variables 3 | -- 4 | 5 | -- global (accessible from other Lua modules) 6 | hello = 'hello' 7 | 8 | -- local (accessible only in this scope) 9 | local world = ' world!' 10 | 11 | -- 12 | -- Functions 13 | -- 14 | 15 | -- declaring our function 16 | function say(text) 17 | print(text) 18 | end 19 | 20 | -- calling our function (note the .. operator to concatenate strings!) 21 | say(hello .. world) 22 | 23 | -- 24 | -- If statements 25 | -- 26 | if world == 'world' then 27 | print('world!') 28 | else 29 | print('hello!') 30 | end 31 | 32 | -- 33 | -- Loops 34 | -- 35 | 36 | -- while loop with counter 37 | local i = 10 38 | while i > 0 do 39 | -- note the lack of -= and += 40 | i = i - 1 41 | print(i) 42 | end 43 | 44 | -- for loop, decrements from 10 to 1 45 | for j = 10, 1, -1 do 46 | print(j) 47 | end 48 | 49 | -- repeat (do-while) loop 50 | i = 10 51 | repeat 52 | i = i - 1 53 | print(i) 54 | until i == 0 55 | 56 | -- 57 | -- Tables 58 | -- 59 | 60 | -- sort of like structs or hash tables in C, and like Python dictionaries 61 | local person = {} 62 | person.name = 'Colton Ogden' 63 | person.age = 26 64 | person.height = 69.5 65 | 66 | -- bracket and dot syntax to access table fields 67 | print(person['name']) 68 | print(person.name) 69 | 70 | -- iterate through table's key-value pairs and print both of each 71 | for key, value in pairs(person) do 72 | print(key, value) 73 | end 74 | -------------------------------------------------------------------------------- /mario-0-exercise/fonts/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-0-exercise/fonts/font.ttf -------------------------------------------------------------------------------- /mario-0-exercise/graphics/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-0-exercise/graphics/tiles.png -------------------------------------------------------------------------------- /mario-0-exercise/main.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Super Mario Bros. Demo 3 | Author: Colton Ogden 4 | Original Credit: Nintendo 5 | 6 | Demonstrates printing 'Hello world' to the screen in a virtual resolution. 7 | ]] 8 | 9 | push = require 'push' 10 | 11 | -- close resolution to NES but 16:9 12 | virtualWidth = 432 13 | virtualHeight = 243 14 | 15 | -- actual window resolution 16 | windowWidth = 1280 17 | windowHeight = 720 18 | 19 | -- performs initialization of all objects and data needed by program 20 | function love.load() 21 | -- makes upscaling look pixel-y instead of blurry 22 | love.graphics.setDefaultFilter('nearest', 'nearest') 23 | 24 | -- sets up a different, better-looking retro font as our default 25 | love.graphics.setFont(love.graphics.newFont('fonts/font.ttf', 8)) 26 | 27 | -- sets up virtual screen resolution for an authentic retro feel 28 | push:setupScreen(virtualWidth, virtualHeight, windowWidth, windowHeight, { 29 | fullscreen = false, 30 | resizable = true 31 | }) 32 | end 33 | 34 | -- called whenever window is resized 35 | function love.resize(w, h) 36 | push:resize(w, h) 37 | end 38 | 39 | -- called whenever a key is pressed 40 | function love.keypressed(key) 41 | if key == 'escape' then 42 | love.event.quit() 43 | end 44 | end 45 | 46 | -- called every frame, with dt passed in as delta in time since last frame 47 | function love.update(dt) 48 | 49 | end 50 | 51 | -- called each frame, used to render to the screen 52 | function love.draw() 53 | -- begin virtual resolution drawing 54 | push:apply('start') 55 | 56 | -- TODO: print 'Hello world!' centered on the screen 57 | -- HINT: love.graphics.printf 58 | 59 | -- end virtual resolution 60 | push:apply('end') 61 | end 62 | -------------------------------------------------------------------------------- /mario-0/fonts/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-0/fonts/font.ttf -------------------------------------------------------------------------------- /mario-0/graphics/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-0/graphics/tiles.png -------------------------------------------------------------------------------- /mario-0/main.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Super Mario Bros. Demo 3 | Author: Colton Ogden 4 | Original Credit: Nintendo 5 | 6 | Demonstrates printing 'Hello world' to the screen in a virtual resolution. 7 | ]] 8 | 9 | push = require 'push' 10 | 11 | -- close resolution to NES but 16:9 12 | virtualWidth = 432 13 | virtualHeight = 243 14 | 15 | -- actual window resolution 16 | windowWidth = 1280 17 | windowHeight = 720 18 | 19 | -- performs initialization of all objects and data needed by program 20 | function love.load() 21 | -- makes upscaling look pixel-y instead of blurry 22 | love.graphics.setDefaultFilter('nearest', 'nearest') 23 | 24 | -- sets up a different, better-looking retro font as our default 25 | love.graphics.setFont(love.graphics.newFont('fonts/font.ttf', 8)) 26 | 27 | -- sets up virtual screen resolution for an authentic retro feel 28 | push:setupScreen(virtualWidth, virtualHeight, windowWidth, windowHeight, { 29 | fullscreen = false, 30 | resizable = true 31 | }) 32 | end 33 | 34 | -- called whenever window is resized 35 | function love.resize(w, h) 36 | push:resize(w, h) 37 | end 38 | 39 | -- called whenever a key is pressed 40 | function love.keypressed(key) 41 | if key == 'escape' then 42 | love.event.quit() 43 | end 44 | end 45 | 46 | -- called every frame, with dt passed in as delta in time since last frame 47 | function love.update(dt) 48 | 49 | end 50 | 51 | -- called each frame, used to render to the screen 52 | function love.draw() 53 | -- begin virtual resolution drawing 54 | push:apply('start') 55 | 56 | -- prints 'Hello world!' centered on the screen 57 | love.graphics.printf('Hello world!', 0, virtualHeight / 2, virtualWidth, 58 | 'center', 0, 1, 1) 59 | 60 | -- end virtual resolution 61 | push:apply('end') 62 | end 63 | -------------------------------------------------------------------------------- /mario-1-exercise/Map.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Contains tile data and necessary code for rendering a tile map to the 3 | screen. 4 | ]] 5 | 6 | require 'Util' 7 | 8 | -- object-oriented boilerplate; establish Map's "prototype" 9 | Map = {} 10 | Map.__index = Map 11 | 12 | TILE_BRICK = 1 13 | TILE_EMPTY = 29 14 | 15 | -- constructor for our map object 16 | function Map:create() 17 | local this = { 18 | -- our texture containing all sprites 19 | spritesheet = love.graphics.newImage('graphics/tiles.png'), 20 | tileWidth = 16, 21 | tileHeight = 16, 22 | mapWidth = 30, 23 | mapHeight = 28, 24 | tiles = {} 25 | } 26 | 27 | -- generate a quad (individual frame/sprite) for each tile 28 | this.tileSprites = generateQuads(this.spritesheet, 16, 16) 29 | 30 | -- cache width and height of map in pixels 31 | this.mapWidthPixels = this.mapWidth * this.tileWidth 32 | this.mapHeightPixels = this.mapHeight * this.tileHeight 33 | 34 | -- more OO boilerplate so we have access to class functions 35 | setmetatable(this, self) 36 | 37 | -- first, fill map with empty tiles 38 | for y = 1, this.mapHeight do 39 | for x = 1, this.mapWidth do 40 | -- TODO: set that tile to be our empty tile defined above 41 | -- HINT: maybe this file has a function for setting tiles? 42 | 43 | end 44 | end 45 | 46 | -- -- fill bottom half of map with tiles 47 | for y = this.mapHeight / 2, this.mapHeight do 48 | for x = 1, this.mapWidth do 49 | -- TODO: set that tile to be our brick tile defined above 50 | -- HINT: maybe this file has a function for setting tiles? 51 | 52 | end 53 | end 54 | 55 | return this 56 | end 57 | 58 | -- returns an integer value for the tile at a given x-y coordinate 59 | function Map:getTile(x, y) 60 | -- TODO: return the tile ID at a given X, Y coordinate 61 | -- HINT: maybe we already have a structure defined in our class for holding tiles? 62 | -- HINT: if this structure is a 1-dimensional "array" of sorts, how can we 63 | -- compute its index given an X and a Y value? maybe by multiplying the map 64 | -- width by some value? 65 | 66 | end 67 | 68 | -- sets a tile at a given x-y coordinate to an integer value 69 | function Map:setTile(x, y, tile) 70 | -- TODO: assign a tile ID at a given X, Y coordinate in this class 71 | -- HINT: we should already have a structure for holding the IDs 72 | -- HINT: just like getTile, how can we calculate the index given an X and 73 | -- Y value? seems like mapWidth is the key here.... 74 | 75 | end 76 | 77 | -- renders our map to the screen, to be called by main's render 78 | function Map:render() 79 | for y = 1, self.mapHeight do 80 | for x = 1, self.mapWidth do 81 | -- TODO: draw to the screen the tile at X, Y 82 | -- HINT: love.graphics.draw has a variant that takes in a quad; we'll 83 | -- probably need this to get a specific tile out of our spritesheet 84 | -- HINT: maybe we can use getTile once implemented? 85 | -- HINT: though Lua tables are 1-indexed, drawing in LÖVE still starts 86 | -- at 0, 0! 87 | 88 | end 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /mario-1-exercise/Util.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Stores utility functions used by our game engine. 3 | ]] 4 | 5 | -- takes a texture, width, and height of tiles and splits it into quads 6 | -- that can be individually drawn 7 | function generateQuads(atlas, tilewidth, tileheight) 8 | local sheetWidth = atlas:getWidth() / tilewidth 9 | local sheetHeight = atlas:getHeight() / tileheight 10 | 11 | local sheetCounter = 1 12 | local quads = {} 13 | 14 | for y = 0, sheetHeight - 1 do 15 | for x = 0, sheetWidth - 1 do 16 | -- this quad represents a square cutout of our atlas that we can 17 | -- individually draw instead of the whole atlas 18 | quads[sheetCounter] = 19 | love.graphics.newQuad(x * tilewidth, y * tileheight, tilewidth, 20 | tileheight, atlas:getDimensions()) 21 | sheetCounter = sheetCounter + 1 22 | end 23 | end 24 | 25 | return quads 26 | end 27 | -------------------------------------------------------------------------------- /mario-1-exercise/fonts/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-1-exercise/fonts/font.ttf -------------------------------------------------------------------------------- /mario-1-exercise/graphics/mario_tiles_numbered.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-1-exercise/graphics/mario_tiles_numbered.png -------------------------------------------------------------------------------- /mario-1-exercise/graphics/playable_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-1-exercise/graphics/playable_left.png -------------------------------------------------------------------------------- /mario-1-exercise/graphics/playable_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-1-exercise/graphics/playable_right.png -------------------------------------------------------------------------------- /mario-1-exercise/graphics/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-1-exercise/graphics/tiles.png -------------------------------------------------------------------------------- /mario-1-exercise/graphics/tiles_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-1-exercise/graphics/tiles_2.png -------------------------------------------------------------------------------- /mario-1-exercise/main.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Super Mario Bros. Demo 3 | Author: Colton Ogden 4 | Original Credit: Nintendo 5 | 6 | Demonstrates rendering a screen of tiles. 7 | ]] 8 | 9 | push = require 'push' 10 | 11 | require 'Map' 12 | 13 | -- close resolution to NES but 16:9 14 | virtualWidth = 432 15 | virtualHeight = 243 16 | 17 | -- actual window resolution 18 | windowWidth = 1280 19 | windowHeight = 720 20 | 21 | -- an object to contain our map data 22 | map = Map:create() 23 | 24 | -- performs initialization of all objects and data needed by program 25 | function love.load() 26 | -- makes upscaling look pixel-y instead of blurry 27 | love.graphics.setDefaultFilter('nearest', 'nearest') 28 | 29 | -- sets up a different, better-looking retro font as our default 30 | love.graphics.setFont(love.graphics.newFont('fonts/font.ttf', 8)) 31 | 32 | -- sets up virtual screen resolution for an authentic retro feel 33 | push:setupScreen(virtualWidth, virtualHeight, windowWidth, windowHeight, { 34 | fullscreen = false, 35 | resizable = true 36 | }) 37 | end 38 | 39 | -- called whenever window is resized 40 | function love.resize(w, h) 41 | push:resize(w, h) 42 | end 43 | 44 | -- called whenever a key is pressed 45 | function love.keypressed(key) 46 | if key == 'escape' then 47 | love.event.quit() 48 | end 49 | end 50 | 51 | -- called every frame, with dt passed in as delta in time since last frame 52 | function love.update(dt) 53 | 54 | end 55 | 56 | -- called each frame, used to render to the screen 57 | function love.draw() 58 | -- begin virtual resolution drawing 59 | push:apply('start') 60 | 61 | -- TODO: clear screen using Mario background blue 62 | -- HINT: love.graphics.clear? 63 | -- HINT: RGB color is (108, 140, 255) with full alpha! 64 | 65 | -- TODO: render our map object onto the screen 66 | -- HINT: check Map.lua? 67 | 68 | -- end virtual resolution 69 | push:apply('end') 70 | end 71 | -------------------------------------------------------------------------------- /mario-1/Map.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Contains tile data and necessary code for rendering a tile map to the 3 | screen. 4 | ]] 5 | 6 | require 'Util' 7 | 8 | -- object-oriented boilerplate; establish Map's "prototype" 9 | Map = {} 10 | Map.__index = Map 11 | 12 | TILE_BRICK = 1 13 | TILE_EMPTY = 29 14 | 15 | -- constructor for our map object 16 | function Map:create() 17 | local this = { 18 | -- our texture containing all sprites 19 | spritesheet = love.graphics.newImage('graphics/tiles.png'), 20 | tileWidth = 16, 21 | tileHeight = 16, 22 | mapWidth = 30, 23 | mapHeight = 28, 24 | tiles = {} 25 | } 26 | 27 | -- generate a quad (individual frame/sprite) for each tile 28 | this.tileSprites = generateQuads(this.spritesheet, 16, 16) 29 | 30 | -- more OO boilerplate so we have access to class functions 31 | setmetatable(this, self) 32 | 33 | -- first, fill map with empty tiles 34 | for y = 1, this.mapHeight do 35 | for x = 1, this.mapWidth do 36 | this:setTile(x, y, TILE_EMPTY) 37 | end 38 | end 39 | 40 | -- fill bottom half of map with tiles 41 | for y = this.mapHeight / 2, this.mapHeight do 42 | for x = 1, this.mapWidth do 43 | this:setTile(x, y, TILE_BRICK) 44 | end 45 | end 46 | 47 | return this 48 | end 49 | 50 | -- returns an integer value for the tile at a given x-y coordinate 51 | function Map:getTile(x, y) 52 | return self.tiles[(y - 1) * self.mapWidth + x] 53 | end 54 | 55 | -- sets a tile at a given x-y coordinate to an integer value 56 | function Map:setTile(x, y, tile) 57 | self.tiles[(y - 1) * self.mapWidth + x] = tile 58 | end 59 | 60 | -- renders our map to the screen, to be called by main's render 61 | function Map:render() 62 | for y = 1, self.mapHeight do 63 | for x = 1, self.mapWidth do 64 | love.graphics.draw(self.spritesheet, self.tileSprites[self:getTile(x, y)], 65 | (x - 1) * self.tileWidth, (y - 1) * self.tileHeight) 66 | end 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /mario-1/Util.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Stores utility functions used by our game engine. 3 | ]] 4 | 5 | -- takes a texture, width, and height of tiles and splits it into quads 6 | -- that can be individually drawn 7 | function generateQuads(atlas, tilewidth, tileheight) 8 | local sheetWidth = atlas:getWidth() / tilewidth 9 | local sheetHeight = atlas:getHeight() / tileheight 10 | 11 | local sheetCounter = 1 12 | local quads = {} 13 | 14 | for y = 0, sheetHeight - 1 do 15 | for x = 0, sheetWidth - 1 do 16 | -- this quad represents a square cutout of our atlas that we can 17 | -- individually draw instead of the whole atlas 18 | quads[sheetCounter] = 19 | love.graphics.newQuad(x * tilewidth, y * tileheight, tilewidth, 20 | tileheight, atlas:getDimensions()) 21 | sheetCounter = sheetCounter + 1 22 | end 23 | end 24 | 25 | return quads 26 | end 27 | -------------------------------------------------------------------------------- /mario-1/fonts/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-1/fonts/font.ttf -------------------------------------------------------------------------------- /mario-1/graphics/mario_tiles_numbered.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-1/graphics/mario_tiles_numbered.png -------------------------------------------------------------------------------- /mario-1/graphics/playable_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-1/graphics/playable_left.png -------------------------------------------------------------------------------- /mario-1/graphics/playable_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-1/graphics/playable_right.png -------------------------------------------------------------------------------- /mario-1/graphics/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-1/graphics/tiles.png -------------------------------------------------------------------------------- /mario-1/graphics/tiles_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-1/graphics/tiles_2.png -------------------------------------------------------------------------------- /mario-1/main.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Super Mario Bros. Demo 3 | Author: Colton Ogden 4 | Original Credit: Nintendo 5 | 6 | Demonstrates rendering a screen of tiles. 7 | ]] 8 | 9 | push = require 'push' 10 | 11 | require 'Map' 12 | 13 | -- close resolution to NES but 16:9 14 | virtualWidth = 432 15 | virtualHeight = 243 16 | 17 | -- actual window resolution 18 | windowWidth = 1280 19 | windowHeight = 720 20 | 21 | -- an object to contain our map data 22 | map = Map:create() 23 | 24 | -- performs initialization of all objects and data needed by program 25 | function love.load() 26 | -- makes upscaling look pixel-y instead of blurry 27 | love.graphics.setDefaultFilter('nearest', 'nearest') 28 | 29 | -- sets up virtual screen resolution for an authentic retro feel 30 | push:setupScreen(virtualWidth, virtualHeight, windowWidth, windowHeight, { 31 | fullscreen = false, 32 | resizable = true 33 | }) 34 | end 35 | 36 | -- called whenever window is resized 37 | function love.resize(w, h) 38 | push:resize(w, h) 39 | end 40 | 41 | -- called whenever a key is pressed 42 | function love.keypressed(key) 43 | if key == 'escape' then 44 | love.event.quit() 45 | end 46 | end 47 | 48 | -- called every frame, with dt passed in as delta in time since last frame 49 | function love.update(dt) 50 | 51 | end 52 | 53 | -- called each frame, used to render to the screen 54 | function love.draw() 55 | -- begin virtual resolution drawing 56 | push:apply('start') 57 | 58 | -- clear screen using Mario background blue 59 | love.graphics.clear(108, 140, 255, 255) 60 | 61 | -- renders our map object onto the screen 62 | map:render() 63 | 64 | -- end virtual resolution 65 | push:apply('end') 66 | end 67 | -------------------------------------------------------------------------------- /mario-10-exercise/Animation.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Holds a collection of frames that switch depending on how much time has 3 | passed. 4 | ]] 5 | 6 | Animation = {} 7 | Animation.__index = Animation 8 | 9 | function Animation:create(params) 10 | local this = { 11 | texture = params.texture, 12 | 13 | -- quads defining this animation 14 | frames = params.frames or {}, 15 | 16 | -- time in seconds each frame takes (1/20 by default) 17 | interval = params.interval or 0.05, 18 | 19 | -- stores amount of time that has elapsed 20 | timer = 0, 21 | 22 | currentFrame = 1 23 | } 24 | 25 | setmetatable(this, self) 26 | return this 27 | end 28 | 29 | function Animation:getCurrentFrame() 30 | return self.frames[self.currentFrame] 31 | end 32 | 33 | function Animation:restart() 34 | self.timer = 0 35 | self.currentFrame = 1 36 | end 37 | 38 | function Animation:update(dt) 39 | self.timer = self.timer + dt 40 | 41 | -- iteratively subtract interval from timer to proceed in the animation, 42 | -- in case we skipped more than one frame 43 | while self.timer > self.interval do 44 | self.timer = self.timer - self.interval 45 | self.currentFrame = (self.currentFrame + 1) % #self.frames 46 | if self.currentFrame == 0 then self.currentFrame = 1 end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /mario-10-exercise/Util.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Stores utility functions used by our game engine. 3 | ]] 4 | 5 | -- takes a texture, width, and height of tiles and splits it into quads 6 | -- that can be individually drawn 7 | function generateQuads(atlas, tilewidth, tileheight) 8 | local sheetWidth = atlas:getWidth() / tilewidth 9 | local sheetHeight = atlas:getHeight() / tileheight 10 | 11 | local sheetCounter = 1 12 | local quads = {} 13 | 14 | for y = 0, sheetHeight - 1 do 15 | for x = 0, sheetWidth - 1 do 16 | -- this quad represents a square cutout of our atlas that we can 17 | -- individually draw instead of the whole atlas 18 | quads[sheetCounter] = 19 | love.graphics.newQuad(x * tilewidth, y * tileheight, tilewidth, 20 | tileheight, atlas:getDimensions()) 21 | sheetCounter = sheetCounter + 1 22 | end 23 | end 24 | 25 | return quads 26 | end 27 | -------------------------------------------------------------------------------- /mario-10-exercise/fonts/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-10-exercise/fonts/font.ttf -------------------------------------------------------------------------------- /mario-10-exercise/graphics/mario.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-10-exercise/graphics/mario.png -------------------------------------------------------------------------------- /mario-10-exercise/graphics/mario1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-10-exercise/graphics/mario1.png -------------------------------------------------------------------------------- /mario-10-exercise/graphics/playable_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-10-exercise/graphics/playable_left.png -------------------------------------------------------------------------------- /mario-10-exercise/graphics/playable_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-10-exercise/graphics/playable_right.png -------------------------------------------------------------------------------- /mario-10-exercise/graphics/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-10-exercise/graphics/tiles.png -------------------------------------------------------------------------------- /mario-10-exercise/graphics/tiles_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-10-exercise/graphics/tiles_2.png -------------------------------------------------------------------------------- /mario-10-exercise/main.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Super Mario Bros. Demo 3 | Author: Colton Ogden 4 | Original Credit: Nintendo 5 | 6 | Adds collision for all collidable tile types. 7 | ]] 8 | 9 | -- global key-handling 10 | love.keyboard.keysPressed = {} 11 | love.keyboard.keysReleased = {} 12 | 13 | push = require 'push' 14 | 15 | require 'Map' 16 | require 'Player' 17 | 18 | -- close resolution to NES but 16:9 19 | virtualWidth = 432 20 | virtualHeight = 243 21 | 22 | -- actual window resolution 23 | windowWidth = 1280 24 | windowHeight = 720 25 | 26 | -- seed RNG 27 | math.randomseed(os.time()) 28 | 29 | -- an object to contain our map data 30 | map = Map:create() 31 | 32 | -- performs initialization of all objects and data needed by program 33 | function love.load() 34 | -- makes upscaling look pixel-y instead of blurry 35 | love.graphics.setDefaultFilter('nearest', 'nearest') 36 | 37 | -- sets up a different, better-looking retro font as our default 38 | love.graphics.setFont(love.graphics.newFont('fonts/font.ttf', 8)) 39 | 40 | -- sets up virtual screen resolution for an authentic retro feel 41 | push:setupScreen(virtualWidth, virtualHeight, windowWidth, windowHeight, { 42 | fullscreen = false, 43 | resizable = true 44 | }) 45 | end 46 | 47 | -- called whenever window is resized 48 | function love.resize(w, h) 49 | push:resize(w, h) 50 | end 51 | 52 | -- global key pressed function 53 | function love.keyboard.wasPressed(key) 54 | if (love.keyboard.keysPressed[key]) then 55 | return true 56 | else 57 | return false 58 | end 59 | end 60 | 61 | -- global key released function 62 | function love.keyboard.wasReleased(key) 63 | if (love.keyboard.keysReleased[key]) then 64 | return true 65 | else 66 | return false 67 | end 68 | end 69 | 70 | -- called whenever a key is pressed 71 | function love.keypressed(key) 72 | if key == 'escape' then 73 | love.event.quit() 74 | end 75 | 76 | love.keyboard.keysPressed[key] = true 77 | end 78 | 79 | -- called whenever a key is released 80 | function love.keyreleased(key) 81 | love.keyboard.keysReleased[key] = true 82 | end 83 | 84 | -- called every frame, with dt passed in as delta in time since last frame 85 | function love.update(dt) 86 | map:update(dt) 87 | 88 | -- reset all keys pressed and released this frame 89 | love.keyboard.keysPressed = {} 90 | love.keyboard.keysReleased = {} 91 | end 92 | 93 | -- called each frame, used to render to the screen 94 | function love.draw() 95 | -- begin virtual resolution drawing 96 | push:apply('start') 97 | 98 | -- clear screen using Mario background blue 99 | love.graphics.clear(108, 140, 255, 255) 100 | 101 | -- renders our map object onto the screen 102 | love.graphics.translate(math.floor(-map.camX), math.floor(-map.camY)) 103 | map:render() 104 | 105 | -- end virtual resolution 106 | push:apply('end') 107 | end 108 | -------------------------------------------------------------------------------- /mario-10/Animation.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Holds a collection of frames that switch depending on how much time has 3 | passed. 4 | ]] 5 | 6 | Animation = {} 7 | Animation.__index = Animation 8 | 9 | function Animation:create(params) 10 | local this = { 11 | texture = params.texture, 12 | 13 | -- quads defining this animation 14 | frames = params.frames or {}, 15 | 16 | -- time in seconds each frame takes (1/20 by default) 17 | interval = params.interval or 0.05, 18 | 19 | -- stores amount of time that has elapsed 20 | timer = 0, 21 | 22 | currentFrame = 1 23 | } 24 | 25 | setmetatable(this, self) 26 | return this 27 | end 28 | 29 | function Animation:getCurrentFrame() 30 | return self.frames[self.currentFrame] 31 | end 32 | 33 | function Animation:restart() 34 | self.timer = 0 35 | self.currentFrame = 1 36 | end 37 | 38 | function Animation:update(dt) 39 | self.timer = self.timer + dt 40 | 41 | -- iteratively subtract interval from timer to proceed in the animation, 42 | -- in case we skipped more than one frame 43 | while self.timer > self.interval do 44 | self.timer = self.timer - self.interval 45 | self.currentFrame = (self.currentFrame + 1) % #self.frames 46 | if self.currentFrame == 0 then self.currentFrame = 1 end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /mario-10/Util.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Stores utility functions used by our game engine. 3 | ]] 4 | 5 | -- takes a texture, width, and height of tiles and splits it into quads 6 | -- that can be individually drawn 7 | function generateQuads(atlas, tilewidth, tileheight) 8 | local sheetWidth = atlas:getWidth() / tilewidth 9 | local sheetHeight = atlas:getHeight() / tileheight 10 | 11 | local sheetCounter = 1 12 | local quads = {} 13 | 14 | for y = 0, sheetHeight - 1 do 15 | for x = 0, sheetWidth - 1 do 16 | -- this quad represents a square cutout of our atlas that we can 17 | -- individually draw instead of the whole atlas 18 | quads[sheetCounter] = 19 | love.graphics.newQuad(x * tilewidth, y * tileheight, tilewidth, 20 | tileheight, atlas:getDimensions()) 21 | sheetCounter = sheetCounter + 1 22 | end 23 | end 24 | 25 | return quads 26 | end 27 | -------------------------------------------------------------------------------- /mario-10/fonts/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-10/fonts/font.ttf -------------------------------------------------------------------------------- /mario-10/graphics/mario.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-10/graphics/mario.png -------------------------------------------------------------------------------- /mario-10/graphics/mario1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-10/graphics/mario1.png -------------------------------------------------------------------------------- /mario-10/graphics/playable_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-10/graphics/playable_left.png -------------------------------------------------------------------------------- /mario-10/graphics/playable_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-10/graphics/playable_right.png -------------------------------------------------------------------------------- /mario-10/graphics/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-10/graphics/tiles.png -------------------------------------------------------------------------------- /mario-10/graphics/tiles_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-10/graphics/tiles_2.png -------------------------------------------------------------------------------- /mario-10/main.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Super Mario Bros. Demo 3 | Author: Colton Ogden 4 | Original Credit: Nintendo 5 | 6 | Adds collision for all collidable tile types. 7 | ]] 8 | 9 | -- global key-handling 10 | love.keyboard.keysPressed = {} 11 | love.keyboard.keysReleased = {} 12 | 13 | push = require 'push' 14 | 15 | require 'Map' 16 | require 'Player' 17 | 18 | -- close resolution to NES but 16:9 19 | virtualWidth = 432 20 | virtualHeight = 243 21 | 22 | -- actual window resolution 23 | windowWidth = 1280 24 | windowHeight = 720 25 | 26 | -- seed RNG 27 | math.randomseed(os.time()) 28 | 29 | -- an object to contain our map data 30 | map = Map:create() 31 | 32 | -- performs initialization of all objects and data needed by program 33 | function love.load() 34 | -- makes upscaling look pixel-y instead of blurry 35 | love.graphics.setDefaultFilter('nearest', 'nearest') 36 | 37 | -- sets up a different, better-looking retro font as our default 38 | love.graphics.setFont(love.graphics.newFont('fonts/font.ttf', 8)) 39 | 40 | -- sets up virtual screen resolution for an authentic retro feel 41 | push:setupScreen(virtualWidth, virtualHeight, windowWidth, windowHeight, { 42 | fullscreen = false, 43 | resizable = true 44 | }) 45 | end 46 | 47 | -- called whenever window is resized 48 | function love.resize(w, h) 49 | push:resize(w, h) 50 | end 51 | 52 | -- global key pressed function 53 | function love.keyboard.wasPressed(key) 54 | if (love.keyboard.keysPressed[key]) then 55 | return true 56 | else 57 | return false 58 | end 59 | end 60 | 61 | -- global key released function 62 | function love.keyboard.wasReleased(key) 63 | if (love.keyboard.keysReleased[key]) then 64 | return true 65 | else 66 | return false 67 | end 68 | end 69 | 70 | -- called whenever a key is pressed 71 | function love.keypressed(key) 72 | if key == 'escape' then 73 | love.event.quit() 74 | end 75 | 76 | love.keyboard.keysPressed[key] = true 77 | end 78 | 79 | -- called whenever a key is released 80 | function love.keyreleased(key) 81 | love.keyboard.keysReleased[key] = true 82 | end 83 | 84 | -- called every frame, with dt passed in as delta in time since last frame 85 | function love.update(dt) 86 | map:update(dt) 87 | 88 | -- reset all keys pressed and released this frame 89 | love.keyboard.keysPressed = {} 90 | love.keyboard.keysReleased = {} 91 | end 92 | 93 | -- called each frame, used to render to the screen 94 | function love.draw() 95 | -- begin virtual resolution drawing 96 | push:apply('start') 97 | 98 | -- clear screen using Mario background blue 99 | love.graphics.clear(108, 140, 255, 255) 100 | 101 | -- renders our map object onto the screen 102 | love.graphics.translate(math.floor(-map.camX), math.floor(-map.camY)) 103 | map:render() 104 | 105 | -- end virtual resolution 106 | push:apply('end') 107 | end 108 | -------------------------------------------------------------------------------- /mario-11-exercise/Animation.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Holds a collection of frames that switch depending on how much time has 3 | passed. 4 | ]] 5 | 6 | Animation = {} 7 | Animation.__index = Animation 8 | 9 | function Animation:create(params) 10 | local this = { 11 | texture = params.texture, 12 | 13 | -- quads defining this animation 14 | frames = params.frames or {}, 15 | 16 | -- time in seconds each frame takes (1/20 by default) 17 | interval = params.interval or 0.05, 18 | 19 | -- stores amount of time that has elapsed 20 | timer = 0, 21 | 22 | currentFrame = 1 23 | } 24 | 25 | setmetatable(this, self) 26 | return this 27 | end 28 | 29 | function Animation:getCurrentFrame() 30 | return self.frames[self.currentFrame] 31 | end 32 | 33 | function Animation:restart() 34 | self.timer = 0 35 | self.currentFrame = 1 36 | end 37 | 38 | function Animation:update(dt) 39 | self.timer = self.timer + dt 40 | 41 | -- iteratively subtract interval from timer to proceed in the animation, 42 | -- in case we skipped more than one frame 43 | while self.timer > self.interval do 44 | self.timer = self.timer - self.interval 45 | self.currentFrame = (self.currentFrame + 1) % #self.frames 46 | if self.currentFrame == 0 then self.currentFrame = 1 end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /mario-11-exercise/Util.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Stores utility functions used by our game engine. 3 | ]] 4 | 5 | -- takes a texture, width, and height of tiles and splits it into quads 6 | -- that can be individually drawn 7 | function generateQuads(atlas, tilewidth, tileheight) 8 | local sheetWidth = atlas:getWidth() / tilewidth 9 | local sheetHeight = atlas:getHeight() / tileheight 10 | 11 | local sheetCounter = 1 12 | local quads = {} 13 | 14 | for y = 0, sheetHeight - 1 do 15 | for x = 0, sheetWidth - 1 do 16 | -- this quad represents a square cutout of our atlas that we can 17 | -- individually draw instead of the whole atlas 18 | quads[sheetCounter] = 19 | love.graphics.newQuad(x * tilewidth, y * tileheight, tilewidth, 20 | tileheight, atlas:getDimensions()) 21 | sheetCounter = sheetCounter + 1 22 | end 23 | end 24 | 25 | return quads 26 | end 27 | -------------------------------------------------------------------------------- /mario-11-exercise/fonts/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-11-exercise/fonts/font.ttf -------------------------------------------------------------------------------- /mario-11-exercise/graphics/mario.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-11-exercise/graphics/mario.png -------------------------------------------------------------------------------- /mario-11-exercise/graphics/mario1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-11-exercise/graphics/mario1.png -------------------------------------------------------------------------------- /mario-11-exercise/graphics/playable_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-11-exercise/graphics/playable_left.png -------------------------------------------------------------------------------- /mario-11-exercise/graphics/playable_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-11-exercise/graphics/playable_right.png -------------------------------------------------------------------------------- /mario-11-exercise/graphics/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-11-exercise/graphics/tiles.png -------------------------------------------------------------------------------- /mario-11-exercise/graphics/tiles_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-11-exercise/graphics/tiles_2.png -------------------------------------------------------------------------------- /mario-11-exercise/main.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Super Mario Bros. Demo 3 | Author: Colton Ogden 4 | Original Credit: Nintendo 5 | 6 | Adds sound effects and music. 7 | ]] 8 | 9 | -- global key-handling 10 | love.keyboard.keysPressed = {} 11 | love.keyboard.keysReleased = {} 12 | 13 | push = require 'push' 14 | 15 | require 'Map' 16 | require 'Player' 17 | 18 | -- close resolution to NES but 16:9 19 | virtualWidth = 432 20 | virtualHeight = 243 21 | 22 | -- actual window resolution 23 | windowWidth = 1280 24 | windowHeight = 720 25 | 26 | -- seed RNG 27 | math.randomseed(os.time()) 28 | 29 | -- an object to contain our map data 30 | map = Map:create() 31 | 32 | -- performs initialization of all objects and data needed by program 33 | function love.load() 34 | -- makes upscaling look pixel-y instead of blurry 35 | love.graphics.setDefaultFilter('nearest', 'nearest') 36 | 37 | -- sets up a different, better-looking retro font as our default 38 | love.graphics.setFont(love.graphics.newFont('fonts/font.ttf', 8)) 39 | 40 | -- sets up virtual screen resolution for an authentic retro feel 41 | push:setupScreen(virtualWidth, virtualHeight, windowWidth, windowHeight, { 42 | fullscreen = false, 43 | resizable = true 44 | }) 45 | end 46 | 47 | -- called whenever window is resized 48 | function love.resize(w, h) 49 | push:resize(w, h) 50 | end 51 | 52 | -- global key pressed function 53 | function love.keyboard.wasPressed(key) 54 | if (love.keyboard.keysPressed[key]) then 55 | return true 56 | else 57 | return false 58 | end 59 | end 60 | 61 | -- global key released function 62 | function love.keyboard.wasReleased(key) 63 | if (love.keyboard.keysReleased[key]) then 64 | return true 65 | else 66 | return false 67 | end 68 | end 69 | 70 | -- called whenever a key is pressed 71 | function love.keypressed(key) 72 | if key == 'escape' then 73 | love.event.quit() 74 | end 75 | 76 | love.keyboard.keysPressed[key] = true 77 | end 78 | 79 | -- called whenever a key is released 80 | function love.keyreleased(key) 81 | love.keyboard.keysReleased[key] = true 82 | end 83 | 84 | -- called every frame, with dt passed in as delta in time since last frame 85 | function love.update(dt) 86 | map:update(dt) 87 | 88 | -- reset all keys pressed and released this frame 89 | love.keyboard.keysPressed = {} 90 | love.keyboard.keysReleased = {} 91 | end 92 | 93 | -- called each frame, used to render to the screen 94 | function love.draw() 95 | -- begin virtual resolution drawing 96 | push:apply('start') 97 | 98 | -- clear screen using Mario background blue 99 | love.graphics.clear(108, 140, 255, 255) 100 | 101 | -- renders our map object onto the screen 102 | love.graphics.translate(math.floor(-map.camX), math.floor(-map.camY)) 103 | map:render() 104 | 105 | -- end virtual resolution 106 | push:apply('end') 107 | end 108 | -------------------------------------------------------------------------------- /mario-11-exercise/music/overworld.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-11-exercise/music/overworld.mp3 -------------------------------------------------------------------------------- /mario-11-exercise/sounds/coin.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-11-exercise/sounds/coin.wav -------------------------------------------------------------------------------- /mario-11-exercise/sounds/hit.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-11-exercise/sounds/hit.wav -------------------------------------------------------------------------------- /mario-11-exercise/sounds/jump.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-11-exercise/sounds/jump.wav -------------------------------------------------------------------------------- /mario-11/Animation.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Holds a collection of frames that switch depending on how much time has 3 | passed. 4 | ]] 5 | 6 | Animation = {} 7 | Animation.__index = Animation 8 | 9 | function Animation:create(params) 10 | local this = { 11 | texture = params.texture, 12 | 13 | -- quads defining this animation 14 | frames = params.frames or {}, 15 | 16 | -- time in seconds each frame takes (1/20 by default) 17 | interval = params.interval or 0.05, 18 | 19 | -- stores amount of time that has elapsed 20 | timer = 0, 21 | 22 | currentFrame = 1 23 | } 24 | 25 | setmetatable(this, self) 26 | return this 27 | end 28 | 29 | function Animation:getCurrentFrame() 30 | return self.frames[self.currentFrame] 31 | end 32 | 33 | function Animation:restart() 34 | self.timer = 0 35 | self.currentFrame = 1 36 | end 37 | 38 | function Animation:update(dt) 39 | self.timer = self.timer + dt 40 | 41 | -- iteratively subtract interval from timer to proceed in the animation, 42 | -- in case we skipped more than one frame 43 | while self.timer > self.interval do 44 | self.timer = self.timer - self.interval 45 | self.currentFrame = (self.currentFrame + 1) % #self.frames 46 | if self.currentFrame == 0 then self.currentFrame = 1 end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /mario-11/Util.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Stores utility functions used by our game engine. 3 | ]] 4 | 5 | -- takes a texture, width, and height of tiles and splits it into quads 6 | -- that can be individually drawn 7 | function generateQuads(atlas, tilewidth, tileheight) 8 | local sheetWidth = atlas:getWidth() / tilewidth 9 | local sheetHeight = atlas:getHeight() / tileheight 10 | 11 | local sheetCounter = 1 12 | local quads = {} 13 | 14 | for y = 0, sheetHeight - 1 do 15 | for x = 0, sheetWidth - 1 do 16 | -- this quad represents a square cutout of our atlas that we can 17 | -- individually draw instead of the whole atlas 18 | quads[sheetCounter] = 19 | love.graphics.newQuad(x * tilewidth, y * tileheight, tilewidth, 20 | tileheight, atlas:getDimensions()) 21 | sheetCounter = sheetCounter + 1 22 | end 23 | end 24 | 25 | return quads 26 | end 27 | -------------------------------------------------------------------------------- /mario-11/fonts/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-11/fonts/font.ttf -------------------------------------------------------------------------------- /mario-11/graphics/mario.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-11/graphics/mario.png -------------------------------------------------------------------------------- /mario-11/graphics/mario1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-11/graphics/mario1.png -------------------------------------------------------------------------------- /mario-11/graphics/playable_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-11/graphics/playable_left.png -------------------------------------------------------------------------------- /mario-11/graphics/playable_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-11/graphics/playable_right.png -------------------------------------------------------------------------------- /mario-11/graphics/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-11/graphics/tiles.png -------------------------------------------------------------------------------- /mario-11/graphics/tiles_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-11/graphics/tiles_2.png -------------------------------------------------------------------------------- /mario-11/main.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Super Mario Bros. Demo 3 | Author: Colton Ogden 4 | Original Credit: Nintendo 5 | 6 | Adds sound effects and music. 7 | ]] 8 | 9 | -- global key-handling 10 | love.keyboard.keysPressed = {} 11 | love.keyboard.keysReleased = {} 12 | 13 | push = require 'push' 14 | 15 | require 'Map' 16 | require 'Player' 17 | 18 | -- close resolution to NES but 16:9 19 | virtualWidth = 432 20 | virtualHeight = 243 21 | 22 | -- actual window resolution 23 | windowWidth = 1280 24 | windowHeight = 720 25 | 26 | -- seed RNG 27 | math.randomseed(os.time()) 28 | 29 | -- an object to contain our map data 30 | map = Map:create() 31 | 32 | -- performs initialization of all objects and data needed by program 33 | function love.load() 34 | -- makes upscaling look pixel-y instead of blurry 35 | love.graphics.setDefaultFilter('nearest', 'nearest') 36 | 37 | -- sets up a different, better-looking retro font as our default 38 | love.graphics.setFont(love.graphics.newFont('fonts/font.ttf', 8)) 39 | 40 | -- sets up virtual screen resolution for an authentic retro feel 41 | push:setupScreen(virtualWidth, virtualHeight, windowWidth, windowHeight, { 42 | fullscreen = false, 43 | resizable = true 44 | }) 45 | end 46 | 47 | -- called whenever window is resized 48 | function love.resize(w, h) 49 | push:resize(w, h) 50 | end 51 | 52 | -- global key pressed function 53 | function love.keyboard.wasPressed(key) 54 | if (love.keyboard.keysPressed[key]) then 55 | return true 56 | else 57 | return false 58 | end 59 | end 60 | 61 | -- global key released function 62 | function love.keyboard.wasReleased(key) 63 | if (love.keyboard.keysReleased[key]) then 64 | return true 65 | else 66 | return false 67 | end 68 | end 69 | 70 | -- called whenever a key is pressed 71 | function love.keypressed(key) 72 | if key == 'escape' then 73 | love.event.quit() 74 | end 75 | 76 | love.keyboard.keysPressed[key] = true 77 | end 78 | 79 | -- called whenever a key is released 80 | function love.keyreleased(key) 81 | love.keyboard.keysReleased[key] = true 82 | end 83 | 84 | -- called every frame, with dt passed in as delta in time since last frame 85 | function love.update(dt) 86 | map:update(dt) 87 | 88 | -- reset all keys pressed and released this frame 89 | love.keyboard.keysPressed = {} 90 | love.keyboard.keysReleased = {} 91 | end 92 | 93 | -- called each frame, used to render to the screen 94 | function love.draw() 95 | -- begin virtual resolution drawing 96 | push:apply('start') 97 | 98 | -- clear screen using Mario background blue 99 | love.graphics.clear(108, 140, 255, 255) 100 | 101 | -- renders our map object onto the screen 102 | love.graphics.translate(math.floor(-map.camX), math.floor(-map.camY)) 103 | map:render() 104 | 105 | -- end virtual resolution 106 | push:apply('end') 107 | end 108 | -------------------------------------------------------------------------------- /mario-11/music/overworld.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-11/music/overworld.mp3 -------------------------------------------------------------------------------- /mario-11/sounds/coin.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-11/sounds/coin.wav -------------------------------------------------------------------------------- /mario-11/sounds/hit.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-11/sounds/hit.wav -------------------------------------------------------------------------------- /mario-11/sounds/jump.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-11/sounds/jump.wav -------------------------------------------------------------------------------- /mario-2-exercise/Map.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Contains tile data and necessary code for rendering a tile map to the 3 | screen. 4 | ]] 5 | 6 | require 'Util' 7 | 8 | -- object-oriented boilerplate; establish Map's "prototype" 9 | Map = {} 10 | Map.__index = Map 11 | 12 | TILE_BRICK = 1 13 | TILE_EMPTY = 29 14 | 15 | -- constructor for our map object 16 | function Map:create() 17 | local this = { 18 | -- our texture containing all sprites 19 | spritesheet = love.graphics.newImage('graphics/tiles.png'), 20 | tileWidth = 16, 21 | tileHeight = 16, 22 | mapWidth = 30, 23 | mapHeight = 28, 24 | tiles = {}, 25 | 26 | -- camera offsets 27 | camX = 0, 28 | camY = -3 29 | } 30 | 31 | -- generate a quad (individual frame/sprite) for each tile 32 | this.tileSprites = generateQuads(this.spritesheet, 16, 16) 33 | 34 | -- cache width and height of map in pixels 35 | this.mapWidthPixels = this.mapWidth * this.tileWidth 36 | this.mapHeightPixels = this.mapHeight * this.tileHeight 37 | 38 | -- sprite batch for efficient tile rendering 39 | this.spriteBatch = love.graphics.newSpriteBatch(this.spritesheet, this.mapWidth * 40 | this.mapHeight) 41 | 42 | -- more OO boilerplate so we have access to class functions 43 | setmetatable(this, self) 44 | 45 | -- first, fill map with empty tiles 46 | for y = 1, this.mapHeight do 47 | for x = 1, this.mapWidth do 48 | this:setTile(x, y, TILE_EMPTY) 49 | end 50 | end 51 | 52 | -- -- fill bottom half of map with tiles 53 | for y = this.mapHeight / 2, this.mapHeight do 54 | for x = 1, this.mapWidth do 55 | this:setTile(x, y, TILE_BRICK) 56 | end 57 | end 58 | 59 | -- create sprite batch from tile quads 60 | for y = 1, this.mapHeight do 61 | for x = 1, this.mapWidth do 62 | -- TODO: add each tile in our map to the sprite batch 63 | -- HINT: it will look very similar to the render function from 64 | -- the last exercise! 65 | -- HINT: see SpriteBatch's add() function in the LÖVE wiki 66 | 67 | end 68 | end 69 | 70 | return this 71 | end 72 | 73 | -- function to update camera offset with delta time 74 | function Map:update(dt) 75 | -- TODO: scroll the map in the X direction (positively) based on dt 76 | -- HINT: should we be cumulatively increasing self.camX? 77 | 78 | end 79 | 80 | -- returns an integer value for the tile at a given x-y coordinate 81 | function Map:getTile(x, y) 82 | return self.tiles[(y - 1) * self.mapWidth + x] 83 | end 84 | 85 | -- sets a tile at a given x-y coordinate to an integer value 86 | function Map:setTile(x, y, tile) 87 | self.tiles[(y - 1) * self.mapWidth + x] = tile 88 | end 89 | 90 | -- renders our map to the screen, to be called by main's render 91 | function Map:render() 92 | -- replace tile-by-tile rendering with spriteBatch draw call 93 | -- TODO: draw our sprite batch instead of all the tiles from last exercise 94 | -- HINT: love.graphics.draw should work! 95 | 96 | end 97 | -------------------------------------------------------------------------------- /mario-2-exercise/Util.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Stores utility functions used by our game engine. 3 | ]] 4 | 5 | -- takes a texture, width, and height of tiles and splits it into quads 6 | -- that can be individually drawn 7 | function generateQuads(atlas, tilewidth, tileheight) 8 | local sheetWidth = atlas:getWidth() / tilewidth 9 | local sheetHeight = atlas:getHeight() / tileheight 10 | 11 | local sheetCounter = 1 12 | local quads = {} 13 | 14 | for y = 0, sheetHeight - 1 do 15 | for x = 0, sheetWidth - 1 do 16 | -- this quad represents a square cutout of our atlas that we can 17 | -- individually draw instead of the whole atlas 18 | quads[sheetCounter] = 19 | love.graphics.newQuad(x * tilewidth, y * tileheight, tilewidth, 20 | tileheight, atlas:getDimensions()) 21 | sheetCounter = sheetCounter + 1 22 | end 23 | end 24 | 25 | return quads 26 | end 27 | -------------------------------------------------------------------------------- /mario-2-exercise/fonts/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-2-exercise/fonts/font.ttf -------------------------------------------------------------------------------- /mario-2-exercise/graphics/playable_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-2-exercise/graphics/playable_left.png -------------------------------------------------------------------------------- /mario-2-exercise/graphics/playable_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-2-exercise/graphics/playable_right.png -------------------------------------------------------------------------------- /mario-2-exercise/graphics/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-2-exercise/graphics/tiles.png -------------------------------------------------------------------------------- /mario-2-exercise/graphics/tiles_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-2-exercise/graphics/tiles_2.png -------------------------------------------------------------------------------- /mario-2-exercise/main.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Super Mario Bros. Demo 3 | Author: Colton Ogden 4 | Original Credit: Nintendo 5 | 6 | Demonstrates constant scrolling of a screen of tiles and placing tiles 7 | in a sprite batch. 8 | ]] 9 | 10 | push = require 'push' 11 | 12 | require 'Map' 13 | 14 | -- close resolution to NES but 16:9 15 | virtualWidth = 432 16 | virtualHeight = 243 17 | 18 | -- actual window resolution 19 | windowWidth = 1280 20 | windowHeight = 720 21 | 22 | -- a speed to multiply delta time to scroll map; smooth value 23 | local scrollSpeed = 62 24 | 25 | -- an object to contain our map data 26 | map = Map:create() 27 | 28 | -- performs initialization of all objects and data needed by program 29 | function love.load() 30 | -- makes upscaling look pixel-y instead of blurry 31 | love.graphics.setDefaultFilter('nearest', 'nearest') 32 | 33 | -- sets up a different, better-looking retro font as our default 34 | love.graphics.setFont(love.graphics.newFont('fonts/font.ttf', 8)) 35 | 36 | -- sets up virtual screen resolution for an authentic retro feel 37 | push:setupScreen(virtualWidth, virtualHeight, windowWidth, windowHeight, { 38 | fullscreen = false, 39 | resizable = true 40 | }) 41 | end 42 | 43 | -- called whenever window is resized 44 | function love.resize(w, h) 45 | push:resize(w, h) 46 | end 47 | 48 | -- called whenever a key is pressed 49 | function love.keypressed(key) 50 | if key == 'escape' then 51 | love.event.quit() 52 | end 53 | end 54 | 55 | -- called every frame, with dt passed in as delta in time since last frame 56 | function love.update(dt) 57 | -- TODO: update our map 58 | -- HINT: check Map.lua? 59 | 60 | end 61 | 62 | -- called each frame, used to render to the screen 63 | function love.draw() 64 | -- begin virtual resolution drawing 65 | push:apply('start') 66 | 67 | -- clear screen using Mario background blue 68 | love.graphics.clear(108, 140, 255, 255) 69 | 70 | -- renders our map object onto the screen 71 | -- TODO: translate our coordinate system so we can simulate camera behavior 72 | -- HINT: love.graphics.translate? 73 | -- HINT: though our camera offsets in map should be positive, should they be 74 | -- positive when translating? 75 | -- HINT: notice anything graphically strange when offsetting by floating-point values? 76 | -- maybe see math.floor in Lua's standard library? 77 | 78 | map:render() 79 | 80 | -- end virtual resolution 81 | push:apply('end') 82 | end 83 | -------------------------------------------------------------------------------- /mario-2/Map.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Contains tile data and necessary code for rendering a tile map to the 3 | screen. 4 | ]] 5 | 6 | require 'Util' 7 | 8 | -- object-oriented boilerplate; establish Map's "prototype" 9 | Map = {} 10 | Map.__index = Map 11 | 12 | TILE_BRICK = 1 13 | TILE_EMPTY = 29 14 | 15 | -- a speed to multiply delta time to scroll map; smooth value 16 | local scrollSpeed = 62 17 | 18 | -- constructor for our map object 19 | function Map:create() 20 | local this = { 21 | -- our texture containing all sprites 22 | spritesheet = love.graphics.newImage('graphics/tiles.png'), 23 | tileWidth = 16, 24 | tileHeight = 16, 25 | mapWidth = 30, 26 | mapHeight = 28, 27 | tiles = {}, 28 | 29 | -- camera offsets 30 | camX = 0, 31 | camY = -3 32 | } 33 | 34 | -- generate a quad (individual frame/sprite) for each tile 35 | this.tileSprites = generateQuads(this.spritesheet, 16, 16) 36 | 37 | -- cache width and height of map in pixels 38 | this.mapWidthPixels = this.mapWidth * this.tileWidth 39 | this.mapHeightPixels = this.mapHeight * this.tileHeight 40 | 41 | -- sprite batch for efficient tile rendering 42 | this.spriteBatch = love.graphics.newSpriteBatch(this.spritesheet, this.mapWidth * 43 | this.mapHeight) 44 | 45 | -- more OO boilerplate so we have access to class functions 46 | setmetatable(this, self) 47 | 48 | -- first, fill map with empty tiles 49 | for y = 1, this.mapHeight do 50 | for x = 1, this.mapWidth do 51 | this:setTile(x, y, TILE_EMPTY) 52 | end 53 | end 54 | 55 | -- -- fill bottom half of map with tiles 56 | for y = this.mapHeight / 2, this.mapHeight do 57 | for x = 1, this.mapWidth do 58 | this:setTile(x, y, TILE_BRICK) 59 | end 60 | end 61 | 62 | -- create sprite batch from tile quads 63 | for y = 1, this.mapHeight do 64 | for x = 1, this.mapWidth do 65 | this.spriteBatch:add(this.tileSprites[this:getTile(x, y)], 66 | (x - 1) * this.tileWidth, (y - 1) * this.tileHeight) 67 | end 68 | end 69 | 70 | return this 71 | end 72 | 73 | -- function to update camera offset with delta time 74 | function Map:update(dt) 75 | self.camX = self.camX + dt * scrollSpeed 76 | end 77 | 78 | -- returns an integer value for the tile at a given x-y coordinate 79 | function Map:getTile(x, y) 80 | return self.tiles[(y - 1) * self.mapWidth + x] 81 | end 82 | 83 | -- sets a tile at a given x-y coordinate to an integer value 84 | function Map:setTile(x, y, tile) 85 | self.tiles[(y - 1) * self.mapWidth + x] = tile 86 | end 87 | 88 | -- renders our map to the screen, to be called by main's render 89 | function Map:render() 90 | -- replace tile-by-tile rendering with spriteBatch draw call 91 | love.graphics.draw(self.spriteBatch) 92 | end 93 | -------------------------------------------------------------------------------- /mario-2/Util.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Stores utility functions used by our game engine. 3 | ]] 4 | 5 | -- takes a texture, width, and height of tiles and splits it into quads 6 | -- that can be individually drawn 7 | function generateQuads(atlas, tilewidth, tileheight) 8 | local sheetWidth = atlas:getWidth() / tilewidth 9 | local sheetHeight = atlas:getHeight() / tileheight 10 | 11 | local sheetCounter = 1 12 | local quads = {} 13 | 14 | for y = 0, sheetHeight - 1 do 15 | for x = 0, sheetWidth - 1 do 16 | -- this quad represents a square cutout of our atlas that we can 17 | -- individually draw instead of the whole atlas 18 | quads[sheetCounter] = 19 | love.graphics.newQuad(x * tilewidth, y * tileheight, tilewidth, 20 | tileheight, atlas:getDimensions()) 21 | sheetCounter = sheetCounter + 1 22 | end 23 | end 24 | 25 | return quads 26 | end 27 | -------------------------------------------------------------------------------- /mario-2/fonts/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-2/fonts/font.ttf -------------------------------------------------------------------------------- /mario-2/graphics/playable_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-2/graphics/playable_left.png -------------------------------------------------------------------------------- /mario-2/graphics/playable_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-2/graphics/playable_right.png -------------------------------------------------------------------------------- /mario-2/graphics/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-2/graphics/tiles.png -------------------------------------------------------------------------------- /mario-2/graphics/tiles_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-2/graphics/tiles_2.png -------------------------------------------------------------------------------- /mario-2/main.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Super Mario Bros. Demo 3 | Author: Colton Ogden 4 | Original Credit: Nintendo 5 | 6 | Demonstrates constant scrolling of a screen of tiles and placing tiles 7 | in a sprite batch. 8 | ]] 9 | 10 | push = require 'push' 11 | 12 | require 'Map' 13 | 14 | -- close resolution to NES but 16:9 15 | virtualWidth = 432 16 | virtualHeight = 243 17 | 18 | -- actual window resolution 19 | windowWidth = 1280 20 | windowHeight = 720 21 | 22 | -- an object to contain our map data 23 | map = Map:create() 24 | 25 | -- performs initialization of all objects and data needed by program 26 | function love.load() 27 | -- makes upscaling look pixel-y instead of blurry 28 | love.graphics.setDefaultFilter('nearest', 'nearest') 29 | 30 | -- sets up a different, better-looking retro font as our default 31 | love.graphics.setFont(love.graphics.newFont('fonts/font.ttf', 8)) 32 | 33 | -- sets up virtual screen resolution for an authentic retro feel 34 | push:setupScreen(virtualWidth, virtualHeight, windowWidth, windowHeight, { 35 | fullscreen = false, 36 | resizable = true 37 | }) 38 | end 39 | 40 | -- called whenever window is resized 41 | function love.resize(w, h) 42 | push:resize(w, h) 43 | end 44 | 45 | -- called whenever a key is pressed 46 | function love.keypressed(key) 47 | if key == 'escape' then 48 | love.event.quit() 49 | end 50 | end 51 | 52 | -- called every frame, with dt passed in as delta in time since last frame 53 | function love.update(dt) 54 | map:update(dt) 55 | end 56 | 57 | -- called each frame, used to render to the screen 58 | function love.draw() 59 | -- begin virtual resolution drawing 60 | push:apply('start') 61 | 62 | -- clear screen using Mario background blue 63 | love.graphics.clear(108, 140, 255, 255) 64 | 65 | -- renders our map object onto the screen 66 | love.graphics.translate(math.floor(-map.camX + 0.5), math.floor(-map.camY + 0.5)) 67 | map:render() 68 | 69 | -- end virtual resolution 70 | push:apply('end') 71 | end 72 | -------------------------------------------------------------------------------- /mario-3-exercise/Map.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Contains tile data and necessary code for rendering a tile map to the 3 | screen. 4 | ]] 5 | 6 | require 'Util' 7 | 8 | -- object-oriented boilerplate; establish Map's "prototype" 9 | Map = {} 10 | Map.__index = Map 11 | 12 | TILE_BRICK = 1 13 | TILE_EMPTY = 29 14 | 15 | -- a speed to multiply delta time to scroll map; smooth value 16 | local scrollSpeed = 62 17 | 18 | -- constructor for our map object 19 | function Map:create() 20 | local this = { 21 | -- our texture containing all sprites 22 | spritesheet = love.graphics.newImage('graphics/tiles.png'), 23 | tileWidth = 16, 24 | tileHeight = 16, 25 | mapWidth = 100, 26 | mapHeight = 28, 27 | tiles = {}, 28 | 29 | -- camera offsets 30 | camX = 0, 31 | camY = -3 32 | } 33 | 34 | -- generate a quad (individual frame/sprite) for each tile 35 | this.tileSprites = generateQuads(this.spritesheet, 16, 16) 36 | 37 | -- cache width and height of map in pixels 38 | this.mapWidthPixels = this.mapWidth * this.tileWidth 39 | this.mapHeightPixels = this.mapHeight * this.tileHeight 40 | 41 | -- sprite batch for efficient tile rendering 42 | this.spriteBatch = love.graphics.newSpriteBatch(this.spritesheet, this.mapWidth * 43 | this.mapHeight) 44 | 45 | -- more OO boilerplate so we have access to class functions 46 | setmetatable(this, self) 47 | 48 | -- first, fill map with empty tiles 49 | for y = 1, this.mapHeight do 50 | for x = 1, this.mapWidth do 51 | this:setTile(x, y, TILE_EMPTY) 52 | end 53 | end 54 | 55 | -- -- fill bottom half of map with tiles 56 | for y = this.mapHeight / 2, this.mapHeight do 57 | for x = 1, this.mapWidth do 58 | this:setTile(x, y, TILE_BRICK) 59 | end 60 | end 61 | 62 | -- create sprite batch from tile quads 63 | for y = 1, this.mapHeight do 64 | for x = 1, this.mapWidth do 65 | this.spriteBatch:add(this.tileSprites[this:getTile(x, y)], 66 | (x - 1) * this.tileWidth, (y - 1) * this.tileHeight) 67 | end 68 | end 69 | 70 | return this 71 | end 72 | 73 | -- function to update camera offset with delta time 74 | function Map:update(dt) 75 | -- TODO: test for left and right keyboard presses and update camera accordingly 76 | -- HINT: love.keyboard.isDown? 77 | -- HINT: we should probably exercise bounds checking so we don't render beyond the 78 | -- map; maybe math.max and math.min can help us out here? 79 | 80 | -- TODO: test for up and down keyboard presses and update camera accordingly 81 | -- HINT: love.keyboard.isDown? 82 | -- HINT: we should probably exercise bounds checking so we don't render beyond the 83 | -- map; maybe math.max and math.min can help us out here? 84 | 85 | end 86 | 87 | -- returns an integer value for the tile at a given x-y coordinate 88 | function Map:getTile(x, y) 89 | return self.tiles[(y - 1) * self.mapWidth + x] 90 | end 91 | 92 | -- sets a tile at a given x-y coordinate to an integer value 93 | function Map:setTile(x, y, tile) 94 | self.tiles[(y - 1) * self.mapWidth + x] = tile 95 | end 96 | 97 | -- renders our map to the screen, to be called by main's render 98 | function Map:render() 99 | -- replace tile-by-tile rendering with spriteBatch draw call 100 | love.graphics.draw(self.spriteBatch) 101 | end 102 | -------------------------------------------------------------------------------- /mario-3-exercise/Util.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Stores utility functions used by our game engine. 3 | ]] 4 | 5 | -- takes a texture, width, and height of tiles and splits it into quads 6 | -- that can be individually drawn 7 | function generateQuads(atlas, tilewidth, tileheight) 8 | local sheetWidth = atlas:getWidth() / tilewidth 9 | local sheetHeight = atlas:getHeight() / tileheight 10 | 11 | local sheetCounter = 1 12 | local quads = {} 13 | 14 | for y = 0, sheetHeight - 1 do 15 | for x = 0, sheetWidth - 1 do 16 | -- this quad represents a square cutout of our atlas that we can 17 | -- individually draw instead of the whole atlas 18 | quads[sheetCounter] = 19 | love.graphics.newQuad(x * tilewidth, y * tileheight, tilewidth, 20 | tileheight, atlas:getDimensions()) 21 | sheetCounter = sheetCounter + 1 22 | end 23 | end 24 | 25 | return quads 26 | end 27 | -------------------------------------------------------------------------------- /mario-3-exercise/fonts/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-3-exercise/fonts/font.ttf -------------------------------------------------------------------------------- /mario-3-exercise/graphics/playable_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-3-exercise/graphics/playable_left.png -------------------------------------------------------------------------------- /mario-3-exercise/graphics/playable_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-3-exercise/graphics/playable_right.png -------------------------------------------------------------------------------- /mario-3-exercise/graphics/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-3-exercise/graphics/tiles.png -------------------------------------------------------------------------------- /mario-3-exercise/graphics/tiles_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-3-exercise/graphics/tiles_2.png -------------------------------------------------------------------------------- /mario-3-exercise/main.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Super Mario Bros. Demo 3 | Author: Colton Ogden 4 | Original Credit: Nintendo 5 | 6 | Demonstrates scrolling of the screen at will using key presses. 7 | ]] 8 | 9 | push = require 'push' 10 | 11 | require 'Map' 12 | 13 | -- close resolution to NES but 16:9 14 | virtualWidth = 432 15 | virtualHeight = 243 16 | 17 | -- actual window resolution 18 | windowWidth = 1280 19 | windowHeight = 720 20 | 21 | -- an object to contain our map data 22 | map = Map:create() 23 | 24 | -- performs initialization of all objects and data needed by program 25 | function love.load() 26 | -- makes upscaling look pixel-y instead of blurry 27 | love.graphics.setDefaultFilter('nearest', 'nearest') 28 | 29 | -- sets up a different, better-looking retro font as our default 30 | love.graphics.setFont(love.graphics.newFont('fonts/font.ttf', 8)) 31 | 32 | -- sets up virtual screen resolution for an authentic retro feel 33 | push:setupScreen(virtualWidth, virtualHeight, windowWidth, windowHeight, { 34 | fullscreen = true, 35 | resizable = true 36 | }) 37 | end 38 | 39 | -- called whenever window is resized 40 | function love.resize(w, h) 41 | push:resize(w, h) 42 | end 43 | 44 | -- called whenever a key is pressed 45 | function love.keypressed(key) 46 | if key == 'escape' then 47 | love.event.quit() 48 | end 49 | end 50 | 51 | -- called every frame, with dt passed in as delta in time since last frame 52 | function love.update(dt) 53 | map:update(dt) 54 | end 55 | 56 | -- called each frame, used to render to the screen 57 | function love.draw() 58 | -- begin virtual resolution drawing 59 | push:apply('start') 60 | 61 | -- clear screen using Mario background blue 62 | love.graphics.clear(108, 140, 255, 255) 63 | 64 | -- renders our map object onto the screen 65 | love.graphics.translate(math.floor(-map.camX), math.floor(-map.camY)) 66 | map:render() 67 | 68 | -- end virtual resolution 69 | push:apply('end') 70 | end 71 | -------------------------------------------------------------------------------- /mario-3/Map.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Contains tile data and necessary code for rendering a tile map to the 3 | screen. 4 | ]] 5 | 6 | require 'Util' 7 | 8 | -- object-oriented boilerplate; establish Map's "prototype" 9 | Map = {} 10 | Map.__index = Map 11 | 12 | TILE_BRICK = 1 13 | TILE_EMPTY = 29 14 | 15 | -- a speed to multiply delta time to scroll map; smooth value 16 | local scrollSpeed = 62 17 | 18 | -- constructor for our map object 19 | function Map:create() 20 | local this = { 21 | -- our texture containing all sprites 22 | spritesheet = love.graphics.newImage('graphics/tiles.png'), 23 | tileWidth = 16, 24 | tileHeight = 16, 25 | mapWidth = 100, 26 | mapHeight = 28, 27 | tiles = {}, 28 | 29 | -- camera offsets 30 | camX = 0, 31 | camY = -3 32 | } 33 | 34 | -- generate a quad (individual frame/sprite) for each tile 35 | this.tileSprites = generateQuads(this.spritesheet, 16, 16) 36 | 37 | -- cache width and height of map in pixels 38 | this.mapWidthPixels = this.mapWidth * this.tileWidth 39 | this.mapHeightPixels = this.mapHeight * this.tileHeight 40 | 41 | -- sprite batch for efficient tile rendering 42 | this.spriteBatch = love.graphics.newSpriteBatch(this.spritesheet, this.mapWidth * 43 | this.mapHeight) 44 | 45 | -- more OO boilerplate so we have access to class functions 46 | setmetatable(this, self) 47 | 48 | -- first, fill map with empty tiles 49 | for y = 1, this.mapHeight do 50 | for x = 1, this.mapWidth do 51 | this:setTile(x, y, TILE_EMPTY) 52 | end 53 | end 54 | 55 | -- -- fill bottom half of map with tiles 56 | for y = this.mapHeight / 2, this.mapHeight do 57 | for x = 1, this.mapWidth do 58 | this:setTile(x, y, TILE_BRICK) 59 | end 60 | end 61 | 62 | -- create sprite batch from tile quads 63 | for y = 1, this.mapHeight do 64 | for x = 1, this.mapWidth do 65 | this.spriteBatch:add(this.tileSprites[this:getTile(x, y)], 66 | (x - 1) * this.tileWidth, (y - 1) * this.tileHeight) 67 | end 68 | end 69 | 70 | return this 71 | end 72 | 73 | -- function to update camera offset with delta time 74 | function Map:update(dt) 75 | if love.keyboard.isDown('left') then 76 | self.camX = math.max(0, self.camX - dt * scrollSpeed) 77 | elseif love.keyboard.isDown('right') then 78 | self.camX = math.min(self.camX + dt * scrollSpeed, self.mapWidthPixels - virtualWidth) 79 | end 80 | 81 | if love.keyboard.isDown('up') then 82 | self.camY = math.max(0, self.camY - dt * scrollSpeed) 83 | elseif love.keyboard.isDown('down') then 84 | self.camY = math.min(self.camY + dt * scrollSpeed, self.mapHeightPixels - virtualHeight) 85 | end 86 | end 87 | 88 | -- returns an integer value for the tile at a given x-y coordinate 89 | function Map:getTile(x, y) 90 | return self.tiles[(y - 1) * self.mapWidth + x] 91 | end 92 | 93 | -- sets a tile at a given x-y coordinate to an integer value 94 | function Map:setTile(x, y, tile) 95 | self.tiles[(y - 1) * self.mapWidth + x] = tile 96 | end 97 | 98 | -- renders our map to the screen, to be called by main's render 99 | function Map:render() 100 | -- replace tile-by-tile rendering with spriteBatch draw call 101 | love.graphics.draw(self.spriteBatch) 102 | end 103 | -------------------------------------------------------------------------------- /mario-3/Util.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Stores utility functions used by our game engine. 3 | ]] 4 | 5 | -- takes a texture, width, and height of tiles and splits it into quads 6 | -- that can be individually drawn 7 | function generateQuads(atlas, tilewidth, tileheight) 8 | local sheetWidth = atlas:getWidth() / tilewidth 9 | local sheetHeight = atlas:getHeight() / tileheight 10 | 11 | local sheetCounter = 1 12 | local quads = {} 13 | 14 | for y = 0, sheetHeight - 1 do 15 | for x = 0, sheetWidth - 1 do 16 | -- this quad represents a square cutout of our atlas that we can 17 | -- individually draw instead of the whole atlas 18 | quads[sheetCounter] = 19 | love.graphics.newQuad(x * tilewidth, y * tileheight, tilewidth, 20 | tileheight, atlas:getDimensions()) 21 | sheetCounter = sheetCounter + 1 22 | end 23 | end 24 | 25 | return quads 26 | end 27 | -------------------------------------------------------------------------------- /mario-3/fonts/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-3/fonts/font.ttf -------------------------------------------------------------------------------- /mario-3/graphics/playable_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-3/graphics/playable_left.png -------------------------------------------------------------------------------- /mario-3/graphics/playable_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-3/graphics/playable_right.png -------------------------------------------------------------------------------- /mario-3/graphics/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-3/graphics/tiles.png -------------------------------------------------------------------------------- /mario-3/graphics/tiles_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-3/graphics/tiles_2.png -------------------------------------------------------------------------------- /mario-3/main.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Super Mario Bros. Demo 3 | Author: Colton Ogden 4 | Original Credit: Nintendo 5 | 6 | Demonstrates scrolling of the screen at will using key presses. 7 | ]] 8 | 9 | push = require 'push' 10 | 11 | require 'Map' 12 | 13 | -- close resolution to NES but 16:9 14 | virtualWidth = 432 15 | virtualHeight = 243 16 | 17 | -- actual window resolution 18 | windowWidth = 1280 19 | windowHeight = 720 20 | 21 | -- an object to contain our map data 22 | map = Map:create() 23 | 24 | -- performs initialization of all objects and data needed by program 25 | function love.load() 26 | -- makes upscaling look pixel-y instead of blurry 27 | love.graphics.setDefaultFilter('nearest', 'nearest') 28 | 29 | -- sets up a different, better-looking retro font as our default 30 | love.graphics.setFont(love.graphics.newFont('fonts/font.ttf', 8)) 31 | 32 | -- sets up virtual screen resolution for an authentic retro feel 33 | push:setupScreen(virtualWidth, virtualHeight, windowWidth, windowHeight, { 34 | fullscreen = true, 35 | resizable = true 36 | }) 37 | end 38 | 39 | -- called whenever window is resized 40 | function love.resize(w, h) 41 | push:resize(w, h) 42 | end 43 | 44 | -- called whenever a key is pressed 45 | function love.keypressed(key) 46 | if key == 'escape' then 47 | love.event.quit() 48 | end 49 | end 50 | 51 | -- called every frame, with dt passed in as delta in time since last frame 52 | function love.update(dt) 53 | map:update(dt) 54 | end 55 | 56 | -- called each frame, used to render to the screen 57 | function love.draw() 58 | -- begin virtual resolution drawing 59 | push:apply('start') 60 | 61 | -- clear screen using Mario background blue 62 | love.graphics.clear(108, 140, 255, 255) 63 | 64 | -- renders our map object onto the screen 65 | love.graphics.translate(math.floor(-map.camX + 0.5), math.floor(-map.camY + 0.5)) 66 | map:render() 67 | 68 | -- end virtual resolution 69 | push:apply('end') 70 | end 71 | -------------------------------------------------------------------------------- /mario-4-exercise/Util.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Stores utility functions used by our game engine. 3 | ]] 4 | 5 | -- takes a texture, width, and height of tiles and splits it into quads 6 | -- that can be individually drawn 7 | function generateQuads(atlas, tilewidth, tileheight) 8 | local sheetWidth = atlas:getWidth() / tilewidth 9 | local sheetHeight = atlas:getHeight() / tileheight 10 | 11 | local sheetCounter = 1 12 | local quads = {} 13 | 14 | for y = 0, sheetHeight - 1 do 15 | for x = 0, sheetWidth - 1 do 16 | -- this quad represents a square cutout of our atlas that we can 17 | -- individually draw instead of the whole atlas 18 | quads[sheetCounter] = 19 | love.graphics.newQuad(x * tilewidth, y * tileheight, tilewidth, 20 | tileheight, atlas:getDimensions()) 21 | sheetCounter = sheetCounter + 1 22 | end 23 | end 24 | 25 | return quads 26 | end 27 | -------------------------------------------------------------------------------- /mario-4-exercise/fonts/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-4-exercise/fonts/font.ttf -------------------------------------------------------------------------------- /mario-4-exercise/graphics/mario.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-4-exercise/graphics/mario.png -------------------------------------------------------------------------------- /mario-4-exercise/graphics/mario1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-4-exercise/graphics/mario1.png -------------------------------------------------------------------------------- /mario-4-exercise/graphics/playable_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-4-exercise/graphics/playable_left.png -------------------------------------------------------------------------------- /mario-4-exercise/graphics/playable_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-4-exercise/graphics/playable_right.png -------------------------------------------------------------------------------- /mario-4-exercise/graphics/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-4-exercise/graphics/tiles.png -------------------------------------------------------------------------------- /mario-4-exercise/graphics/tiles_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-4-exercise/graphics/tiles_2.png -------------------------------------------------------------------------------- /mario-4-exercise/main.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Super Mario Bros. Demo 3 | Author: Colton Ogden 4 | Original Credit: Nintendo 5 | 6 | Adds obstacles to the game map besides just tiles and sky. 7 | ]] 8 | 9 | push = require 'push' 10 | 11 | require 'Map' 12 | 13 | -- close resolution to NES but 16:9 14 | virtualWidth = 432 15 | virtualHeight = 243 16 | 17 | -- actual window resolution 18 | windowWidth = 1280 19 | windowHeight = 720 20 | 21 | -- an object to contain our map data 22 | map = Map:create() 23 | 24 | -- performs initialization of all objects and data needed by program 25 | function love.load() 26 | -- makes upscaling look pixel-y instead of blurry 27 | love.graphics.setDefaultFilter('nearest', 'nearest') 28 | 29 | -- sets up a different, better-looking retro font as our default 30 | love.graphics.setFont(love.graphics.newFont('fonts/font.ttf', 8)) 31 | 32 | -- sets up virtual screen resolution for an authentic retro feel 33 | push:setupScreen(virtualWidth, virtualHeight, windowWidth, windowHeight, { 34 | fullscreen = false, 35 | resizable = true 36 | }) 37 | end 38 | 39 | -- called whenever window is resized 40 | function love.resize(w, h) 41 | push:resize(w, h) 42 | end 43 | 44 | -- called whenever a key is pressed 45 | function love.keypressed(key) 46 | if key == 'escape' then 47 | love.event.quit() 48 | end 49 | end 50 | 51 | -- called every frame, with dt passed in as delta in time since last frame 52 | function love.update(dt) 53 | map:update(dt) 54 | end 55 | 56 | -- called each frame, used to render to the screen 57 | function love.draw() 58 | -- begin virtual resolution drawing 59 | push:apply('start') 60 | 61 | -- clear screen using Mario background blue 62 | love.graphics.clear(108, 140, 255, 255) 63 | 64 | -- renders our map object onto the screen 65 | love.graphics.translate(math.floor(-map.camX), math.floor(-map.camY)) 66 | map:render() 67 | 68 | -- end virtual resolution 69 | push:apply('end') 70 | end 71 | -------------------------------------------------------------------------------- /mario-4/Util.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Stores utility functions used by our game engine. 3 | ]] 4 | 5 | -- takes a texture, width, and height of tiles and splits it into quads 6 | -- that can be individually drawn 7 | function generateQuads(atlas, tilewidth, tileheight) 8 | local sheetWidth = atlas:getWidth() / tilewidth 9 | local sheetHeight = atlas:getHeight() / tileheight 10 | 11 | local sheetCounter = 1 12 | local quads = {} 13 | 14 | for y = 0, sheetHeight - 1 do 15 | for x = 0, sheetWidth - 1 do 16 | -- this quad represents a square cutout of our atlas that we can 17 | -- individually draw instead of the whole atlas 18 | quads[sheetCounter] = 19 | love.graphics.newQuad(x * tilewidth, y * tileheight, tilewidth, 20 | tileheight, atlas:getDimensions()) 21 | sheetCounter = sheetCounter + 1 22 | end 23 | end 24 | 25 | return quads 26 | end 27 | -------------------------------------------------------------------------------- /mario-4/fonts/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-4/fonts/font.ttf -------------------------------------------------------------------------------- /mario-4/graphics/mario.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-4/graphics/mario.png -------------------------------------------------------------------------------- /mario-4/graphics/mario1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-4/graphics/mario1.png -------------------------------------------------------------------------------- /mario-4/graphics/playable_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-4/graphics/playable_left.png -------------------------------------------------------------------------------- /mario-4/graphics/playable_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-4/graphics/playable_right.png -------------------------------------------------------------------------------- /mario-4/graphics/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-4/graphics/tiles.png -------------------------------------------------------------------------------- /mario-4/graphics/tiles_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-4/graphics/tiles_2.png -------------------------------------------------------------------------------- /mario-4/main.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Super Mario Bros. Demo 3 | Author: Colton Ogden 4 | Original Credit: Nintendo 5 | 6 | Adds obstacles to the game map besides just tiles and sky. 7 | ]] 8 | 9 | push = require 'push' 10 | 11 | require 'Map' 12 | 13 | -- close resolution to NES but 16:9 14 | virtualWidth = 432 15 | virtualHeight = 243 16 | 17 | -- actual window resolution 18 | windowWidth = 1280 19 | windowHeight = 720 20 | 21 | -- an object to contain our map data 22 | map = Map:create() 23 | 24 | -- performs initialization of all objects and data needed by program 25 | function love.load() 26 | -- makes upscaling look pixel-y instead of blurry 27 | love.graphics.setDefaultFilter('nearest', 'nearest') 28 | 29 | -- sets up a different, better-looking retro font as our default 30 | love.graphics.setFont(love.graphics.newFont('fonts/font.ttf', 8)) 31 | 32 | -- sets up virtual screen resolution for an authentic retro feel 33 | push:setupScreen(virtualWidth, virtualHeight, windowWidth, windowHeight, { 34 | fullscreen = false, 35 | resizable = true 36 | }) 37 | end 38 | 39 | -- called whenever window is resized 40 | function love.resize(w, h) 41 | push:resize(w, h) 42 | end 43 | 44 | -- called whenever a key is pressed 45 | function love.keypressed(key) 46 | if key == 'escape' then 47 | love.event.quit() 48 | end 49 | end 50 | 51 | -- called every frame, with dt passed in as delta in time since last frame 52 | function love.update(dt) 53 | map:update(dt) 54 | end 55 | 56 | -- called each frame, used to render to the screen 57 | function love.draw() 58 | -- begin virtual resolution drawing 59 | push:apply('start') 60 | 61 | -- clear screen using Mario background blue 62 | love.graphics.clear(108, 140, 255, 255) 63 | 64 | -- renders our map object onto the screen 65 | love.graphics.translate(math.floor(-map.camX), math.floor(-map.camY)) 66 | map:render() 67 | 68 | -- end virtual resolution 69 | push:apply('end') 70 | end 71 | -------------------------------------------------------------------------------- /mario-5-exercise/Player.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Represents our player in the game, with its own sprite. 3 | ]] 4 | 5 | Player = {} 6 | Player.__index = Player 7 | 8 | -- determines sprite flipping 9 | direction = 'right' 10 | 11 | function Player:create(map) 12 | local this = { 13 | x = 0, 14 | y = 0, 15 | width = 16, 16 | height = 32, 17 | 18 | -- offset from top left to center to support sprite flipping 19 | xOffset = 8, 20 | yOffset = 16, 21 | 22 | -- reference to map for checking tiles 23 | map = map, 24 | texture = love.graphics.newImage('graphics/mario1.png'), 25 | 26 | -- animation frames 27 | frames = {}, 28 | 29 | -- current animation frame 30 | currentFrame = nil, 31 | 32 | -- used to determine behavior and animations 33 | state = 'idle', 34 | 35 | -- x and y velocity 36 | dx = 0, 37 | dy = 0 38 | } 39 | 40 | -- position on top of map tiles 41 | this.y = map.tileHeight * ((map.mapHeight - 2) / 2) - this.height 42 | this.x = map.tileWidth * 10 43 | 44 | this.frames = { 45 | -- first frame in the sheet, idle pose 46 | -- TODO: construct Mario's first idle frame 47 | -- HINT: love.graphics.newQuad 48 | -- HINT: Mario's size is 16 pixels wide by 32 pixels tall 49 | 50 | } 51 | 52 | this.currentFrame = this.frames[1] 53 | 54 | -- behavior map we can call based on player state 55 | this.behaviors = { 56 | ['idle'] = function(dt) 57 | -- basic sprite flipping example 58 | -- TODO: set direction depending on which key we've pressed 59 | -- HINT: direction can be either 'left' or 'right' 60 | -- HINT: love.keyboard.wasPressed, which we've defined ourselves in 61 | -- main.lua 62 | 63 | end 64 | } 65 | 66 | setmetatable(this, self) 67 | return this 68 | end 69 | 70 | function Player:update(dt) 71 | -- TODO: call our behavior function depending on our current state 72 | -- HINT: self.behaviors should have all possible behaviors 73 | -- HINT: our state should act as a key 74 | -- HINT: the behavior itself is a function... so let's call it like one? 75 | 76 | end 77 | 78 | function Player:render() 79 | local scaleX 80 | 81 | -- set negative x scale factor if facing left, which will flip the sprite 82 | -- when applied 83 | -- TODO: set our horizontal scale depending on which direction he's facing 84 | -- HINT: a positive scale of 1 means he'll be facing to the right 85 | 86 | 87 | -- draw sprite with scale factor and offsets 88 | love.graphics.draw(self.texture, self.currentFrame, self.x + self.xOffset, 89 | self.y + self.yOffset, 0, scaleX, 1, self.xOffset, self.yOffset) 90 | end 91 | -------------------------------------------------------------------------------- /mario-5-exercise/Util.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Stores utility functions used by our game engine. 3 | ]] 4 | 5 | -- takes a texture, width, and height of tiles and splits it into quads 6 | -- that can be individually drawn 7 | function generateQuads(atlas, tilewidth, tileheight) 8 | local sheetWidth = atlas:getWidth() / tilewidth 9 | local sheetHeight = atlas:getHeight() / tileheight 10 | 11 | local sheetCounter = 1 12 | local quads = {} 13 | 14 | for y = 0, sheetHeight - 1 do 15 | for x = 0, sheetWidth - 1 do 16 | -- this quad represents a square cutout of our atlas that we can 17 | -- individually draw instead of the whole atlas 18 | quads[sheetCounter] = 19 | love.graphics.newQuad(x * tilewidth, y * tileheight, tilewidth, 20 | tileheight, atlas:getDimensions()) 21 | sheetCounter = sheetCounter + 1 22 | end 23 | end 24 | 25 | return quads 26 | end 27 | -------------------------------------------------------------------------------- /mario-5-exercise/fonts/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-5-exercise/fonts/font.ttf -------------------------------------------------------------------------------- /mario-5-exercise/graphics/mario.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-5-exercise/graphics/mario.png -------------------------------------------------------------------------------- /mario-5-exercise/graphics/mario1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-5-exercise/graphics/mario1.png -------------------------------------------------------------------------------- /mario-5-exercise/graphics/playable_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-5-exercise/graphics/playable_left.png -------------------------------------------------------------------------------- /mario-5-exercise/graphics/playable_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-5-exercise/graphics/playable_right.png -------------------------------------------------------------------------------- /mario-5-exercise/graphics/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-5-exercise/graphics/tiles.png -------------------------------------------------------------------------------- /mario-5-exercise/graphics/tiles_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-5-exercise/graphics/tiles_2.png -------------------------------------------------------------------------------- /mario-5-exercise/main.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Super Mario Bros. Demo 3 | Author: Colton Ogden 4 | Original Credit: Nintendo 5 | 6 | Adds Mario to the game. 7 | ]] 8 | 9 | -- global key-handling 10 | love.keyboard.keysPressed = {} 11 | love.keyboard.keysReleased = {} 12 | 13 | push = require 'push' 14 | 15 | require 'Map' 16 | require 'Player' 17 | 18 | -- close resolution to NES but 16:9 19 | virtualWidth = 432 20 | virtualHeight = 243 21 | 22 | -- actual window resolution 23 | windowWidth = 1280 24 | windowHeight = 720 25 | 26 | -- seed RNG 27 | math.randomseed(os.time()) 28 | 29 | -- an object to contain our map data 30 | map = Map:create() 31 | 32 | -- performs initialization of all objects and data needed by program 33 | function love.load() 34 | -- makes upscaling look pixel-y instead of blurry 35 | love.graphics.setDefaultFilter('nearest', 'nearest') 36 | 37 | -- sets up a different, better-looking retro font as our default 38 | love.graphics.setFont(love.graphics.newFont('fonts/font.ttf', 8)) 39 | 40 | -- sets up virtual screen resolution for an authentic retro feel 41 | push:setupScreen(virtualWidth, virtualHeight, windowWidth, windowHeight, { 42 | fullscreen = false, 43 | resizable = true 44 | }) 45 | end 46 | 47 | -- called whenever window is resized 48 | function love.resize(w, h) 49 | push:resize(w, h) 50 | end 51 | 52 | -- global key pressed function 53 | function love.keyboard.wasPressed(key) 54 | if (love.keyboard.keysPressed[key]) then 55 | return true 56 | else 57 | return false 58 | end 59 | end 60 | 61 | -- global key released function 62 | function love.keyboard.wasReleased(key) 63 | if (love.keyboard.keysReleased[key]) then 64 | return true 65 | else 66 | return false 67 | end 68 | end 69 | 70 | -- called whenever a key is pressed 71 | function love.keypressed(key) 72 | if key == 'escape' then 73 | love.event.quit() 74 | end 75 | 76 | love.keyboard.keysPressed[key] = true 77 | end 78 | 79 | -- called whenever a key is released 80 | function love.keyreleased(key) 81 | love.keyboard.keysReleased[key] = true 82 | end 83 | 84 | -- called every frame, with dt passed in as delta in time since last frame 85 | function love.update(dt) 86 | map:update(dt) 87 | 88 | -- reset all keys pressed and released this frame 89 | love.keyboard.keysPressed = {} 90 | love.keyboard.keysReleased = {} 91 | end 92 | 93 | -- called each frame, used to render to the screen 94 | function love.draw() 95 | -- begin virtual resolution drawing 96 | push:apply('start') 97 | 98 | -- clear screen using Mario background blue 99 | love.graphics.clear(108, 140, 255, 255) 100 | 101 | -- renders our map object onto the screen 102 | love.graphics.translate(math.floor(-map.camX), math.floor(-map.camY)) 103 | map:render() 104 | 105 | -- end virtual resolution 106 | push:apply('end') 107 | end 108 | -------------------------------------------------------------------------------- /mario-5/Map.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Contains tile data and necessary code for rendering a tile map to the 3 | screen. 4 | ]] 5 | 6 | require 'Util' 7 | 8 | -- object-oriented boilerplate; establish Map's "prototype" 9 | Map = {} 10 | Map.__index = Map 11 | 12 | TILE_BRICK = 1 13 | TILE_EMPTY = 29 14 | TILE_QUESTION = 25 15 | 16 | -- pipe tiles 17 | PIPE_TOP_LEFT = 265 18 | PIPE_TOP_RIGHT = 266 19 | PIPE_BOTTOM_LEFT = 298 20 | PIPE_BOTTOM_RIGHT = 299 21 | 22 | -- cloud tiles 23 | CLOUD_TOP_LEFT = 661 24 | CLOUD_TOP_MIDDLE = 662 25 | CLOUD_TOP_RIGHT = 663 26 | CLOUD_BOTTOM_LEFT = 694 27 | CLOUD_BOTTOM_MIDDLE = 695 28 | CLOUD_BOTTOM_RIGHT = 696 29 | 30 | -- bush tiles 31 | BUSH_LEFT = 309 32 | BUSH_MIDDLE = 310 33 | BUSH_RIGHT = 311 34 | 35 | -- a speed to multiply delta time to scroll map; smooth value 36 | local scrollSpeed = 124 37 | 38 | -- constructor for our map object 39 | function Map:create() 40 | local this = { 41 | -- our texture containing all sprites 42 | spritesheet = love.graphics.newImage('graphics/tiles.png'), 43 | tileWidth = 16, 44 | tileHeight = 16, 45 | mapWidth = 100, 46 | mapHeight = 28, 47 | tiles = {}, 48 | 49 | -- camera offsets 50 | camX = 0, 51 | camY = -3, 52 | } 53 | 54 | -- associate player with map 55 | this.player = Player:create(this) 56 | 57 | -- generate a quad (individual frame/sprite) for each tile 58 | this.tileSprites = generateQuads(this.spritesheet, 16, 16) 59 | 60 | -- cache width and height of map in pixels 61 | this.mapWidthPixels = this.mapWidth * this.tileWidth 62 | this.mapHeightPixels = this.mapHeight * this.tileHeight 63 | 64 | -- sprite batch for efficient tile rendering 65 | this.spriteBatch = love.graphics.newSpriteBatch(this.spritesheet, this.mapWidth * 66 | this.mapHeight) 67 | 68 | -- more OO boilerplate so we have access to class functions 69 | setmetatable(this, self) 70 | 71 | -- first, fill map with empty tiles 72 | for y = 1, this.mapHeight do 73 | for x = 1, this.mapWidth do 74 | this:setTile(x, y, TILE_EMPTY) 75 | end 76 | end 77 | 78 | -- begin generating the terrain using vertical scan lines 79 | local x = 1 80 | while x < this.mapWidth do 81 | -- 2% chance to generate a cloud 82 | -- make sure we're 3 tiles from edge at least 83 | if x < this.mapWidth - 3 then 84 | if math.random(20) == 1 then 85 | -- choose a random vertical spot above where blocks/pipes generate 86 | local cloudStart = math.random(this.mapHeight / 2 - 6) 87 | 88 | this:setTile(x, cloudStart, CLOUD_TOP_LEFT) 89 | this:setTile(x, cloudStart + 1, CLOUD_BOTTOM_LEFT) 90 | this:setTile(x + 1, cloudStart, CLOUD_TOP_MIDDLE) 91 | this:setTile(x + 1, cloudStart + 1, CLOUD_BOTTOM_MIDDLE) 92 | this:setTile(x + 2, cloudStart, CLOUD_TOP_RIGHT) 93 | this:setTile(x + 2, cloudStart + 1, CLOUD_BOTTOM_RIGHT) 94 | end 95 | end 96 | 97 | -- 5% chance to generate a pipe 98 | if math.random(20) == 1 then 99 | -- left side of pipe 100 | this:setTile(x, this.mapHeight / 2 - 2, PIPE_TOP_LEFT) 101 | this:setTile(x, this.mapHeight / 2 - 1, PIPE_BOTTOM_LEFT) 102 | 103 | -- creates column of tiles going to bottom of map 104 | for y = this.mapHeight / 2, this.mapHeight do 105 | this:setTile(x, y, TILE_BRICK) 106 | end 107 | 108 | -- next vertical scan line 109 | x = x + 1 110 | 111 | -- right side of pipe 112 | this:setTile(x, this.mapHeight / 2 - 2, PIPE_TOP_RIGHT) 113 | this:setTile(x, this.mapHeight / 2 - 1, PIPE_BOTTOM_RIGHT) 114 | 115 | -- creates column of tiles going to bottom of map 116 | for y = this.mapHeight / 2, this.mapHeight do 117 | this:setTile(x, y, TILE_BRICK) 118 | end 119 | 120 | -- next vertical scan line 121 | x = x + 1 122 | 123 | -- 10% chance to generate bush, being sure to generate away from edge 124 | elseif math.random(10) == 1 and x < this.mapWidth - 3 then 125 | local bushLevel = this.mapHeight / 2 - 1 126 | 127 | -- place bush component and then column of bricks 128 | this:setTile(x, bushLevel, BUSH_LEFT) 129 | for y = this.mapHeight / 2, this.mapHeight do 130 | this:setTile(x, y, TILE_BRICK) 131 | end 132 | x = x + 1 133 | 134 | this:setTile(x, bushLevel, BUSH_MIDDLE) 135 | for y = this.mapHeight / 2, this.mapHeight do 136 | this:setTile(x, y, TILE_BRICK) 137 | end 138 | x = x + 1 139 | 140 | this:setTile(x, bushLevel, BUSH_RIGHT) 141 | for y = this.mapHeight / 2, this.mapHeight do 142 | this:setTile(x, y, TILE_BRICK) 143 | end 144 | x = x + 1 145 | 146 | -- 10% chance to not generate anything, creating a gap 147 | elseif math.random(10) ~= 1 then 148 | -- creates column of tiles going to bottom of map 149 | for y = this.mapHeight / 2, this.mapHeight do 150 | this:setTile(x, y, TILE_BRICK) 151 | end 152 | 153 | -- chance to create a block for Mario to hit 154 | if math.random(15) == 1 then 155 | this:setTile(x, this.mapHeight / 2 - 4, TILE_QUESTION) 156 | end 157 | 158 | -- next vertical scan line 159 | x = x + 1 160 | else 161 | -- increment X so we skip two scanlines, creating a 2-tile gap 162 | x = x + 2 163 | end 164 | end 165 | 166 | -- create sprite batch from tile quads 167 | for y = 1, this.mapHeight do 168 | for x = 1, this.mapWidth do 169 | this.spriteBatch:add(this.tileSprites[this:getTile(x, y)], 170 | (x - 1) * this.tileWidth, (y - 1) * this.tileHeight) 171 | end 172 | end 173 | 174 | return this 175 | end 176 | 177 | -- function to update camera offset based on player coordinates 178 | function Map:update(dt) 179 | self.player:update(dt) 180 | 181 | -- keep camera's X coordinate following the player, preventing camera from 182 | -- scrolling past 0 to the left and the map's width 183 | self.camX = math.max(0, math.min(self.player.x - virtualWidth / 2, 184 | math.min(self.mapWidthPixels - virtualWidth, self.player.x))) 185 | end 186 | 187 | -- returns an integer value for the tile at a given x-y coordinate 188 | function Map:getTile(x, y) 189 | return self.tiles[(y - 1) * self.mapWidth + x] 190 | end 191 | 192 | -- sets a tile at a given x-y coordinate to an integer value 193 | function Map:setTile(x, y, tile) 194 | self.tiles[(y - 1) * self.mapWidth + x] = tile 195 | end 196 | 197 | -- renders our map to the screen, to be called by main's render 198 | function Map:render() 199 | -- replace tile-by-tile rendering with spriteBatch draw call 200 | love.graphics.draw(self.spriteBatch) 201 | self.player:render() 202 | end 203 | -------------------------------------------------------------------------------- /mario-5/Player.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Represents our player in the game, with its own sprite. 3 | ]] 4 | 5 | Player = {} 6 | Player.__index = Player 7 | 8 | function Player:create(map) 9 | local this = { 10 | x = 0, 11 | y = 0, 12 | width = 16, 13 | height = 32, 14 | 15 | -- offset from top left to center to support sprite flipping 16 | xOffset = 8, 17 | yOffset = 16, 18 | 19 | -- reference to map for checking tiles 20 | map = map, 21 | texture = love.graphics.newImage('graphics/mario1.png'), 22 | 23 | -- animation frames 24 | frames = {}, 25 | 26 | -- current animation frame 27 | currentFrame = nil, 28 | 29 | -- used to determine behavior and animations 30 | state = 'idle', 31 | 32 | -- determines sprite flipping 33 | direction = 'left', 34 | 35 | -- x and y velocity 36 | dx = 0, 37 | dy = 0 38 | } 39 | 40 | -- position on top of map tiles 41 | this.y = map.tileHeight * ((map.mapHeight - 2) / 2) - this.height 42 | this.x = map.tileWidth * 10 43 | 44 | this.frames = { 45 | -- first frame in the sheet, idle pose 46 | love.graphics.newQuad(0, 0, 16, 32, this.texture:getDimensions()) 47 | } 48 | 49 | this.currentFrame = this.frames[1] 50 | 51 | -- behavior map we can call based on player state 52 | this.behaviors = { 53 | ['idle'] = function(dt) 54 | -- basic sprite flipping example 55 | if love.keyboard.wasPressed('left') then 56 | direction = 'left' 57 | end 58 | if love.keyboard.wasPressed('right') then 59 | direction = 'right' 60 | end 61 | end 62 | } 63 | 64 | setmetatable(this, self) 65 | return this 66 | end 67 | 68 | function Player:update(dt) 69 | self.behaviors[self.state](dt) 70 | end 71 | 72 | function Player:render() 73 | local scaleX 74 | 75 | -- set negative x scale factor if facing left, which will flip the sprite 76 | -- when applied 77 | if direction == 'right' then 78 | scaleX = 1 79 | else 80 | scaleX = -1 81 | end 82 | 83 | -- draw sprite with scale factor and offsets 84 | love.graphics.draw(self.texture, self.currentFrame, self.x + self.xOffset, 85 | self.y + self.yOffset, 0, scaleX, 1, self.xOffset, self.yOffset) 86 | end 87 | -------------------------------------------------------------------------------- /mario-5/Util.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Stores utility functions used by our game engine. 3 | ]] 4 | 5 | -- takes a texture, width, and height of tiles and splits it into quads 6 | -- that can be individually drawn 7 | function generateQuads(atlas, tilewidth, tileheight) 8 | local sheetWidth = atlas:getWidth() / tilewidth 9 | local sheetHeight = atlas:getHeight() / tileheight 10 | 11 | local sheetCounter = 1 12 | local quads = {} 13 | 14 | for y = 0, sheetHeight - 1 do 15 | for x = 0, sheetWidth - 1 do 16 | -- this quad represents a square cutout of our atlas that we can 17 | -- individually draw instead of the whole atlas 18 | quads[sheetCounter] = 19 | love.graphics.newQuad(x * tilewidth, y * tileheight, tilewidth, 20 | tileheight, atlas:getDimensions()) 21 | sheetCounter = sheetCounter + 1 22 | end 23 | end 24 | 25 | return quads 26 | end 27 | -------------------------------------------------------------------------------- /mario-5/fonts/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-5/fonts/font.ttf -------------------------------------------------------------------------------- /mario-5/graphics/mario.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-5/graphics/mario.png -------------------------------------------------------------------------------- /mario-5/graphics/mario1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-5/graphics/mario1.png -------------------------------------------------------------------------------- /mario-5/graphics/playable_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-5/graphics/playable_left.png -------------------------------------------------------------------------------- /mario-5/graphics/playable_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-5/graphics/playable_right.png -------------------------------------------------------------------------------- /mario-5/graphics/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-5/graphics/tiles.png -------------------------------------------------------------------------------- /mario-5/graphics/tiles_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-5/graphics/tiles_2.png -------------------------------------------------------------------------------- /mario-5/main.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Super Mario Bros. Demo 3 | Author: Colton Ogden 4 | Original Credit: Nintendo 5 | 6 | Adds Mario to the game. 7 | ]] 8 | 9 | -- global key-handling 10 | love.keyboard.keysPressed = {} 11 | love.keyboard.keysReleased = {} 12 | 13 | push = require 'push' 14 | 15 | require 'Map' 16 | require 'Player' 17 | 18 | -- close resolution to NES but 16:9 19 | virtualWidth = 432 20 | virtualHeight = 243 21 | 22 | -- actual window resolution 23 | windowWidth = 1280 24 | windowHeight = 720 25 | 26 | -- seed RNG 27 | math.randomseed(os.time()) 28 | 29 | -- an object to contain our map data 30 | map = Map:create() 31 | 32 | -- performs initialization of all objects and data needed by program 33 | function love.load() 34 | -- makes upscaling look pixel-y instead of blurry 35 | love.graphics.setDefaultFilter('nearest', 'nearest') 36 | 37 | -- sets up a different, better-looking retro font as our default 38 | love.graphics.setFont(love.graphics.newFont('fonts/font.ttf', 8)) 39 | 40 | -- sets up virtual screen resolution for an authentic retro feel 41 | push:setupScreen(virtualWidth, virtualHeight, windowWidth, windowHeight, { 42 | fullscreen = false, 43 | resizable = true 44 | }) 45 | end 46 | 47 | -- called whenever window is resized 48 | function love.resize(w, h) 49 | push:resize(w, h) 50 | end 51 | 52 | -- global key pressed function 53 | function love.keyboard.wasPressed(key) 54 | if (love.keyboard.keysPressed[key]) then 55 | return true 56 | else 57 | return false 58 | end 59 | end 60 | 61 | -- global key released function 62 | function love.keyboard.wasReleased(key) 63 | if (love.keyboard.keysReleased[key]) then 64 | return true 65 | else 66 | return false 67 | end 68 | end 69 | 70 | -- called whenever a key is pressed 71 | function love.keypressed(key) 72 | if key == 'escape' then 73 | love.event.quit() 74 | end 75 | 76 | love.keyboard.keysPressed[key] = true 77 | end 78 | 79 | -- called whenever a key is released 80 | function love.keyreleased(key) 81 | love.keyboard.keysReleased[key] = true 82 | end 83 | 84 | -- called every frame, with dt passed in as delta in time since last frame 85 | function love.update(dt) 86 | map:update(dt) 87 | 88 | -- reset all keys pressed and released this frame 89 | love.keyboard.keysPressed = {} 90 | love.keyboard.keysReleased = {} 91 | end 92 | 93 | -- called each frame, used to render to the screen 94 | function love.draw() 95 | -- begin virtual resolution drawing 96 | push:apply('start') 97 | 98 | -- clear screen using Mario background blue 99 | love.graphics.clear(108, 140, 255, 255) 100 | 101 | -- renders our map object onto the screen 102 | love.graphics.translate(math.floor(-map.camX), math.floor(-map.camY)) 103 | map:render() 104 | 105 | -- end virtual resolution 106 | push:apply('end') 107 | end 108 | -------------------------------------------------------------------------------- /mario-6-exercise/Player.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Represents our player in the game, with its own sprite. 3 | ]] 4 | 5 | Player = {} 6 | Player.__index = Player 7 | 8 | function Player:create(map) 9 | local this = { 10 | x = 0, 11 | y = 0, 12 | width = 16, 13 | height = 32, 14 | 15 | -- offset from top left to center to support sprite flipping 16 | xOffset = 8, 17 | yOffset = 16, 18 | 19 | -- reference to map for checking tiles 20 | map = map, 21 | texture = love.graphics.newImage('graphics/mario1.png'), 22 | 23 | -- animation frames 24 | frames = {}, 25 | 26 | -- current animation frame 27 | currentFrame = nil, 28 | 29 | -- used to determine behavior and animations 30 | state = 'idle', 31 | 32 | -- determines sprite flipping 33 | direction = 'right', 34 | 35 | -- x and y velocity 36 | dx = 0, 37 | dy = 0 38 | } 39 | 40 | -- position on top of map tiles 41 | this.y = map.tileHeight * ((map.mapHeight - 2) / 2) - this.height 42 | this.x = map.tileWidth * 10 43 | 44 | this.frames = { 45 | -- first frame in the sheet, idle pose 46 | love.graphics.newQuad(0, 0, 16, 32, this.texture:getDimensions()) 47 | } 48 | 49 | this.currentFrame = this.frames[1] 50 | 51 | -- behavior map we can call based on player state 52 | this.behaviors = { 53 | ['idle'] = function(dt) 54 | -- basic sprite flipping example 55 | if love.keyboard.isDown('left') then 56 | direction = 'left' 57 | -- TODO: set a velocity for moving left 58 | 59 | elseif love.keyboard.isDown('right') then 60 | direction = 'right' 61 | -- TODO: set a velocity for moving right 62 | 63 | else 64 | -- TODO: set a velocity for standing still 65 | 66 | end 67 | end 68 | } 69 | 70 | setmetatable(this, self) 71 | return this 72 | end 73 | 74 | function Player:update(dt) 75 | self.behaviors[self.state](dt) 76 | 77 | -- TODO: move the player based on their horizontal velocity 78 | -- HINT: don't forget to take into consideration delta time! 79 | 80 | end 81 | 82 | function Player:render() 83 | local scaleX 84 | 85 | -- set negative x scale factor if facing left, which will flip the sprite 86 | -- when applied 87 | if direction == 'right' then 88 | scaleX = 1 89 | else 90 | scaleX = -1 91 | end 92 | 93 | -- draw sprite with scale factor and offsets 94 | love.graphics.draw(self.texture, self.currentFrame, math.floor(self.x + self.xOffset), 95 | math.floor(self.y + self.yOffset), 0, scaleX, 1, self.xOffset, self.yOffset) 96 | end 97 | -------------------------------------------------------------------------------- /mario-6-exercise/Util.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Stores utility functions used by our game engine. 3 | ]] 4 | 5 | -- takes a texture, width, and height of tiles and splits it into quads 6 | -- that can be individually drawn 7 | function generateQuads(atlas, tilewidth, tileheight) 8 | local sheetWidth = atlas:getWidth() / tilewidth 9 | local sheetHeight = atlas:getHeight() / tileheight 10 | 11 | local sheetCounter = 1 12 | local quads = {} 13 | 14 | for y = 0, sheetHeight - 1 do 15 | for x = 0, sheetWidth - 1 do 16 | -- this quad represents a square cutout of our atlas that we can 17 | -- individually draw instead of the whole atlas 18 | quads[sheetCounter] = 19 | love.graphics.newQuad(x * tilewidth, y * tileheight, tilewidth, 20 | tileheight, atlas:getDimensions()) 21 | sheetCounter = sheetCounter + 1 22 | end 23 | end 24 | 25 | return quads 26 | end 27 | -------------------------------------------------------------------------------- /mario-6-exercise/fonts/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-6-exercise/fonts/font.ttf -------------------------------------------------------------------------------- /mario-6-exercise/graphics/mario.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-6-exercise/graphics/mario.png -------------------------------------------------------------------------------- /mario-6-exercise/graphics/mario1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-6-exercise/graphics/mario1.png -------------------------------------------------------------------------------- /mario-6-exercise/graphics/playable_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-6-exercise/graphics/playable_left.png -------------------------------------------------------------------------------- /mario-6-exercise/graphics/playable_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-6-exercise/graphics/playable_right.png -------------------------------------------------------------------------------- /mario-6-exercise/graphics/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-6-exercise/graphics/tiles.png -------------------------------------------------------------------------------- /mario-6-exercise/graphics/tiles_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-6-exercise/graphics/tiles_2.png -------------------------------------------------------------------------------- /mario-6-exercise/main.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Super Mario Bros. Demo 3 | Author: Colton Ogden 4 | Original Credit: Nintendo 5 | 6 | Adds movement to Mario. 7 | ]] 8 | 9 | -- global key-handling 10 | love.keyboard.keysPressed = {} 11 | love.keyboard.keysReleased = {} 12 | 13 | push = require 'push' 14 | 15 | require 'Map' 16 | require 'Player' 17 | 18 | -- close resolution to NES but 16:9 19 | virtualWidth = 432 20 | virtualHeight = 243 21 | 22 | -- actual window resolution 23 | windowWidth = 1280 24 | windowHeight = 720 25 | 26 | -- seed RNG 27 | math.randomseed(os.time()) 28 | 29 | -- an object to contain our map data 30 | map = Map:create() 31 | 32 | -- performs initialization of all objects and data needed by program 33 | function love.load() 34 | -- makes upscaling look pixel-y instead of blurry 35 | love.graphics.setDefaultFilter('nearest', 'nearest') 36 | 37 | -- sets up a different, better-looking retro font as our default 38 | love.graphics.setFont(love.graphics.newFont('fonts/font.ttf', 8)) 39 | 40 | -- sets up virtual screen resolution for an authentic retro feel 41 | push:setupScreen(virtualWidth, virtualHeight, windowWidth, windowHeight, { 42 | fullscreen = false, 43 | resizable = true 44 | }) 45 | end 46 | 47 | -- called whenever window is resized 48 | function love.resize(w, h) 49 | push:resize(w, h) 50 | end 51 | 52 | -- global key pressed function 53 | function love.keyboard.wasPressed(key) 54 | if (love.keyboard.keysPressed[key]) then 55 | return true 56 | else 57 | return false 58 | end 59 | end 60 | 61 | -- global key released function 62 | function love.keyboard.wasReleased(key) 63 | if (love.keyboard.keysReleased[key]) then 64 | return true 65 | else 66 | return false 67 | end 68 | end 69 | 70 | -- called whenever a key is pressed 71 | function love.keypressed(key) 72 | if key == 'escape' then 73 | love.event.quit() 74 | end 75 | 76 | love.keyboard.keysPressed[key] = true 77 | end 78 | 79 | -- called whenever a key is released 80 | function love.keyreleased(key) 81 | love.keyboard.keysReleased[key] = true 82 | end 83 | 84 | -- called every frame, with dt passed in as delta in time since last frame 85 | function love.update(dt) 86 | map:update(dt) 87 | 88 | -- reset all keys pressed and released this frame 89 | love.keyboard.keysPressed = {} 90 | love.keyboard.keysReleased = {} 91 | end 92 | 93 | -- called each frame, used to render to the screen 94 | function love.draw() 95 | -- begin virtual resolution drawing 96 | push:apply('start') 97 | 98 | -- clear screen using Mario background blue 99 | love.graphics.clear(108, 140, 255, 255) 100 | 101 | -- renders our map object onto the screen 102 | love.graphics.translate(math.floor(-map.camX), math.floor(-map.camY)) 103 | map:render() 104 | 105 | -- end virtual resolution 106 | push:apply('end') 107 | end 108 | -------------------------------------------------------------------------------- /mario-6/Map.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Contains tile data and necessary code for rendering a tile map to the 3 | screen. 4 | ]] 5 | 6 | require 'Util' 7 | 8 | -- object-oriented boilerplate; establish Map's "prototype" 9 | Map = {} 10 | Map.__index = Map 11 | 12 | TILE_BRICK = 1 13 | TILE_EMPTY = 29 14 | TILE_QUESTION = 25 15 | 16 | -- pipe tiles 17 | PIPE_TOP_LEFT = 265 18 | PIPE_TOP_RIGHT = 266 19 | PIPE_BOTTOM_LEFT = 298 20 | PIPE_BOTTOM_RIGHT = 299 21 | 22 | -- cloud tiles 23 | CLOUD_TOP_LEFT = 661 24 | CLOUD_TOP_MIDDLE = 662 25 | CLOUD_TOP_RIGHT = 663 26 | CLOUD_BOTTOM_LEFT = 694 27 | CLOUD_BOTTOM_MIDDLE = 695 28 | CLOUD_BOTTOM_RIGHT = 696 29 | 30 | -- bush tiles 31 | BUSH_LEFT = 309 32 | BUSH_MIDDLE = 310 33 | BUSH_RIGHT = 311 34 | 35 | -- a speed to multiply delta time to scroll map; smooth value 36 | local scrollSpeed = 124 37 | 38 | -- constructor for our map object 39 | function Map:create() 40 | local this = { 41 | -- our texture containing all sprites 42 | spritesheet = love.graphics.newImage('graphics/tiles.png'), 43 | tileWidth = 16, 44 | tileHeight = 16, 45 | mapWidth = 100, 46 | mapHeight = 28, 47 | tiles = {}, 48 | 49 | -- camera offsets 50 | camX = 0, 51 | camY = -3, 52 | } 53 | 54 | -- associate player with map 55 | this.player = Player:create(this) 56 | 57 | -- generate a quad (individual frame/sprite) for each tile 58 | this.tileSprites = generateQuads(this.spritesheet, 16, 16) 59 | 60 | -- cache width and height of map in pixels 61 | this.mapWidthPixels = this.mapWidth * this.tileWidth 62 | this.mapHeightPixels = this.mapHeight * this.tileHeight 63 | 64 | -- sprite batch for efficient tile rendering 65 | this.spriteBatch = love.graphics.newSpriteBatch(this.spritesheet, this.mapWidth * 66 | this.mapHeight) 67 | 68 | -- more OO boilerplate so we have access to class functions 69 | setmetatable(this, self) 70 | 71 | -- first, fill map with empty tiles 72 | for y = 1, this.mapHeight do 73 | for x = 1, this.mapWidth do 74 | this:setTile(x, y, TILE_EMPTY) 75 | end 76 | end 77 | 78 | -- begin generating the terrain using vertical scan lines 79 | local x = 1 80 | while x < this.mapWidth do 81 | -- 2% chance to generate a cloud 82 | -- make sure we're 3 tiles from edge at least 83 | if x < this.mapWidth - 3 then 84 | if math.random(20) == 1 then 85 | -- choose a random vertical spot above where blocks/pipes generate 86 | local cloudStart = math.random(this.mapHeight / 2 - 6) 87 | 88 | this:setTile(x, cloudStart, CLOUD_TOP_LEFT) 89 | this:setTile(x, cloudStart + 1, CLOUD_BOTTOM_LEFT) 90 | this:setTile(x + 1, cloudStart, CLOUD_TOP_MIDDLE) 91 | this:setTile(x + 1, cloudStart + 1, CLOUD_BOTTOM_MIDDLE) 92 | this:setTile(x + 2, cloudStart, CLOUD_TOP_RIGHT) 93 | this:setTile(x + 2, cloudStart + 1, CLOUD_BOTTOM_RIGHT) 94 | end 95 | end 96 | 97 | -- 5% chance to generate a pipe 98 | if math.random(20) == 1 then 99 | -- left side of pipe 100 | this:setTile(x, this.mapHeight / 2 - 2, PIPE_TOP_LEFT) 101 | this:setTile(x, this.mapHeight / 2 - 1, PIPE_BOTTOM_LEFT) 102 | 103 | -- creates column of tiles going to bottom of map 104 | for y = this.mapHeight / 2, this.mapHeight do 105 | this:setTile(x, y, TILE_BRICK) 106 | end 107 | 108 | -- next vertical scan line 109 | x = x + 1 110 | 111 | -- right side of pipe 112 | this:setTile(x, this.mapHeight / 2 - 2, PIPE_TOP_RIGHT) 113 | this:setTile(x, this.mapHeight / 2 - 1, PIPE_BOTTOM_RIGHT) 114 | 115 | -- creates column of tiles going to bottom of map 116 | for y = this.mapHeight / 2, this.mapHeight do 117 | this:setTile(x, y, TILE_BRICK) 118 | end 119 | 120 | -- next vertical scan line 121 | x = x + 1 122 | 123 | -- 10% chance to generate bush, being sure to generate away from edge 124 | elseif math.random(10) == 1 and x < this.mapWidth - 3 then 125 | local bushLevel = this.mapHeight / 2 - 1 126 | 127 | -- place bush component and then column of bricks 128 | this:setTile(x, bushLevel, BUSH_LEFT) 129 | for y = this.mapHeight / 2, this.mapHeight do 130 | this:setTile(x, y, TILE_BRICK) 131 | end 132 | x = x + 1 133 | 134 | this:setTile(x, bushLevel, BUSH_MIDDLE) 135 | for y = this.mapHeight / 2, this.mapHeight do 136 | this:setTile(x, y, TILE_BRICK) 137 | end 138 | x = x + 1 139 | 140 | this:setTile(x, bushLevel, BUSH_RIGHT) 141 | for y = this.mapHeight / 2, this.mapHeight do 142 | this:setTile(x, y, TILE_BRICK) 143 | end 144 | x = x + 1 145 | 146 | -- 10% chance to not generate anything, creating a gap 147 | elseif math.random(10) ~= 1 then 148 | -- creates column of tiles going to bottom of map 149 | for y = this.mapHeight / 2, this.mapHeight do 150 | this:setTile(x, y, TILE_BRICK) 151 | end 152 | 153 | -- chance to create a block for Mario to hit 154 | if math.random(15) == 1 then 155 | this:setTile(x, this.mapHeight / 2 - 4, TILE_QUESTION) 156 | end 157 | 158 | -- next vertical scan line 159 | x = x + 1 160 | else 161 | -- increment X so we skip two scanlines, creating a 2-tile gap 162 | x = x + 2 163 | end 164 | end 165 | 166 | -- create sprite batch from tile quads 167 | for y = 1, this.mapHeight do 168 | for x = 1, this.mapWidth do 169 | this.spriteBatch:add(this.tileSprites[this:getTile(x, y)], 170 | (x - 1) * this.tileWidth, (y - 1) * this.tileHeight) 171 | end 172 | end 173 | 174 | return this 175 | end 176 | 177 | -- function to update camera offset based on player coordinates 178 | function Map:update(dt) 179 | self.player:update(dt) 180 | 181 | -- keep camera's X coordinate following the player, preventing camera from 182 | -- scrolling past 0 to the left and the map's width 183 | self.camX = math.max(0, math.min(self.player.x - virtualWidth / 2, 184 | math.min(self.mapWidthPixels - virtualWidth, self.player.x))) 185 | end 186 | 187 | -- returns an integer value for the tile at a given x-y coordinate 188 | function Map:getTile(x, y) 189 | return self.tiles[(y - 1) * self.mapWidth + x] 190 | end 191 | 192 | -- sets a tile at a given x-y coordinate to an integer value 193 | function Map:setTile(x, y, tile) 194 | self.tiles[(y - 1) * self.mapWidth + x] = tile 195 | end 196 | 197 | -- renders our map to the screen, to be called by main's render 198 | function Map:render() 199 | -- replace tile-by-tile rendering with spriteBatch draw call 200 | love.graphics.draw(self.spriteBatch) 201 | self.player:render() 202 | end 203 | -------------------------------------------------------------------------------- /mario-6/Player.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Represents our player in the game, with its own sprite. 3 | ]] 4 | 5 | Player = {} 6 | Player.__index = Player 7 | 8 | function Player:create(map) 9 | local this = { 10 | x = 0, 11 | y = 0, 12 | width = 16, 13 | height = 32, 14 | 15 | -- offset from top left to center to support sprite flipping 16 | xOffset = 8, 17 | yOffset = 16, 18 | 19 | -- reference to map for checking tiles 20 | map = map, 21 | texture = love.graphics.newImage('graphics/mario1.png'), 22 | 23 | -- animation frames 24 | frames = {}, 25 | 26 | -- current animation frame 27 | currentFrame = nil, 28 | 29 | -- used to determine behavior and animations 30 | state = 'idle', 31 | 32 | -- determines sprite flipping 33 | direction = 'right', 34 | 35 | -- x and y velocity 36 | dx = 0, 37 | dy = 0 38 | } 39 | 40 | -- position on top of map tiles 41 | this.y = map.tileHeight * ((map.mapHeight - 2) / 2) - this.height 42 | this.x = map.tileWidth * 10 43 | 44 | this.frames = { 45 | -- first frame in the sheet, idle pose 46 | love.graphics.newQuad(0, 0, 16, 32, this.texture:getDimensions()) 47 | } 48 | 49 | this.currentFrame = this.frames[1] 50 | 51 | -- behavior map we can call based on player state 52 | this.behaviors = { 53 | ['idle'] = function(dt) 54 | -- basic sprite flipping example 55 | if love.keyboard.isDown('left') then 56 | direction = 'left' 57 | this.dx = -80 58 | elseif love.keyboard.isDown('right') then 59 | direction = 'right' 60 | this.dx = 80 61 | else 62 | this.dx = 0 63 | end 64 | end 65 | } 66 | 67 | setmetatable(this, self) 68 | return this 69 | end 70 | 71 | function Player:update(dt) 72 | self.behaviors[self.state](dt) 73 | 74 | -- new X calculation on velocity 75 | self.x = self.x + self.dx * dt 76 | end 77 | 78 | function Player:render() 79 | local scaleX 80 | 81 | -- set negative x scale factor if facing left, which will flip the sprite 82 | -- when applied 83 | if direction == 'right' then 84 | scaleX = 1 85 | else 86 | scaleX = -1 87 | end 88 | 89 | -- draw sprite with scale factor and offsets 90 | love.graphics.draw(self.texture, self.currentFrame, math.floor(self.x + self.xOffset), 91 | math.floor(self.y + self.yOffset), 0, scaleX, 1, self.xOffset, self.yOffset) 92 | end 93 | -------------------------------------------------------------------------------- /mario-6/Util.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Stores utility functions used by our game engine. 3 | ]] 4 | 5 | -- takes a texture, width, and height of tiles and splits it into quads 6 | -- that can be individually drawn 7 | function generateQuads(atlas, tilewidth, tileheight) 8 | local sheetWidth = atlas:getWidth() / tilewidth 9 | local sheetHeight = atlas:getHeight() / tileheight 10 | 11 | local sheetCounter = 1 12 | local quads = {} 13 | 14 | for y = 0, sheetHeight - 1 do 15 | for x = 0, sheetWidth - 1 do 16 | -- this quad represents a square cutout of our atlas that we can 17 | -- individually draw instead of the whole atlas 18 | quads[sheetCounter] = 19 | love.graphics.newQuad(x * tilewidth, y * tileheight, tilewidth, 20 | tileheight, atlas:getDimensions()) 21 | sheetCounter = sheetCounter + 1 22 | end 23 | end 24 | 25 | return quads 26 | end 27 | -------------------------------------------------------------------------------- /mario-6/fonts/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-6/fonts/font.ttf -------------------------------------------------------------------------------- /mario-6/graphics/mario.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-6/graphics/mario.png -------------------------------------------------------------------------------- /mario-6/graphics/mario1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-6/graphics/mario1.png -------------------------------------------------------------------------------- /mario-6/graphics/playable_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-6/graphics/playable_left.png -------------------------------------------------------------------------------- /mario-6/graphics/playable_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-6/graphics/playable_right.png -------------------------------------------------------------------------------- /mario-6/graphics/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-6/graphics/tiles.png -------------------------------------------------------------------------------- /mario-6/graphics/tiles_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-6/graphics/tiles_2.png -------------------------------------------------------------------------------- /mario-6/main.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Super Mario Bros. Demo 3 | Author: Colton Ogden 4 | Original Credit: Nintendo 5 | 6 | Adds movement to Mario. 7 | ]] 8 | 9 | -- global key-handling 10 | love.keyboard.keysPressed = {} 11 | love.keyboard.keysReleased = {} 12 | 13 | push = require 'push' 14 | 15 | require 'Map' 16 | require 'Player' 17 | 18 | -- close resolution to NES but 16:9 19 | virtualWidth = 432 20 | virtualHeight = 243 21 | 22 | -- actual window resolution 23 | windowWidth = 1280 24 | windowHeight = 720 25 | 26 | -- seed RNG 27 | math.randomseed(os.time()) 28 | 29 | -- an object to contain our map data 30 | map = Map:create() 31 | 32 | -- performs initialization of all objects and data needed by program 33 | function love.load() 34 | -- makes upscaling look pixel-y instead of blurry 35 | love.graphics.setDefaultFilter('nearest', 'nearest') 36 | 37 | -- sets up a different, better-looking retro font as our default 38 | love.graphics.setFont(love.graphics.newFont('fonts/font.ttf', 8)) 39 | 40 | -- sets up virtual screen resolution for an authentic retro feel 41 | push:setupScreen(virtualWidth, virtualHeight, windowWidth, windowHeight, { 42 | fullscreen = false, 43 | resizable = true 44 | }) 45 | end 46 | 47 | -- called whenever window is resized 48 | function love.resize(w, h) 49 | push:resize(w, h) 50 | end 51 | 52 | -- global key pressed function 53 | function love.keyboard.wasPressed(key) 54 | if (love.keyboard.keysPressed[key]) then 55 | return true 56 | else 57 | return false 58 | end 59 | end 60 | 61 | -- global key released function 62 | function love.keyboard.wasReleased(key) 63 | if (love.keyboard.keysReleased[key]) then 64 | return true 65 | else 66 | return false 67 | end 68 | end 69 | 70 | -- called whenever a key is pressed 71 | function love.keypressed(key) 72 | if key == 'escape' then 73 | love.event.quit() 74 | end 75 | 76 | love.keyboard.keysPressed[key] = true 77 | end 78 | 79 | -- called whenever a key is released 80 | function love.keyreleased(key) 81 | love.keyboard.keysReleased[key] = true 82 | end 83 | 84 | -- called every frame, with dt passed in as delta in time since last frame 85 | function love.update(dt) 86 | map:update(dt) 87 | 88 | -- reset all keys pressed and released this frame 89 | love.keyboard.keysPressed = {} 90 | love.keyboard.keysReleased = {} 91 | end 92 | 93 | -- called each frame, used to render to the screen 94 | function love.draw() 95 | -- begin virtual resolution drawing 96 | push:apply('start') 97 | 98 | -- clear screen using Mario background blue 99 | love.graphics.clear(108, 140, 255, 255) 100 | 101 | -- renders our map object onto the screen 102 | love.graphics.translate(math.floor(-map.camX), math.floor(-map.camY)) 103 | map:render() 104 | 105 | -- end virtual resolution 106 | push:apply('end') 107 | end 108 | -------------------------------------------------------------------------------- /mario-7-exercise/Animation.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Holds a collection of frames that switch depending on how much time has 3 | passed. 4 | ]] 5 | 6 | Animation = {} 7 | Animation.__index = Animation 8 | 9 | function Animation:create(params) 10 | local this = { 11 | texture = params.texture, 12 | 13 | -- quads defining this animation 14 | frames = params.frames or {}, 15 | 16 | -- time in seconds each frame takes (1/20 by default) 17 | interval = params.interval or 0.05, 18 | 19 | -- stores amount of time that has elapsed 20 | timer = 0, 21 | 22 | currentFrame = 1 23 | } 24 | 25 | setmetatable(this, self) 26 | return this 27 | end 28 | 29 | function Animation:getCurrentFrame() 30 | return self.frames[self.currentFrame] 31 | end 32 | 33 | function Animation:restart() 34 | self.timer = 0 35 | self.currentFrame = 1 36 | end 37 | 38 | function Animation:update(dt) 39 | self.timer = self.timer + dt 40 | 41 | -- iteratively subtract interval from timer to proceed in the animation, 42 | -- in case we skipped more than one frame 43 | 44 | -- TODO: update our current frame number based on dt 45 | -- HINT: we should increment our frame by 1 for every interval passed, 46 | -- but be mindful of exceeding the total # of frames! maybe % and the 47 | -- # table operator in Lua will come in handy? 48 | -- HINT: we will need some kind of a while loop to take into account the 49 | -- subtraction of interval from timer, but don't forget to actually subtract 50 | -- the interval from the timer so this loop eventually terminates! 51 | 52 | end 53 | -------------------------------------------------------------------------------- /mario-7-exercise/Player.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Represents our player in the game, with its own sprite. 3 | ]] 4 | 5 | require 'Animation' 6 | 7 | Player = {} 8 | Player.__index = Player 9 | 10 | local WALKING_SPEED = 140 11 | 12 | function Player:create(map) 13 | local this = { 14 | x = 0, 15 | y = 0, 16 | width = 16, 17 | height = 32, 18 | 19 | -- offset from top left to center to support sprite flipping 20 | xOffset = 8, 21 | yOffset = 16, 22 | 23 | -- reference to map for checking tiles 24 | map = map, 25 | texture = love.graphics.newImage('graphics/mario1.png'), 26 | 27 | -- current animation frame 28 | currentFrame = nil, 29 | 30 | -- current animation being updated 31 | animation = nil, 32 | 33 | -- used to determine behavior and animations 34 | state = 'idle', 35 | 36 | -- determines sprite flipping 37 | direction = 'right', 38 | 39 | -- x and y velocity 40 | dx = 0, 41 | dy = 0 42 | } 43 | 44 | -- position on top of map tiles 45 | this.y = map.tileHeight * ((map.mapHeight - 2) / 2) - this.height 46 | this.x = map.tileWidth * 10 47 | 48 | -- initialize all player animations 49 | this.animations = { 50 | ['idle'] = Animation:create({ 51 | texture = this.texture, 52 | frames = { 53 | love.graphics.newQuad(0, 0, 16, 32, this.texture:getDimensions()) 54 | } 55 | }), 56 | ['walking'] = Animation:create({ 57 | texture = this.texture, 58 | frames = { 59 | love.graphics.newQuad(18, 0, 16, 32, this.texture:getDimensions()), 60 | love.graphics.newQuad(34, 0, 16, 32, this.texture:getDimensions()), 61 | love.graphics.newQuad(50, 0, 16, 32, this.texture:getDimensions()), 62 | love.graphics.newQuad(34, 0, 16, 32, this.texture:getDimensions()), 63 | }, 64 | interval = 0.07 65 | }) 66 | } 67 | 68 | -- initialize animation and current frame we should render 69 | this.animation = this.animations['idle'] 70 | this.currentFrame = this.animation:getCurrentFrame() 71 | 72 | -- behavior map we can call based on player state 73 | this.behaviors = { 74 | ['idle'] = function(dt) 75 | -- begin moving if left or right is pressed 76 | if love.keyboard.isDown('left') then 77 | direction = 'left' 78 | this.dx = -WALKING_SPEED 79 | this.state = 'walking' 80 | this.animations['walking']:restart() 81 | this.animation = this.animations['walking'] 82 | elseif love.keyboard.isDown('right') then 83 | direction = 'right' 84 | this.dx = WALKING_SPEED 85 | this.state = 'walking' 86 | this.animations['walking']:restart() 87 | this.animation = this.animations['walking'] 88 | end 89 | end, 90 | -- TODO: define a new state, 'walking', which takes in keyboard input and 91 | -- updates dx accordingly, but also switching to 'idle' state and animation 92 | -- when no directions are being pressed 93 | -- HINT: see the 'idle' behavior defined above for a template! 94 | 95 | } 96 | 97 | setmetatable(this, self) 98 | return this 99 | end 100 | 101 | function Player:update(dt) 102 | self.behaviors[self.state](dt) 103 | self.animation:update(dt) 104 | self.currentFrame = self.animation:getCurrentFrame() 105 | self.x = self.x + self.dx * dt 106 | end 107 | 108 | function Player:render() 109 | local scaleX 110 | 111 | -- set negative x scale factor if facing left, which will flip the sprite 112 | -- when applied 113 | if direction == 'right' then 114 | scaleX = 1 115 | else 116 | scaleX = -1 117 | end 118 | 119 | -- draw sprite with scale factor and offsets 120 | love.graphics.draw(self.texture, self.currentFrame, math.floor(self.x + self.xOffset), 121 | math.floor(self.y + self.yOffset), 0, scaleX, 1, self.xOffset, self.yOffset) 122 | end 123 | -------------------------------------------------------------------------------- /mario-7-exercise/Util.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Stores utility functions used by our game engine. 3 | ]] 4 | 5 | -- takes a texture, width, and height of tiles and splits it into quads 6 | -- that can be individually drawn 7 | function generateQuads(atlas, tilewidth, tileheight) 8 | local sheetWidth = atlas:getWidth() / tilewidth 9 | local sheetHeight = atlas:getHeight() / tileheight 10 | 11 | local sheetCounter = 1 12 | local quads = {} 13 | 14 | for y = 0, sheetHeight - 1 do 15 | for x = 0, sheetWidth - 1 do 16 | -- this quad represents a square cutout of our atlas that we can 17 | -- individually draw instead of the whole atlas 18 | quads[sheetCounter] = 19 | love.graphics.newQuad(x * tilewidth, y * tileheight, tilewidth, 20 | tileheight, atlas:getDimensions()) 21 | sheetCounter = sheetCounter + 1 22 | end 23 | end 24 | 25 | return quads 26 | end 27 | -------------------------------------------------------------------------------- /mario-7-exercise/fonts/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-7-exercise/fonts/font.ttf -------------------------------------------------------------------------------- /mario-7-exercise/graphics/mario.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-7-exercise/graphics/mario.png -------------------------------------------------------------------------------- /mario-7-exercise/graphics/mario1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-7-exercise/graphics/mario1.png -------------------------------------------------------------------------------- /mario-7-exercise/graphics/playable_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-7-exercise/graphics/playable_left.png -------------------------------------------------------------------------------- /mario-7-exercise/graphics/playable_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-7-exercise/graphics/playable_right.png -------------------------------------------------------------------------------- /mario-7-exercise/graphics/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-7-exercise/graphics/tiles.png -------------------------------------------------------------------------------- /mario-7-exercise/graphics/tiles_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-7-exercise/graphics/tiles_2.png -------------------------------------------------------------------------------- /mario-7-exercise/main.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Super Mario Bros. Demo 3 | Author: Colton Ogden 4 | Original Credit: Nintendo 5 | 6 | Adds animation to Mario. 7 | ]] 8 | 9 | -- global key-handling 10 | love.keyboard.keysPressed = {} 11 | love.keyboard.keysReleased = {} 12 | 13 | push = require 'push' 14 | 15 | require 'Map' 16 | require 'Player' 17 | 18 | -- close resolution to NES but 16:9 19 | virtualWidth = 432 20 | virtualHeight = 243 21 | 22 | -- actual window resolution 23 | windowWidth = 1280 24 | windowHeight = 720 25 | 26 | -- seed RNG 27 | math.randomseed(os.time()) 28 | 29 | -- an object to contain our map data 30 | map = Map:create() 31 | 32 | -- performs initialization of all objects and data needed by program 33 | function love.load() 34 | -- makes upscaling look pixel-y instead of blurry 35 | love.graphics.setDefaultFilter('nearest', 'nearest') 36 | 37 | -- sets up a different, better-looking retro font as our default 38 | love.graphics.setFont(love.graphics.newFont('fonts/font.ttf', 8)) 39 | 40 | -- sets up virtual screen resolution for an authentic retro feel 41 | push:setupScreen(virtualWidth, virtualHeight, windowWidth, windowHeight, { 42 | fullscreen = false, 43 | resizable = true 44 | }) 45 | end 46 | 47 | -- called whenever window is resized 48 | function love.resize(w, h) 49 | push:resize(w, h) 50 | end 51 | 52 | -- global key pressed function 53 | function love.keyboard.wasPressed(key) 54 | if (love.keyboard.keysPressed[key]) then 55 | return true 56 | else 57 | return false 58 | end 59 | end 60 | 61 | -- global key released function 62 | function love.keyboard.wasReleased(key) 63 | if (love.keyboard.keysReleased[key]) then 64 | return true 65 | else 66 | return false 67 | end 68 | end 69 | 70 | -- called whenever a key is pressed 71 | function love.keypressed(key) 72 | if key == 'escape' then 73 | love.event.quit() 74 | end 75 | 76 | love.keyboard.keysPressed[key] = true 77 | end 78 | 79 | -- called whenever a key is released 80 | function love.keyreleased(key) 81 | love.keyboard.keysReleased[key] = true 82 | end 83 | 84 | -- called every frame, with dt passed in as delta in time since last frame 85 | function love.update(dt) 86 | map:update(dt) 87 | 88 | -- reset all keys pressed and released this frame 89 | love.keyboard.keysPressed = {} 90 | love.keyboard.keysReleased = {} 91 | end 92 | 93 | -- called each frame, used to render to the screen 94 | function love.draw() 95 | -- begin virtual resolution drawing 96 | push:apply('start') 97 | 98 | -- clear screen using Mario background blue 99 | love.graphics.clear(108, 140, 255, 255) 100 | 101 | -- renders our map object onto the screen 102 | love.graphics.translate(math.floor(-map.camX), math.floor(-map.camY)) 103 | map:render() 104 | 105 | -- end virtual resolution 106 | push:apply('end') 107 | end 108 | -------------------------------------------------------------------------------- /mario-7/Animation.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Holds a collection of frames that switch depending on how much time has 3 | passed. 4 | ]] 5 | 6 | Animation = {} 7 | Animation.__index = Animation 8 | 9 | function Animation:create(params) 10 | local this = { 11 | texture = params.texture, 12 | 13 | -- quads defining this animation 14 | frames = params.frames or {}, 15 | 16 | -- time in seconds each frame takes (1/20 by default) 17 | interval = params.interval or 0.05, 18 | 19 | -- stores amount of time that has elapsed 20 | timer = 0, 21 | 22 | currentFrame = 1 23 | } 24 | 25 | setmetatable(this, self) 26 | return this 27 | end 28 | 29 | function Animation:getCurrentFrame() 30 | return self.frames[self.currentFrame] 31 | end 32 | 33 | function Animation:restart() 34 | self.timer = 0 35 | self.currentFrame = 1 36 | end 37 | 38 | function Animation:update(dt) 39 | self.timer = self.timer + dt 40 | 41 | -- iteratively subtract interval from timer to proceed in the animation, 42 | -- in case we skipped more than one frame 43 | while self.timer > self.interval do 44 | self.timer = self.timer - self.interval 45 | self.currentFrame = (self.currentFrame + 1) % #self.frames 46 | if self.currentFrame == 0 then self.currentFrame = 1 end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /mario-7/Player.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Represents our player in the game, with its own sprite. 3 | ]] 4 | 5 | require 'Animation' 6 | 7 | Player = {} 8 | Player.__index = Player 9 | 10 | local WALKING_SPEED = 140 11 | 12 | function Player:create(map) 13 | local this = { 14 | x = 0, 15 | y = 0, 16 | width = 16, 17 | height = 32, 18 | 19 | -- offset from top left to center to support sprite flipping 20 | xOffset = 8, 21 | yOffset = 16, 22 | 23 | -- reference to map for checking tiles 24 | map = map, 25 | texture = love.graphics.newImage('graphics/mario1.png'), 26 | 27 | -- current animation frame 28 | currentFrame = nil, 29 | 30 | -- current animation being updated 31 | animation = nil, 32 | 33 | -- used to determine behavior and animations 34 | state = 'idle', 35 | 36 | -- determines sprite flipping 37 | direction = 'right', 38 | 39 | -- x and y velocity 40 | dx = 0, 41 | dy = 0 42 | } 43 | 44 | -- position on top of map tiles 45 | this.y = map.tileHeight * ((map.mapHeight - 2) / 2) - this.height 46 | this.x = map.tileWidth * 10 47 | 48 | -- initialize all player animations 49 | this.animations = { 50 | ['idle'] = Animation:create({ 51 | texture = this.texture, 52 | frames = { 53 | love.graphics.newQuad(0, 0, 16, 32, this.texture:getDimensions()) 54 | } 55 | }), 56 | ['walking'] = Animation:create({ 57 | texture = this.texture, 58 | frames = { 59 | love.graphics.newQuad(18, 0, 16, 32, this.texture:getDimensions()), 60 | love.graphics.newQuad(34, 0, 16, 32, this.texture:getDimensions()), 61 | love.graphics.newQuad(50, 0, 16, 32, this.texture:getDimensions()), 62 | love.graphics.newQuad(34, 0, 16, 32, this.texture:getDimensions()), 63 | }, 64 | interval = 0.07 65 | }) 66 | } 67 | 68 | -- initialize animation and current frame we should render 69 | this.animation = this.animations['idle'] 70 | this.currentFrame = this.animation:getCurrentFrame() 71 | 72 | -- behavior map we can call based on player state 73 | this.behaviors = { 74 | ['idle'] = function(dt) 75 | -- begin moving if left or right is pressed 76 | if love.keyboard.isDown('left') then 77 | direction = 'left' 78 | this.dx = -WALKING_SPEED 79 | this.state = 'walking' 80 | this.animations['walking']:restart() 81 | this.animation = this.animations['walking'] 82 | elseif love.keyboard.isDown('right') then 83 | direction = 'right' 84 | this.dx = WALKING_SPEED 85 | this.state = 'walking' 86 | this.animations['walking']:restart() 87 | this.animation = this.animations['walking'] 88 | end 89 | end, 90 | ['walking'] = function(dt) 91 | -- keep track of input to switch movement while walking, or reset 92 | -- to idle if we're not moving 93 | if love.keyboard.isDown('left') then 94 | direction = 'left' 95 | this.dx = -WALKING_SPEED 96 | elseif love.keyboard.isDown('right') then 97 | direction = 'right' 98 | this.dx = WALKING_SPEED 99 | else 100 | this.dx = 0 101 | this.state = 'idle' 102 | this.animation = this.animations['idle'] 103 | end 104 | end 105 | } 106 | 107 | setmetatable(this, self) 108 | return this 109 | end 110 | 111 | function Player:update(dt) 112 | self.behaviors[self.state](dt) 113 | self.animation:update(dt) 114 | self.currentFrame = self.animation:getCurrentFrame() 115 | self.x = self.x + self.dx * dt 116 | end 117 | 118 | function Player:render() 119 | local scaleX 120 | 121 | -- set negative x scale factor if facing left, which will flip the sprite 122 | -- when applied 123 | if direction == 'right' then 124 | scaleX = 1 125 | else 126 | scaleX = -1 127 | end 128 | 129 | -- draw sprite with scale factor and offsets 130 | love.graphics.draw(self.texture, self.currentFrame, math.floor(self.x + self.xOffset), 131 | math.floor(self.y + self.yOffset), 0, scaleX, 1, self.xOffset, self.yOffset) 132 | end 133 | -------------------------------------------------------------------------------- /mario-7/Util.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Stores utility functions used by our game engine. 3 | ]] 4 | 5 | -- takes a texture, width, and height of tiles and splits it into quads 6 | -- that can be individually drawn 7 | function generateQuads(atlas, tilewidth, tileheight) 8 | local sheetWidth = atlas:getWidth() / tilewidth 9 | local sheetHeight = atlas:getHeight() / tileheight 10 | 11 | local sheetCounter = 1 12 | local quads = {} 13 | 14 | for y = 0, sheetHeight - 1 do 15 | for x = 0, sheetWidth - 1 do 16 | -- this quad represents a square cutout of our atlas that we can 17 | -- individually draw instead of the whole atlas 18 | quads[sheetCounter] = 19 | love.graphics.newQuad(x * tilewidth, y * tileheight, tilewidth, 20 | tileheight, atlas:getDimensions()) 21 | sheetCounter = sheetCounter + 1 22 | end 23 | end 24 | 25 | return quads 26 | end 27 | -------------------------------------------------------------------------------- /mario-7/fonts/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-7/fonts/font.ttf -------------------------------------------------------------------------------- /mario-7/graphics/mario.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-7/graphics/mario.png -------------------------------------------------------------------------------- /mario-7/graphics/mario1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-7/graphics/mario1.png -------------------------------------------------------------------------------- /mario-7/graphics/playable_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-7/graphics/playable_left.png -------------------------------------------------------------------------------- /mario-7/graphics/playable_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-7/graphics/playable_right.png -------------------------------------------------------------------------------- /mario-7/graphics/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-7/graphics/tiles.png -------------------------------------------------------------------------------- /mario-7/graphics/tiles_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-7/graphics/tiles_2.png -------------------------------------------------------------------------------- /mario-7/main.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Super Mario Bros. Demo 3 | Author: Colton Ogden 4 | Original Credit: Nintendo 5 | 6 | Adds animation to Mario. 7 | ]] 8 | 9 | -- global key-handling 10 | love.keyboard.keysPressed = {} 11 | love.keyboard.keysReleased = {} 12 | 13 | push = require 'push' 14 | 15 | require 'Map' 16 | require 'Player' 17 | 18 | -- close resolution to NES but 16:9 19 | virtualWidth = 432 20 | virtualHeight = 243 21 | 22 | -- actual window resolution 23 | windowWidth = 1280 24 | windowHeight = 720 25 | 26 | -- seed RNG 27 | math.randomseed(os.time()) 28 | 29 | -- an object to contain our map data 30 | map = Map:create() 31 | 32 | -- performs initialization of all objects and data needed by program 33 | function love.load() 34 | -- makes upscaling look pixel-y instead of blurry 35 | love.graphics.setDefaultFilter('nearest', 'nearest') 36 | 37 | -- sets up a different, better-looking retro font as our default 38 | love.graphics.setFont(love.graphics.newFont('fonts/font.ttf', 8)) 39 | 40 | -- sets up virtual screen resolution for an authentic retro feel 41 | push:setupScreen(virtualWidth, virtualHeight, windowWidth, windowHeight, { 42 | fullscreen = false, 43 | resizable = true 44 | }) 45 | end 46 | 47 | -- called whenever window is resized 48 | function love.resize(w, h) 49 | push:resize(w, h) 50 | end 51 | 52 | -- global key pressed function 53 | function love.keyboard.wasPressed(key) 54 | if (love.keyboard.keysPressed[key]) then 55 | return true 56 | else 57 | return false 58 | end 59 | end 60 | 61 | -- global key released function 62 | function love.keyboard.wasReleased(key) 63 | if (love.keyboard.keysReleased[key]) then 64 | return true 65 | else 66 | return false 67 | end 68 | end 69 | 70 | -- called whenever a key is pressed 71 | function love.keypressed(key) 72 | if key == 'escape' then 73 | love.event.quit() 74 | end 75 | 76 | love.keyboard.keysPressed[key] = true 77 | end 78 | 79 | -- called whenever a key is released 80 | function love.keyreleased(key) 81 | love.keyboard.keysReleased[key] = true 82 | end 83 | 84 | -- called every frame, with dt passed in as delta in time since last frame 85 | function love.update(dt) 86 | map:update(dt) 87 | 88 | -- reset all keys pressed and released this frame 89 | love.keyboard.keysPressed = {} 90 | love.keyboard.keysReleased = {} 91 | end 92 | 93 | -- called each frame, used to render to the screen 94 | function love.draw() 95 | -- begin virtual resolution drawing 96 | push:apply('start') 97 | 98 | -- clear screen using Mario background blue 99 | love.graphics.clear(108, 140, 255, 255) 100 | 101 | -- renders our map object onto the screen 102 | love.graphics.translate(math.floor(-map.camX), math.floor(-map.camY)) 103 | map:render() 104 | 105 | -- end virtual resolution 106 | push:apply('end') 107 | end 108 | -------------------------------------------------------------------------------- /mario-8-exercise/Animation.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Holds a collection of frames that switch depending on how much time has 3 | passed. 4 | ]] 5 | 6 | Animation = {} 7 | Animation.__index = Animation 8 | 9 | function Animation:create(params) 10 | local this = { 11 | texture = params.texture, 12 | 13 | -- quads defining this animation 14 | frames = params.frames or {}, 15 | 16 | -- time in seconds each frame takes (1/20 by default) 17 | interval = params.interval or 0.05, 18 | 19 | -- stores amount of time that has elapsed 20 | timer = 0, 21 | 22 | currentFrame = 1 23 | } 24 | 25 | setmetatable(this, self) 26 | return this 27 | end 28 | 29 | function Animation:getCurrentFrame() 30 | return self.frames[self.currentFrame] 31 | end 32 | 33 | function Animation:restart() 34 | self.timer = 0 35 | self.currentFrame = 1 36 | end 37 | 38 | function Animation:update(dt) 39 | self.timer = self.timer + dt 40 | 41 | -- iteratively subtract interval from timer to proceed in the animation, 42 | -- in case we skipped more than one frame 43 | while self.timer > self.interval do 44 | self.timer = self.timer - self.interval 45 | self.currentFrame = (self.currentFrame + 1) % #self.frames 46 | if self.currentFrame == 0 then self.currentFrame = 1 end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /mario-8-exercise/Player.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Represents our player in the game, with its own sprite. 3 | ]] 4 | 5 | require 'Animation' 6 | 7 | Player = {} 8 | Player.__index = Player 9 | 10 | local WALKING_SPEED = 140 11 | local JUMP_VELOCITY = 400 12 | 13 | function Player:create(map) 14 | local this = { 15 | x = 0, 16 | y = 0, 17 | width = 16, 18 | height = 32, 19 | 20 | -- offset from top left to center to support sprite flipping 21 | xOffset = 8, 22 | yOffset = 16, 23 | 24 | -- reference to map for checking tiles 25 | map = map, 26 | texture = love.graphics.newImage('graphics/mario1.png'), 27 | 28 | -- current animation frame 29 | currentFrame = nil, 30 | 31 | -- current animation being updated 32 | animation = nil, 33 | 34 | -- used to determine behavior and animations 35 | state = 'idle', 36 | 37 | -- determines sprite flipping 38 | direction = 'right', 39 | 40 | -- x and y velocity 41 | dx = 0, 42 | dy = 0 43 | } 44 | 45 | -- position on top of map tiles 46 | this.y = map.tileHeight * ((map.mapHeight - 2) / 2) - this.height 47 | this.x = map.tileWidth * 10 48 | 49 | -- initialize all player animations 50 | this.animations = { 51 | ['idle'] = Animation:create({ 52 | texture = this.texture, 53 | frames = { 54 | love.graphics.newQuad(0, 0, 16, 32, this.texture:getDimensions()) 55 | } 56 | }), 57 | ['walking'] = Animation:create({ 58 | texture = this.texture, 59 | frames = { 60 | love.graphics.newQuad(18, 0, 16, 32, this.texture:getDimensions()), 61 | love.graphics.newQuad(34, 0, 16, 32, this.texture:getDimensions()), 62 | love.graphics.newQuad(50, 0, 16, 32, this.texture:getDimensions()), 63 | love.graphics.newQuad(34, 0, 16, 32, this.texture:getDimensions()), 64 | }, 65 | interval = 0.07 66 | }), 67 | -- TODO: add a jumping animation for Mario 68 | -- HINT: it will just be one frame, though note the frame starts at X: 88! 69 | 70 | } 71 | 72 | -- initialize animation and current frame we should render 73 | this.animation = this.animations['idle'] 74 | this.currentFrame = this.animation:getCurrentFrame() 75 | 76 | -- behavior map we can call based on player state 77 | this.behaviors = { 78 | ['idle'] = function(dt) 79 | -- begin moving if left or right is pressed 80 | if love.keyboard.wasPressed('space') then 81 | this.dy = -JUMP_VELOCITY 82 | this.state = 'jumping' 83 | this.animation = this.animations['jumping'] 84 | elseif love.keyboard.isDown('left') then 85 | direction = 'left' 86 | this.dx = -WALKING_SPEED 87 | this.state = 'walking' 88 | this.animations['walking']:restart() 89 | this.animation = this.animations['walking'] 90 | elseif love.keyboard.isDown('right') then 91 | direction = 'right' 92 | this.dx = WALKING_SPEED 93 | this.state = 'walking' 94 | this.animations['walking']:restart() 95 | this.animation = this.animations['walking'] 96 | else 97 | this.dx = 0 98 | end 99 | end, 100 | ['walking'] = function(dt) 101 | -- keep track of input to switch movement while walking, or reset 102 | -- to idle if we're not moving 103 | if love.keyboard.wasPressed('space') then 104 | this.dy = -JUMP_VELOCITY 105 | this.state = 'jumping' 106 | this.animation = this.animations['jumping'] 107 | elseif love.keyboard.isDown('left') then 108 | direction = 'left' 109 | this.dx = -WALKING_SPEED 110 | elseif love.keyboard.isDown('right') then 111 | direction = 'right' 112 | this.dx = WALKING_SPEED 113 | else 114 | this.dx = 0 115 | this.state = 'idle' 116 | this.animation = this.animations['idle'] 117 | end 118 | end, 119 | ['jumping'] = function(dt) 120 | if love.keyboard.isDown('left') then 121 | direction = 'left' 122 | this.dx = -WALKING_SPEED 123 | elseif love.keyboard.isDown('right') then 124 | direction = 'right' 125 | this.dx = WALKING_SPEED 126 | end 127 | 128 | -- TODO: apply map's gravity to y velocity 129 | -- HINT: gravity is defined in Map.lua! 130 | 131 | 132 | if this.y == this.map.tileHeight * 133 | ((this.map.mapHeight - 2) / 2) - this.height then 134 | -- TODO: reset Mario to idle animation and state 135 | -- HINT: be sure to also set his Y velocity so he stops falling! 136 | 137 | end 138 | end 139 | } 140 | 141 | setmetatable(this, self) 142 | return this 143 | end 144 | 145 | function Player:update(dt) 146 | self.behaviors[self.state](dt) 147 | self.animation:update(dt) 148 | self.currentFrame = self.animation:getCurrentFrame() 149 | self.x = self.x + self.dx * dt 150 | 151 | -- TODO: apply velocity and prevent going beneath tiles 152 | -- HINT: math.min is your friend; don't let him go below map height / 2! 153 | -- HINT: don't forget to subtract 1 from map height since things are drawn 154 | -- 0-based! 155 | -- also, don't forget to subtract his height, or he'll be below the ground! 156 | 157 | end 158 | 159 | function Player:render() 160 | local scaleX 161 | 162 | -- set negative x scale factor if facing left, which will flip the sprite 163 | -- when applied 164 | if direction == 'right' then 165 | scaleX = 1 166 | else 167 | scaleX = -1 168 | end 169 | 170 | -- draw sprite with scale factor and offsets 171 | love.graphics.draw(self.texture, self.currentFrame, math.floor(self.x + self.xOffset), 172 | math.floor(self.y + self.yOffset), 0, scaleX, 1, self.xOffset, self.yOffset) 173 | end 174 | -------------------------------------------------------------------------------- /mario-8-exercise/Util.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Stores utility functions used by our game engine. 3 | ]] 4 | 5 | -- takes a texture, width, and height of tiles and splits it into quads 6 | -- that can be individually drawn 7 | function generateQuads(atlas, tilewidth, tileheight) 8 | local sheetWidth = atlas:getWidth() / tilewidth 9 | local sheetHeight = atlas:getHeight() / tileheight 10 | 11 | local sheetCounter = 1 12 | local quads = {} 13 | 14 | for y = 0, sheetHeight - 1 do 15 | for x = 0, sheetWidth - 1 do 16 | -- this quad represents a square cutout of our atlas that we can 17 | -- individually draw instead of the whole atlas 18 | quads[sheetCounter] = 19 | love.graphics.newQuad(x * tilewidth, y * tileheight, tilewidth, 20 | tileheight, atlas:getDimensions()) 21 | sheetCounter = sheetCounter + 1 22 | end 23 | end 24 | 25 | return quads 26 | end 27 | -------------------------------------------------------------------------------- /mario-8-exercise/fonts/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-8-exercise/fonts/font.ttf -------------------------------------------------------------------------------- /mario-8-exercise/graphics/mario.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-8-exercise/graphics/mario.png -------------------------------------------------------------------------------- /mario-8-exercise/graphics/mario1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-8-exercise/graphics/mario1.png -------------------------------------------------------------------------------- /mario-8-exercise/graphics/playable_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-8-exercise/graphics/playable_left.png -------------------------------------------------------------------------------- /mario-8-exercise/graphics/playable_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-8-exercise/graphics/playable_right.png -------------------------------------------------------------------------------- /mario-8-exercise/graphics/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-8-exercise/graphics/tiles.png -------------------------------------------------------------------------------- /mario-8-exercise/graphics/tiles_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-8-exercise/graphics/tiles_2.png -------------------------------------------------------------------------------- /mario-8-exercise/main.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Super Mario Bros. Demo 3 | Author: Colton Ogden 4 | Original Credit: Nintendo 5 | 6 | Adds jumping to Mario. 7 | ]] 8 | 9 | -- global key-handling 10 | love.keyboard.keysPressed = {} 11 | love.keyboard.keysReleased = {} 12 | 13 | push = require 'push' 14 | 15 | require 'Map' 16 | require 'Player' 17 | 18 | -- close resolution to NES but 16:9 19 | virtualWidth = 432 20 | virtualHeight = 243 21 | 22 | -- actual window resolution 23 | windowWidth = 1280 24 | windowHeight = 720 25 | 26 | -- seed RNG 27 | math.randomseed(os.time()) 28 | 29 | -- an object to contain our map data 30 | map = Map:create() 31 | 32 | -- performs initialization of all objects and data needed by program 33 | function love.load() 34 | -- makes upscaling look pixel-y instead of blurry 35 | love.graphics.setDefaultFilter('nearest', 'nearest') 36 | 37 | -- sets up a different, better-looking retro font as our default 38 | love.graphics.setFont(love.graphics.newFont('fonts/font.ttf', 8)) 39 | 40 | -- sets up virtual screen resolution for an authentic retro feel 41 | push:setupScreen(virtualWidth, virtualHeight, windowWidth, windowHeight, { 42 | fullscreen = false, 43 | resizable = true 44 | }) 45 | end 46 | 47 | -- called whenever window is resized 48 | function love.resize(w, h) 49 | push:resize(w, h) 50 | end 51 | 52 | -- global key pressed function 53 | function love.keyboard.wasPressed(key) 54 | if (love.keyboard.keysPressed[key]) then 55 | return true 56 | else 57 | return false 58 | end 59 | end 60 | 61 | -- global key released function 62 | function love.keyboard.wasReleased(key) 63 | if (love.keyboard.keysReleased[key]) then 64 | return true 65 | else 66 | return false 67 | end 68 | end 69 | 70 | -- called whenever a key is pressed 71 | function love.keypressed(key) 72 | if key == 'escape' then 73 | love.event.quit() 74 | end 75 | 76 | love.keyboard.keysPressed[key] = true 77 | end 78 | 79 | -- called whenever a key is released 80 | function love.keyreleased(key) 81 | love.keyboard.keysReleased[key] = true 82 | end 83 | 84 | -- called every frame, with dt passed in as delta in time since last frame 85 | function love.update(dt) 86 | map:update(dt) 87 | 88 | -- reset all keys pressed and released this frame 89 | love.keyboard.keysPressed = {} 90 | love.keyboard.keysReleased = {} 91 | end 92 | 93 | -- called each frame, used to render to the screen 94 | function love.draw() 95 | -- begin virtual resolution drawing 96 | push:apply('start') 97 | 98 | -- clear screen using Mario background blue 99 | love.graphics.clear(108, 140, 255, 255) 100 | 101 | -- renders our map object onto the screen 102 | love.graphics.translate(math.floor(-map.camX), math.floor(-map.camY)) 103 | map:render() 104 | 105 | -- end virtual resolution 106 | push:apply('end') 107 | end 108 | -------------------------------------------------------------------------------- /mario-8/Animation.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Holds a collection of frames that switch depending on how much time has 3 | passed. 4 | ]] 5 | 6 | Animation = {} 7 | Animation.__index = Animation 8 | 9 | function Animation:create(params) 10 | local this = { 11 | texture = params.texture, 12 | 13 | -- quads defining this animation 14 | frames = params.frames or {}, 15 | 16 | -- time in seconds each frame takes (1/20 by default) 17 | interval = params.interval or 0.05, 18 | 19 | -- stores amount of time that has elapsed 20 | timer = 0, 21 | 22 | currentFrame = 1 23 | } 24 | 25 | setmetatable(this, self) 26 | return this 27 | end 28 | 29 | function Animation:getCurrentFrame() 30 | return self.frames[self.currentFrame] 31 | end 32 | 33 | function Animation:restart() 34 | self.timer = 0 35 | self.currentFrame = 1 36 | end 37 | 38 | function Animation:update(dt) 39 | self.timer = self.timer + dt 40 | 41 | -- iteratively subtract interval from timer to proceed in the animation, 42 | -- in case we skipped more than one frame 43 | while self.timer > self.interval do 44 | self.timer = self.timer - self.interval 45 | self.currentFrame = (self.currentFrame + 1) % #self.frames 46 | if self.currentFrame == 0 then self.currentFrame = 1 end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /mario-8/Player.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Represents our player in the game, with its own sprite. 3 | ]] 4 | 5 | require 'Animation' 6 | 7 | Player = {} 8 | Player.__index = Player 9 | 10 | local WALKING_SPEED = 140 11 | local JUMP_VELOCITY = 400 12 | 13 | function Player:create(map) 14 | local this = { 15 | x = 0, 16 | y = 0, 17 | width = 16, 18 | height = 32, 19 | 20 | -- offset from top left to center to support sprite flipping 21 | xOffset = 8, 22 | yOffset = 16, 23 | 24 | -- reference to map for checking tiles 25 | map = map, 26 | texture = love.graphics.newImage('graphics/mario1.png'), 27 | 28 | -- current animation frame 29 | currentFrame = nil, 30 | 31 | -- current animation being updated 32 | animation = nil, 33 | 34 | -- used to determine behavior and animations 35 | state = 'idle', 36 | 37 | -- determines sprite flipping 38 | direction = 'right', 39 | 40 | -- x and y velocity 41 | dx = 0, 42 | dy = 0 43 | } 44 | 45 | -- position on top of map tiles 46 | this.y = map.tileHeight * ((map.mapHeight - 2) / 2) - this.height 47 | this.x = map.tileWidth * 10 48 | 49 | -- initialize all player animations 50 | this.animations = { 51 | ['idle'] = Animation:create({ 52 | texture = this.texture, 53 | frames = { 54 | love.graphics.newQuad(0, 0, 16, 32, this.texture:getDimensions()) 55 | } 56 | }), 57 | ['walking'] = Animation:create({ 58 | texture = this.texture, 59 | frames = { 60 | love.graphics.newQuad(18, 0, 16, 32, this.texture:getDimensions()), 61 | love.graphics.newQuad(34, 0, 16, 32, this.texture:getDimensions()), 62 | love.graphics.newQuad(50, 0, 16, 32, this.texture:getDimensions()), 63 | love.graphics.newQuad(34, 0, 16, 32, this.texture:getDimensions()), 64 | }, 65 | interval = 0.07 66 | }), 67 | ['jumping'] = Animation:create({ 68 | texture = this.texture, 69 | frames = { 70 | love.graphics.newQuad(88, 0, 16, 32, this.texture:getDimensions()) 71 | } 72 | }) 73 | } 74 | 75 | -- initialize animation and current frame we should render 76 | this.animation = this.animations['idle'] 77 | this.currentFrame = this.animation:getCurrentFrame() 78 | 79 | -- behavior map we can call based on player state 80 | this.behaviors = { 81 | ['idle'] = function(dt) 82 | -- begin moving if left or right is pressed 83 | if love.keyboard.wasPressed('space') then 84 | this.dy = -JUMP_VELOCITY 85 | this.state = 'jumping' 86 | this.animation = this.animations['jumping'] 87 | elseif love.keyboard.isDown('left') then 88 | direction = 'left' 89 | this.dx = -WALKING_SPEED 90 | this.state = 'walking' 91 | this.animations['walking']:restart() 92 | this.animation = this.animations['walking'] 93 | elseif love.keyboard.isDown('right') then 94 | direction = 'right' 95 | this.dx = WALKING_SPEED 96 | this.state = 'walking' 97 | this.animations['walking']:restart() 98 | this.animation = this.animations['walking'] 99 | else 100 | this.dx = 0 101 | end 102 | end, 103 | ['walking'] = function(dt) 104 | -- keep track of input to switch movement while walking, or reset 105 | -- to idle if we're not moving 106 | if love.keyboard.wasPressed('space') then 107 | this.dy = -JUMP_VELOCITY 108 | this.state = 'jumping' 109 | this.animation = this.animations['jumping'] 110 | elseif love.keyboard.isDown('left') then 111 | direction = 'left' 112 | this.dx = -WALKING_SPEED 113 | elseif love.keyboard.isDown('right') then 114 | direction = 'right' 115 | this.dx = WALKING_SPEED 116 | else 117 | this.dx = 0 118 | this.state = 'idle' 119 | this.animation = this.animations['idle'] 120 | end 121 | end, 122 | ['jumping'] = function(dt) 123 | if love.keyboard.isDown('left') then 124 | direction = 'left' 125 | this.dx = -WALKING_SPEED 126 | elseif love.keyboard.isDown('right') then 127 | direction = 'right' 128 | this.dx = WALKING_SPEED 129 | end 130 | 131 | -- apply map's gravity before y velocity 132 | this.dy = this.dy + this.map.gravity 133 | 134 | -- simple way of stopping fall; stop when halfway down map 135 | if this.y == this.map.tileHeight * 136 | ((this.map.mapHeight - 2) / 2) - this.height then 137 | this.dy = 0 138 | this.state = 'idle' 139 | this.animation = this.animations['idle'] 140 | end 141 | end 142 | } 143 | 144 | setmetatable(this, self) 145 | return this 146 | end 147 | 148 | function Player:update(dt) 149 | self.behaviors[self.state](dt) 150 | self.animation:update(dt) 151 | self.currentFrame = self.animation:getCurrentFrame() 152 | self.x = self.x + self.dx * dt 153 | 154 | -- apply velocity and prevent going beneath tiles 155 | self.y = math.min(self.y + self.dy * dt, self.map.tileHeight * 156 | ((self.map.mapHeight - 2) / 2) - self.height) 157 | end 158 | 159 | function Player:render() 160 | local scaleX 161 | 162 | -- set negative x scale factor if facing left, which will flip the sprite 163 | -- when applied 164 | if direction == 'right' then 165 | scaleX = 1 166 | else 167 | scaleX = -1 168 | end 169 | 170 | -- draw sprite with scale factor and offsets 171 | love.graphics.draw(self.texture, self.currentFrame, math.floor(self.x + self.xOffset), 172 | math.floor(self.y + self.yOffset), 0, scaleX, 1, self.xOffset, self.yOffset) 173 | end 174 | -------------------------------------------------------------------------------- /mario-8/Util.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Stores utility functions used by our game engine. 3 | ]] 4 | 5 | -- takes a texture, width, and height of tiles and splits it into quads 6 | -- that can be individually drawn 7 | function generateQuads(atlas, tilewidth, tileheight) 8 | local sheetWidth = atlas:getWidth() / tilewidth 9 | local sheetHeight = atlas:getHeight() / tileheight 10 | 11 | local sheetCounter = 1 12 | local quads = {} 13 | 14 | for y = 0, sheetHeight - 1 do 15 | for x = 0, sheetWidth - 1 do 16 | -- this quad represents a square cutout of our atlas that we can 17 | -- individually draw instead of the whole atlas 18 | quads[sheetCounter] = 19 | love.graphics.newQuad(x * tilewidth, y * tileheight, tilewidth, 20 | tileheight, atlas:getDimensions()) 21 | sheetCounter = sheetCounter + 1 22 | end 23 | end 24 | 25 | return quads 26 | end 27 | -------------------------------------------------------------------------------- /mario-8/fonts/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-8/fonts/font.ttf -------------------------------------------------------------------------------- /mario-8/graphics/mario.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-8/graphics/mario.png -------------------------------------------------------------------------------- /mario-8/graphics/mario1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-8/graphics/mario1.png -------------------------------------------------------------------------------- /mario-8/graphics/playable_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-8/graphics/playable_left.png -------------------------------------------------------------------------------- /mario-8/graphics/playable_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-8/graphics/playable_right.png -------------------------------------------------------------------------------- /mario-8/graphics/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-8/graphics/tiles.png -------------------------------------------------------------------------------- /mario-8/graphics/tiles_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-8/graphics/tiles_2.png -------------------------------------------------------------------------------- /mario-8/main.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Super Mario Bros. Demo 3 | Author: Colton Ogden 4 | Original Credit: Nintendo 5 | 6 | Adds jumping to Mario. 7 | ]] 8 | 9 | -- global key-handling 10 | love.keyboard.keysPressed = {} 11 | love.keyboard.keysReleased = {} 12 | 13 | push = require 'push' 14 | 15 | require 'Map' 16 | require 'Player' 17 | 18 | -- close resolution to NES but 16:9 19 | virtualWidth = 432 20 | virtualHeight = 243 21 | 22 | -- actual window resolution 23 | windowWidth = 1280 24 | windowHeight = 720 25 | 26 | -- seed RNG 27 | math.randomseed(os.time()) 28 | 29 | -- an object to contain our map data 30 | map = Map:create() 31 | 32 | -- performs initialization of all objects and data needed by program 33 | function love.load() 34 | -- makes upscaling look pixel-y instead of blurry 35 | love.graphics.setDefaultFilter('nearest', 'nearest') 36 | 37 | -- sets up a different, better-looking retro font as our default 38 | love.graphics.setFont(love.graphics.newFont('fonts/font.ttf', 8)) 39 | 40 | -- sets up virtual screen resolution for an authentic retro feel 41 | push:setupScreen(virtualWidth, virtualHeight, windowWidth, windowHeight, { 42 | fullscreen = false, 43 | resizable = true 44 | }) 45 | end 46 | 47 | -- called whenever window is resized 48 | function love.resize(w, h) 49 | push:resize(w, h) 50 | end 51 | 52 | -- global key pressed function 53 | function love.keyboard.wasPressed(key) 54 | if (love.keyboard.keysPressed[key]) then 55 | return true 56 | else 57 | return false 58 | end 59 | end 60 | 61 | -- global key released function 62 | function love.keyboard.wasReleased(key) 63 | if (love.keyboard.keysReleased[key]) then 64 | return true 65 | else 66 | return false 67 | end 68 | end 69 | 70 | -- called whenever a key is pressed 71 | function love.keypressed(key) 72 | if key == 'escape' then 73 | love.event.quit() 74 | end 75 | 76 | love.keyboard.keysPressed[key] = true 77 | end 78 | 79 | -- called whenever a key is released 80 | function love.keyreleased(key) 81 | love.keyboard.keysReleased[key] = true 82 | end 83 | 84 | -- called every frame, with dt passed in as delta in time since last frame 85 | function love.update(dt) 86 | map:update(dt) 87 | 88 | -- reset all keys pressed and released this frame 89 | love.keyboard.keysPressed = {} 90 | love.keyboard.keysReleased = {} 91 | end 92 | 93 | -- called each frame, used to render to the screen 94 | function love.draw() 95 | -- begin virtual resolution drawing 96 | push:apply('start') 97 | 98 | -- clear screen using Mario background blue 99 | love.graphics.clear(108, 140, 255, 255) 100 | 101 | -- renders our map object onto the screen 102 | love.graphics.translate(math.floor(-map.camX), math.floor(-map.camY)) 103 | map:render() 104 | 105 | -- end virtual resolution 106 | push:apply('end') 107 | end 108 | -------------------------------------------------------------------------------- /mario-9-exercise/Animation.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Holds a collection of frames that switch depending on how much time has 3 | passed. 4 | ]] 5 | 6 | Animation = {} 7 | Animation.__index = Animation 8 | 9 | function Animation:create(params) 10 | local this = { 11 | texture = params.texture, 12 | 13 | -- quads defining this animation 14 | frames = params.frames or {}, 15 | 16 | -- time in seconds each frame takes (1/20 by default) 17 | interval = params.interval or 0.05, 18 | 19 | -- stores amount of time that has elapsed 20 | timer = 0, 21 | 22 | currentFrame = 1 23 | } 24 | 25 | setmetatable(this, self) 26 | return this 27 | end 28 | 29 | function Animation:getCurrentFrame() 30 | return self.frames[self.currentFrame] 31 | end 32 | 33 | function Animation:restart() 34 | self.timer = 0 35 | self.currentFrame = 1 36 | end 37 | 38 | function Animation:update(dt) 39 | self.timer = self.timer + dt 40 | 41 | -- iteratively subtract interval from timer to proceed in the animation, 42 | -- in case we skipped more than one frame 43 | while self.timer > self.interval do 44 | self.timer = self.timer - self.interval 45 | self.currentFrame = (self.currentFrame + 1) % #self.frames 46 | if self.currentFrame == 0 then self.currentFrame = 1 end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /mario-9-exercise/Player.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Represents our player in the game, with its own sprite. 3 | ]] 4 | 5 | require 'Animation' 6 | 7 | Player = {} 8 | Player.__index = Player 9 | 10 | local WALKING_SPEED = 140 11 | local JUMP_VELOCITY = 400 12 | 13 | function Player:create(map) 14 | local this = { 15 | x = 0, 16 | y = 0, 17 | width = 16, 18 | height = 32, 19 | 20 | -- offset from top left to center to support sprite flipping 21 | xOffset = 8, 22 | yOffset = 16, 23 | 24 | -- reference to map for checking tiles 25 | map = map, 26 | texture = love.graphics.newImage('graphics/mario1.png'), 27 | 28 | -- current animation frame 29 | currentFrame = nil, 30 | 31 | -- current animation being updated 32 | animation = nil, 33 | 34 | -- used to determine behavior and animations 35 | state = 'idle', 36 | 37 | -- determines sprite flipping 38 | direction = 'right', 39 | 40 | -- x and y velocity 41 | dx = 0, 42 | dy = 0 43 | } 44 | 45 | -- position on top of map tiles 46 | this.y = map.tileHeight * ((map.mapHeight - 2) / 2) - this.height 47 | this.x = map.tileWidth * 10 48 | 49 | -- initialize all player animations 50 | this.animations = { 51 | ['idle'] = Animation:create({ 52 | texture = this.texture, 53 | frames = { 54 | love.graphics.newQuad(0, 0, 16, 32, this.texture:getDimensions()) 55 | } 56 | }), 57 | ['walking'] = Animation:create({ 58 | texture = this.texture, 59 | frames = { 60 | love.graphics.newQuad(18, 0, 16, 32, this.texture:getDimensions()), 61 | love.graphics.newQuad(34, 0, 16, 32, this.texture:getDimensions()), 62 | love.graphics.newQuad(50, 0, 16, 32, this.texture:getDimensions()), 63 | love.graphics.newQuad(34, 0, 16, 32, this.texture:getDimensions()), 64 | }, 65 | interval = 0.07 66 | }), 67 | ['jumping'] = Animation:create({ 68 | texture = this.texture, 69 | frames = { 70 | love.graphics.newQuad(88, 0, 16, 32, this.texture:getDimensions()) 71 | } 72 | }) 73 | } 74 | 75 | -- initialize animation and current frame we should render 76 | this.animation = this.animations['idle'] 77 | this.currentFrame = this.animation:getCurrentFrame() 78 | 79 | -- behavior map we can call based on player state 80 | this.behaviors = { 81 | ['idle'] = function(dt) 82 | -- begin moving if left or right is pressed 83 | if love.keyboard.wasPressed('space') then 84 | this.dy = -JUMP_VELOCITY 85 | this.state = 'jumping' 86 | this.animation = this.animations['jumping'] 87 | elseif love.keyboard.isDown('left') then 88 | direction = 'left' 89 | this.dx = -WALKING_SPEED 90 | this.state = 'walking' 91 | this.animations['walking']:restart() 92 | this.animation = this.animations['walking'] 93 | elseif love.keyboard.isDown('right') then 94 | direction = 'right' 95 | this.dx = WALKING_SPEED 96 | this.state = 'walking' 97 | this.animations['walking']:restart() 98 | this.animation = this.animations['walking'] 99 | else 100 | this.dx = 0 101 | end 102 | end, 103 | ['walking'] = function(dt) 104 | -- keep track of input to switch movement while walking, or reset 105 | -- to idle if we're not moving 106 | if love.keyboard.wasPressed('space') then 107 | this.dy = -JUMP_VELOCITY 108 | this.state = 'jumping' 109 | this.animation = this.animations['jumping'] 110 | elseif love.keyboard.isDown('left') then 111 | direction = 'left' 112 | this.dx = -WALKING_SPEED 113 | elseif love.keyboard.isDown('right') then 114 | direction = 'right' 115 | this.dx = WALKING_SPEED 116 | else 117 | this.dx = 0 118 | this.state = 'idle' 119 | this.animation = this.animations['idle'] 120 | end 121 | end, 122 | ['jumping'] = function(dt) 123 | if love.keyboard.isDown('left') then 124 | direction = 'left' 125 | this.dx = -WALKING_SPEED 126 | elseif love.keyboard.isDown('right') then 127 | direction = 'right' 128 | this.dx = WALKING_SPEED 129 | end 130 | 131 | -- apply map's gravity before y velocity 132 | this.dy = this.dy + this.map.gravity 133 | 134 | if this.y == this.map.tileHeight * 135 | ((this.map.mapHeight - 2) / 2) - this.height then 136 | this.dy = 0 137 | this.state = 'idle' 138 | this.animation = this.animations['idle'] 139 | end 140 | end 141 | } 142 | 143 | setmetatable(this, self) 144 | return this 145 | end 146 | 147 | function Player:update(dt) 148 | self.behaviors[self.state](dt) 149 | self.animation:update(dt) 150 | self.currentFrame = self.animation:getCurrentFrame() 151 | self.x = self.x + self.dx * dt 152 | 153 | -- if we have negative y velocity (jumping), check if we collide 154 | -- with any blocks above us 155 | if self.dy < 0 then 156 | -- TODO: if any two tiles right above us are not empty, reset our Y 157 | -- velocity, then check whether they're question blocks and change them 158 | -- to a dark version if they are 159 | -- HINT: be sure to check both tiles right above you by using your X 160 | -- coordinate and your X coordinate + your width! 161 | -- HINT: self.map:tileAt should come in handy! 162 | -- don't forget to call self.map:refreshSpriteBatch if we change a tile! 163 | -- HINT: self.map:setTile will come in handy 164 | -- HINT: math.floor will come in handy so we don't try and accidentally 165 | -- set a floating-point index in our map! 166 | -- HINT: 167 | end 168 | 169 | -- apply velocity and prevent going beneath tiles 170 | self.y = math.min(self.y + self.dy * dt, self.map.tileHeight * 171 | ((self.map.mapHeight - 2) / 2) - self.height) 172 | end 173 | 174 | function Player:render() 175 | local scaleX 176 | 177 | -- set negative x scale factor if facing left, which will flip the sprite 178 | -- when applied 179 | if direction == 'right' then 180 | scaleX = 1 181 | else 182 | scaleX = -1 183 | end 184 | 185 | -- draw sprite with scale factor and offsets 186 | love.graphics.draw(self.texture, self.currentFrame, math.floor(self.x + self.xOffset), 187 | math.floor(self.y + self.yOffset), 0, scaleX, 1, self.xOffset, self.yOffset) 188 | end 189 | -------------------------------------------------------------------------------- /mario-9-exercise/Util.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Stores utility functions used by our game engine. 3 | ]] 4 | 5 | -- takes a texture, width, and height of tiles and splits it into quads 6 | -- that can be individually drawn 7 | function generateQuads(atlas, tilewidth, tileheight) 8 | local sheetWidth = atlas:getWidth() / tilewidth 9 | local sheetHeight = atlas:getHeight() / tileheight 10 | 11 | local sheetCounter = 1 12 | local quads = {} 13 | 14 | for y = 0, sheetHeight - 1 do 15 | for x = 0, sheetWidth - 1 do 16 | -- this quad represents a square cutout of our atlas that we can 17 | -- individually draw instead of the whole atlas 18 | quads[sheetCounter] = 19 | love.graphics.newQuad(x * tilewidth, y * tileheight, tilewidth, 20 | tileheight, atlas:getDimensions()) 21 | sheetCounter = sheetCounter + 1 22 | end 23 | end 24 | 25 | return quads 26 | end 27 | -------------------------------------------------------------------------------- /mario-9-exercise/fonts/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-9-exercise/fonts/font.ttf -------------------------------------------------------------------------------- /mario-9-exercise/graphics/mario.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-9-exercise/graphics/mario.png -------------------------------------------------------------------------------- /mario-9-exercise/graphics/mario1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-9-exercise/graphics/mario1.png -------------------------------------------------------------------------------- /mario-9-exercise/graphics/playable_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-9-exercise/graphics/playable_left.png -------------------------------------------------------------------------------- /mario-9-exercise/graphics/playable_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-9-exercise/graphics/playable_right.png -------------------------------------------------------------------------------- /mario-9-exercise/graphics/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-9-exercise/graphics/tiles.png -------------------------------------------------------------------------------- /mario-9-exercise/graphics/tiles_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-9-exercise/graphics/tiles_2.png -------------------------------------------------------------------------------- /mario-9-exercise/main.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Super Mario Bros. Demo 3 | Author: Colton Ogden 4 | Original Credit: Nintendo 5 | 6 | Adds ability to hit bricks. 7 | ]] 8 | 9 | -- global key-handling 10 | love.keyboard.keysPressed = {} 11 | love.keyboard.keysReleased = {} 12 | 13 | push = require 'push' 14 | 15 | require 'Map' 16 | require 'Player' 17 | 18 | -- close resolution to NES but 16:9 19 | virtualWidth = 432 20 | virtualHeight = 243 21 | 22 | -- actual window resolution 23 | windowWidth = 1280 24 | windowHeight = 720 25 | 26 | -- seed RNG 27 | math.randomseed(os.time()) 28 | 29 | -- an object to contain our map data 30 | map = Map:create() 31 | 32 | -- performs initialization of all objects and data needed by program 33 | function love.load() 34 | -- makes upscaling look pixel-y instead of blurry 35 | love.graphics.setDefaultFilter('nearest', 'nearest') 36 | 37 | -- sets up a different, better-looking retro font as our default 38 | love.graphics.setFont(love.graphics.newFont('fonts/font.ttf', 8)) 39 | 40 | -- sets up virtual screen resolution for an authentic retro feel 41 | push:setupScreen(virtualWidth, virtualHeight, windowWidth, windowHeight, { 42 | fullscreen = false, 43 | resizable = true 44 | }) 45 | end 46 | 47 | -- called whenever window is resized 48 | function love.resize(w, h) 49 | push:resize(w, h) 50 | end 51 | 52 | -- global key pressed function 53 | function love.keyboard.wasPressed(key) 54 | if (love.keyboard.keysPressed[key]) then 55 | return true 56 | else 57 | return false 58 | end 59 | end 60 | 61 | -- global key released function 62 | function love.keyboard.wasReleased(key) 63 | if (love.keyboard.keysReleased[key]) then 64 | return true 65 | else 66 | return false 67 | end 68 | end 69 | 70 | -- called whenever a key is pressed 71 | function love.keypressed(key) 72 | if key == 'escape' then 73 | love.event.quit() 74 | end 75 | 76 | love.keyboard.keysPressed[key] = true 77 | end 78 | 79 | -- called whenever a key is released 80 | function love.keyreleased(key) 81 | love.keyboard.keysReleased[key] = true 82 | end 83 | 84 | -- called every frame, with dt passed in as delta in time since last frame 85 | function love.update(dt) 86 | map:update(dt) 87 | 88 | -- reset all keys pressed and released this frame 89 | love.keyboard.keysPressed = {} 90 | love.keyboard.keysReleased = {} 91 | end 92 | 93 | -- called each frame, used to render to the screen 94 | function love.draw() 95 | -- begin virtual resolution drawing 96 | push:apply('start') 97 | 98 | -- clear screen using Mario background blue 99 | love.graphics.clear(108, 140, 255, 255) 100 | 101 | -- renders our map object onto the screen 102 | love.graphics.translate(math.floor(-map.camX), math.floor(-map.camY)) 103 | map:render() 104 | 105 | -- end virtual resolution 106 | push:apply('end') 107 | end 108 | -------------------------------------------------------------------------------- /mario-9/Animation.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Holds a collection of frames that switch depending on how much time has 3 | passed. 4 | ]] 5 | 6 | Animation = {} 7 | Animation.__index = Animation 8 | 9 | function Animation:create(params) 10 | local this = { 11 | texture = params.texture, 12 | 13 | -- quads defining this animation 14 | frames = params.frames or {}, 15 | 16 | -- time in seconds each frame takes (1/20 by default) 17 | interval = params.interval or 0.05, 18 | 19 | -- stores amount of time that has elapsed 20 | timer = 0, 21 | 22 | currentFrame = 1 23 | } 24 | 25 | setmetatable(this, self) 26 | return this 27 | end 28 | 29 | function Animation:getCurrentFrame() 30 | return self.frames[self.currentFrame] 31 | end 32 | 33 | function Animation:restart() 34 | self.timer = 0 35 | self.currentFrame = 1 36 | end 37 | 38 | function Animation:update(dt) 39 | self.timer = self.timer + dt 40 | 41 | -- iteratively subtract interval from timer to proceed in the animation, 42 | -- in case we skipped more than one frame 43 | while self.timer > self.interval do 44 | self.timer = self.timer - self.interval 45 | self.currentFrame = (self.currentFrame + 1) % #self.frames 46 | if self.currentFrame == 0 then self.currentFrame = 1 end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /mario-9/Util.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Stores utility functions used by our game engine. 3 | ]] 4 | 5 | -- takes a texture, width, and height of tiles and splits it into quads 6 | -- that can be individually drawn 7 | function generateQuads(atlas, tilewidth, tileheight) 8 | local sheetWidth = atlas:getWidth() / tilewidth 9 | local sheetHeight = atlas:getHeight() / tileheight 10 | 11 | local sheetCounter = 1 12 | local quads = {} 13 | 14 | for y = 0, sheetHeight - 1 do 15 | for x = 0, sheetWidth - 1 do 16 | -- this quad represents a square cutout of our atlas that we can 17 | -- individually draw instead of the whole atlas 18 | quads[sheetCounter] = 19 | love.graphics.newQuad(x * tilewidth, y * tileheight, tilewidth, 20 | tileheight, atlas:getDimensions()) 21 | sheetCounter = sheetCounter + 1 22 | end 23 | end 24 | 25 | return quads 26 | end 27 | -------------------------------------------------------------------------------- /mario-9/fonts/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-9/fonts/font.ttf -------------------------------------------------------------------------------- /mario-9/graphics/mario.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-9/graphics/mario.png -------------------------------------------------------------------------------- /mario-9/graphics/mario1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-9/graphics/mario1.png -------------------------------------------------------------------------------- /mario-9/graphics/playable_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-9/graphics/playable_left.png -------------------------------------------------------------------------------- /mario-9/graphics/playable_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-9/graphics/playable_right.png -------------------------------------------------------------------------------- /mario-9/graphics/tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-9/graphics/tiles.png -------------------------------------------------------------------------------- /mario-9/graphics/tiles_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs50/mario-demo/c1e8b56fc17b4e2350e9c83c5539d39df4c2d0b6/mario-9/graphics/tiles_2.png -------------------------------------------------------------------------------- /mario-9/main.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Super Mario Bros. Demo 3 | Author: Colton Ogden 4 | Original Credit: Nintendo 5 | 6 | Adds ability to hit bricks. 7 | ]] 8 | 9 | -- global key-handling 10 | love.keyboard.keysPressed = {} 11 | love.keyboard.keysReleased = {} 12 | 13 | push = require 'push' 14 | 15 | require 'Map' 16 | require 'Player' 17 | 18 | -- close resolution to NES but 16:9 19 | virtualWidth = 432 20 | virtualHeight = 243 21 | 22 | -- actual window resolution 23 | windowWidth = 1280 24 | windowHeight = 720 25 | 26 | -- seed RNG 27 | math.randomseed(os.time()) 28 | 29 | -- an object to contain our map data 30 | map = Map:create() 31 | 32 | -- performs initialization of all objects and data needed by program 33 | function love.load() 34 | -- makes upscaling look pixel-y instead of blurry 35 | love.graphics.setDefaultFilter('nearest', 'nearest') 36 | 37 | -- sets up a different, better-looking retro font as our default 38 | love.graphics.setFont(love.graphics.newFont('fonts/font.ttf', 8)) 39 | 40 | -- sets up virtual screen resolution for an authentic retro feel 41 | push:setupScreen(virtualWidth, virtualHeight, windowWidth, windowHeight, { 42 | fullscreen = false, 43 | resizable = true 44 | }) 45 | end 46 | 47 | -- called whenever window is resized 48 | function love.resize(w, h) 49 | push:resize(w, h) 50 | end 51 | 52 | -- global key pressed function 53 | function love.keyboard.wasPressed(key) 54 | if (love.keyboard.keysPressed[key]) then 55 | return true 56 | else 57 | return false 58 | end 59 | end 60 | 61 | -- global key released function 62 | function love.keyboard.wasReleased(key) 63 | if (love.keyboard.keysReleased[key]) then 64 | return true 65 | else 66 | return false 67 | end 68 | end 69 | 70 | -- called whenever a key is pressed 71 | function love.keypressed(key) 72 | if key == 'escape' then 73 | love.event.quit() 74 | end 75 | 76 | love.keyboard.keysPressed[key] = true 77 | end 78 | 79 | -- called whenever a key is released 80 | function love.keyreleased(key) 81 | love.keyboard.keysReleased[key] = true 82 | end 83 | 84 | -- called every frame, with dt passed in as delta in time since last frame 85 | function love.update(dt) 86 | map:update(dt) 87 | 88 | -- reset all keys pressed and released this frame 89 | love.keyboard.keysPressed = {} 90 | love.keyboard.keysReleased = {} 91 | end 92 | 93 | -- called each frame, used to render to the screen 94 | function love.draw() 95 | -- begin virtual resolution drawing 96 | push:apply('start') 97 | 98 | -- clear screen using Mario background blue 99 | love.graphics.clear(108, 140, 255, 255) 100 | 101 | -- renders our map object onto the screen 102 | love.graphics.translate(math.floor(-map.camX), math.floor(-map.camY)) 103 | map:render() 104 | 105 | -- end virtual resolution 106 | push:apply('end') 107 | end 108 | --------------------------------------------------------------------------------