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

news2024/9/22 5:37:45

这段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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1947994.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue实现简单小案例

一、创建文件夹 二、引用vue.js <script src"../js/vue.js"></script> 三、准备一个容器 <div id"app"><h1>Hello,{{name}}</h1> </div> 四、创建实例 <script>new Vue({el:"#app", //el用于指…

tcache attack

Tcache Attack tcache让堆利用更加简单&#xff1a; tcache回顾&#xff1a; 在 tcache 中新增了两个结构体&#xff0c;分别是 tcache_entry 和 tcache_perthread_struct&#xff1a; /* We overlay this structure on the user-data portion of a chunk when the chunk is …

数据库概念以及增删改

1.概念 、 2.通用语法 3.数据增删改

mac大文件清理软件哪个好 mac大文件怎么清理 苹果电脑清理软件推荐免费

mac采用固态硬盘技术&#xff0c;数据存储和系统响应速度优势明显&#xff0c;但是mac的内存弊端同样体现在其固态硬盘的技术&#xff0c;导致用户无法通过机械硬盘进行扩充内存。而我们日常使用电脑会产生大量系统垃圾、用户缓存等文件&#xff0c;平时下载的电影和大型游戏安…

通信原理-实验六:实验测验

实验六 实验测验 一&#xff1a;测验内容和要求 测试需要完成以下几个步骤&#xff1a; 配置好以下网络图&#xff1b;占总分10%&#xff08;缺少一个扣一分&#xff09;根据下面图配置好对应的IP和网关以及路由等相关配置&#xff0c;保证设备之间连通正常&#xff1b;占总…

【日常记录】【JS】对一个数组,按照某个字段的值,进行分组

文章目录 1. 前言2. lodash 的分组3. Object.groupBy()参考链接 1. 前言 在开发中&#xff0c;经常会遇到一组数据&#xff0c;要按照某个字段进行分组&#xff0c;这个时候会有很多种方法&#xff0c;可以使用 forEach、reduce、等其他方法 reduce 方法 function groupBy(arr…

JS基础知识学习笔记全

JS基础知识学习笔记全 一、引入方式 1、内部脚本 &#xff08;一般定义在body下面会改善执行速度&#xff09; <body></body><!-- 内部脚本 --><script>/* 打开页面警告框显示的内容 */alert(helloJS);</script>2、外部脚本 外部专门新建一…

Redis-主从模式

目录 前言 一.主从节点介绍 二.配置redis主从结构 二.主从复制 四.拓扑结构 五.数据同步 全量复制&#xff08;Full Sync Replication&#xff09; 局部复制&#xff08;Partial Replication&#xff09; Redis的学习专栏&#xff1a;http://t.csdnimg.cn/a8cvV 前言 …

H3CNE(vlan与子接口技术)

目录 10.1 vlan间通信技术 示例一&#xff08;多臂路由&#xff09;&#xff1a; 10.2 子接口技术 示例二&#xff08;子接口技术&#xff09;&#xff1a; 10.3 vlannif接口技术 10.3.1 三层交换机与VLANNIF技术 示例三VLANNIF配置&#xff08;将交换机当成路由器使用&…

鸿蒙仓颉语言【cryptocj 库】RC2、 RC4 、AES对称加密算法

2 提供RC2、 RC4 、AES对称加密算法 前置条件&#xff1a;NA 场景&#xff1a; 支持对称加密算法。 约束&#xff1a;RC2密钥长度一般16字节&#xff0c;加密块长度8字节&#xff1b;AES加密块长度16字节 性能&#xff1a; 支持版本几何性能持平 可靠性&#xff1a; NA …

pytest:4种方法实现 - 重复执行用例 - 展示迭代次数

简介&#xff1a;在软件测试中&#xff0c;我们经常需要重复执行测试用例&#xff0c;以确保代码的稳定性和可靠性。在本文中&#xff0c;我们将介绍四种方法来实现重复执行测试用例&#xff0c;并显示当前迭代次数和剩余执行次数。这些方法将帮助你更好地追踪测试执行过程&…

3.多租户调研1

https://gitee.com/xiaoqiangBUG/hello-ruoyi-cloud.git 1.mybatis plus 的插件 TenantLineInnerInterceptor 是 MyBatis Plus 框架中的一个拦截器&#xff0c;它用于实现多租户系统的数据隔离。在多租户应用中&#xff0c;不同的租户应该只能访问到自己的数据&#xff0c;而…

URL过滤、DNS过滤和内容过滤的总结

目录 URL过滤 URL和URI URL -- 统一资源定位符 URI --- 统一资源的标识符 URL和URI之间的区别 URL过滤的方式 HTTP协议获取URL的方式 HTTP协议做控制管理的流程 HTTPS协议做控制管理的流程 1&#xff0c;配置SSL的解密功能 2&#xff0c;直接针对加密流量进行过滤 例…

javaEE-03-cookie与session

文章目录 Cookie创建Cookie获取Cookie更新CookieCookie 生命控制Cookie 有效路径 Session 会话创建和获取sessionSession 域数据的存取Session 生命周期控制浏览器和 Session 之间关联 Cookie Cookie 是服务器通知客户端保存键值对的一种技术,客户端有了 Cookie 后&#xff0c…

深入解析 GPT-4o mini:强大功能与创新应用

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &#x1f4e2;本文由 JohnKi 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f4e2;未来很长&#…

品牌故事线:如何在活动策划中保持品牌信息的连贯性?

在活动运营中保持品牌一致性和传达清晰的品牌信息&#xff0c;是确保活动成功并提升品牌形象的关键。 以下是一些具体的方法和建议。 码字不易&#xff0c;如果回答对你有所帮助&#xff0c;请不吝给一个三连哦&#xff01; 一、明确品牌定位与核心价值 首先&#xff0c;需…

一起刷C语言菜鸟教程100题(27-35)

先说明这个虽然菜鸟教程也有答案&#xff0c;但是这个专栏的博客是自己过手写了一遍&#xff0c;有自己的理解&#xff0c;有些习题自己是变化了一些&#xff0c;更适合练手&#xff0c;也会写的更普遍一些~ 今天我们一起继续刷题&#xff0c;链接放在这里供大家自行使用 C 语…

小红书(社招二面)算法原题

萝卜快跑涨价 距离我们上次谈 萝卜快跑 不足半月&#xff0c;萝卜快跑迎来了不少"反转"。 先是被曝远程后台有人操控&#xff0c;真实日成本超 400&#xff1a; 最近还被不少网友吐槽&#xff1a;萝卜快跑涨价了&#xff0c;如今价格和网约车持平。 据不少博主实测&a…

如何在 Windows 上安装并配置 VNC 远程连接树莓派,并结合Cpolar实现公网远程访问

目录 ⛳️推荐 前言 1. 使用 Raspberry Pi Imager 安装 Raspberry Pi OS 2. Windows安装VNC远程树莓派 3. 使用VNC Viewer公网远程访问树莓派 3.1 安装Cpolar步骤 3.2 配置固定的公网地址 3.3 VNC远程连接测试 4. 固定远程连接公网地址 4.1 固定TCP地址测试 ⛳️推荐…

FastAPI(六十九)实战开发《在线课程学习系统》接口开发--修改密码

源码见&#xff1a;"fastapi_study_road-learning_system_online_courses: fastapi框架实战之--在线课程学习系统" 之前我们分享了FastAPI&#xff08;六十八&#xff09;实战开发《在线课程学习系统》接口开发--用户 个人信息接口开发。这次我们去分享实战开发《在线…