lua 游戏架构 之 游戏 AI (五)ai_autofight_find_way

avatar
作者
猴君
阅读量:0

这段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在自动战斗模式下寻找路径的逻辑。以下是对代码的具体解释:

1. **引入基类**:
   - 使用 `require` 函数引入 `ai_base` 类,作为基础类。

2. **定义 `ai_autofight_find_way` 类**:
   - 使用 `class` 关键字定义了 `ai_autofight_find_way` 类,并继承自 `BASE`(即 `ai_base`)。

3. **构造函数 (`ctor`)**:
   - 构造函数接受一个 `entity` 参数,并设置 `_type` 属性为 `eAType_AUTOFIGHT_FIND_WAY`,表示自动战斗中寻找路径的行为。
   - 初始化 `_target` 为 `nil`,用于后续存储找到的目标。

4. **`IsValid` 方法**:

  •    - 这个方法用于验证AI是否应该寻找路径。它首先检查实体是否开启了自动战斗(`_AutoFight`),是否死亡或无法攻击。
  •    - 检查实体的行为,如果处于准备战斗或禁止攻击状态,则返回 `false`。
  •    - 计算警报范围 `radius`,可能基于实体的属性或世界配置。
  •    - 根据不同的地图类型和条件,确定是否需要寻找路径。

5. **`OnEnter` 方法**:
   - 当AI组件进入激活状态时执行。根据当前地图类型和条件,计算目标位置并使实体移动到该位置。

6. **`OnLeave` 方法**:
   - 当AI组件离开激活状态时执行。当前实现中直接返回 `true`。

7. **`OnUpdate` 方法**:
   - 每帧调用,用于更新AI状态。如果基类的 `OnUpdate` 方法返回 `true`,则当前方法也返回 `true`。

8. **`OnLogic` 方法**:
   - 逻辑更新方法,如果基类的 `OnLogic` 方法返回 `true`,则当前方法返回 `false`,表示只执行一次。

9. **创建组件函数**:
   - `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,则继续执行以下逻辑:

  1. 获取实体的警报范围 radius
  2. 检查游戏世界配置,如果存在自动战斗半径配置,则使用该配置值覆盖实体的警报范围。
  3. 根据当前的地图类型,执行不同的逻辑来寻找目标位置。例如:
    • 如果是 g_BASE_DUNGEONg_ACTIVITY 等地图类型,会检查所有物品掉落点,寻找激活的物品并移动到该位置。
    • 如果是开放区域(g_FIELD),会寻找有活着的怪物的刷新点或最近的地图增益点,并移动实体到该位置。
  4. 使用 vec3_dist 函数计算实体当前位置到目标位置的距离,并根据这个距离来确定是否移动实体。
  5. 如果找到目标位置,则调用 entity:MoveTo(_pos) 方法移动实体到该位置,然后返回 false 退出函数。
  6. 如果没有找到目标位置或不满足移动条件,则返回 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  

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!