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/drawing.lua | 2 +- client/main.lua | 21 ++++++++---- client/movement.lua | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++ common/coords.lua | 10 ++++++ 4 files changed, 117 insertions(+), 8 deletions(-) create mode 100644 client/movement.lua diff --git a/client/drawing.lua b/client/drawing.lua index 5334a93..31c5893 100644 --- a/client/drawing.lua +++ b/client/drawing.lua @@ -93,7 +93,7 @@ local function draw_map(camera,map) end end - if _G.debugmode then + if false and _G.debugmode then love.graphics.setColor(0,1,0) local function p(q,r) return coords.Hex:make(q,r):to_pos() end diff --git a/client/main.lua b/client/main.lua index d58551e..4941814 100644 --- a/client/main.lua +++ b/client/main.lua @@ -15,6 +15,7 @@ local camera = require"camera".Camera:make() local Chunk = require"common.chunk".Chunk local util = require"util" local Map = require"common.map".Map +local movement = require"movement" -- local pprint=require"common.pprint" -- pprint.setup{show_all=true} @@ -56,26 +57,26 @@ local function update_local_player(pl,dt) local dy = kd"s"-kd"w" if dx == 0 and dy == 0 then - pl.pos_dirty = false return end if dx ~= 0 and dy ~= 0 then + -- 60degrees direction, to follow hex grid + -- instead of 45degrees diagonal dx = dx * 0.5 dy = dy * (math.sqrt(3)/2) end - pl.pos.x = pl.pos.x + SPEED * dt * dx - pl.pos.y = pl.pos.y + SPEED * dt * dy + local try_pos = Pos:make(pl.pos.x + SPEED*dt*dx, pl.pos.y + SPEED*dt*dy) + pl.pos = movement.collide_with_terrain(pl.pos,try_pos,map) pl.pos_dirty = true - - end local function sync_local_player(pl) -- send updated info about local player to server if pl.pos_dirty then peer:send(json.encode{t="ppos",x=pl.pos.x,y=pl.pos.y}) + pl.pos_dirty = false end end @@ -95,9 +96,7 @@ function love.update(dt) local mh = camera:screen_to_world(Pos:make(love.mouse.getPosition())):to_hex():round() if false== map:at(mh) and love.mouse.isDown(1) then - print("place at",mh) map:set_at(mh,true) - -- print(mh,true) send_settile(mh,true) elseif map:at(mh) and love.mouse.isDown(2) then map:set_at(mh,false) @@ -185,6 +184,8 @@ function love.draw() local wm = camera:screen_to_world(sm) local hm = wm:to_hex() + sdf_d,sdf_gx,sdf_gy = movement.hex_sdgf(wm, coords.Hex:make(3,3)) + love.graphics.origin() if _G.debugmode and local_player then util.print_good({ @@ -198,7 +199,13 @@ function love.draw() "voob "..tostring(camera.zoom), "-", "fps "..tostring(love.timer.getFPS()), + "-", + "sdf "..tostring(sdf_d) + },10,10) + love.graphics.setColor(0,1,0) + love.graphics.setLineWidth(5) + love.graphics.line(sm.x,sm.y, sm.x+50*sdf_gx, sm.y+50*sdf_gy) end end 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 +} diff --git a/common/coords.lua b/common/coords.lua index 49c2bf2..420b287 100644 --- a/common/coords.lua +++ b/common/coords.lua @@ -75,6 +75,16 @@ function Hex.offset_in_chunk(self) local cp,offs = self:chunk_and_offset() return offs end +function Hex.iter_neighbours(self,radius) + assert(radius > 0,"radius must be at least 1") + return coroutine.wrap(function() + for q = -radius,radius do + for r = math.max(-radius,-q-radius), math.min(radius,-q+radius) do + coroutine.yield(self+Hex:make(q,r)) + end + end + end) +end function Hex.__add(self,other) return Hex:make(self.q+other.q, self.r+other.r, self.s+other.s) end function Hex.__sub(self,other) return Hex:make(self.q-other.q, self.r-other.r, self.s-other.s) end function Hex.__tostring(self) return string.format("H(%.2f,%.2f)",self.q,self.r) end -- cgit v1.2.3