Y3编辑器文档4:触发器1(对话、装备、特效、行为树、排行榜、不同步问题)

文章目录

    • 一、触发器简介
      • 1.1 触发器界面
      • 1.2 ECA语句编辑及快捷键
      • 1.3 参数设置
      • 1.4 变量设置
      • 1.5 实体触发器
      • 1.6 函数库与触发器复用
    • 二、触发器的多层结构
      • 2.1 子触发器(在游戏内对新的事件进行注册)
      • 2.2 触发器变量作用域
      • 2.3 复合条件
      • 2.4 循环
      • 2.5 计时器
      • 2.6 单位组
      • 2.7 玩家组
      • 2.8 运动器
    • 三、案例
      • 3.1 NPC对话
      • 3.2 装备限制
        • 3.2.1 装备拾取
        • 3.2.2 物品丢弃
      • 3.3 特效
        • 3.3.1 特效生成
        • 3.3.2 特效销毁
      • 3.4 行为树
      • 3.5 使用排行榜
      • 3.6 特效可见性
      • 3.7 不同步问题
        • 3.7.1 本地配置不同步日志环境
        • 3.7.2 线上游戏不同步定位
        • 3.7.2 定位不同步问题
        • 3.7.3 游戏不同步的原因

  • Y3编辑器文档1:编辑器简介及菜单栏详解(文件、编辑、窗口、细节、调试)
  • Y3编辑器文档2:场景编辑(地形编辑、物件放置)
  • Y3编辑器文档3:物体编辑器
  • Y3编辑器文档4:触发器1(对话、装备、特效、行为树、排行榜、不同步问题)

一、触发器简介

参考官方文档《触发器》

  触发器是Y3编辑器中实现游戏逻辑的核心组件,它通过“事件-条件-动作”(Event-Condition-Action,简称ECA)的模式来实现各种效果。

  • 事件(Event):触发器的导火索,当设定的事件发生时触发器才会执行。
  • 条件(Condition):触发执行必须满足的条件。
  • 动作(Action):触发执行的结果。
    在这里插入图片描述

1.1 触发器界面

  通过主界面顶部功能栏或使用快捷键F4可进入触发器。进入触发器窗口后,你可以添加或删除文件夹和触发器,并使用搜索功能快速找到你想找的触发器;还可以对选定的触发器进行以下操作: 复制, 剪切, 粘贴, 删除, 重命名, 禁用, 和转化成Lua代码。
在这里插入图片描述
右键单击触发器并选择禁用使其无效。单击启用以重新激活无效的触发器

在这里插入图片描述

  你可以在变量管理事件管理中提前写入常用的变量和事件,以便在游戏中随时调用,从而减少开发项目的记忆成本和后期维护成本。比如可以提前设置事件并添加事件中需要的参数,然后在触发器-自定义事件中进行调用。
在这里插入图片描述
在这里插入图片描述
  在主界面-编辑-通用设置-编辑设置中勾选“缩略图滚动条”,可以显示ECA缩略图滚动条。左键点击可以定位到想要的行数,长按左键可以拖动滚动条。错误、点选与搜索结果也会以色块形式显示到普通滚动条上。缩略图可以收起,收起后需要重新在“主界面-编辑-通用设置-编辑设置”打开。
在这里插入图片描述
在这里插入图片描述

1.2 ECA语句编辑及快捷键

  点击事件/条件/动作后面的"+"为当前部分新建语句,也可以在空白处点击鼠标右键,新建事件,条件,动作,子触发器与注释。如果想要更改已有的事件,条件或者动作,双击对应语句即可弹出选项窗口,选择新的内容后即可进行覆盖。

功能快捷键说明
新建触发器Ctrl + T
新生子触发器Ctrl + Q
新建事件Ctrl + E
新建条件Ctrl + D
新建动作Ctrl + R
新建注释Ctrl + N
ECA注释开关Ctrl + M将当前语句转为注释
设为无效Ctrl + W使触发器文件或eca条目无效(被跳过)
条目下移Ctrl + ↓快速将当前选中的eca下移一行
条目上移Ctrl + ↑快速将当前选中的eca上移一行
新建收纳盒Ctrl + P
添加至收纳盒Ctrl + O添加当前选中eca到收纳盒
变量引用查看A
全局元素引用查看S
跳转到函数D

在这里插入图片描述

功能快捷键说明
查看上一个触发器Ctrl + ←
查看下一个触发器Ctrl + →
搜索框Ctrl + F
跳转到物编器Ctrl + J
切换到触发器Ctrl + 1
切换到函数库Ctrl + 2
切换到触发总览Ctrl + 3
跳转F3
复制参数Shift + C右键单击参数使用。复制时,整个参数的值和类型都将被复制到剪贴板上
粘贴参数Shift + V右键单击参数使用,复制的参数类型与目标参数类型必须相同

在这里插入图片描述

1.3 参数设置

  未赋值的参数会以红色显示,已赋值的参数会以填入的数据类型分配颜色,可选的参数则在次级菜单中选用,额外的选项会以蓝色显示,文本信息说明了当前触发器语句的含义。需要指定所有的必选参数,本条动作以及所在的触发器才能正常运行。
在这里插入图片描述
参数有以下几种类型:

  • 预设对象:当参数类型为可以放置在场景中的对象时( 单位,装饰,物体,特效等),可以直接选择该预设对象作为参数。
    在这里插入图片描述
  • 数值:你可以直接将一个固定的数字、文本或布尔值赋给一个参数
  • 变量:包括全局变量和局部变量
  • 函数:一组官方打包的获取数据的动作,可以直接提供对应的返回值。
    在这里插入图片描述
  • 通用:用于设置 自定义值 和调用表格编辑器。
    在这里插入图片描述
      当您单击转换为变量时,会在当前语句之前创建一个变量赋值语句,将当前语句结果赋值给一个变量。转换为变量后,您可以单击取消变量转换以恢复到之前的状态。

在这里插入图片描述

1.4 变量设置

你可以通过触发器界面设置变量,或通过ECA选择界面设置变量。

在这里插入图片描述
变量在创建时,需要声明其类型,部分变量类型必须设置默认值。

  • 非对象型变量:字符串、浮点数、整数、布尔值
  • 对象型变量:单位、单位组、玩家组、单位命令

变量根据作用域类型分为全局变量和局部变量。

  • 全局变量:可以在本项目内的任何触发器中读取和写入。
  • 局部变量:只能在当前触发器中生效。

  数组是相同数据类型的集合,数组中的每个元素都在其基本值上添加了一个索引,使我们在数组中更容易找到和访问它们。整数、单位、实体、单位名字、物品实体等变量都可以设置为数组。比如下面代码设为按下H键之后,在Boss周围召唤6个黑暗守卫(使用数组guards实现)。

在这里插入图片描述

  变量只能用于储存数据的媒介,并不会实时更新,任何对变量的操作都需要使用赋值动作进行实现。请不要忘记在触发器中编写更新变量的语句。例如,在游戏运行的时候设置变量“玩家人数”为6,5分钟后有一位玩家退出游戏,此时游戏中玩家人数为5,如果没有编写变量更新语句,那么变量“玩家人数”依然为6。你需要编写能表达“在玩家退出游戏的时候玩家人数减1”的逻辑。

在这里插入图片描述

1.5 实体触发器

  在物体编辑器->触发器中可进行实体的逻辑编写,即为实体触发器,其优势在于它可以更简单地将逻辑和目标物体绑定,而与其他摆件无关,所以可以减少性能的消耗。实体触发器的编辑与上述触发器操作一致,一个摆件也可以设置多条触发。

1.6 函数库与触发器复用

  你可以将自己常用的触发器语句/功能模块添加到函数库,接着就可以在触发器中找到该函数语句进行重用。与触发器的操作逻辑相同,在左侧创建函数后,在右侧编辑功能库函数。

在这里插入图片描述

初始化玩家技能槽位

函数描述定义了函数在操作列表中的显示方式。 函数描述包括以下内容:

  • 名称:要生成的触发器语句的名称。
  • 说明:要生成的触发器语句的内容和格式。
  • 注意事项:当前函数的说明。

在这里插入图片描述

函数体包括以下内容:

  • 参数:定义了该函数的输入数据,可设置参数类型,如定时器、单位和整数。
  • 返回值:定义了函数执行后返回的结果(输出数据)。你可以点击 "+"来设置返回值的名称和类型,以方便触发器的后续调用。
  • 动作:显示了函数的具体行动逻辑

  在触发器中选择想要复制的触发进行复制,这条触发器的源代码内容会写入到粘贴板中,此时可以在记事本等文本文件中进行本地粘贴保存,或者在新项目的触发器界面选择右键粘贴(Ctrl+V)。
在这里插入图片描述

二、触发器的多层结构

2.1 子触发器(在游戏内对新的事件进行注册)

  触发器具有一个事件,这个事件是通过一个注册来实现的,即对一个事件进行注册,从而让系统在对应的事件触发时能继续按照条件与动作顺序执行。全局触发器是在游戏初始化时就注册完成的,如果要在游戏内对新的事件进行注册,实现更复杂的游戏逻辑,就需要通过子触发器来实现。

  子触发器是在触发器的“动作”中创建的,用于在游戏进程中对新的事件进行注册,注册后会独立运行(按ECA的逻辑执行)。子触发器是一种变量类型,可以返回这个触发器实例,以方便在使用完毕后销毁它(如果需要的话)。因为是游戏进程中注册,所以我们可以把变量中的数据传递给子触发器进行注册。

下面是一个简单的示例,使用子触发器将单位(关羽)移动到相应的点。
在这里插入图片描述

2.2 触发器变量作用域

  作用域是指变量在程序中被定义后可以被访问的范围,它决定了变量的可见性和生命周期。全局变量可以在任何触发器及其子触发器的范围内有效,局部变量可以在当前触发器及其子触发器的范围内有效,子触发器局部变量只能在当前子触发器的范围内有效。作用域的存在可以防止变量命名冲突,提供封装和隔离性,提高程序的可靠性和可维护性。

  局部作用域中的变量优先级更高。比如在子触发器中定义了一个局部变量,这个变量与全局变量或者外部作用域中的变量(另外一个触发器中定义的变量)同名时,子触发器内部只能访问和修改这个局部变量,其余两个同名变量被隐藏,无法直接访问或修改。所以说,作用域机制提供了一定程度的隔离,可以防止子触发器意外修改外部变量,从而避免潜在的错误和不可预测的行为,保持代码的清晰和可维护性。

  根据作用域划分,触发器中的变量可以分为全局变量、函数局部变量、触发局部变量、子触发器局部变量和组变量。根据类型划分,可以分为对象型变量(如单位、单位组、玩家组、单位命令)和非对象型变量(如字符串、实数、整数、布尔值)。

  子触发器内部对于赋值运算(非单位类型局部变量的修改)只会在子触发器内部生效,不会影响外部;但是对对象型变量(如单位组、玩家组等)的操作以及对单位属性的修改会同步影响到外部。这种设计有助于在保持局部变量隔离性的同时,允许对共享资源进行必要的修改(单位对象是一个全局可访问的对象)。

  下面使用全局字符串、全局单位类型、局部字符串、局部单位类型;全局单位组、局部单位组这6个变量进行测试。

  1. 先打印每个类型的初始值

    [Info]: 全局字符串AAA
    [Info]: 全局单位类型UnitName:134219749张飞)
    [Info]: 局部字符串BBB
    [Info]: 局部单位类型UnitName:134274912(关羽)[Info]: 全局单位组中单位数量1
    [Info]: 局部单位组中单位数量1
    
  2. 通过子触发器修改这4个变量的值并打印
    在这里插入图片描述
    在这里插入图片描述

    [Info]: 全局字符串CCC
    [Info]: 全局单位类型UnitName:134220068(大乔)
    [Info]: 局部字符串DDD
    [Info]: 局部单位类型UnitName:134257382(小乔)[Info]: 全局单位组中单位数量2
    [Info]: 局部单位组中单位数量2
    
  3. 通过其他子触发器再次打印变量的值:

    [Info): 全局字符串CCC
    [Info): 全局单位类型UnitName:134220068(大乔)
    [Info]: 局部字符串BBB
    [Info): 局部单位类型UnitName:134274912(关羽)[Info]: 全局单位组中单位数量2
    [Info]: 局部单位组中单位数量2
    

2.3 复合条件

  if流程语句的结构如下,条件可以是取反(not语句)、所有条件成立(and语句)、任意条件语句(or语句)或者所有条件不成立。
在这里插入图片描述
所有条件成立:A和B都等于1 ,则单位会移动到指定的点
在这里插入图片描述
任意条件成立:A和B有一个等于1,则单位会移动到指定的点。
在这里插入图片描述
所有条件不成立:整数A 和整数B 都不等于 1,则单位会移动到指定的点。
在这里插入图片描述
如果A等于1,则单位会移动到指定的点A;否则,会移动到点B。
在这里插入图片描述

2.4 循环

在这里插入图片描述
循环是指重复执行某项动作。在Y3编辑器中,循环有三种类型:

  1. 指定次数或指定整数变量重复执行:这是最常用的循环触发方式,例如,设定NPC敲门三次,动作会重复三次后自动停止。

  2. 条件成立,重复执行:在条件满足的情况下,进行无限次的循环,直到条件不再满足。例如,如果条件是门关着就敲门,那么敲门动作会一直重复,直到门开了才会停止。

  3. 遍历数组变量循环:根据数组的索引数字,对数组中的每个单位重复执行动作。例如,有一个不同门的列表,NPC会按照顺序敲门,直到所有门都被敲过。

下面是《触发器2:循环》中的例子,通过第一种循环实现在BOSS周围出现6个次元种子的效果。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

2.5 计时器

在这里插入图片描述

  计时器就好比一个沙漏,在计时结束后开始执行某项动作。我们可以用计时器来处理和时间有关的游戏逻辑。在编辑器中,计时器一共有三种:

  1. 运行单次计时器:计时器运行一次后开始执行动作。例如,倒数三秒后约翰的门就会打开。
  2. 运行循环计时器:计时器循环运行,每次运行后都会执行动作。例如,每隔一秒约翰的门就会打开一次。
  3. 运行固定次数的计时器:计时器中间停留固定时间,固定次数的计时器。例如,每隔一秒约翰的门就会打开一次,一共打开三次。

  下面是《触发器:计时器》中,使用计时器实现一只只次元入侵者每隔2s在“裂缝”处爬出的效果。

在这里插入图片描述

注意:你可以选择True并立即执行动作,或者选择False在2秒后执行第一个动作。

在这里插入图片描述

2.6 单位组

在这里插入图片描述

  单位组是由一个或多个单位组成的集合,在单位组中,我们能同时对所有的单位或选择某些单位发布命令。合理利用单位组,我们可以在游戏制作中节省大量的时间。下面是在《触发器:单位组》中,使用单位组实现主人公发送特技“次元入侵”,入侵者瞬间死伤殆尽的效果。

在这里插入图片描述

在这里插入图片描述

2.7 玩家组

在这里插入图片描述
  玩家组是一个包含一个或多个玩家的集合。您可以直接对玩家组中的所有玩家执行操作。例如,下面的语句表示在点A创建一个关羽单位,并将该单位分配给玩家组BB中的每个玩家,单位面向180°角度。
在这里插入图片描述

2.8 运动器

在这里插入图片描述

  运动器可以为单位或者特效等添加运动效果,比如沿着直线运动或追踪某一个单位等,是制作技能、特效等场景中十分常用的功能。

  在Y3编辑器中,运动器分为追踪运动器、曲线运动器、直线运动器、环绕运动器(对单位)、环绕运动器(对点)。您可以设置运动器的 方向、距离、初始速度和加速度,以及一些可选参数。
在这里插入图片描述
下面是《触发器:运动器》中,使用环绕运动器(对单位),实现电球的环绕和爆炸效果。
在这里插入图片描述

  • 在物编器中创建一个自定义投射物"Electric ball"(球形蓝色电流攻击)作为环绕的球体,设置缩放0.4倍大小,勾选循环播放,让特效一直播放。
  • 设置自定义投射物"Explosion"(蓝色电流冲击波,0.3倍缩放)作为结束时的爆炸效果。
  • 新建电球投射物:创建一个投射物变量"Electric ball",投射物类型选择预设的投射物"Electric ball",选择在点创建,创建的位置点,选择探险者所在的点。
  • 新建运动器:选择运动器->环绕运动器对单位,两个参数分别设为变量"Electric ball"和探险者,环绕半径为200,角速度为100。在可选参数中,添加环绕时间5s和环绕高度100
  • 新建投射物爆破动作:在运动完成动作列表下添加动作,在探险者所在的点创建投射物"Explosion",持续时间1s
  • 优化游戏内存:销毁投射物"Electric ball"并移除运动器(为单位移除运动器,单位选择探险者)。

在这里插入图片描述

三、案例

3.1 NPC对话

  1. 添加“Talk”属性:通过物体编辑器给NPC添加自定义属性“Talk”来存储对话内容。
    在这里插入图片描述

  2. UI设计:通过界面编辑器设计聊天UI,这包括背景、NPC头像、文本和退出按钮。
    在这里插入图片描述

在这里插入图片描述
3. 显示聊天内容:创建一个显示函数,参数为“NPC”单位。在函数里创建两个本地变量存储NPC头像的ID和自定义属性“Talk”。然后再创建界面组件“Icon”和“Talk text”并读取这两个变量。这样当你具体调用与某个NPC的对话函数时,玩家UI就会出现其头像和文字内容。
在这里插入图片描述

  1. 统一NPC对话入口
    • 当玩家靠近并点击NPC时,程序通过自定义函数统一处理触发事件,传递玩家角色和NPC作为参数。
    • 函数获取对应NPC的一系列信息参数,并显示在聊天界面。
    • 设计退出按钮,当玩家点击时,设置一个布尔值标记为‘true’以关闭界面。另外使用循环计时器检测玩家与NPC的距离,如果超过500也会关闭界面。和前面的统一入口的实现逻辑是类似的,我们需要把不同的退出方式进行单独处理,并最后通知在同一个出口上。

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  1. 功能优化:为了保持界面关闭和显示功能在同一模块下,选择一种相对消耗性能的方式,即使用一个布尔值类型的变量Switch来控制界面的开启和关闭状态。界面在启动时会循环检测Swich的状态,默认状态下,Swich为false,当玩家点击退出按钮时,我们在事件的动作中设置Swich为True
    在这里插入图片描述

  2. 整体实现:当玩家在地图上选择一个单位时,如果检测到是NPC,则调用函数Z02.一次NPC对话事件,并让玩家重新选中玩家角色。只有当玩家与NPC的距离小于400时,才启动对话功能,否则提示距离过远。
    在这里插入图片描述

在这里插入图片描述

3.2 装备限制

  对装备的品类或者数量做限制是RPG类游戏中一个常见的机制,例如一个玩家觉得只能携带一把武器和一件装甲,比如在FPS游戏中,玩家同时只能持有一把枪,想要切换到另一把枪就会把当前持有的武器移除掉,这些实际上的游戏效果都是装备限制。

3.2.1 装备拾取

  当玩家拾取装备时,如果玩家单位上没有该类型的装备,则会直接进行装备。否则拾取的装备将被直接丢弃(系统丢弃,不会通知玩家,也不会触发任何与玩家丢弃装备相关的游戏事件或流程)。

  1. 装备数据存储:使用自定义值功能为每个装备添加一个type属性,用于标识装备的类型。设置一个SystemDiscard标记,用于指示某个装备的丢弃是由系统自动处理的,而不是玩家的操作。

  2. 拾取物品:当玩家拾取一个物品时,系统会创建两个局部变量,分别存储玩家单位和拾取的物品。

  3. 逻辑判断

    • 如果玩家单位已经装备了相同type的装备,那么新拾取的装备将被系统丢弃。
    • 如果玩家单位没有装备相同type的装备,玩家将成功获得新装备,同时,系统会更新玩家持有物品的状态,以便后续可以显示相应的特效或其他游戏效果。
      在这里插入图片描述

以上代码逻辑为:

  1. 初始化变量:设置变量unit为获得物品的单位,item为单位获得的物品。

  2. 检查物品类型:如果item存在自定义键值"type",则继续执行。

  3. 检查玩家是否已装备相同类型的物品

    • 如果玩家单位unit已经存在自定义键值type,并且与itemtype相同,则执行系统丢弃逻辑。
    • 如果unit不存在自定义键值type,或者typeitemtype不同,则执行装备逻辑。
  4. 系统丢弃逻辑

    • 设置item的自定义键值"SystemDiscard"True,表示这是一个系统自动处理的丢弃。
    • itemunit所在位置移除。
  5. 装备逻辑

    • 向玩家发送消息,告知玩家已经获得了新的物品。
    • 更新玩家单位的自定义键值typeitemtype
    • 发送自定义事件ProjectCreation,参数为unititem,用于可能的特效或其他逻辑的触发。
3.2.2 物品丢弃

与拾取物品类似,当一个单位丢弃物品时,先使用局部变量对参数进行缓存,然后执行判断:

  • 玩家丢弃:如果物品被判断是玩家手动抛弃,获取物品type,清空单位对应自定义值项,这个清空的动作会导致特效结构检测到数据清空,从而触发特效的销毁。
  • 系统丢弃:如果该物品被检测为系统丢弃,则跳过整个流程,当做无事发生(系统丢弃是静默的,不会通知玩家,不更新玩家状态,因此不会有新的特效或其他游戏效果被激活)。
    在这里插入图片描述

以上代码逻辑为:

  1. 初始化变量:设置变量unit为失去物品的单位,item为单位失去的物品。

  2. 检查物品类型:如果item存在自定义键值"type",则继续执行。否则发送调试信息“丢弃的物品无类型”

  3. 检查是否为系统丢弃

    • 如果item的自定义键值"SystemDiscard"False,则执行玩家丢弃逻辑。
    • 如果为True,则表示这是一个系统自动处理的丢弃,不执行任何操作。
  4. 玩家丢弃逻辑

    • 设置typeitem的自定义键值type(在玩家丢弃装备的过程中,临时存储装备的类型信息,以便在后续的逻辑中使用这个信息来更新玩家状态、触发相关逻辑或发送消息等)
    • 删除unit的自定义键值type,表示玩家已经失去了这个类型的装备。
    • 向玩家发送消息,告知玩家已经丢弃了物品。
  5. 系统丢弃逻辑

    • 删除item的自定义键值"SystemDiscard",重置物品的状态,这样,如果物品再次被拾取或处理,系统可以重新判断其状态,而不是基于之前的标记。
    • 向玩家发送调试消息“丢弃的物品为系统丢弃”

3.3 特效

  想要为一个已有的事件附加另一个效果,可以使用自定义事件功能,比如在获得新装备后,可以发送一个自定义事件,来激活对应的特效功能。

3.3.1 特效生成

通过发送和接收自定义事件来传输信息,实现特效的激活:

  • 武器特效:武器特效是直接绑定在角色手部的拖尾,所以我们只需要直接将对应的特效路径以自定义值的形式保存在物品上,就可以用同一个函数启用所有的武器特效。

  • 护甲特效:由多个特效组合而成,需要一个函数来不断生成和销毁这些特效。
    在这里插入图片描述

  • 特效分敛:根据自定义事件传递的物品和type参数,分敛到不同类型的特效创建函数,执行对应的效果。
    在这里插入图片描述

如果自定义事件ProjectCreation被触发,则根据物品类型通知不同的特效生成机制:

  • 如果自定义事件参数item的物品类型等于“寒冰剑”或“闪电发生器”,则设置变量projectType为物品的projectType,并为事件单位unit的物品item生成特效projectType
  • 如果item的物品类型等于“冰霜斗篷”,则为事件单位unit的物品item生成盔甲01特效
  • 如果item的物品类型等于“地精的护服”,则为事件单位unit的物品item生成盔甲02特效
3.3.2 特效销毁

  在函数库中,我们新建三个函数,分别是武器,盔甲1和盔甲2,来分别执行武器和盔甲的特效创建和销毁过程。

  对于武器特效功能,是在单位的节点添加一个拖尾特效projectType,同时启动一个循环计时器,定期检查玩家是否仍然持有该武器。如果玩家更换或丢弃了武器,特效将被移除,并对所有结构进行销毁。
在这里插入图片描述

  • 定义了一个名为“X01: 生成武器特效”的函数,该函数接收三个参数:unit(单位),item(物品),projectType(特效类型)。
  • 为单位添加魔法效果projectType,并将此创建魔法效果赋值给变量project,方便后续删除
  • 设置变量typeitemtype
  • 启动循环计时器,每隔一秒检测一次,检测unittype是否等于item。是则继续,否则删除计时器和project

  盔甲1的特效显示逻辑和武器特效类似,也是每秒检测一次。不同的是,盔甲的特效将在每秒计时器到时后立即生成,并在两秒后将其销毁。
在这里插入图片描述

为了显示盔甲2的特殊效果,我们为单位创建了两个投射物,并使用局部变量进行储存。

  • 角度:为了实现球体的环绕效果,我们需要定义一个角度,这个角度将会每帧进行递增,以实现帧叠加以后的球体圆周运动。
    在这里插入图片描述
  • 检测:使用一个帧计时器(0.03秒)来检测装备是否还在被装备,如果它被丢弃,对所有结构进行销毁;否则继续执行
  • 循环:当程序一直持续循环运行,球体的角度会在每一帧减去6,并根据一个固定的距离,以角色位置为圆心,通过极坐标系确定本帧投射物该在的位置。
    在这里插入图片描述
  1. 函数定义:定义了一个名为X03: 生成护甲2特效的函数,接收两个参数:unit(单位)和item(物品)。

  2. 变量初始化

    • type变量被设置为itemtype属性,用于区分不同类型的装备。
    • int变量被初始化为0,可能用于控制特效的生成或更新。
    • angle01angle02变量被初始化为90.0和270.0,用于确定特效的初始位置。
    • 在单位的位置创建两个护甲特效,分别命名为project[1]project[2]
  3. 循环计时器:创建一个循环计时器,每0.03秒执行一次,用于更新特效的位置。 如果单位装备了“地精的护服”,则继续执行循环,否则销毁所有特效。

  4. 更新特效位置

    • angle01angle02变量用于控制特效的旋转角度,每帧都会变化。根据旋转角度,更新两个特效project[1]project[2]的位置point1point2.。
    • 设置变量int每帧加1,然后进行条件判断:
      • 如果int = 1,在point2创建特效project[3]
      • 如果2 ≤ int ≤ 13,持续更新特效project[3]的位置(点point2)。
      • 如果int = 60,重置int = 0

  总的逻辑是每帧检测一次,如果依旧装备“地精的护服”,创建两个特效project[1]project[2]环绕其持有角色,然后每一秒的前13帧,在project[2]的位置同时创建和更新特效project[3]

3.4 行为树

  行为树即是用树状结构分支使AI能根据条件执行不同行为(如走、跑、跳、攻击),让单位通过模拟真实玩家的行动而拥有“智能”行为,增强玩家的参与感,例如PVE的敌人,BOSS的战斗模式,玩家召唤物等。

  1. 初始化行为树:初始化前,需要创建一个全局变量(比如unitPlayer)来存储玩家角色,创建一个全局变量单位组来存储敌方单位。通过动作:“玩家-选择单位”,让玩家选中自己的主控单位。为了更沉浸的游玩体验,使用“镜头 - 跟随单位”功能让玩家的视角能始终跟随在自己的英雄上。
    在这里插入图片描述

  2. 行为树分敛

    • 游戏初始化结束(游戏开始0.1秒后),每隔1秒遍历一次地方单位组,每次遍历所有单位,根据地方单位类型(近卫和治疗者),对其应用对应的行为树。
    • 判断完单位类型后,通过自定义事件传递单位参数来实现行为树的分发。参数必须是变量,不支持数组。比如行为树是针对某单位执行,那么就需要将单位作为参数传递出去。
      在这里插入图片描述
      在这里插入图片描述
  3. 实现守卫AI

    • 新建一个单位类型局部变量unit,存储通过“自定义事件:AI_Guard”发送过来的单位。
    • 新建一个实数类型局部变量distance,用于储存敌人和治疗者之间的距离,通过判断distance是否小于300来判断治疗者是否安全。
      • 如果治疗者不安全(距离小于300),则给单位unit向玩家角色unitPlayer发出一个攻击指令。
      • 如果治疗者安全,判断守卫自身HP,低于50%则移动至治疗者(给单位unit向unitHealer发出一个移动指令)。
      • 如果守卫安全,检查警戒区域内是否有敌人,有则攻击,无则巡逻。

在这里插入图片描述

  1. 实现治疗者AI:新建一个单位类型局部变量unit,存储通过“自定义事件:AI_Healer”发送过来的单位。优先保障自身安全,满HP(自己的HP是否等于其MaxHP)则治疗友军中HP最低的单位(如果有的话),没有需要治疗的单位则进行攻击。
    • 使用一个函数获取当前最需要治疗的友军。
      • 新建局部变量Temp(百分比格式,初始化为1)和unit(单位类型)分别存储单位组中最低的HP百分比及遍历到的单位。
      • 逐一将unit血量和Temp对比,如果unit血量更低则将Temp更新为这个值。当遍历完全以后,我们就获取到了单位组中HP百分比最低的那一个单位。
      • 返回HP百分比最低的那一个单位

在这里插入图片描述

  • 如果HP百分比最低单位为空(友军都是满血),判断自己身边是否有敌人,如果有,就使用飞弹攻击它;没有则在警戒区域的中心不动,以方便巡逻的守卫保护自己。
  • 如果有HP百分比最低的单位,则停止当前的动作(这里并非使用‘技能’来实现治疗,所以为了防止治疗者边攻击边治疗,需要先停止攻击动作)并对其进行治疗。为了让这种动作更具真实感,我们可以播放对应的动画动作,并为治疗效果添加一个特效。

在这里插入图片描述

3.5 使用排行榜

  1. 点击主界面【细节】-【存档设置】打开存档槽设置

  2. 点击加号创建一个新的存档槽并修改存档槽数据类型为整数
    在这里插入图片描述

  3. 选择确认计入排行榜,这个存档槽位就变成了排行榜存档。可以选择排行榜排序规则(升序降序)与排行榜最大人数。
    在这里插入图片描述

  4. 使用以下eca可以获取排行榜上所有玩家的存档值,配合界面eca让排行榜在界面显示。玩家->整数型增量存档eca可以让整数类型存档保持只增效果。
    在这里插入图片描述

排行榜数据在游戏启动后不会再刷新,使用双槽位可以实现周排行榜

  • 创建两个整数类型存档槽位:A与B,A存储第一周所有玩家的排行榜数据

  • 当一周结束时(通过时间戳判断),使用B榜存储玩家在第二周的排行数据

  • 在第二周开始时通过ECA清除玩家A槽位数据,以备第三周存储玩家排行数据。需通过在作者之家清除A榜数据(权限需找运营申请)。
    在这里插入图片描述

  • 当第二周结束时,将玩家排行榜数据存储在A榜并清除B槽位数据,循环往复

3.6 特效可见性

特效默认为全玩家可见,可通过ECA控制特效可见性。

  • 通过“玩家”列表操作,实现对某个玩家的特效显示/屏蔽
  • 通过“玩家组”列表操作,实现对某个玩家组的特效显示/屏蔽,比如某玩家的所有同盟玩家,某玩家的所有敌对玩家等等

在这里插入图片描述
在这里插入图片描述

3.7 不同步问题

参考《不同步相关》

3.7.1 本地配置不同步日志环境

  用户由于使用ECA不当,频繁出现游戏逻辑不同步的问题,会影响游戏正常运行。此时可通过在本地多开测试来进行调试。在【通用设置-调试】打开本地多开同步检测。

在这里插入图片描述
  使用Lua文件配置更详细的不同步日志:打开地图路径下Script文件夹下的main.lua文件,在lua文件中配置不同步日志相关API
在这里插入图片描述

GameAPI.api_set_enable_detail_snapshot(true)
GameAPI.api_set_detail_snapshot_enable_tag(0xfffffff)
GameAPI.api_set_enable_eca_snapshot(true)
GameAPI.api_set_snapshot_traceback_level(2)

  配置完毕后,本地多开运行游戏。如遇游戏逻辑不同步,会有弹窗提示,并在本地生成不同步日志以供用户定位不同步问题。
在这里插入图片描述
在这里插入图片描述

不同步日志Lua配置API说明:

API描述参数返回值
api_set_enable_detail_snapshot控制不同步日志记录的总开关。关闭后,其他设置接口将不生效,但可以提升性能。enable(bool):是否开启,默认为false
api_set_snapshot_traceback_level设置日志堆栈记录详细等级,默认0不记录
1:仅记录最近一层堆栈
2:记录完整堆栈(数据压缩)
3:记录完整日志(不压缩,数据量稍大)
记录越完整越有利于定位问题,但开销增大
level(int32):堆栈记录等级,默认值为0
api_set_enable_timer_snapshot开启或关闭计时器不同步检测日志(检测额外创建的ECA计时器),但计时器不一致并不一定意味着游戏内容不同步。enable(bool):默认值为false
api_set_enable_eca_snapshot开启或关闭ECA不同步检测日志,开销较高。可通过参数过滤掉一些安全的API以防止误报,例如创建特效、UI操作等enable(bool):默认值为false
filter_mode(int32):过滤模式,默认为1
1:剔除模式,不记录filter_set中指定的api;
0:包含模式,仅记录filter_set中指定的api;
filter_set(table):过滤集合,默认为"client_only","client_possible"
可传入想要剔除/包含的API(取决于上个参数)
如"GameAPI:print_to_dialog",“GameAPI:get_function_return_value。”
api_set_detail_snapshot_enable_tag设置不同步详细日志级别。越详细越利于定位不同步产生点,但性能消耗会增高tag(UInt64):控制开启哪些日志的mask
1运动器tick;2运动器碰撞检测
4寻路回调;8寻路坐标更新
16血量变化;32坐标瞬变
0xFFFFFFFF全部开启;默认开启16+32
add_detail_log记录自定义日志log(string):日志内容bool,值恒定为true
3.7.2 线上游戏不同步定位
  1. 在本地配置不同步日志环境
    打开地图路径下Script文件夹下的main.lua文件,在lua文件中配置不同步日志相关API,这样线上游戏发生不同步时,根据配置API玩家客户端会自动上传日志。例如:
GameAPI.api_set_enable_detail_snapshot(true)
GameAPI.api_set_detail_snapshot_enable_tag(0xfffffff)
GameAPI.api_set_enable_eca_snapshot(true)
GameAPI.api_set_snapshot_traceback_level(2)
  1. 下载不同步日志
    使用KK账号登录编辑器,打开任意地图,点击【菜单栏】【调试】【查看不同步日志】查看线上游戏的不同步日志。
    在这里插入图片描述
    查找需要定位问题的对局,下载不同步日志,不同步日志包含对局中所有玩家的对战信息。
    在这里插入图片描述
3.7.2 定位不同步问题

  不同步日志文件中包含的是出现不同步情况的帧信息,通过对比玩家日志差异,可以大致定位到问题所在。打开不同步日志文件夹,使用第三方文本对比工具进行对比(推荐使用BeyondCompare)。比如在main.lua文件中进行了如下配置:

GameAPI.api_set_enable_detail_snapshot(true);
GameAPI.api_set_enable_timer_snapshot(true);

打开不同步日志,查看逐帧信息:

在这里插入图片描述
将多名玩家日志成对拖放到beyondcompare中进行对比,发现玩家2比玩家1多了一个timer:
在这里插入图片描述
  通过对不同步信息附近的帧信息进行理解,可大致定位问题为某个客户端上多了一个循环计时器,在项目中进行查找可能的问题所在。
在这里插入图片描述

3.7.3 游戏不同步的原因
  • 本地值
    • 在单个客户端(即单个玩家的设备)上有效的数据,比如界面显示、特效、镜头位置、声音等,这些数据在不同的客户端之间不需要同步,因为它们只影响单个玩家的体验。
    • 任意值与【本地值】进行逻辑运算后得到的值等价于【本地值】
    • 动作类ECA中,修改【本地值】,不会影响游戏的全局逻辑,因此不会影响游戏的同步性。
  • 本地操作:针对本地值的操作,如【获取本地玩家】、【获取本地控件坐标】、【获取镜头焦点】、【获取滑动条当前值】等,这些获取本地值的ECA函数,在不同客户端得到的结果也是不同的。
  • 全局值:在所有客户端之间需要保持一致的值,比如单位、技能、物品等。这些值的一致性对于游戏的公平性和逻辑一致性至关重要。
  • 全局操作:针对全局值的操作,比如修改单位的状态、技能的效果、物品的数量等。这些操作会影响游戏的全局逻辑,因此需要确保在所有客户端上都能正确同步。

  导致游戏逻辑不同步的一个重要原因就是将【本地值】作为参数,传递到【全局操作】中,或者是使用【本地值】进行逻辑判断后,进行【全局操作】。这会导致不同客户端执行不同的逻辑,最终导致不同客户端上【全局值】不同,即游戏逻辑不同步。更多内容详见文档多人联机同步机制。

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

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

相关文章

【开源】A065—基于SpringBoot的库存管理系统的设计与实现

🙊作者简介:在校研究生,拥有计算机专业的研究生开发团队,分享技术代码帮助学生学习,独立完成自己的网站项目。 代码可以查看项目链接获取⬇️,记得注明来意哦~🌹 赠送计算机毕业设计600个选题ex…

Ariba Procurement: Administration_Cloud Basics

# SAP Ariba Procurement: Administration_Cloud Basics 认识Ariba Cloud SAP Ariba Procurement 是一个云计算平台… The Ariba Cloud 平台需要简单理解的概念: Datacenter数据中心:SAP Ariba在世界各地有许多数据中心。这些数据中心构成了Ariba云的基本物理基础设施。 …

vulnhub靶场【shenron】--1

前言 靶机:shenron-1 攻击:kali 都采用虚拟机,网卡为桥接模式 主机发现 使用arp-scan -l或netdiscover -r 192.168.1.1/24扫描 信息收集 使用nmap扫描端口 网站信息探测 查看页面,发现是apache2的默认界面,查看…

等保2.0数据库测评之SQL server数据库测评

一、SQL server数据库介绍 SQL server美国Microsoft公司推出的一种关系型数据库系统。SQL Server是一个可扩展的、高性能的、为分布式客户机/服务器计算所设计的数据库管理系统。 本次安装环境为Windows10专业版操作系统,数据库版本为Microsoft SQL Server 2019 (…

无人机之报警器的工作原理!

一、电量监测技术 电量监测是无人机电量指示和报警功能的基础。通过实时监测无人机的电池电量,系统能够准确判断电池的剩余使用时间,并在电量不足时发出报警。电量监测技术通常包括以下几个方面: 电压检测:无人机电池内部通常配…

【pyspark学习从入门到精通23】机器学习库_6

目录 分割连续变量 标准化连续变量 分类 分割连续变量 我们经常处理高度非线性的连续特征,而且只用一个系数很难拟合到我们的模型中。 在这种情况下,可能很难只通过一个系数来解释这样一个特征与目标之间的关系。有时,将值划分到离散的桶中…

解密时序数据库的未来:TDengine Open Day技术沙龙精彩回顾

在数字化时代,开源已成为推动技术创新和知识共享的核心力量,尤其在数据领域,开源技术的涌现不仅促进了行业的快速发展,也让更多的开发者和技术爱好者得以参与其中。随着物联网、工业互联网等技术的广泛应用,时序数据库…

QT 使用共享内存 实现进程间通讯

QSharedMemory:如果两个进程运行在同一台机器上,且对性能要求非常高(如实时数据共享、图像渲染等),建议使用共享内存。 优点: 高性能: 共享内存是进程间通信的最快方式之一,因为数…

OpenCV实验:图片加水印

第二篇:图片添加水印(加 logo) 1. 实验原理 水印原理: 图片添加水印是图像叠加的一种应用,分为透明水印和不透明水印。水印的实现通常依赖于像素值操作,将水印图片融合到目标图片中,常用的方法…

深入解析下oracle的number底层存储格式

oracle数据库中,number数据类型用来存储数值数据,它既可以存储负数数值,也可以存储正数数值。相对于其他类型数据,number格式的数据底层存储格式要复杂得多。今天我们就详细探究下oracle的number底层存储格式。 一、环境搭建 1.…

SparkSQL与Hive的整合

文章目录 SparkSQL与Hive的整合1.1. Spark On Hive1.1.1. Hive的准备工作1.1.2. Spark的准备工作1.1.3. Spark代码开发1.1.4. Spark On Hive案例 1.2. Hive On Spark1.3. SparkSQL命令行1.4. SparkSQL分布式查询引擎1.4.1. 开启ThriftServer服务1.4.2. beeline连接ThriftServer…

(持续更新)linux网络编程中需要注意的内核参数与网络机制

目录 零、基本说明 一、内核参数 二、相关机制 1、GRO (1)适用场景 (2)优缺点 (3)相关操作 2、Nagle 算法 (1)基本规则 (2)优缺点 (3&…

DevExpress WPF中文教程:Grid - 如何移动和调整列大小?(一)

DevExpress WPF拥有120个控件和库,将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpress WPF能创建有着强大互动功能的XAML基础应用程序,这些应用程序专注于当代客户的需求和构建未来新一代支持触摸的解决方案。 无论是Office办公软件…

Matlab笔记---clear、clc、clear all应用

在MATLAB中,clear、clc 和 clear all 是三个常用的命令,它们各自有不同的作用: clc: clc 命令用于清除MATLAB命令窗口中的所有输出。它不会删除任何变量、函数或文件,只是清除屏幕上的显示内容,让你可以更…

Qt 一个简单的QChart 绘图

Qt 一个简单的QChart 绘图 先上程序运行结果图&#xff1a; “sample9_1QChart.h” 文件代码如下&#xff1a; #pragma once#include <QtWidgets/QMainWindow> #include "ui_sample9_1QChart.h"#include <QtCharts> //必须这么设置 QT_CHARTS_USE_NAME…

分布式事物XA、BASE、TCC、SAGA、AT

分布式事务——Seata 一、Seata的架构&#xff1a; 1、什么是Seata&#xff1a; 它是一款分布式事务解决方案。官网查看&#xff1a;Seata 2.执行过程 在分布式事务中&#xff0c;会有一个入口方法去调用各个微服务&#xff0c;每一个微服务都有一个分支事务&#xff0c;因…

MySQL为什么使用B+树来作索引

我来详细解释一下B树的结构和特点。 graph TDA[根节点 40|70] --> B[20|30]A --> C[50|60]A --> D[80|90]B --> E[10|15]B --> F[25|28]B --> G[35|38]C --> H[45|48]C --> I[55|58]C --> J[65|68]D --> K[75|78]D --> L[85|88]D --> M[9…

python 下载 b站视频 和音频

video_bvid&#xff1a; import os import requests import json import re from bs4 import BeautifulSoup import subprocess # from detail_video import video_bvid# video_bvid 是一个从外部得到的单个视频ID video_bvid BV1cx421Q7veclass BilibiliVideoAudio:def __in…

以太网链路详情

文章目录 1、交换机1、常见的概念1、冲突域2、广播域3、以太网卡1、以太网卡帧 4、mac地址1、mac地址表示2、mac地址分类3、mac地址转换为二进制 2、交换机的工作原理1、mac地址表2、交换机三种数据帧处理行为3、为什么会泛洪4、转发5、丢弃 3、mac表怎么获得4、同网段数据通信…

Shell编程 脚本的运行方式与注释

目录 shell脚本的运行方式 1. 路径运行 2.bash或sh加脚本运行 ​编辑 3.source在加脚本路径运行 shell脚本注释 单行注释 多行注释 shell脚本的运行方式 我们在/usr/etc/demo01目录下新建了一个脚本 a.sh &#xff0c;脚本内容是要求输出数字1&#xff0c;怎么运行呢 1…