mirror of
https://github.com/ElCeejo/creatura.git
synced 2025-07-12 17:42:27 -04:00
Add Context Based Steering, ABM Spawning fixes
This commit is contained in:
parent
5b4b5e7331
commit
64de7d9ed5
4 changed files with 185 additions and 101 deletions
168
methods.lua
168
methods.lua
|
@ -14,25 +14,27 @@ local function diff(a, b) -- Get difference between 2 angles
|
|||
return atan2(sin(b - a), cos(b - a))
|
||||
end
|
||||
|
||||
local function clamp(val, min, max)
|
||||
if val < min then
|
||||
val = min
|
||||
elseif max < val then
|
||||
val = max
|
||||
local function clamp(val, _min, _max)
|
||||
if val < _min then
|
||||
val = _min
|
||||
elseif _max < val then
|
||||
val = _max
|
||||
end
|
||||
return val
|
||||
end
|
||||
|
||||
local vec_add = vector.add
|
||||
local vec_normal = vector.normalize
|
||||
local vec_len = vector.length
|
||||
local vec_dist = vector.distance
|
||||
local vec_dir = vector.direction
|
||||
local vec_dot = vector.dot
|
||||
local vec_multi = vector.multiply
|
||||
local vec_add = vector.add
|
||||
local vec_sub = vector.subtract
|
||||
local yaw2dir = minetest.yaw_to_dir
|
||||
local dir2yaw = minetest.dir_to_yaw
|
||||
|
||||
--[[local function debugpart(pos, time, tex)
|
||||
local function debugpart(pos, time, tex)
|
||||
minetest.add_particle({
|
||||
pos = pos,
|
||||
texture = tex or "creatura_particle_red.png",
|
||||
|
@ -40,7 +42,7 @@ local dir2yaw = minetest.dir_to_yaw
|
|||
glow = 16,
|
||||
size = 16
|
||||
})
|
||||
end]]
|
||||
end
|
||||
|
||||
---------------------
|
||||
-- Local Utilities --
|
||||
|
@ -101,35 +103,50 @@ end]]
|
|||
return false
|
||||
end]]
|
||||
|
||||
function creatura.get_collision_ranged(self, range)
|
||||
local yaw = self.object:get_yaw()
|
||||
local pos = self.object:get_pos()
|
||||
local get_node_def = creatura.get_node_def
|
||||
local get_node_height = creatura.get_node_height_from_def
|
||||
|
||||
function creatura.get_collision_ranged(self, dir, range)
|
||||
local pos, yaw = self.object:get_pos(), self.object:get_yaw()
|
||||
if not pos then return end
|
||||
local width = self.width
|
||||
local height = self.height
|
||||
local width = self.width + 0.1
|
||||
local height = self.height + self.stepheight
|
||||
pos.y = pos.y + 0.01
|
||||
local m_dir = vec_normal(yaw2dir(yaw))
|
||||
m_dir.x, m_dir.z = m_dir.x * 0.5, m_dir.z * 0.5
|
||||
local ahead = vec_add(pos, vec_multi(m_dir, width + 0.5))
|
||||
dir = vec_normal(dir or yaw2dir(yaw))
|
||||
yaw = dir2yaw(dir)
|
||||
local ahead = vec_add(pos, vec_multi(dir, width))
|
||||
-- Loop
|
||||
local cos_yaw = cos(yaw)
|
||||
local sin_yaw = sin(yaw)
|
||||
local pos_x, pos_y, pos_z = ahead.x, ahead.y, ahead.z
|
||||
local dir_x, dir_y, dir_z = dir.x, dir.y, dir.z
|
||||
local dist
|
||||
local collision
|
||||
for i = 0, range or 4 do
|
||||
pos_x = pos_x + m_dir.x * i
|
||||
pos_y = pos_y + m_dir.y * i
|
||||
pos_z = pos_z + m_dir.z * i
|
||||
pos_x = pos_x + dir_x
|
||||
pos_y = pos_y + dir_y
|
||||
pos_z = pos_z + dir_z
|
||||
local pos2
|
||||
for x = -width, width, width / ceil(width) do
|
||||
pos2 = {
|
||||
x = cos_yaw * ((pos_x + x) - pos_x) + pos_x,
|
||||
y = pos_y,
|
||||
z = sin_yaw * ((pos_x + x) - pos_x) + pos_z
|
||||
}
|
||||
for y = 0, height, height / ceil(height) do
|
||||
local pos2 = {
|
||||
x = cos(yaw) * ((pos_x + x) - pos_x) + pos_x,
|
||||
y = pos.y + y,
|
||||
z = sin(yaw) * ((pos_x + x) - pos_x) + pos_z
|
||||
}
|
||||
if pos2.y - pos.y > (self.stepheight or 1.1)
|
||||
and creatura.get_node_def(pos2).walkable then
|
||||
return true, pos2
|
||||
pos2.y = pos_y + y
|
||||
local dist2 = vec_dist(pos, pos2)
|
||||
if not dist
|
||||
or dist2 < dist then
|
||||
if pos2.y - pos_y > (self.stepheight or 1.1)
|
||||
and get_node_def(pos2).walkable then
|
||||
collision = pos2
|
||||
dist = dist2
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if collision then return true, collision end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
@ -144,16 +161,19 @@ function creatura.get_collision(self)
|
|||
local m_dir = vec_normal(yaw2dir(yaw))
|
||||
local ahead = vec_add(pos, vec_multi(m_dir, width + 1)) -- 1 node out from edge of box
|
||||
-- Loop
|
||||
local cos_yaw = cos(yaw)
|
||||
local sin_yaw = sin(yaw)
|
||||
local pos_x, pos_z = ahead.x, ahead.z
|
||||
for x = -width, width, width / ceil(width) do
|
||||
local pos2 = {
|
||||
x = cos_yaw * ((pos_x + x) - pos_x) + pos_x,
|
||||
y = pos.y,
|
||||
z = sin_yaw * ((pos_x + x) - pos_x) + pos_z
|
||||
}
|
||||
for y = 0, height, height / ceil(height) do
|
||||
local pos2 = {
|
||||
x = cos(yaw) * ((pos_x + x) - pos_x) + pos_x,
|
||||
y = pos.y + y,
|
||||
z = sin(yaw) * ((pos_x + x) - pos_x) + pos_z
|
||||
}
|
||||
pos2.y = pos.y + y
|
||||
if pos2.y - pos.y > (self.stepheight or 1.1)
|
||||
and creatura.get_node_def(pos2).walkable then
|
||||
and get_node_def(pos2).walkable then
|
||||
return true, pos2
|
||||
end
|
||||
end
|
||||
|
@ -179,6 +199,65 @@ local function get_avoidance_dir(self)
|
|||
end
|
||||
end
|
||||
|
||||
local steer_directions = {
|
||||
vec_normal({x = 1, y = 0, z = 0}),
|
||||
vec_normal({x = 1, y = 0, z = 1}),
|
||||
vec_normal({x = 0, y = 0, z = 1}),
|
||||
vec_normal({x = -1, y = 0, z = 0}),
|
||||
vec_normal({x = -1, y = 0, z = -1}),
|
||||
vec_normal({x = 0, y = 0, z = -1}),
|
||||
vec_normal({x = 1, y = 0, z = -1}),
|
||||
vec_normal({x = -1, y = 0, z = 1})
|
||||
}
|
||||
|
||||
function creatura.get_context_steering(self, goal, range)
|
||||
local pos, vel = self.object:get_pos(), self.object:get_velocity()
|
||||
if not pos then return end
|
||||
local heading = vec_normal(vel)
|
||||
local dir2goal = vec_normal(vec_dir(pos, goal))
|
||||
local output_dir = {x = 0, y = dir2goal.y, z = 0}
|
||||
range = range or 8
|
||||
if range < 2 then range = 2 end
|
||||
for _, _dir in ipairs(steer_directions) do
|
||||
local dir = table.copy(_dir)
|
||||
local score = vec_dot(dir2goal, dir)
|
||||
local interest = clamp(score, 0, 1)
|
||||
local danger = 0
|
||||
if interest >= 0 then
|
||||
local width = self.width
|
||||
local height = self.height
|
||||
local collision
|
||||
if width <= 0.5
|
||||
and height <= 1 then
|
||||
local pos2 = vec_add(pos, dir)
|
||||
local pos2_name = minetest.get_node(pos2).name
|
||||
collision = get_node_height(pos2_name) > self.stepheight and pos2
|
||||
if not collision then
|
||||
local above = {x = pos2.x, y = pos2.y + 1, z = pos2.z}
|
||||
collision = get_node_def(above).walkable and pos2
|
||||
end
|
||||
else
|
||||
local s_range = range * clamp(interest, 0.5, 1)
|
||||
_, collision = creatura.get_collision_ranged(self, dir, s_range)
|
||||
end
|
||||
if collision then
|
||||
local dir2col = vec_dir(pos, collision)
|
||||
local dist2col = vec_dist(pos, collision) - self.width
|
||||
local ahead = vec_add(pos, vec_multi(heading, self.width + dist2col))
|
||||
local avd_force = vec_normal(vec_sub(ahead, collision))
|
||||
dir.y = avd_force.y / 4
|
||||
local dot_weight = vec_dot(vec_normal(dir2col), dir)
|
||||
local dist_weight = (range - dist2col) / range
|
||||
interest = interest - dot_weight
|
||||
danger = dist_weight
|
||||
end
|
||||
end
|
||||
score = clamp(interest - danger, 0, 1)
|
||||
output_dir = vector.add(output_dir, vector.multiply(dir, score))
|
||||
end
|
||||
return output_dir
|
||||
end
|
||||
|
||||
-------------
|
||||
-- Actions --
|
||||
-------------
|
||||
|
@ -414,4 +493,27 @@ creatura.register_movement_method("creatura:obstacle_avoidance", function(self)
|
|||
_self:turn_to(goal_yaw, turn_rate)
|
||||
end
|
||||
return func
|
||||
end)
|
||||
|
||||
creatura.register_movement_method("creatura:context_based_steering", function(self)
|
||||
local steer_to
|
||||
local steer_timer = 0.25
|
||||
self:set_gravity(-9.8)
|
||||
local function func(_self, goal, speed_factor)
|
||||
local pos = _self.object:get_pos()
|
||||
if not pos then return end
|
||||
if vec_dist(pos, goal) < clamp(self.width, 0.5, 1) then
|
||||
_self:halt()
|
||||
return true
|
||||
end
|
||||
-- Calculate Movement
|
||||
steer_timer = (steer_timer > 0 and steer_timer - self.dtime) or 0.25
|
||||
steer_to = (steer_timer <= 0 and creatura.get_context_steering(self, goal, 2)) or steer_to
|
||||
local speed = abs(_self.speed or 2) * speed_factor or 0.5
|
||||
local turn_rate = abs(_self.turn_rate or 5)
|
||||
-- Apply Movement
|
||||
_self:set_forward_velocity(speed)
|
||||
_self:turn_to(dir2yaw(steer_to or vec_dir(pos, goal)), turn_rate)
|
||||
end
|
||||
return func
|
||||
end)
|
Loading…
Add table
Add a link
Reference in a new issue