Update "literary" parts.
This commit is contained in:
parent
b9e1de7e67
commit
d1952ef33e
@ -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
|
||||||
|
2
main.lua
2
main.lua
@ -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
|
||||||
|
2
map.lua
2
map.lua
@ -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
|
||||||
|
11
tile.lua
11
tile.lua
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user