From d1952ef33e329293f3d694352c593f8933d7b42e Mon Sep 17 00:00:00 2001 From: Gonzalo Delgado Date: Tue, 10 Sep 2024 07:45:47 -0300 Subject: [PATCH] Update "literary" parts. --- flickyclone.org | 72 +++++++++++++++++++++++++++++++++---------------- main.lua | 2 -- map.lua | 2 +- tile.lua | 11 -------- 4 files changed, 50 insertions(+), 37 deletions(-) diff --git a/flickyclone.org b/flickyclone.org index 7c7481d..70a866a 100644 --- a/flickyclone.org +++ b/flickyclone.org @@ -244,13 +244,14 @@ But ~map~ would feel too lonely being the only functional function around, let's end #+end_src -To achieve Flicky's screen wrap-around trick (or part of it at least), we need a ~normalize~ function that given a number ~n~, it returns a new ~n~ that's within a range offset the original ~n~, so if the screen's width is 100, and ~n~ is ~110~, normalizing it would result in ~10~, if that makes sense: +To achieve Flicky's screen wrap-around trick (or part of it at least), we'll start with a ~normalize~ function that given a number ~n~, it returns a new ~n~ that's within a range offset the original ~n~, so if the screen's width is 100, and ~n~ is ~110~, normalizing it would result in ~10~, if that makes sense: #+begin_src lua :tangle helpers.lua function helpers.normalize(n, min, max) local range = max - min return ((n - min) % range + range) % range + min end #+end_src +[[Try implementing wrap-around with polar coordinates and cosine][I'm planning on later replacing that with polar coordinates]] instead, think of levels being drawn on a cylinder, and the player seamlessly moving around it, so ~normalize~ will hopefully go away in the future. We'll need a function to tell if two rectangles overlap for basic collision detection, with rectangles being Lua tables with these field names: - x :: horizontal pixel position @@ -275,7 +276,9 @@ Finally return the helpers object containing all those functions, and load it fr #+end_src * Swept AABB - +#+begin_notes +This is not being used yet, but I'm keeping it for now since I believe it'll be useful for better collision handling at high speeds, like falls, or enemies going directly at the player. +#+end_notes This is what I ended up learning with more difficulty than I expected. I could probably get away with something simpler for this game, but I want to learn as much as I can. In any case, the idea here is to detect two rectangles colliding and avoid the "tunneling" problem @@ -349,6 +352,9 @@ Lua: #+end_src * Broad-Phasing collisions with solid tiles +#+begin_notes +This is also not wired into the game just yet, although I'm not sure this will be needed, so I may end up removing it. +#+end_notes This basically comes down to finding the (solid) tiles within the rectangle determined by the initial and final positions of an entity (the player for now). @@ -567,11 +573,16 @@ Let's add a function to draw some debug stuff, starting with just the player's p #+end_src * Map class +I'm using the venerable [[https://www.mapeditor.org/][Tiled]] to create the level(s). Tiled is so nice it exports levels as .lua files containing a big table with all the information one can need. -Go through each tile in the map and query its value to draw solid tiles (value > 0) in color (this will later be replaced by drawing the actual tile graphics form the tilesheet.) - -Tile values deserve a quick explanation: Tiled provides a single-dimension array representing each tile on the map. Each element in the array is simply a number representing the tile's index (to be mapped to a spritesheet), with 0 meaning "no tile here". +I'll need an interface for that though, to make it more manageable and fit my style of coding, so let's create a ~Map~ class that can provide useful functionality, such as: +- load the .lua file generated by Tiled +- spit out [[Tile class][~Tile~ objects]] that provide information such as their location and whether their solid + tiles or not +- draw the whole level to the screen +- query tiles by pixel position +Of course, a ~Map~ will be an ~Object subclass: #+begin_src lua :tangle map.lua local Object = require("object") local Tile = require("tile") @@ -579,10 +590,13 @@ Tile values deserve a quick explanation: Tiled provides a single-dimension array local inspect = require("inspect/inspect") local Map = Object:extend() +#+end_src +The map will be initialized with just the path to the Tiled Lua file, which it'll load and get some metadata from. Mainly, it'll look up a "platforms" layer, which is where I'm laying out the blocks that make up a level's platforms. +#+begin_src lua :tangle map.lua function Map:init(filename) Map.__super.init(self) - local map = dofile(filename) -- love.filesystem.load(filename) + local map = love.filesystem.load(filename)() local platform_layers = helpers.filter(map.layers, function (layer) return layer.name == "platforms" end) self.width = map.width self.height = map.height @@ -591,7 +605,14 @@ Tile values deserve a quick explanation: Tiled provides a single-dimension array self.data = platform_layers[1].data self.max_pixel = {x=map.width*map.tilewidth, y=map.height*map.tileheight} end +#+end_src +We'll need a way to iterate through each of the platform tiles of a level. Tiled provides a single-dimension array representing each tile on the map. Each element in the array is simply a number representing the tile's index (to be mapped to a spritesheet), with 0 meaning "no tile here". + +We'll translate that to instances of our [[Tile class][~Tile~ class]] which will use that value for its ~solid~ attribute. + +I tried using the ~__ipairs~ metamethod to get a nice ~for i, tile in ipairs(map) do~ bit of code, but turns out ~__ipairs~ was deprecated and removed from Lua, bummer. Instead, we'll simply have a ~itertiles~ method that will do the same: +#+begin_src lua :tangle map.lua function Map:itertiles() local function iterator(map, index) index = index + 1 @@ -605,7 +626,10 @@ Tile values deserve a quick explanation: Tiled provides a single-dimension array end return iterator, self, 0 end +#+end_src +Having ~Map:itertiles~ makes drawing the map to the screen super simple: +#+begin_src lua :tangle map.lua function Map:draw() for i, tile in self:itertiles() do love.graphics.setColor(0.9, 0.8, 0.7) @@ -617,7 +641,10 @@ Tile values deserve a quick explanation: Tiled provides a single-dimension array love.graphics.rectangle("line", tile.pixel_rect.x, tile.pixel_rect.y, tile.w, tile.h) end end +#+end_src +Finally, we'll need the map to provide the tile for a given pixel position on the screen. This will be useful for how I'm doing collision detection. +#+begin_src lua :tangle map.lua function Map:get_tile_from_pixel(pixel) local tile_x = math.floor(pixel.x/self.tilewidth) + 1 local tile_y = math.floor(pixel.y/self.tileheight) + 1 @@ -630,15 +657,21 @@ Tile values deserve a quick explanation: Tiled provides a single-dimension array * Tile class -#+begin_src lua :tangle tile.lua - local inspect = require("inspect/inspect") - local Vector = require("vector") - local helpers = require("helpers") +Tiles will be represented as very simple objects consisting of basic attributes such as: +- width and height +- tile-space position (not pixel) +- whether their solid or not - local default_w = 8 - local default_h = 8 + +I'm making tiles be vectors in order to easily find adjacent tiles by adding or subtracting unit vectors. +#+begin_src lua :tangle tile.lua + local Vector = require("vector") local Tile = Vector:extend() +#+end_src + +The initializer is otherwise simple, storing the arguments and creating a ~pixel_rect~ table to easily map a tile to its pixels on the screen. +#+begin_src lua :tangle tile.lua function Tile:init(x, y, w, h, value) Tile.__super.init(self, x, y) @@ -654,19 +687,9 @@ Tile values deserve a quick explanation: Tiled provides a single-dimension array self.solid = not (value == 0 or value == nil) end - function Tile:from_pixel(pixel, w, h) - w = w or default_w - h = h or default_h - return Tile:new(math.floor(pixel.x/w) + 1, math.floor(pixel.y/h) + 1, w, h) - end - return Tile #+end_src -#+begin_src lua - local Tile = require("tile") -#+end_src - * Entity class If my mind's bandwidth allows, I may also learn some ECS, so let's start with an entity class that has position and velocity (which should later become components?). @@ -1086,8 +1109,11 @@ Explain ~game.map:box2d_draw~ call for debugging purposes. ** DONE Bugfix level wrap-around Can't move past right side of the level, but going to the left works (?) -** TODO Update "literary" parts +** DONE Update "literary" parts DEADLINE: <2024-09-11 miƩ> +:LOGBOOK: +CLOCK: [2024-09-10 mar 07:08]--[2024-09-10 mar 07:45] => 0:37 +:END: ** TODO Player slide Tweak friction values diff --git a/main.lua b/main.lua index 31d4a0b..729a42a 100644 --- a/main.lua +++ b/main.lua @@ -121,8 +121,6 @@ end local states = require("states") -local Tile = require("tile") - local Entity = require("entity") local scale_width, scale_height, current_state diff --git a/map.lua b/map.lua index db6e376..2fdf9ca 100644 --- a/map.lua +++ b/map.lua @@ -7,7 +7,7 @@ local Map = Object:extend() function Map:init(filename) Map.__super.init(self) - local map = dofile(filename) -- love.filesystem.load(filename) + local map = love.filesystem.load(filename)() local platform_layers = helpers.filter(map.layers, function (layer) return layer.name == "platforms" end) self.width = map.width self.height = map.height diff --git a/tile.lua b/tile.lua index 661b356..3893399 100644 --- a/tile.lua +++ b/tile.lua @@ -1,9 +1,4 @@ -local inspect = require("inspect/inspect") local Vector = require("vector") -local helpers = require("helpers") - -local default_w = 8 -local default_h = 8 local Tile = Vector:extend() @@ -21,10 +16,4 @@ function Tile:init(x, y, w, h, value) self.solid = not (value == 0 or value == nil) end -function Tile:from_pixel(pixel, w, h) - w = w or default_w - h = h or default_h - return Tile:new(math.floor(pixel.x/w) + 1, math.floor(pixel.y/h) + 1, w, h) -end - return Tile