定义一个名为 `ai_autofight_find_target` 的类,继承自 `ai_base` 类。
这个类用于处理游戏中AI自动战斗中寻找目标的逻辑。以下是对代码的具体解释:
1. **引入基类**: 使用 `require` 函数引入 `ai_base` 类,作为基础类。
2. **定义 `ai_autofight_find_target` 类**: 使用 `class` 关键字定义了 `ai_autofight_find_target` 类,并继承自 `BASE`(即 `ai_base`)。
3. **构造函数 (`ctor`)**:
- - 构造函数 `entity` 游戏实体,并设置 `_type` 属性为 `eAType_AUTOFIGHT_FIND_TARGET`,表示自动战斗中寻找目标的行为。
- - 初始化 `_target` 为 `nil`,用于后续存储找到的目标。
4. **`IsValid` 方法**:
- - 这个方法用于验证AI是否应该寻找目标。它首先检查实体是否开启了自动战斗(`_AutoFight`),是否死亡或无法攻击。
- - 计算警报范围 `filterdist`,可能基于实体的属性或世界配置。
- - 检查实体的预命令类型,如果是点击移动或摇杆移动,则返回 `false`。
- - 检查实体的当前技能 `_curSkill`,根据技能类型(伤害、诅咒、祝福)和相关逻辑来确定目标。
5. **`OnEnter` 方法**:
- 当AI组件进入激活状态时执行。如果已经找到目标 `_target`,则根据当前技能设置目标或移动到目标位置。
6. **`OnLeave` 方法**:
- 当AI组件离开激活状态时执行。清除目标 `_target`。
7. **`OnUpdate` 方法**:
- 每帧调用,用于更新AI状态。如果基类的 `OnUpdate` 方法返回 `true`,则当前方法也返回 `true`。
8. **`OnLogic` 方法**:
- 逻辑更新方法,如果基类的 `OnLogic` 方法返回 `true`,则当前方法返回 `false`,表示只执行一次。
9. **创建组件函数**:
- `create_component` 函数用于创建 `ai_autofight_find_target` 类的新实例,传入一个实体和一个优先级。
代码中的一些关键点:
- - `IsDead()`:检查实体是否死亡。
- - `CanAttack()`:检查实体是否可以攻击。
- - `GetPropertyValue(ePropID_alertRange)`:获取实体的警报范围属性。
- - `game_get_world()`:获取游戏世界配置。
- - `GetEnmities()`:获取实体的敌对列表。
- - `GetRadius()`:获取实体的半径。
- - `MoveTo()`:移动到指定位置。
- - `SetTarget()`:设置目标实体。
这个脚本为游戏中的AI提供了一个自动战斗中寻找目标的基础框架,可以根据具体游戏的需求进行扩展和修改。
----------------------------------------------------------------
local require = require
local BASE = require("logic/entity/ai/ai_base").ai_base;
ai_autofight_find_target = class("ai_autofight_find_target", BASE);
function ai_autofight_find_target:ctor(entity)
self._type = eAType_AUTOFIGHT_FIND_TARGET;
self._target = nil;
end
function ai_autofight_find_target:IsValid()
local entity = self._entity;
if not entity._AutoFight then
return false;
end
if entity:IsDead() or not entity:CanAttack() then
return false;
end
local filterdist = entity:GetPropertyValue(ePropID_alertRange)
local world = game_get_world();
if world._cfg.autofightradius then
filterdist = world._cfg.autofightradius
end
local r1 = entity:GetRadius();
if entity._PreCommand == ePreTypeClickMove or entity._PreCommand == ePreTypeJoystickMove then
return false;
end
if entity._curSkill then
local target = nil;
local ignoreDist = false;
local stype = entity._curSkill._cfg.type;
if stype == eSE_Damage or stype == eSE_DBuff then -- 伤害、诅咒
if entity._forceAttackTarget and not entity._forceAttackTarget:IsDead() then
ignoreDist = true;
target = entity._forceAttackTarget;
else
local enmities = entity:GetEnmities();
if enmities then
local enmity = enmities[1];
if enmity then
ignoreDist = true;
target = enmity;
end
end
if not target or target._groupType == eGroupType_N then
local tentity = nil
local mapType = game_get_map_type();
for i,v in ipairs(entity._alives[2]) do
local r2 = entity._alives[2][i].entity:GetRadius();
local radius = r1 + r2;
if radius then
if mapType then
if mapType == g_ARENA_SOLO or mapType == g_TAOIST then
break;
end
end
if entity._alives[2][i].dist < entity._curSkill._range + radius then
tentity = entity._alives[2][i];
if entity._alives[2][i+1] and entity._alives[2][i+1].dist then
if tentity.dist < entity._alives[2][i+1].dist then
tentity = entity._alives[2][i+1];
else
tentity = entity._alives[2][i];
end
else
tentity = entity._alives[2][i];
break;
end
else
if v.entity:GetEntityType() == eET_Player and v.dist > filterdist then
if entity._alives[2][i+1] and entity._alives[2][i+1].dist < filterdist then
tentity = entity._alives[2][i+1];
break;
end
else
tentity = entity._alives[2][i-1];
break;
end
end
end
end
if entity._groupType == eGroupType_O then
local nentity = tentity or entity._alives[2][1] or entity._alives[3][1]
if target and nentity and nentity.entity and nentity.entity._groupType ~= eGroupType_N then
target = nentity.entity;
elseif not ignoreDist then
target = tentity or 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 nentity and nentity.entity._groupType == eGroupType_N and nentity.dist > db_common.droppick.AutoFightMapbuffAutoRange then
target = nil;
return false;
end
end
else
target = tentity or entity._alives[2][1] -- 敌方
end
end
end
elseif stype == eSE_Buff then -- 祝福
if entity._target and entity._target._guid == entity._guid then
return false;
end
self._target = entity;
return true;
end
if target then
if ignoreDist then
if not (target:GetEntityType() == eET_Pet and (not entity._enmities or #entity._enmities <= 0)) then
self._target = target;
end
if not entity._target or entity._target:IsDead() then
return true;
end
return target._guid ~= entity._target._guid;
else
local r2 = target.entity:GetRadius();
local radius = r1 + r2;
if target.dist < filterdist + radius then
if not (target.entity:GetEntityType() == eET_Pet and (not entity._enmities or #entity._enmities <= 0)) then
self._target = target.entity;
end
if not entity._target or entity._target:IsDead() then
return true;
end
if target.dist < entity._curSkill._range + radius then
return false;
end
return target.entity._guid ~= entity._target._guid;
end
end
end
end
return false;
end
function ai_autofight_find_target:OnEnter()
if BASE.OnEnter(self) then
local entity = self._entity;
--log("ai_autofight_find_target")
entity._behavior:Clear(eEBGuard);
if self._target then
if entity._curSkill then
entity:SetTarget(self._target);
else
entity:MoveTo(self._target._curPos);
end
end
return true;
end
return false;
end
function ai_autofight_find_target:OnLeave()
if BASE.OnLeave(self) then
self._target = nil;
return true;
end
return false;
end
function ai_autofight_find_target:OnUpdate(dTime)
if BASE.OnUpdate(self, dTime) then
return true;
end
return false;
end
function ai_autofight_find_target: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_target.new(entity, priority);
end