diff --git a/README.md b/README.md index 50580e4..616d6d9 100644 --- a/README.md +++ b/README.md @@ -2,79 +2,79 @@ ## INTRODUCTION this is a library for using hexagonal grids in amulet/lua. -it is extremely incomplete. the following list of features is -either implemented shoddily, or not at all. +it is extremely incomplete. the following list of features is +either implemented shoddily, or not at all. -if you want an actual good resource, go to TODO LINK. +if you want an actual good resource, go to [amit's guide to hexagonal grids](#RESOURCES USED TO DEVELOP THIS LIBRARY, AND FOR WHICH I AM GRATEFUL). ## GETTING STARTED 1) initialize a map. -2) iterate over the map and draw some hexagons. +2) iterate over the map and draw some hexagons. ## COORDINATE SYSTEMS - + As much coordinate manipulation as possible is done internally. Depending on the task, uses either Axial, Cube, or Offset coordinates. ## MAPS & MAP STORAGE - + Some map shapes: parallelogram, rectangular, hexagonal, triangular. (and more) The storage system used is based on the map shape - see chart: - -| SHAPE | MAP STORAGE | + +| SHAPE | MAP STORAGE | | ----------------- | --------------------------------------------- | -| parallelogram | unordered, hash-like | -| rectangular | unordered, hash-like | -| hexagonal | unordered, hash-like | -| triangular | unordered, hash-like | -| ring | ordered, array-like | -| spiral | ordered, array-like | -| arbitrary | unordered, hash-like | - +| parallelogram | unordered, hash-like | +| rectangular | unordered, hash-like | +| hexagonal | unordered, hash-like | +| triangular | unordered, hash-like | +| ring | ordered, array-like | +| spiral | ordered, array-like | +| arbitrary | unordered, hash-like | + * note that a spiral map is just a hexagonal one with a particular order. -By default, the unordered, hash-like maps have pseudo-random noise stored -as their values. This can be useful for a whole bunch of things, but if you -wish, you can simply iterate over your map and set every value to 'true'. +By default, the unordered, hash-like maps have pseudo-random noise stored +as their values. This can be useful for a whole bunch of things, but if you +wish, you can simply iterate over your map and set every value to 'true'. ## CONVENTIONS AND TERMINOLOGY -If you have read amit's guide to hexagon grids, (see TODO LINK), a lot of the +If you have read amit's guide to hexagon grids, (see TODO LINK), a lot of the terminology will be familiar to you - I utilize many conventions he does in his guide. That being said... -Because so many similar kinds of data structures with different goals are used -in this library it can be hard to remember precisely what they all refer to. +Because so many similar kinds of data structures with different goals are used +in this library it can be hard to remember precisely what they all refer to. The following table shows what each table/vector/array refers to in the code: -| NAME | REFERS TO | +| NAME | REFERS TO | | ---- | ------------------------------------------------------------ | | cube | xyz, *vector* used for most maps, with constraint x+y+z=0. | | pix | xy, *vector* true screen pixel coordinates | | off | xy, 'offset', *vector* used for UI implementations | | map | xy, *table* of unit hexagon centerpoints arranged in a shape | - * note that 'axial', vec2() coordinates are a subset of cube coordinates, - where you simply omit the z value. for many algorithms this is done, but - instead of using a seperate reference name 'axial' in these cases, I used - the name 'cube' for both. I found this to be simpler. when an algorithm + * note that 'axial', vec2() coordinates are a subset of cube coordinates, + where you simply omit the z value. for many algorithms this is done, but + instead of using a seperate reference name 'axial' in these cases, I used + the name 'cube' for both. I found this to be simpler. when an algorithm asks for a cube, give it a cube. if you want to know if it works with axial as well, look at the code and see if it uses a 'z' value. -Other terminology: +Other terminology: * TODO -## RESOURCES USED TO DEVELOP THIS LIBRARY, AND FOR WHICH I AM GRATEFUL - +## RESOURCES USED TO DEVELOP THIS LIBRARY, AND FOR WHICH I AM GRATEFUL + * [Hex Map 1](https://catlikecoding.com/unity/tutorials/hex-map/) - unity tutorial for hexagon grids with some useful generalized math. * [3Blue1Brown - Essence of Linear Algebra](https://youtube.com/watch?v=fNk_zzaMoSs&list=PLZHQObOWTQDPD3MizzM2xVFitgF8hE_ab) - amazing series on linear algebra by 3Blue1Brown -* [Hexagonal Grids](https://redblobgames.com/grid/hexagons) - THE resource on hexagonal grids on the internet. - +* [Hexagonal Grids](https://redblobgames.com/grid/hexagons) - THE resource on hexagonal grids on the internet. + * [Amulet Docs](http://amulet.xyz/doc) - amulet documentation. - + diff --git a/hex.lua b/hex.lua index 0d422c5..0d62931 100644 --- a/hex.lua +++ b/hex.lua @@ -13,21 +13,21 @@ end ============================================================================]]-- -- all possible vector directions from a given hex by edge -local CUBE_DIRECTIONS = {vec2( 1 , 0), +local CUBE_DIRECTIONS = {vec2( 0 , 1), + vec2( 1 , 0), vec2( 1 , -1), vec2( 0 , -1), vec2(-1 , 0), - vec2(-1 , 1), - vec2( 0 , 1)} + vec2(-1 , 1)} -- return hex vector direction via integer index |direction|. function cube_direction(direction) - return CUBE_DIRECTIONS[(6 + (direction % 6)) % 6 + 1] + return CUBE_DIRECTIONS[(direction % 6) % 6 + 1] end -- return hexagon adjacent to |hex| in integer index |direction|. function cube_neighbour(hex, direction) - return hex + CUBE_DIRECTIONS[(6 + (direction % 6)) % 6 + 1] + return hex + CUBE_DIRECTIONS[(direction % 6) % 6 + 1] end -- return cube coords at location 60deg away to the left; counter-clockwise @@ -95,7 +95,7 @@ end function pixel_to_cube(pix, layout) local W = layout.orientation.W - local pix = (pix - layout.origin) / layout.size + local pix = (pix - layout.origin) / layout.size local s = W[1][1] * pix[1] + W[1][2] * pix[2] local t = W[2][1] * pix[1] + W[2][2] * pix[2] @@ -126,8 +126,7 @@ function offset_to_cube(off) end --[[============================================================================ - ----- MAPS & STORAGE ----- - + ----- MAPS & STORAGE ----- MAPS STORE CUBE COORDINATES. MAPS STORE CUBE COORDINATES. MAPS STORE CUBE COOR This means, you are not to draw using the coordinates stored in your map. @@ -135,14 +134,26 @@ end If you wish to draw a hexagon to the screen, you must first use cube_to_pixel to retrieve the center of the hexagon on each set of cube coordinates stored - in your map. + in your map. Then, depending on how you are going to draw, either call + am.circle with |sides| = 6, or gather the vertices with hex_corners and + use am.draw - TODO, haven't used am.draw yet. Information about the maps' dimensions are stored in a metatable, so you can - retrieve details about arbitrary maps after they are created. + retrieve details about maps after they are created. + + ----- NOISE ----- + To simplify terrain generation, unordered, hash-like maps automatically + calculate and store perlin noise as their values. You can modify the nature + of the noise by providing different |frequencies| as a tables of values, for + example: {1, 2, 4, 8} or {1, 0.5, 0.25, 0.125}. These just increase the + complexity of the curvature of the noise. The default is {1}. + ----- TODO ----- TODO make all functions work regardless of layout. as it stands, they kind of do, just not always nicely. + ============================================================================]]-- +----- ORDERED MAPS ----- -- returns ordered ring-shaped map of |radius| from |center|. function ring_map(center, radius) @@ -163,8 +174,9 @@ function ring_map(center, radius) end -- returns ordered hexagonal map of |radius| rings from |center|. --- the only difference between hex_spiral_map and hex_hexagonal_map is that --- hex_spiral_map is ordered, in a spiral path from the |center|. +-- the only difference between spiral_map and hexagonal_map is that +-- spiral_map is ordered, in a spiral path from the |center|. + function spiral_map(center, radius) local map = {center} local mt = {__index={center=center, radius=radius}} @@ -177,7 +189,9 @@ function spiral_map(center, radius) return map end --- returns unordered parallelogram-shaped map of |width| and |height|. +----- UNORDERED, HASH-LIKE MAPS ----- + +-- returns unordered parallelogram-shaped map of |width| and |height| with perlin noise function parallelogram_map(width, height) local map = {} local mt = {__index={width=width, height=height}} @@ -186,13 +200,13 @@ function parallelogram_map(width, height) for i = 0, width do for j = 0, height do - map[vec2(i, -j)] = true + map[vec2(i, j)] = true end end return map end --- returns unordered triangular map of |size|. +-- returns unordered triangular map of |size| with perlin noise function triangular_map(size) local map = {} local mt = {__index={size=size}} @@ -207,7 +221,7 @@ function triangular_map(size) return map end --- returns unordered hexagonal map of |radius|. +-- returns unordered hexagonal map of |radius| with perlin noise function hexagonal_map(radius) local map = {} local mt = {__index={radius=radius}} @@ -225,21 +239,60 @@ function hexagonal_map(radius) return map end --- returns unordered rectangular map of |width| and |height|. -function rectangular_map(width, height) +-- returns unordered rectangular map of |width| and |height| with perlin noise +function rectangular_map(width, height, frequencies) + local map = {} local mt = {__index={width=width, height=height}} + local frequencies = frequencies or {1} setmetatable(map, mt) for i = 0, width do for j = 0, height do - map[vec2(i, -j - math.floor(i/2))] = true + + -- calculate noise + local idelta = assert(i / width, "width must be greater than 0") + local jdelta = assert(j / height, "height must be greater than 0") + local noise = 0 + + for _,freq in pairs(frequencies) do + noise = noise + 1/freq * math.perlin(vec2(freq * idelta, + freq * jdelta)) + end + + -- this is what makes it a rectangle + local hex = vec2(i, j - math.floor(i/2)) + + -- store hex in the map paired with its associated noise value + map[hex] = noise end end return map end +--[[============================================================================ + ----- NOISE ----- +============================================================================]]-- + +function simplex_map(frequency, exponent, width, height) + local map = {} + + for i = 0, height do + for j = 0, width do + local idelta = i/width - 0.5 + local jdelta = j/height - 0.5 + map[vec2(i, j)] = math.simplex(idelta, jdelta) + end + end +end + + + + + + + --[[============================================================================ ----- PATHFINDING ----- ============================================================================]]-- diff --git a/main.lua b/main.lua index a24cc10..fe4834e 100644 --- a/main.lua +++ b/main.lua @@ -48,7 +48,7 @@ am.ascii_color_map = } --[[============================================================================ - ----- WINDOW SETUP ----- + ----- SETUP ----- ============================================================================]]-- local win = am.window { -- base resolution = 3/4 * WXGA standard 16:10 @@ -58,29 +58,26 @@ local win = am.window clear_color = BASE03 } ---[[============================================================================ - -============================================================================]]-- local map = rectangular_map(45, 31) local layout = layout(vec2(-268, win.top - 10)) --[[============================================================================ ----- SCENE GRAPH / NODES ----- ============================================================================]]-- -local panel; local world; local game; --[[ +local panel; local world; local game --[[ panel | - #------> game ------> win.scene + +------> game ------> win.scene | world ---==========================================================================]]-- -local backdrop; local menu; local title; --[[ + ]]-- +local backdrop; local menu; local title --[[ backdrop | - #------> title ------> win.scene + +------> title ------> win.scene | menu @@ -105,11 +102,12 @@ end function show_coords() game:action(function() game:remove("coords") - game:remove("select") + game:remove("selected") local hex = pixel_to_cube(win:mouse_position(), layout) local mouse = cube_to_offset(hex) + -- check mouse is within bounds of game map if mouse.x > 0 and mouse.x < map.width and mouse.y > 0 and mouse.y < map.height then @@ -120,9 +118,9 @@ function show_coords() world:append(coords) - local color = vec4(1, 1, 1, 0.2) + local color = vec4(1) local pix = cube_to_pixel(hex, layout) - world:append(am.circle(pix, layout.size.x, color, 6):tag"select") + world:append(am.circle(pix, layout.size.x, color, 6):tag"selected") end end) end @@ -135,37 +133,44 @@ end function game_init() + -- setup nodes world = am.group{}:tag"world" panel = am.group{}:tag"panel" game = am.group{world, panel}:tag"game" - local hexes = {} - for cube,_ in pairs(map) do - hexes[math.perlin(cube)] = cube_to_pixel(cube, layout) - end - + -- render world world:action(coroutine.create(function() + + -- background panel for gui elements panel:append(am.rect(win.left, win.top, -268, win.bottom):tag"bg") - for noise, hex in pairs(hexes) do - local off = cube_to_offset(hex) - local tag = tostring(hex) + -- begin map generation + for hex,noise in pairs(map) do + + -- determine cell color based on noise + local color = vec4((noise + 1) / 2) -- determine cell shading mask based on map position + local off = cube_to_offset(hex) local mask = vec4(0, 0, 0, math.max(((off.x-23)/30)^2, ((off.y-16)/20)^2)) + -- determine hexagon center for drawing + local center = cube_to_pixel(hex, layout) - -- determine cell color based on noise - local color = vec4(math.random()) - mask + -- prepend hexagon to screen + world:prepend(am.circle(center, 11, color, 6):tag(tostring(hex))) + am.wait(am.delay(0.01)) + -- fade in bg panel panel"bg".color = BASE03/am.frame_time - - world:prepend(am.circle(hex, 11, color, 6):tag(tag)) - am.wait(am.delay(0.01)) end - show_coords() - keep_time() + + show_coords() -- mouse-hover events + keep_time() -- scoring + end)) + + -- make it so win.scene = game end diff --git a/sprites.lua b/sprites.lua deleted file mode 100644 index e21578b..0000000 --- a/sprites.lua +++ /dev/null @@ -1,54 +0,0 @@ - - -grass1 = -[[ -......gggggggggg...... -.....ggggKggggggg..... -....ggggggKggggggg.... -....gggggggggggggg.... -...gggggggggggggggg... -...gggggggggggggggg... -..gggggggggggggggggg.. -..gggggggggggggggggg.. -.ggggggggggkggggggggg. -ggggggggggmmgggggggggg -.gggggggggggggggggggg. -..ggggggggglgggggggg.. -..gggggggglggggggggg.. -...gggggggLgggggggg... -...gggggggggggggggg... -....ggggggsggggggg.... -.....ggggggsgggggg.... -.....ggggggSggggg..... -......gggggggggg...... -]] - - - - - - - - - - - - - - -titlebutton = -[[ -KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK -KwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwK -KwkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkK -KwkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkK -KwkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkK -KwkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkK -KwkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkK -KwkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkK -KwkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkK -KwkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkK -KkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkK -KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK -]] -