Browse Source

better readme

master
churchianity 6 years ago
parent
commit
f2eeca61b9
  1. 81
      README.md
  2. 100
      src/hexyz.lua
  3. 65
      src/main.lua

81
README.md

@ -1,80 +1,51 @@
## INTRODUCTION ## 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.
This is a small and simple library for using hexagonal grids in amulet + lua. I wrote it for a tower defense game I'm making.
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).
It's not really well documented. If you want an actual good resource, go to [amit's guide to hexagonal grids](https://redblobgames.com/grid/hexagons).
So much of what is here I derived from amit's work.
## GETTING STARTED
1) initialize a map.
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 |
| ----------------- | --------------------------------------------- |
| 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'.
## CONVENTIONS AND TERMINOLOGY
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.
## CONVENTIONS & TERMINOLOGY
If you have read amit's guide to hexagon grid, 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.
The following table shows what each table/vector/array refers to in the code: 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. |
| hex | xyz, *vector* used for most tasks, with constraint x+y+z=0 |
| pix | xy, *vector* true screen pixel coordinates | | pix | xy, *vector* true screen pixel coordinates |
| off | xy, 'offset', *vector* used for UI implementations | | off | xy, 'offset', *vector* used for UI implementations |
| map | xy, *table* of unit hexagon centerpoints arranged in a shape | | 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
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.
* note that 'hex' here is a catch-all term for cube/axial, as they can often be used interchangeably.
Other terminology:
## MAPS & MAP STORAGE
* TODO
The storage system used is based on the map shape - see chart:
## RESOURCES USED TO DEVELOP THIS LIBRARY, AND FOR WHICH I AM GRATEFUL
| SHAPE | STORAGE TYPE | KEY | VALUE |
| ------------- | ---------------------- | ------------ | ------------- |
| ring | ordered, array-like | index | vec2(i, j) |
| spiral | ordered, array-like | index | vec2(i, j) |
| parallelogram | unordered, hash-like | vec2(i, j) | simplex noise |
| rectangular | unordered, hash-like | vec2(i, j) | simplex noise |
| hexagonal | unordered, hash-like | vec2(i, j) | simplex noise |
| triangular | unordered, hash-like | vec2(i, j) | simplex noise |
* [Hex Map 1](https://catlikecoding.com/unity/tutorials/hex-map/) - unity tutorial for hexagon grids with some useful generalized math.
* note that a spiral map is just a hexagonal one with a particular order.
* [3Blue1Brown - Essence of Linear Algebra](https://youtube.com/watch?v=fNk_zzaMoSs&list=PLZHQObOWTQDPD3MizzM2xVFitgF8hE_ab) - amazing series on linear algebra by 3Blue1Brown
The noise values on the hashmaps are seeded. You can optionally provide a seed after the map's dimensions as an argument, otherwise it's a random seed.
## RESOURCES
* [Hex Map 1](https://catlikecoding.com/unity/tutorials/hex-map/) - unity tutorial for hexagon grids with some useful generalized math.
* [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. * [Amulet Docs](http://amulet.xyz/doc) - amulet documentation.

100
src/hexyz.lua

@ -156,9 +156,6 @@ end
-- returns ordered ring-shaped map of |radius| from |center|. -- returns ordered ring-shaped map of |radius| from |center|.
function ring_map(center, radius) function ring_map(center, radius)
local map = {} local map = {}
local mt = {__index={center=center, radius=radius}}
setmetatable(map, mt)
local walk = center + HEX_DIRECTIONS[6] * radius local walk = center + HEX_DIRECTIONS[6] * radius
@ -168,137 +165,118 @@ function ring_map(center, radius)
walk = hex_neighbour(walk, i) walk = hex_neighbour(walk, i)
end end
end end
setmetatable(map, {__index={center=center, radius=radius}})
return map return map
end end
-- returns ordered spiral hexagonal map of |radius| rings from |center|. -- returns ordered spiral hexagonal map of |radius| rings from |center|.
function spiral_map(center, radius) function spiral_map(center, radius)
local map = {center} local map = {center}
local mt = {__index={center=center, radius=radius}}
setmetatable(map, mt)
for i = 1, radius do for i = 1, radius do
table.append(map, ring_map(center, i)) table.append(map, ring_map(center, i))
end end
setmetatable(map, {__index={center=center, radius=radius}})
return map return map
end end
-- returns unordered parallelogram-shaped map of |width| and |height| with simplex noise -- returns unordered parallelogram-shaped map of |width| and |height| with simplex noise
function parallelogram_map(width, height, frequencies)
local map = {}
local mt = {__index={width=width, height=height, frequencies=frequencies}}
local frequencies = frequencies or {1}
function parallelogram_map(width, height, seed)
local seed = seed or math.random(width * height)
setmetatable(map, mt)
-- fill the map
local map = {}
for i = 0, width do for i = 0, width do
for j = 0, height do for j = 0, height do
-- calculate noise
local idelta = assert(i / width, "width must be greater than 0")
local jdelta = assert(j / height, "height must be greater than 0")
-- generate noise
local idelta = i / width
local jdelta = j / height
local noise = 0 local noise = 0
for _,freq in pairs(frequencies) do
noise = noise
+ assert(1/freq, "frequencies must be non-zero")
* math.simplex(vec2(freq * idelta, freq * jdelta))
for oct = 1, math.max(width, height) do
noise = noise + 1/4^oct * math.simplex(vec2(idelta + seed * width, jdelta + seed * height) * 2^oct)
end end
-- straightforward iteration produces a parallelogram -- straightforward iteration produces a parallelogram
map[vec2(i, j)] = noise map[vec2(i, j)] = noise
end end
end end
setmetatable(map, {__index={width=width, height=height, seed=seed}})
return map return map
end end
-- returns unordered triangular map of |size| with simplex noise -- returns unordered triangular map of |size| with simplex noise
function triangular_map(size, frequencies)
local map = {}
local mt = {__index={size=size, frequencies=frequencies}}
local frequencies = frequencies or {1}
function triangular_map(size, seed)
local seed = seed or math.random(size)
setmetatable(map, mt)
-- fill the map
local map = {}
for i = 0, size do for i = 0, size do
for j = size - i, size do for j = size - i, size do
-- calculate noise
local idelta = assert(i / size, "size must be greater than 0")
local jdelta = assert(j / -size, "size must be greater than 0")
-- generate noise
local idelta = i / size
local jdelta = j / size
local noise = 0 local noise = 0
for _,freq in pairs(frequencies) do
noise = noise
+ assert(1/freq, "frequencies must be non-zero")
* math.simplex(vec2(freq * idelta, freq * jdelta))
for oct = 1, size do
noise = noise + 1/3^oct * math.simplex(vec2(idelta + seed * size, jdelta + seed * size) * 2^oct)
end end
map[vec2(i, j)] = noise map[vec2(i, j)] = noise
end end
end end
setmetatable(map, {__index={size=size, seed=seed}})
return map return map
end end
-- returns unordered hexagonal map of |radius| with simplex noise -- returns unordered hexagonal map of |radius| with simplex noise
function hexagonal_map(radius, frequencies)
local map = {}
local mt = {__index={radius=radius, frequencies=frequencies}}
local frequencies = frequencies or {1}
function hexagonal_map(radius, seed)
local seed = seed or math.random(radius * 2)
setmetatable(map, mt)
-- fill the map
local map = {}
for i = -radius, radius do for i = -radius, radius do
local j1 = math.max(-radius, -i - radius) local j1 = math.max(-radius, -i - radius)
local j2 = math.min(radius, -i + radius) local j2 = math.min(radius, -i + radius)
for j = j1, j2 do for j = j1, j2 do
-- calculate noise
local idelta = assert(i / radius*2, "radius must be greater than 0")
local jdelta = assert(j / (j2-j1), "radius must be greater than 0")
local noise = 0
for _,freq in pairs(frequencies) do
noise = noise
+ assert(1/freq, "frequencies must be non-zero")
* math.perlin(vec2(freq * idelta, freq * jdelta))
end
-- populate
map[vec2(i, j)] = noise
map[vec2(i, j)] = true
end end
end end
setmetatable(map, {__index={radius=radius, seed=seed}})
return map return map
end end
-- returns unordered rectangular map of |width| and |height| with simplex noise -- returns unordered rectangular map of |width| and |height| with simplex noise
function rectangular_map(width, height, frequencies)
function rectangular_map(width, height, seed)
local seed = seed or math.random(width * height)
-- fill the map
local map = {} local map = {}
local mt = {__index={width=width, height=height, frequencies=frequencies}}
local frequencies = frequencies or {1}
setmetatable(map, mt)
for i = 0, width do for i = 0, width do
for j = 0, height do for j = 0, height do
-- calculate noise
local idelta = assert(i / width, "width must be greater than 0")
local jdelta = assert(j / height, "height must be greater than 0")
-- generate noise
local idelta = i / width
local jdelta = j / height
local noise = 0 local noise = 0
for _,freq in pairs(frequencies) do
noise = noise
+ assert(1/freq, "frequencies must be non-zero")
* math.simplex(vec2(freq * idelta, freq * jdelta))
for oct = 1, math.max(width, height) do
noise = noise + 2/3^oct * math.simplex(vec2(idelta + seed * width, jdelta + seed * height) * 2^oct)
end end
-- store hex in the map paired with its associated noise value -- store hex in the map paired with its associated noise value
map[vec2(i, j - math.floor(i/2))] = noise map[vec2(i, j - math.floor(i/2))] = noise
end end
end end
setmetatable(map, {__index={width=width, height=height, seed=seed}})
return map return map
end end

65
src/main.lua

@ -1,6 +1,8 @@
require"hexyz" require"hexyz"
math.randomseed(os.time())
--[[============================================================================ --[[============================================================================
----- COLOR CONSTANTS ----- ----- COLOR CONSTANTS -----
============================================================================]]-- ============================================================================]]--
@ -24,7 +26,7 @@ local BLUE = vec4(0.14, 0.54, 0.82, 1)
local CYAN = vec4(0.16, 0.63, 0.59, 1) local CYAN = vec4(0.16, 0.63, 0.59, 1)
local GREEN = vec4(0.52, 0.60, 0 , 1) local GREEN = vec4(0.52, 0.60, 0 , 1)
am.ascii_color_map =
table.merge(am.ascii_color_map,
{ {
E = EIGENGRAU, E = EIGENGRAU,
K = BASE03, K = BASE03,
@ -35,15 +37,42 @@ am.ascii_color_map =
S = BASE1, S = BASE1,
w = BASE2, w = BASE2,
W = BASE3, W = BASE3,
y = YELLOW,
o = ORANGE,
r = RED,
m = MAGENTA,
v = VIOLET,
b = BLUE,
c = CYAN,
g = GREEN
}
Y = YELLOW,
O = ORANGE,
R = RED,
M = MAGENTA,
V = VIOLET,
B = BLUE,
C = CYAN,
G = GREEN
})
local beetle =
[[
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . y . . . . . . . . .
. . . . . . . . y . y . . . . . . . .
. . . . . . . . y y y . . . . . . . .
. . . . . . . y y . y y . . . . . . .
. . . . . . . y y . y y . . . . . . .
. . . . . . y O y O y O y . . . . . .
. . . . . . o O y o y O o . . . . . .
. . o . . o y o O O O o y o . . o . .
. . . o . o W o O O O o W o . o . . .
. . . . o O o O O O O O o O o . . . .
. . . . o o O O O O O O O o o . . . .
. . . . . . o O O O O O o . . . . . .
. . o o o o O o o o o o O o o o o . .
. . . . . o O O o o o O O o o . . . .
. . . . . o O O O o O O O o . . . . .
. . . . . o O O O o O O O o . . . . .
. . . . . . o O O o O O o . . . . . .
. . . . . o o o O o O o o o . . . . .
. . . o o o . . o O o . . o o o . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
]]
--[[============================================================================ --[[============================================================================
----- SETUP ----- ----- SETUP -----
@ -57,7 +86,7 @@ local win = am.window
clear_color = BASE03 clear_color = BASE03
} }
local map = rectangular_map(45, 31, {2, 4, 8})
local map = rectangular_map(45, 31)
local layout = layout(vec2(-268, win.bottom)) local layout = layout(vec2(-268, win.bottom))
local home = hex_to_pixel(vec2(23, 4), layout) local home = hex_to_pixel(vec2(23, 4), layout)
@ -159,7 +188,7 @@ function game_init()
-- passable -- passable
elseif noise < 0 then elseif noise < 0 then
color = vec4(0.10, 0.25, 0.05, (noise + 1.9) / 2)
color = vec4(0.10, 0.25, (noise + 1.9) / 18, (noise + 1.9) / 2)
-- passable -- passable
elseif noise < 0.5 then elseif noise < 0.5 then
@ -188,18 +217,14 @@ function game_init()
-- sleep -- sleep
--am.wait(am.delay(0.01)) --am.wait(am.delay(0.01))
end end
-- home base
world:append(am.translate(home)
^ am.rotate(0):tag"homer"
^ am.circle(vec2(0), 22, ORANGE, 3)):tag"home"
-- home base
world:append(am.translate(home) world:append(am.translate(home)
^ am.rotate(60):tag"homer2"
^ am.circle(vec2(0), 22, YELLOW, 3)):tag"home"
^ am.rotate(0):tag"home_rotate"
^ am.circle(vec2(0), 22, MAGENTA, 5)):tag"home"
world:action(function() world:action(function()
world"homer".angle = am.frame_time / 6
world"homer2".angle = am.frame_time / 3
world"home_rotate".angle = am.frame_time / 5
end) end)
show_coords() -- mouse-hover events show_coords() -- mouse-hover events

Loading…
Cancel
Save