Update "literary" parts.

This commit is contained in:
Gonzalo Delgado 2024-09-10 07:45:47 -03:00
parent b9e1de7e67
commit d1952ef33e
4 changed files with 50 additions and 37 deletions

View File

@ -244,13 +244,14 @@ But ~map~ would feel too lonely being the only functional function around, let's
end end
#+end_src #+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 #+begin_src lua :tangle helpers.lua
function helpers.normalize(n, min, max) function helpers.normalize(n, min, max)
local range = max - min local range = max - min
return ((n - min) % range + range) % range + min return ((n - min) % range + range) % range + min
end end
#+end_src #+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: 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 - x :: horizontal pixel position
@ -275,7 +276,9 @@ Finally return the helpers object containing all those functions, and load it fr
#+end_src #+end_src
* Swept AABB * 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. 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 In any case, the idea here is to detect two rectangles colliding and avoid the "tunneling" problem
@ -349,6 +352,9 @@ Lua:
#+end_src #+end_src
* Broad-Phasing collisions with solid tiles * 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 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). 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 #+end_src
* Map class * 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.) 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
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". - 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 #+begin_src lua :tangle map.lua
local Object = require("object") local Object = require("object")
local Tile = require("tile") 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 inspect = require("inspect/inspect")
local Map = Object:extend() 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) function Map:init(filename)
Map.__super.init(self) 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) local platform_layers = helpers.filter(map.layers, function (layer) return layer.name == "platforms" end)
self.width = map.width self.width = map.width
self.height = map.height 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.data = platform_layers[1].data
self.max_pixel = {x=map.width*map.tilewidth, y=map.height*map.tileheight} self.max_pixel = {x=map.width*map.tilewidth, y=map.height*map.tileheight}
end 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() function Map:itertiles()
local function iterator(map, index) local function iterator(map, index)
index = index + 1 index = index + 1
@ -605,7 +626,10 @@ Tile values deserve a quick explanation: Tiled provides a single-dimension array
end end
return iterator, self, 0 return iterator, self, 0
end end
#+end_src
Having ~Map:itertiles~ makes drawing the map to the screen super simple:
#+begin_src lua :tangle map.lua
function Map:draw() function Map:draw()
for i, tile in self:itertiles() do for i, tile in self:itertiles() do
love.graphics.setColor(0.9, 0.8, 0.7) 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) love.graphics.rectangle("line", tile.pixel_rect.x, tile.pixel_rect.y, tile.w, tile.h)
end end
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) function Map:get_tile_from_pixel(pixel)
local tile_x = math.floor(pixel.x/self.tilewidth) + 1 local tile_x = math.floor(pixel.x/self.tilewidth) + 1
local tile_y = math.floor(pixel.y/self.tileheight) + 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 * Tile class
#+begin_src lua :tangle tile.lua Tiles will be represented as very simple objects consisting of basic attributes such as:
local inspect = require("inspect/inspect") - width and height
local Vector = require("vector") - tile-space position (not pixel)
local helpers = require("helpers") - 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() 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) function Tile:init(x, y, w, h, value)
Tile.__super.init(self, x, y) 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) self.solid = not (value == 0 or value == nil)
end 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 return Tile
#+end_src #+end_src
#+begin_src lua
local Tile = require("tile")
#+end_src
* Entity class * 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?). 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 ** DONE Bugfix level wrap-around
Can't move past right side of the level, but going to the left works (?) 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é> 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 ** TODO Player slide
Tweak friction values Tweak friction values

View File

@ -121,8 +121,6 @@ end
local states = require("states") local states = require("states")
local Tile = require("tile")
local Entity = require("entity") local Entity = require("entity")
local scale_width, scale_height, current_state local scale_width, scale_height, current_state

View File

@ -7,7 +7,7 @@ local Map = Object:extend()
function Map:init(filename) function Map:init(filename)
Map.__super.init(self) 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) local platform_layers = helpers.filter(map.layers, function (layer) return layer.name == "platforms" end)
self.width = map.width self.width = map.width
self.height = map.height self.height = map.height

View File

@ -1,9 +1,4 @@
local inspect = require("inspect/inspect")
local Vector = require("vector") local Vector = require("vector")
local helpers = require("helpers")
local default_w = 8
local default_h = 8
local Tile = Vector:extend() local Tile = Vector:extend()
@ -21,10 +16,4 @@ function Tile:init(x, y, w, h, value)
self.solid = not (value == 0 or value == nil) self.solid = not (value == 0 or value == nil)
end 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 return Tile