mirror of
https://github.com/ElCeejo/creatura.git
synced 2025-05-21 07:13:19 -04:00
Movement Improvements
Pathfinding movement functions for A* and theta have been combined into one generic function. Ensure next goal is always set if there is a valid path. Checking if mob has reached the next goal improved - now works no matter the mob height, and the height of the node below the goal. mob:pos_in_box now can take a size param that is a table. Using this to add stepheight to the box height, to fix issues with mobs stepping up onto next node before hitting the goal. Mobs now won't overshoot their goal, if they're very close to it (less than mob width) their speed reduces. The more a mob is turning, the less forward velocity it has. This should fix spinning a lot. mob:pos_in_box now can take a size param that is a table, containing the separate width and height. pathfinding.moveable no longer pads an extra 0.2 around the width of mob when checking if there is sufficient space for the mob. pathfinding.get_neighbors now uses max_fall for the maximum distance we can go down, instead of stepheight.
This commit is contained in:
parent
35069011d6
commit
076e8423ae
3 changed files with 75 additions and 81 deletions
129
methods.lua
129
methods.lua
|
@ -14,12 +14,12 @@ local function diff(a, b) -- Get difference between 2 angles
|
|||
end
|
||||
|
||||
local function clamp(val, min, max)
|
||||
if val < min then
|
||||
val = min
|
||||
elseif max < val then
|
||||
val = max
|
||||
end
|
||||
return val
|
||||
if val < min then
|
||||
val = min
|
||||
elseif max < val then
|
||||
val = max
|
||||
end
|
||||
return val
|
||||
end
|
||||
|
||||
local function vec_center(v)
|
||||
|
@ -104,26 +104,26 @@ end
|
|||
-- Rotate on Z axis in random direction until 90 degree angle is reached
|
||||
|
||||
function creatura.action_fallover(self)
|
||||
local zrot = 0
|
||||
local init = false
|
||||
local zrot = 0
|
||||
local init = false
|
||||
local dir = 1
|
||||
local function func(self)
|
||||
if not init then
|
||||
self:animate("stand")
|
||||
local function func(self)
|
||||
if not init then
|
||||
self:animate("stand")
|
||||
if random(2) < 2 then
|
||||
dir = -1
|
||||
end
|
||||
init = true
|
||||
end
|
||||
local rot = self.object:get_rotation()
|
||||
init = true
|
||||
end
|
||||
local rot = self.object:get_rotation()
|
||||
local goal = (pi * 0.5) * dir
|
||||
local dif = abs(rot.z - goal)
|
||||
zrot = rot.z + (dif * dir) * 0.15
|
||||
self.object:set_rotation({x = rot.x, y = rot.y, z = zrot})
|
||||
if (dir > 0 and zrot >= goal)
|
||||
self.object:set_rotation({x = rot.x, y = rot.y, z = zrot})
|
||||
if (dir > 0 and zrot >= goal)
|
||||
or (dir < 0 and zrot <= goal) then return true end
|
||||
end
|
||||
self:set_action(func)
|
||||
end
|
||||
self:set_action(func)
|
||||
end
|
||||
|
||||
----------------------
|
||||
|
@ -164,26 +164,40 @@ function get_line_of_sight(a, b)
|
|||
return true
|
||||
end
|
||||
|
||||
local function movement_theta_pathfind(self, pos2, speed)
|
||||
local function get_goal_pos(self, goal)
|
||||
local node_name = minetest.get_node(vec_raise(goal, -1)).name
|
||||
local node_height = creatura.get_node_height(node_name) - 1
|
||||
return vector.new(goal.x, goal.y + node_height, goal.z)
|
||||
end
|
||||
|
||||
local function movement_generic_pathfind(self, method, pos2, speed)
|
||||
local pos = self.object:get_pos()
|
||||
|
||||
self._path = self._path or {}
|
||||
local temp_goal = self._movement_data.temp_goal
|
||||
if not temp_goal
|
||||
or self:pos_in_box({x = temp_goal.x, y = pos.y + self.height * 0.5, z = temp_goal.z}) then
|
||||
self._movement_data.temp_goal = creatura.get_next_move(self, pos2)
|
||||
temp_goal = self._movement_data.temp_goal
|
||||
end
|
||||
if #self._path < 1 then
|
||||
self._path = creatura.find_theta_path(self, self.object:get_pos(), pos2, self.width, self.height, 500) or {}
|
||||
local temp_goal
|
||||
if #self._path == 0 then
|
||||
self._path = method(self, self.object:get_pos(), pos2, self.width, self.height, 500) or {}
|
||||
if #self._path > 0 then
|
||||
temp_goal = self._path[1]
|
||||
else
|
||||
temp_goal = creatura.get_next_move(self, pos2)
|
||||
end
|
||||
else
|
||||
temp_goal = self._path[2] or self._path[1]
|
||||
if self:pos_in_box({x = temp_goal.x, y = pos.y + self.height * 0.5, z = temp_goal.z}) then
|
||||
temp_goal = self._path[1]
|
||||
if self:pos_in_box(get_goal_pos(self, temp_goal)) then
|
||||
table.remove(self._path, 1)
|
||||
if #self._path > 0 then
|
||||
temp_goal = self._path[1]
|
||||
else
|
||||
temp_goal = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local dir = vector.direction(self.object:get_pos(), pos2)
|
||||
local tyaw = minetest.dir_to_yaw(dir)
|
||||
local turn_rate = self.turn_rate or 10
|
||||
local advance = speed or 2
|
||||
if temp_goal then
|
||||
dir = vector.direction(self.object:get_pos(), temp_goal)
|
||||
tyaw = minetest.dir_to_yaw(dir)
|
||||
|
@ -194,59 +208,33 @@ local function movement_theta_pathfind(self, pos2, speed)
|
|||
self:halt()
|
||||
return
|
||||
end
|
||||
local goal_dist = vector.distance(vector.new(pos.x, 0, pos.z), vector.new(temp_goal.x, 0, temp_goal.z))
|
||||
if goal_dist < advance / 10 then
|
||||
advance = math.min(advance, goal_dist * 10)
|
||||
end
|
||||
end
|
||||
self:turn_to(tyaw, turn_rate)
|
||||
self:animate("walk")
|
||||
self:set_gravity(-9.8)
|
||||
self:set_forward_velocity(speed or 2)
|
||||
if self:pos_in_box(pos2) then
|
||||
|
||||
-- The more we're turning, the less we should be moving forward
|
||||
local turn_amount = math.min(self.dtime * turn_rate, abs(tyaw - self.object:get_yaw()) % pi2) / (self.dtime * turn_rate)
|
||||
advance = advance * (1 - turn_amount)
|
||||
self:set_forward_velocity(advance)
|
||||
|
||||
if self:pos_in_box(get_goal_pos(self, pos2)) then
|
||||
self:halt()
|
||||
end
|
||||
end
|
||||
|
||||
local function movement_theta_pathfind(self, pos2, speed)
|
||||
return movement_generic_pathfind(self, creatura.find_theta_path, pos2, speed)
|
||||
end
|
||||
creatura.register_movement_method("creatura:theta_pathfind", movement_theta_pathfind)
|
||||
|
||||
local function movement_pathfind(self, pos2, speed)
|
||||
local pos = self.object:get_pos()
|
||||
local temp_goal = self._movement_data.temp_goal
|
||||
self._path = self._path or {}
|
||||
if (not temp_goal
|
||||
or self:pos_in_box({x = temp_goal.x, y = pos.y + self.height * 0.5, z = temp_goal.z}))
|
||||
and #self._path < 1 then
|
||||
self._movement_data.temp_goal = creatura.get_next_move(self, pos2)
|
||||
temp_goal = self._movement_data.temp_goal
|
||||
end
|
||||
if #self._path < 2 then
|
||||
self._path = creatura.find_path(self, self.object:get_pos(), pos2, self.width, self.height, 100) or {}
|
||||
else
|
||||
temp_goal = self._path[2]
|
||||
if self:pos_in_box({x = temp_goal.x, y = pos.y + self.height * 0.5, z = temp_goal.z}) then
|
||||
table.remove(self._path, 1)
|
||||
end
|
||||
end
|
||||
local dir = vector.direction(self.object:get_pos(), pos2)
|
||||
local tyaw = minetest.dir_to_yaw(dir)
|
||||
local turn_rate = self.turn_rate or 10
|
||||
if temp_goal then
|
||||
dir = vector.direction(self.object:get_pos(), temp_goal)
|
||||
tyaw = minetest.dir_to_yaw(dir)
|
||||
if #self._path < 2
|
||||
and not self:is_pos_safe(temp_goal) then
|
||||
self:animate("walk")
|
||||
self:set_forward_velocity(0)
|
||||
self:halt()
|
||||
return
|
||||
end
|
||||
end
|
||||
self:turn_to(tyaw, turn_rate)
|
||||
self:animate("walk")
|
||||
self:set_gravity(-9.8)
|
||||
self:set_forward_velocity(speed or 2)
|
||||
if self:pos_in_box(pos2) then
|
||||
self:halt()
|
||||
end
|
||||
return movement_generic_pathfind(self, creatura.find_path, pos2, speed)
|
||||
end
|
||||
|
||||
creatura.register_movement_method("creatura:pathfind", movement_pathfind)
|
||||
|
||||
-- Obstacle Avoidance
|
||||
|
@ -379,4 +367,3 @@ local function movement_neighbors(self, pos2, speed)
|
|||
end
|
||||
|
||||
creatura.register_movement_method("creatura:neighbors", movement_neighbors)
|
||||
|
||||
|
|
17
mob_meta.lua
17
mob_meta.lua
|
@ -268,12 +268,19 @@ end
|
|||
function mob:pos_in_box(pos, size)
|
||||
if not pos then return false end
|
||||
local center = self:get_center_pos()
|
||||
local width = size or self.width
|
||||
local height = size or (self.height * 0.5)
|
||||
if not size
|
||||
and self.width < 0.5 then
|
||||
width = 0.5
|
||||
|
||||
local width = math.max(self.width, 0.5)
|
||||
local height = (self.height * 0.5)
|
||||
if size then
|
||||
if type(size) == "table" then
|
||||
width = size[1]
|
||||
height = size[2]
|
||||
else
|
||||
width = size
|
||||
height = size
|
||||
end
|
||||
end
|
||||
|
||||
local edge_a = {
|
||||
x = center.x - width,
|
||||
y = center.y - height,
|
||||
|
|
|
@ -20,14 +20,14 @@ end
|
|||
|
||||
local function moveable(pos, width, height)
|
||||
local pos1 = {
|
||||
x = pos.x - (width + 0.2),
|
||||
x = pos.x - width,
|
||||
y = pos.y,
|
||||
z = pos.z - (width + 0.2),
|
||||
z = pos.z - width,
|
||||
}
|
||||
local pos2 = {
|
||||
x = pos.x + (width + 0.2),
|
||||
x = pos.x + width,
|
||||
y = pos.y,
|
||||
z = pos.z + (width + 0.2),
|
||||
z = pos.z + width,
|
||||
}
|
||||
for z = pos1.z, pos2.z do
|
||||
for x = pos1.x, pos2.x do
|
||||
|
@ -141,7 +141,7 @@ local function get_neighbors(self, pos, goal, swim, fly, climb, tbl, open, close
|
|||
local height = self.height
|
||||
local result = {}
|
||||
local max_up = self.stepheight or 1
|
||||
local max_down = self.stepheight or 1
|
||||
local max_down = self.max_fall or 1
|
||||
|
||||
local node_name = minetest.get_node(pos).name
|
||||
-- Get the height of the node collision box (and of its node box, if different)
|
||||
|
|
Loading…
Add table
Reference in a new issue