Browse Source

everything is so much cleaner now.

master
churchianity 6 years ago
parent
commit
fa7492f50d
  1. 222
      hex.lua
  2. 70
      main.lua

222
hex.lua

@ -1,81 +1,64 @@
--[[ AXIAL/CUBE COORDINATE SYSTEM FOR AMULET/LUA]]
--[[
all hexes in functions are assumed to be amulet vectors.
in amulet, vector arithmetic works already with [ + - * / ]
things like equality and distance are implemented here.
some algorithms use axial coordinates for hexes: vec2(s, t)
others use cube coordinates: vec3(s, t, z) where s + t + z = 0
this is for simplicity - many algorithms don't care about the
third coordinate, and if they do, the missing coordinate can
be calculated from the other two.
-- note on orientation:
because of the way amulet draws hexagons, it's much easier to assume
the user wants to use the flat map. rotation after the fact to
achieve other orienations is probably possible, but might have some
aliasing issues. TODO work on this.
some of the primary resources used to develop this library:
- https://redblobgames.com/grid/hexagons - simply amazing.
- http://amulet.xyz/doc - amulet documentation
- TODO that place that had the inner circle/outer circle ratio??
----- [[ AXIAL/CUBE COORDINATE SYSTEM FOR AMULET/LUA]] -------------------------
--[[ author@churchianity.ca
-- INTRODUCTION
this is a library for making grids of hexagons using lua.
it has made use of exclusively standard lua 5.2 functionality,
making it as portable as possible. it doesn't even use a point
class, (or classes/metatables at all) simply returning tables
of integers, which can later be unpacked into your amulet
vectors, or whatever else you want to use.
this can result in some nasty looking lines with lots of table
unpacks, but if your graphics library likes traditional lua
types, you will be better off.
it supports triangular, hexagonal, rectangular, and
parallelogram map shapes.
it supports non-regular hexagons, though it's trickier to get
working in amulet. TODO work on this.
-- NOTE ON ORIENTATION + AMULET
because of the way amulet draws hexagons (amulet essentially
draws a 6-sided circle from a centerpoint, instead of of a
series of lines connecting points), the flat orientation is
default and recommended. other orientations are possible
with am.rotate, but can cause aliasing issues. TODO work on this.
-- RESOURCES USED TO DEVELOP THIS LIBRARY
https://redblobgames.com/grid/hexagons - simply amazing. amit is a god.
http://amulet.xyz/doc - amulet documentation
TODO that place that had the inner circle/outer circle ratio??
]] ]]
----- [[ GENERALLY USEFUL FUNCTIONS ]] -----------------------------------------
-- GENERALLY USEFUL FUNCTIONS --------------------------------------------------
-- just incase you don't already have a rounding function.
function round(n) function round(n)
return n % 1 >= 0.5 and math.ceil(n) or math.floor(n) return n % 1 >= 0.5 and math.ceil(n) or math.floor(n)
end end
---- [[ HEX CONSTANTS ]] -------------------------------------------------------
-- HEX CONSTANTS ---------------------------------------------------------------
-- all possible vector directions from a given hex by edge
HEX_DIRECTIONS = {vec2( 1 , 0),
vec2( 1 , -1),
vec2( 0 , -1),
vec2(-1 , 0),
vec2(-1 , 1),
vec2( 0 , 1)}
-- all possible vector directions from a given hex by edge
HEX_DIRECTIONS = {{ 1 , 0},
{ 1 , -1},
{ 0 , -1},
{-1 , 0},
{-1 , 1},
{ 0 , 1}}
-- HEX UTILITY FUNCTIONS ------------------------------------------------------- -- HEX UTILITY FUNCTIONS -------------------------------------------------------
function hex_equals(a, b)
return a.s == a.t and b.s == b.t
end
function hex_nequals(a, b)
return not hex_equals(a, b)
end
function hex_length(hex)
return ((math.abs(hex.s) + math.abs(hex.t) + math.abs(-hex.s - hex.t)) / 2)
end
function hex_distance(a, b)
return hex_length(a - b)
end
function hex_direction(direction)
return HEX_DIRECTIONS[direction]
end
function hex_neighbour(hex, direction)
return hex + HEX_DIRECTIONS[direction]
end
function hex_round(hex)
rs = round(hex.s)
rt = round(hex.t)
rz = round(-hex.s + -hex.t)
function hex_round(s, t)
rs = round(s)
rt = round(t)
rz = round(-s - t)
sdelta = math.abs(rs - hex.s)
tdelta = math.abs(rt - hex.t)
zdelta = math.abs(rz + hex.s + hex.t)
sdelta = math.abs(rs - s)
tdelta = math.abs(rt - t)
zdelta = math.abs(rz + s + t)
if sdelta > tdelta and sdelta > zdelta then if sdelta > tdelta and sdelta > zdelta then
rs = -rt - rz rs = -rt - rz
@ -85,33 +68,106 @@ function hex_round(hex)
rz = -rs - rt rz = -rs - rt
end end
return vec2(rs, rt)
return {rs, rt}
end
----- [[ LAYOUT, ORIENTATION & COORDINATE CONVERSION ]] -----------------------
-- forward & inverse matrices used for the flat orientation.
FLAT_ORIENTATION = {3.0/2.0, 0.0, 3.0^0.5/2.0, 3.0^0.5,
2.0/3.0, 0.0, -1.0/3.0 , 3.0^0.5/3.0}
-- forward & inverse matrices used for the pointy orientation.
POINTY_ORIENTATION = {3.0^0.5, 3.0^0.5/2.0, 0.0, 3.0/2.0,
3.0^0.5/3.0, -1.0/3.0, 0.0, 2.0/3.0}
-- layout.
function layout(size, orientation, origin, width, height, radius)
return {size = size or {11, 11},
orientation = orientation or FLAT_ORIENTATION,
origin = origin or {0, 0},
width = width or 45,
height = height or 31,
radius = radius or width or 6}
end
-- hex to screen
function hex_to_pixel(s, t, layout)
M = layout.orientation
x = (M[1] * s + M[2] * t) * layout.size[1]
y = (M[3] * s + M[4] * t) * layout.size[2]
return {x + layout.origin[1], y + layout.origin[2]}
end end
-- COORDINATE CONVERSION FUNCTIONS ---------------------------------------------
-- screen to hex
function pixel_to_hex(x, y, layout)
M = layout.orientation
px = {(x - layout.origin[1]) / layout.size[1],
(y - layout.origin[2]) / layout.size[2]}
-- forward & inverse matrices used for coordinate conversion
local M = mat2(3.0/2.0, 0.0, 3.0^0.5/2.0, 3.0^0.5 )
local W = mat2(2.0/3.0, 0.0, -1.0/3.0 , 3.0^0.5/3.0)
s = M[5] * px[1] + M[6] * px[2]
t = M[7] * px[1] + M[8] * px[2]
-- hex to screen
function hex_to_pixel(hex)
return hex_round(s, t)
end
x = (M[1][1] * hex.s + M[1][2] * hex.t) * map.size
y = (M[2][1] * hex.s + M[2][2] * hex.t) * map.size
----- [[ MAP STORAGE & RETRIEVAL ]] --------------------------------------------
--[[ all functions return a table of tables; a map of points
storage functions take a range of hex coordinates, and return pixel ones.
retrieval functions do the opposite.
everything except map shape is determined by layout.
pick a pair of functions based on the shape of map you want to use.
it is not advised to use a single layout instance with multiple shapes. ]]
-- returns parallelogram-shaped map. width and height are used.
function ogram_map_store(layout)
map = {}
for s = 0, layout.width do
for t = 0, layout.height do
table.insert(map, hex_to_pixel(s, t, layout))
end
end
return map
end
return vec2(x + map.origin.x, y + map.origin.y)
-- returns triangular map. radius is used as the triangle side length.
function tri_map_store(layout)
map = {}
for s = 0, layout.radius do
for t = layout.radius - s, layout.radius do
table.insert(map, hex_to_pixel(s, t, layout))
end
end
return map
end end
-- screen to hex
function pixel_to_hex(pix)
pix = vec2(pix.x - map.origin.x) / map.size,
(pix.y - map.origin.y) / map.size
-- returns hexagonal map. length of map is radius * 2 + 1
function hex_map_store(layout)
map = {}
for s = -layout.radius, layout.radius do
t1 = math.max(-layout.radius, -s - layout.radius)
t2 = math.min(layout.radius, -s + layout.radius)
s = W[1][1] * pix.x + W[1][2] * pix.y
t = W[2][1] * pix.x + W[2][2] * pix.y
for t = t1, t2 do
table.insert(map, hex_to_pixel(s, t, layout))
end
end
return map
end
return hex_round(vec2(s, t))
-- returns rectangular map. width and height are used.
function rect_map_store(layout)
map = {}
for s = 0, layout.width do
soffset = math.floor(s / 2)
for t = -soffset, layout.height - soffset do
table.insert(map, hex_to_pixel(s, t, layout))
end
end
return map
end end
-- MAP FUNCTIONS ---------------------------------------------------------------

70
main.lua

@ -1,32 +1,68 @@
--[[ WARZONE 2 - HEXAGONAL GRID RESOURCE BASED TOWER DEFENSE GAME]]
--[[
----- [[ WARZONE 2 - HEXAGONAL GRID RESOURCE BASED TOWER DEFENSE GAME]] -------
--[[ author@churchianity.ca
]] ]]
require "hex" require "hex"
-- ENTRY POINT -----------------------------------------------------------------
win = am.window {
----- [[ DUMMY FUNCTIONS ]] ------------------------------------------------------
function draw_axes()
xaxis = am.line(vec2(-win.width / 2, 0) , vec2(win.width / 2, 0))
yaxis = am.line(vec2(0, -win.height / 2), vec2(0, win.height / 2))
title_scene:append(xaxis)
title_scene:append(yaxis)
end
function rcolor()
return vec4(math.random(20, 80) / 100)
end
function show_hex_coords()
win.scene:action(function()
x, y = unpack(pixel_to_hex(win:mouse_position().x, win:mouse_position().y, layout))
test_scene = (
am.translate(vec2(unpack(hex_to_pixel(x, y, layout))))
^ am.text(string.format("%d, %d", x, y)))
end)
end
local win = am.window {
title = "Warzone 2: Electric Boogaloo", title = "Warzone 2: Electric Boogaloo",
-- BASE RESOLUTION = 3/4 * WXGA Standard 16:10 Aspect Ratio -- BASE RESOLUTION = 3/4 * WXGA Standard 16:10 Aspect Ratio
width = 1280 * 3 / 4,
height = 800 * 3 / 4,
width = 1280 * 3 / 4, -- 960
height = 800 * 3 / 4} -- 600
local layout = layout({11, 11},
FLAT_ORIENTATION,
vec2(win.left, win.bottom),
45, 31)
clear_color = vec4(0, 0, 0, 0)
}
----- [[ MAP RENDERING ]] ------------------------------------------------
-- GROUPS
local grid = am.group()
function render_map(layout)
coords = rect_map_store(layout)
map = am.group()
--[[
xaxis = am.line(vec2(-win.width / 2, 0) , vec2(win.width / 2, 0))
yaxis = am.line(vec2(0, -win.height / 2), vec2(0, win.height / 2))
for _,v in pairs(coords) do
map:append(am.circle(vec2(unpack(v)), layout.size[1], rcolor(), 6))
end
return map
end
grid:append(xaxis)
grid:append(yaxis)
--]]
----- [[ MAIN ]] -----------------------------------------------------------
win.scene = grid
local game_scene = render_map(layout)
local test_scene = am.group()
win.scene = am.group{test_scene, game_scene}
test_scene:action(function()
x, y = unpack(pixel_to_hex(win:mouse_position().x, win:mouse_position().y, layout))
test_scene:replace("text",
am.translate(vec2(unpack(hex_to_pixel(x, y, layout))))
^ am.text(string.format("%d, %d", x, y)))
am.delay(1)
end)
Loading…
Cancel
Save