这段Lua脚本定义了一个名为 ai_autofight_find_way
的类,继承自 ai_base
类。
lua 游戏架构 之 游戏 AI (一)ai_base-CSDN博客文章浏览阅读238次。定义了一套接口和属性,可以基于这个基础类派生出具有特定行为的AI组件。例如,可以创建追逐敌人的AI、巡逻的AI或使用特定策略的AI等,都继承自这个基础类https://blog.csdn.net/heyuchang666/article/details/140624481?spm=1001.2014.3001.5502
这个类用于处理游戏中AI在自动战斗模式下寻找路径的逻辑。以下是对代码的具体解释:
引入基类:
- 使用
require
函数引入ai_base
类,作为基础类。
- 使用
定义
ai_autofight_find_way
类:- 使用
class
关键字定义了ai_autofight_find_way
类,并继承自BASE
(即ai_base
)。
- 使用
**构造函数 (
ctor
)**:- 构造函数接受一个
entity
参数,并设置_type
属性为eAType_AUTOFIGHT_FIND_WAY
,表示自动战斗中寻找路径的行为。 - 初始化
_target
为nil
,用于后续存储找到的目标。
- 构造函数接受一个
IsValid
方法:
- 这个方法用于验证AI是否应该寻找路径。它首先检查实体是否开启了自动战斗(
_AutoFight
),是否死亡或无法攻击。
- 这个方法用于验证AI是否应该寻找路径。它首先检查实体是否开启了自动战斗(
- 检查实体的行为,如果处于准备战斗或禁止攻击状态,则返回
false
。
- 检查实体的行为,如果处于准备战斗或禁止攻击状态,则返回
- 计算警报范围
radius
,可能基于实体的属性或世界配置。
- 计算警报范围
- 根据不同的地图类型和条件,确定是否需要寻找路径。
OnEnter
方法:- 当AI组件进入激活状态时执行。根据当前地图类型和条件,计算目标位置并使实体移动到该位置。
OnLeave
方法:- 当AI组件离开激活状态时执行。当前实现中直接返回
true
。
- 当AI组件离开激活状态时执行。当前实现中直接返回
OnUpdate
方法:- 每帧调用,用于更新AI状态。如果基类的
OnUpdate
方法返回true
,则当前方法也返回true
。
- 每帧调用,用于更新AI状态。如果基类的
OnLogic
方法:- 逻辑更新方法,如果基类的
OnLogic
方法返回true
,则当前方法返回false
,表示只执行一次。
- 逻辑更新方法,如果基类的
创建组件函数:
create_component
函数用于创建ai_autofight_find_way
类的新实例,传入一个实体和一个优先级。
代码中的一些关键点:
IsDead()
:检查实体是否死亡。
CanAttack()
:检查实体是否可以攻击。
GetPropertyValue(ePropID_alertRange)
:获取实体的警报范围属性。
game_get_world()
:获取游戏世界配置。
Test(eEBPrepareFight)
和Test(eEBDisAttack)
:检查实体的行为状态。
MoveTo()
:移动到指定位置。
这个脚本为游戏中的AI提供了一个自动战斗中寻找路径的基础框架,可以根据具体游戏的需求进行扩展和修改。以下是一些具体的逻辑处理:
- 根据不同的地图类型(如
g_BASE_DUNGEON
、g_ACTIVITY
等),AI的行为可能会有所不同。
- 根据不同的地图类型(如
- 计算与目标的距离,并根据距离决定是否移动。
- 考虑地图上的特定点(如物品掉落点、怪物刷新点)来决定移动路径。
- 使用
vec3_dist
函数计算两个位置之间的距离,并根据距离决定是否移动到该位置。
- 使用
整体而言,这个类的目的是在自动战斗模式下,根据游戏世界的当前状态和配置,为AI实体找到合适的移动路径。
重点解释一下 OnEnter:
function ai_autofight_find_way:OnEnter()
if BASE.OnEnter(self) then
local entity = self._entity;
local radius = entity:GetPropertyValue(ePropID_alertRange);
local logic = game_get_logic();
local world = game_get_world();
if world then
-- 如果世界配置中有自动战斗半径,则使用该值
if world._cfg.autofightradius then
radius = world._cfg.autofightradius;
end
-- 根据不同的地图类型执行不同的逻辑
if world._mapType == g_BASE_DUNGEON or world._mapType == g_ACTIVITY or ... then
-- 检查所有掉落物品,如果物品处于激活状态,则移动到该物品位置
for k,v in pairs(world._ItemDrops) do
if v and v:GetStatus() == eSItemDropActive then
local _pos = logic_pos_to_world_pos(v._curPos);
entity:MoveTo(_pos);
return false; -- 移动到物品位置后,退出函数
end
end
-- 如果地图类型是开放区域,并且有怪物刷新点或当前活动区域
if world._openType == g_FIELD then
-- 寻找一个有活着的怪物的刷新点
local _pos = nil;
local isfind = false;
for k1,v1 in pairs(world._curArea._spawns) do
for k2,v2 in pairs(v1._monsters) do
if not v2:IsDead() then
isfind = true;
break;
end
end
if isfind then
_pos = v1._cfg.pos;
break;
end
end
-- 如果没有找到有活着的怪物的刷新点,使用第一个刷新点的位置
if not _pos then
_pos = world._curArea._spawns[1]._cfg.pos;
end
-- 计算实体当前位置到刷新点或地图增益点的距离
local dist = vec3_dist(entity._curPos,world_pos_to_logic_pos(_pos));
local mindist = dist;
-- 寻找最近的地图增益点
for k,v in pairs(world._mapbuffs) do
if v and v:GetStatus() == 1 then
local distbuff = vec3_dist(v._curPos,entity._curPos);
if distbuff < mindist and distbuff < db_common.droppick.AutoFightMapbuffAutoRange then
mindist = distbuff;
_pos = logic_pos_to_world_pos(v._curPos);
end
end
end
-- 移动实体到计算出的位置
entity:MoveTo(_pos);
end
-- 其他地图类型的逻辑...
elseif world._mapType == g_FIELD or world._mapType == g_Life then
-- 对于其他地图类型,寻找最近的地图增益点并移动实体
-- ...
end
end
return false; -- 如果没有找到目标位置或执行了移动逻辑,则返回false
end
return false; -- 如果没有调用基类的OnEnter或基类返回false,则返回false
end
在
OnEnter
方法中,首先调用基类的
OnEnter
方法,如果它返回
false
,则直接返回
false
。如果基类的
OnEnter
方法返回
true
,则继续执行以下逻辑:
- 获取实体的警报范围
radius
。 - 检查游戏世界配置,如果存在自动战斗半径配置,则使用该配置值覆盖实体的警报范围。
- 根据当前的地图类型,执行不同的逻辑来寻找目标位置。例如: - 如果是
g_BASE_DUNGEON
、g_ACTIVITY
等地图类型,会检查所有物品掉落点,寻找激活的物品并移动到该位置。- 如果是开放区域(g_FIELD
),会寻找有活着的怪物的刷新点或最近的地图增益点,并移动实体到该位置。 - 使用
vec3_dist
函数计算实体当前位置到目标位置的距离,并根据这个距离来确定是否移动实体。 - 如果找到目标位置,则调用
entity:MoveTo(_pos)
方法移动实体到该位置,然后返回false
退出函数。 - 如果没有找到目标位置或不满足移动条件,则返回
false
。
整体而言,
OnEnter
方法的目的是确定AI在自动战斗模式下应该移动到哪个位置,并执行移动操作。
全部代码实现:
----------------------------------------------------------------
module(..., package.seeall)
local require = require
local BASE = require("logic/entity/ai/ai_base").ai_base;
------------------------------------------------------
ai_autofight_find_way = class("ai_autofight_find_way", BASE);
function ai_autofight_find_way:ctor(entity)
self._type = eAType_AUTOFIGHT_FIND_WAY;
self._target = nil;
end
function ai_autofight_find_way:IsValid()
local entity = self._entity;
if not entity._AutoFight then
return false;
end
if entity:IsDead() or not entity:CanAttack() then
return false;
end
if entity._behavior:Test(eEBPrepareFight) then
return false;
end
if entity._behavior:Test(eEBDisAttack) then
return false;
end
local radius = entity:GetPropertyValue(ePropID_alertRange);
local world = game_get_world();
if world then
if world._cfg.autofightradius then
radius = world._cfg.autofightradius;
end
local target = entity._alives[2][1]; -- 敌方
if entity._alives[3][1] then--中立
local trap = entity._alives[3][1];
if trap.entity and trap.entity._traptype == eSTrapActive then
target = entity._alives[3][1];
end
end
if target then
if target.dist < radius then
if target.entity._groupType == eGroupType_N and target.dist > db_common.droppick.AutoFightMapbuffAutoRange then
else
return false;
end
end
else
if world._mapType == g_TOURNAMENT then
return false;
end
end
if world._mapType == g_BASE_DUNGEON or world._mapType == g_ACTIVITY or world._mapType == g_FACTION_DUNGEON or world._mapType == g_TOWER or world._mapType == g_WEAPON_NPC or world._mapType == g_RIGHTHEART or world._mapType == g_ANNUNCIATE or world._mapType == g_FIGHT_NPC or world._mapType == g_Pet_Waken then
if world._openType == g_FIELD then
if #world._spawns == 0 and not world._curArea then
return false
end
else
local spawnID = math.abs(g_game_context:GetDungeonSpawnID())
if spawnID == 0 then
return false
end
local dist = nil;
if spawnID ~= 0 then
spawnPointID = db_spawn_area[spawnID].spawnPoints[1]
_pos = db_spawn_point[spawnPointID].pos
dist = vec3_dist(entity._curPos,world_pos_to_logic_pos(_pos))
if dist and dist < 100 then
return false
end
end
end
elseif world._mapType == g_FIELD or world._mapType == g_Life then
if entity._PVPStatus ~= g_PeaceMode then
return false;
end
local dist = vec3_dist(entity._curPos,entity._AutoFight_Point)
if dist < radius then
return false;
end
local value = g_game_context:getAutoFightRadius()
if value and value == g_OneMap then
return false;
end
else -- TODO
return false;
end
end
return true;
end
function ai_autofight_find_way:OnEnter()
if BASE.OnEnter(self) then
local entity = self._entity;
local radius = entity:GetPropertyValue(ePropID_alertRange)
local logic = game_get_logic();
local world = game_get_world();
if world then
if world._cfg.autofightradius then
radius = world._cfg.autofightradius
end
if world._mapType == g_BASE_DUNGEON or world._mapType == g_ACTIVITY or world._mapType == g_FACTION_DUNGEON or world._mapType == g_TOWER or world._mapType == g_WEAPON_NPC or world._mapType == g_RIGHTHEART or world._mapType == g_ANNUNCIATE or world._mapType == g_FIGHT_NPC or world._mapType == g_Pet_Waken then
for k,v in pairs(world._ItemDrops) do
if v and v:GetStatus() == eSItemDropActive then
local _pos = logic_pos_to_world_pos(v._curPos)
entity:MoveTo(_pos)
return false;
end
end
if world._openType == g_FIELD then
if #world._spawns > 0 or world._curArea then
local _pos = nil;
local isfind = false
for k1,v1 in pairs(world._curArea._spawns) do
for k2,v2 in pairs(v1._monsters) do
if not v2:IsDead() then
isfind = true;
break;
end
end
if isfind then
_pos = v1._cfg.pos;
break;
end
end
if not _pos then
_pos = world._curArea._spawns[1]._cfg.pos
end
--local _pos = world._curArea._spawns[1]._cfg.pos
local dist = vec3_dist(entity._curPos,world_pos_to_logic_pos(_pos))
local mindist = dist
for k,v in pairs(world._mapbuffs) do
if v and v:GetStatus() == 1 then
local distbuff = vec3_dist(v._curPos,entity._curPos)
if distbuff < mindist and distbuff < db_common.droppick.AutoFightMapbuffAutoRange then
mindist = distbuff
_pos = logic_pos_to_world_pos(v._curPos)
end
end
end
entity:MoveTo(_pos)
end
else
local _pos = nil
local spawnID = math.abs(g_game_context:GetDungeonSpawnID())
local dist = 99999999999;
if spawnID ~= 0 then
spawnPointID = db_spawn_area[spawnID].spawnPoints[1]
_pos = db_spawn_point[spawnPointID].pos
dist = vec3_dist(entity._curPos,world_pos_to_logic_pos(_pos))
end
local mindist = dist
local isspawn = true
for k,v in pairs(world._mapbuffs) do
if v and v:GetStatus() == 1 then
local distbuff = vec3_dist(v._curPos,entity._curPos)
if distbuff < mindist and distbuff < db_common.droppick.AutoFightMapbuffAutoRange then
mindist = distbuff
isspawn = false;
_pos = logic_pos_to_world_pos(v._curPos)
end
end
end
if mindist < 150 and isspawn and g_game_context:GetDungeonSpawnID() < 0 then
g_game_context:SetDungeonSpawnID(0);
_pos = nil;
end
if _pos then
entity:MoveTo(_pos)
end
end
elseif world._mapType == g_FIELD or world._mapType == g_Life then
for k,v in pairs(world._mapbuffs) do
if v and v:GetStatus() == 1 then
local distbuff = vec3_dist(v._curPos,entity._AutoFight_Point)
if distbuff < radius and distbuff < db_common.droppick.AutoFightMapbuffAutoRange then
local _pos = logic_pos_to_world_pos(v._curPos)
entity:MoveTo(_pos)
return false;
end
end
end
end
end
return false;
end
return false;
end
function ai_autofight_find_way:OnLeave()
if BASE.OnLeave(self) then
return true;
end
return false;
end
function ai_autofight_find_way:OnUpdate(dTime)
if BASE.OnUpdate(self, dTime) then
return true;
end
return false;
end
function ai_autofight_find_way:OnLogic(dTick)
if BASE.OnLogic(self, dTick) then
return false; -- only one frame
end
return false;
end
function create_component(entity, priority)
return ai_autofight_find_way.new(entity, priority);
end
版权归原作者 heyuchang666 所有, 如有侵权,请联系我们删除。