From 0abc0b9b7928cd72b1c92b3af9f30674397013d1 Mon Sep 17 00:00:00 2001 From: ubq323 Date: Mon, 13 Feb 2023 01:18:57 +0000 Subject: add collisions and sliding for player movement --- client/movement.lua | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 client/movement.lua (limited to 'client/movement.lua') diff --git a/client/movement.lua b/client/movement.lua new file mode 100644 index 0000000..78af5f0 --- /dev/null +++ b/client/movement.lua @@ -0,0 +1,92 @@ +local Pos = require"common.coords".Pos + +local function sign(x) + if x == 0 then return 0 + elseif x < 0 then return -1 + elseif x > 0 then return 1 + end +end +local function clamp(x,minv,maxv) + return math.min(math.max(x,minv),maxv) +end + + + +-- https://iquilezles.org/articles/distgradfunctions2d/ +local function iqz_hex_sdgf(px,py, r) + local kx,ky,kz = -0.866025404,0.5,0.577350269 + local sx,sy = sign(px),sign(py) + px = math.abs(px) + py = math.abs(py) + local w = kx*px+ky*py + + local _1 = 2*math.min(w,0) + px = px - _1*kx + py = py - _1*ky + + px = px - clamp(px,-kz*r,kz*r) + py = py - r + + d = math.sqrt(px*px+py*py)*sign(py) + + local gx,gy + if w<0 then + gx = -ky*px - kx*py + gy = -kx*px + ky*py + else + gx,gy = px,py + end + + -- dist, gradx, grady + return d, sx*gx/d, sy*gy/d + +end + + +-- rotate by 30 degrees +local c30,s30 = math.cos(math.rad(30)), math.sin(math.rad(30)) +local function t_in(x,y) return c30*x-s30*y, s30*x+c30*y end +local function t_out(x,y) return c30*x+s30*y, -s30*x+c30*y end + +-- above sdf has flat top not pointy-top hexagons +-- so need to rotate +-- also, 'r' parameter in the above seems to be distance to side +-- but in our unit system, 1 unit is distance to vertex +-- conversion factor between these is also cos(30deg) +-- this is distance to hex at 0,0 +local function hex_sdgf_00(pos) + local tpx,tpy = t_in(pos.x,pos.y) + local d,gx,gy = iqz_hex_sdgf(tpx,tpy,c30) + return d, t_out(gx,gy) +end + +local function hex_sdgf(pos, hex) + return hex_sdgf_00(pos-hex:to_pos()) +end + +local PLAYER_SIZE = 0.35 +local function collide_with_terrain(old_pos, try_pos, map, tries_remaining) + tries_remaining = tries_remaining or 3 + if tries_remaining <= 0 then return old_pos end + local try_h = try_pos:to_hex():round() + for near_h in try_h:iter_neighbours(1) do + local tile = map:at(near_h) + if tile then + local d,gx,gy = hex_sdgf(try_pos, near_h) + if d < PLAYER_SIZE then + local push_dist = PLAYER_SIZE - d + local push_dir = Pos:make(gx,gy) + local new_try_pos = try_pos + (push_dist*push_dir) + return collide_with_terrain(old_pos,new_try_pos,map,tries_remaining-1) + end + end + end + + return try_pos +end + + +return { + collide_with_terrain = collide_with_terrain, + hex_sdgf=hex_sdgf +} -- cgit v1.2.3