设为首页
收藏本站
切换到宽版
官网
论坛
资格申请
更新日志
游戏体验
热搜
活动
交友
discuz
登录
|
VV Engine 官方中心
»
论坛
›
引擎交流
›
引擎教程
›
啃完 VV 引擎人物系统百余个 API 后,我总结出了这套设 ...
返回列表
发新帖
查看:
25
|
回复:
3
啃完 VV 引擎人物系统百余个 API 后,我总结出了这套设计逻辑
[复制链接]
13674307070
13674307070
当前离线
积分
1301
30
主题
72
回帖
1301
积分
金牌会员
金牌会员, 积分 1301, 距离下一级还需 1699 积分
金牌会员, 积分 1301, 距离下一级还需 1699 积分
积分
1301
发消息
发表于 2026-8-20 08:21:22
|
显示全部楼层
|
阅读模式
这两天把 VV 引擎人物相关的接口从头到尾过了一遍,数量不少,从改名改色到技能释放,从货币操作到状态叠加,零零总总一百多个函数。刚看的时候头大,觉得就是一堆零散的命令。但全部过完之后再回头看,发现这些接口背后其实有一套很清晰的设计逻辑。
这篇不做参数罗列,那些文档里都有。我想聊的是
学完之后的理解
—— 这些接口为什么这么设计、分类的逻辑是什么、实际用的时候有哪些坑和心得。
一、属性系统:数值与状态的两条线
人物属性这块,最核心的设计思路是
把 "基础数值" 和 "临时状态" 分开处理
。
基础数值走的是一套体系。获取单条属性用 gethumability,传一个属性 ID 就能拿到对应的值,生命、魔法、攻防道术全是一个接口搞定,靠 ID 区分。修改属性则走 addattrlist,用字符串批量加,还能分组管理,一组属性对应一个名字,要清除的时候直接按组删。这种设计比 "每个属性一个 get/set" 要简洁得多,新增属性也不用加接口。
刷新属性有单独的 recalcabilitys,这个细节很重要 —— 你改了装备、加了 buff,属性不是自动变的,得手动调一次刷新。刚开始容易忘,改完属性发现面板没变化,查半天就是没调刷新。这其实是引擎在告诉你:
属性计算是有成本的,不要频繁触发
。
临时状态走的是另一套体系,核心是 changemode。无敌、隐身、冰冻、麻痹、吸血、护身…… 二十多种状态全塞在这一个接口里,靠模式 ID 区分。这个设计的好处是统一,所有状态的施加、计时、到期清除走的是同一套底层逻辑。但坏处也明显 —— 参数含义不统一,有的模式 param1 是几率,有的是属性值,有的是范围,写的时候必须对着文档查,记不住。
这里有个心得:
状态类效果尽量用 changemode,不要自己用定时器模拟
。引擎底层的状态系统会处理到期清除、死亡清除、地图切换清除这些边界情况,自己写定时器很容易漏,到时候 buff 永久存在或者卡状态,排查起来很痛苦。
改名这个功能单独拿出来说,因为它的设计很有意思。 changehumname 不是直接改,而是走了一整套流程 —— 先查询、再过滤、再检查重名、最后执行改名,每个阶段都有对应的回调函数可以挂逻辑。这说明引擎把改名当成一个
有状态的事务
来处理,而不是简单的赋值。实际用的时候,这些回调就是你插提示、插特效、插记录的地方,不用自己去判断改名成功还是失败,引擎会在对应阶段调你的函数。
二、货币与装备:经济系统的底层支撑
货币这块接口不多,但设计得很务实。
最基础的是 querymoney 和 changemoney,靠货币 ID 操作,1 到 100 号,对应货币表里的配置。然后又补了一套按名称操作的 getbindmoney 和 consumebindmoney,直接传 "金币"" 元宝 " 这种汉字名称。
两套并存的原因很现实:
写脚本的时候记 ID 容易错,记名字更直观
。但按名称查多了一层映射,性能上略差。所以高频调用(比如战斗中反复扣金币)用 ID,低频调用(比如 NPC 对话里查一下有没有元宝)用名字,按场景选。
changemoney 有个 send 参数,控制是否推送到客户端。这个细节很关键 —— 批量操作货币的时候,比如一次性加十种货币,前九次都设 send=false,最后一次设 true,客户端只刷新一次。如果每次都推送,界面会闪十下,体验很差。
装备系统的接口体现了一个原则:
位置驱动
。不管是穿戴 takeonitem、脱下 takeoffitem、还是改外观 changeitemshape、加特效 changedresseffect,第一个参数永远是玩家,第二个就是位置。装备在哪个格子,比装备是什么更重要。这和传奇类游戏的底层数据结构一致 —— 装备数据就是按槽位存的,操作自然也按槽位来。
背包格子数可以动态扩, setbagcount 支持 46 到 126 格。这个功能看起来简单,但背后意味着引擎的背包数据结构是动态的,不是写死的数组。实际用的时候要注意,扩格子是即时生效的,但缩格子的时候如果玩家包里东西超过了新上限,会发生什么文档没说,这种边界情况一定要自己测试。
外观相关的接口有个统一趋势: setfeature 一个接口搞定衣服、武器、翅膀、盾牌的外观和特效,靠 type 区分。比之前每个部位一个接口要清爽得多。但参数也多,time、param1、param2 各有含义,尤其是 param2 控制隐藏斗笠、头发、盾牌这些附属部件,做时装的时候经常要用,容易搞混。
三、技能体系:从数据到释放的完整链路
技能是人物系统里最复杂的一块,接口也最多。我把它分成四层来看。
第一层是技能数据。
getskillinfo 查等级、强化、熟练度, setskillinfo 改这些值, addskill 加技能, delskill 删技能, clearskill 全清, getallskills 拿整个技能列表。这一层是纯数据操作,没有副作用,改完就是改完了。
这里有个设计细节: getskillinfo 的返回值,没有技能的时候返回 nil 而不是 0。这个区别很重要,Lua 里 nil 和 0 是两回事,判断的时候要写 if lv and lv > 0,不能只写 if lv > 0,否则 nil 会报错。刚上手的时候很容易踩这个坑。
第二层是技能释放。
三个接口对应三种目标: releasemagic 对攻击目标或自身放, releasemagic_target 对指定对象放, releasemagic_pos 对指定坐标放。覆盖了所有释放场景,而且参数结构一致,只是目标参数不同。
这一层的心得是:
脚本释放技能和玩家手动释放走的不是完全相同的逻辑
。脚本释放可以指定是否显示施法动作,还能换技能外观(NewEffect 参数),这意味着你可以用一个技能的伤害判定配另一个技能的视觉效果,做自定义技能的时候非常有用。但反过来,脚本释放的技能可能不会触发某些只有手动释放才会触发的 buff 或被动,这个要实测。
第三层是技能冷却。
GetSkillRawCD 拿原始 CD, GetSkillCD 拿当前剩余 CD, SetSkillDecCD 减 CD, SkillResetCD 重置 CD。CD 的单位是毫秒,不是秒,这个一定要注意,传错了差一千倍。
减 CD 和重置 CD 是两个概念。减 CD 是在当前剩余时间上扣,适合做 "减少 5 秒冷却" 这种效果。重置 CD 是直接清零,适合做 "下一次技能无冷却" 这种效果。不要混着用。
第四层是技能强化。
setskillpower 加威力, setskilldefpower 加防御威力, setmagicskillefft 改特效。这一层是做自定义技能和装备特效的核心。威力支持按点数加和按百分比加两种模式,做装备词条的时候很灵活。
技能名字和 ID 的互转有 getskillname 和 getskillindex,写通用逻辑的时候建议用 ID,因为名字可能被改,ID 不会变。但配置表和脚本里写名字更可读,实际项目里可以做一层映射。
四、玩家对象与交互:对象化的便利
整个系统的基石是玩家对象。几乎所有接口的第一个参数都是 play/actor,就是这个对象。
获取玩家对象有三种方式:按名字 GetPlayerByName,按唯一 ID GetPlayerByID,拿全服列表 GetPlayerList。前两个是精确查找,第三个是遍历。做全服公告、范围技能、排行榜统计的时候用列表遍历。
这里有个坑: GetPlayerByName 和 GetPlayerByID 查不到的时候返回什么,文档没明说。按 Lua 的惯例应该是 nil,但调用之前最好判空,不然对一个 nil 对象调方法直接报错。
GetPlayerList 有个参数控制是否剔除离线挂机玩家。这个细节说明引擎把在线玩家和离线挂机角色是分开管理的,遍历的时候要想清楚你需不需要包含挂机的,不然统计在线人数会偏大。
攻击模式这块接口很简单,改、强制改、查,三个接口。但 setattackmode 强制修改有个时间参数,时间到了自动恢复。这个设计的应用场景很明确 —— 比如某个技能持续 5 秒内强制全体攻击,时间到了自动切回去,不用自己写定时器恢复。引擎帮你处理了状态回滚。
GM 权限的获取和设置是独立的接口, getgmlevel 和 setgmlevel。这个值和攻击模式、玩家状态都不挂钩,是单独的权限标记。做命令系统、管理员功能的时候靠这个判断。
复活 realive 和踢人 kick 是两个简单但高频的接口,没什么好说的,参数就一个玩家对象。但注意复活只是把人拉起来,血量蓝量回多少、有没有无敌保护,这些都要自己额外处理,不是复活接口全包的。
五、延时与伤害:异步逻辑的处理方式
delaygoto 是整个系统里我觉得设计得最巧妙的接口之一。
传统脚本里做延时逻辑很麻烦,要么用定时器,要么自己记时间戳轮询。VV 引擎的 delaygoto 直接把 "延时多久之后调用哪个函数" 封装成了一个调用,参数传进去,到点自动执行。还支持换地图是否删除这个延时,考虑到了玩家中途飞走的情况。
参数传递有两种写法,一种是把参数拼在函数字符串里,一种是单独传最后。两种都能用,但单独传的方式更清晰,也避免了参数里有逗号导致解析错误。
cleardelaygoto 可以取消还没执行的延时,这个很重要。比如玩家用了一个 5 秒后生效的技能,3 秒的时候他掉线了或者换地图了,这时候就得手动取消延时,不然人都走了效果还在原地触发,就出 bug 了。
范围伤害 RangeHarm 是另一个重量级接口。一个函数搞定了范围判定、伤害计算、附加效果(击退、冰冻、麻痹、吸血、真实伤害等十几种)、状态检查、目标筛选、特效播放。做 AOE 技能的时候基本靠它。
这个接口的参数很多,但逻辑是清晰的:先定范围和伤害,再加附加效果,最后控制目标类型和特效。心得是:
附加效果的 addvalue 含义因 addtype 不同而不同
,击退是距离、冰冻是时间、吸血是数值、百分比伤害是比例,写的时候一定要对着查,不要想当然。
飘血飘字 sendattackeff 单独拿出来说,因为它有个很实用的设计:hitter 参数传 "*" 就是广播,视野内所有人都能看到。不传的话只有攻击者能看到。做自定义伤害数字的时候,这个参数决定了是个人秀还是全服可见,效果完全不同。
六、状态与战斗:效果叠加的设计思路
ChangeState 是后来补充的状态接口,和 changemode 有点重叠,但更偏向战斗中的 debuff。石化、冰冻、蛛网、红绿毒、定身、瘫痪、禁锢、吸血吸蓝、间隔掉血、禁技能,十四种状态。
两个状态接口并存的原因,我的理解是: changemode 更偏向
角色模式
(无敌、隐身、管理模式这些), ChangeState 更偏向
战斗 debuff
。但实际边界有点模糊,比如冰冻、麻痹、禁锢两边都有。用的时候选一个体系就行,不要混着来,不然状态清除的时候可能漏。
速度相关有两套接口: changespeed 按等级改(-10 到 10,每级 10%), changespeedex 按百分比改(精确到 1%)。前者适合做装备词条("攻击速度 + 3"),后者适合做技能 buff("移动速度提升 25%")。按场景选,不要都用一套。
穿人穿怪 throughhum 支持单独穿人、单独穿怪、都穿,还能区分玩家和宝宝。这个功能做特殊地图(比如安全区穿人)和变身道具的时候很有用。注意是有时间限制的,不是永久的,要持续穿就得自己续。
目标设置 settargetcert 有个很重要的注释:
玩家实际攻击目标由客户端锁定,服务端改了会失效
。这个说明很诚实,告诉你这个接口对玩家可能不好使,主要用于怪物、英雄、宝宝这些服务端完全控制的单位。不要指望用这个强制玩家打某个目标,做不到的。
距离检测有三个接口:判断是否在范围内 CheckActorRange、获取具体距离 GetActorRange、判断是否在某个坐标范围内 CheckHumInRange。前两个是对象对对象,第三个是对象对坐标。做技能范围判定的时候用第一个就够了,需要显示具体距离(比如 "目标距离你 15 米")的时候用第二个。
自动寻路 AutoGotoXY 让玩家自动走到某个点,做任务引导的时候很实用。但寻路过程中玩家可以手动打断,这个要考虑到,不要假设寻路一定能到达。
七、杂项功能:细节见真章
剩下的接口比较散,但每个都有它的用处。
离线挂机
offlineplay 有个很重要的注意事项:使用前必须关闭所有定时器。因为离线后玩家对象还在,但客户端连接断了,如果定时器里有往客户端发消息的逻辑,就会出问题。这个坑不注意的话,离线挂机角色会导致服务端报错甚至崩溃。
转生系统
四个接口:转生成败 renewlevel、加属性点 bonuspoint、查属性点 getbonuspoint、重置属性点 restbonuspoint。设计中规中矩,转生次数、转生后等级、分配点数三个核心参数一次传入。注意属性点的范围限制(0 到 1000),不要超。
骑马系统
三个接口:检查是否骑马 checkonhorse、上马 ridehorse、下马 dismounthorse。上马的时候要传坐骑外观、特效、人物骑马外观、坐骑类型(单人 / 双人 / 连体),参数不少。做坐骑系统的时候,这些外观值要和配置表对应好。
足迹特效
setmoveeff 是个很有意思的小功能,玩家走路的时候脚下播放特效。支持单步和两步两种播放模式,做高端称号或者特殊装备的地面特效的时候用。注意这个是纯视觉效果,不影响移动逻辑。
骰子功能
playdice 做赌博或者随机事件的时候用。骰子点数存在私人变量 D0 到 D5 里,动画结束后调回调函数。这个设计很巧妙,用私人变量传结果,不用额外的返回值机制。
宝箱开启
opendragonbox 直接传宝箱 ID 和次数,不读配置表的次数只认传入的参数。这个说明接口优先级高于配置表,做 GM 命令或者特殊活动的时候可以绕过配置直接开。
神佑系统
一组接口,开关神佑显示、开单个格子、关单个格子、全开全关、检测状态。还区分普通和时装两套。这个系统的接口设计得很规整,操作和检测一一对应,用起来不容易乱。
自定义广播
setotherparams 是个很灵活的功能,服务端设 5 个自定义属性值,客户端注册事件监听变化。可以用来做自定义数据同步,比如足迹特效的参数就是这么传的。相当于引擎给你留了 5 个自定义同步通道,不够用的话就得自己走前后端消息了。
八、整体感受与学习建议
把这一百多个接口全部过完之后,最大的感受是:
VV 引擎的人物系统是 "够用就好" 的工程化产物,不是追求优雅的学术设计
。
它有明显的历史包袱。比如 changemode 和 ChangeState 功能重叠,比如属性获取靠 ID 而不是具名函数,比如有些接口参数含义不统一。这些都是长期迭代积累下来的,不是一次性设计的结果。
但它也有很多务实的亮点。比如 delaygoto 把异步逻辑简化成一次调用,比如 addattrlist 用字符串和分组管理属性,比如货币同时支持 ID 和名称两种操作方式,比如改名拆成多阶段回调。这些设计都是从实际开发需求里长出来的,解决的是真实的痛点。
学习这套系统,我的建议是:
第一,不要试图记住所有接口。
一百多个函数,记不住也没必要。先建立分类框架 —— 属性、货币、装备、技能、对象、战斗、状态、杂项,八大块。用到哪一块的时候去查对应的接口,查两次就记住了。
第二,重点理解设计模式而不是参数。
参数文档里都有,查一下就行。但设计模式是通用的 —— 比如状态类效果用引擎自带的不要自己模拟,比如批量操作最后再推送客户端,比如延时逻辑要记得取消。这些理解了,换个引擎也能用。
第三,边界情况比正常逻辑更重要。
大部分接口正常调用都没问题,出问题都在边界 —— 玩家掉线了延时还在不在、换地图了 buff 清不清、缩背包了东西超了怎么办、技能对 nil 对象调会怎样。这些文档不一定写,要自己测。
第四,写脚本之前先想清楚数据流向。
一个功能涉及哪些属性变化、要不要刷新、要不要推客户端、有没有状态叠加、需不需要延时回调,先在脑子里过一遍再动手。不然写一半发现漏了东西,返工更慢。
VV 引擎的人物系统不完美,但足够用,而且该有的功能基本都有。理解了它的设计逻辑,写脚本的时候就不是在 "对着文档抄参数",而是在 "用一套工具解决问题"。这个转变,就是从新手到熟练的关键。
回复
举报
13674307070
13674307070
当前离线
积分
1301
30
主题
72
回帖
1301
积分
金牌会员
金牌会员, 积分 1301, 距离下一级还需 1699 积分
金牌会员, 积分 1301, 距离下一级还需 1699 积分
积分
1301
发消息
楼主
发表于 2026-8-20 08:32:44
|
显示全部楼层
挠头的500贡献
回复
举报
13674307070
13674307070
当前离线
积分
1301
30
主题
72
回帖
1301
积分
金牌会员
金牌会员, 积分 1301, 距离下一级还需 1699 积分
金牌会员, 积分 1301, 距离下一级还需 1699 积分
积分
1301
发消息
楼主
发表于 2026-8-20 08:53:12
|
显示全部楼层
挠头的500积分
回复
举报
13674307070
13674307070
当前离线
积分
1301
30
主题
72
回帖
1301
积分
金牌会员
金牌会员, 积分 1301, 距离下一级还需 1699 积分
金牌会员, 积分 1301, 距离下一级还需 1699 积分
积分
1301
发消息
楼主
发表于 2026-8-21 08:22:24
|
显示全部楼层
挠头的500贡献
回复
举报
返回列表
发新帖
高级模式
B
Color
Image
Link
Quote
Code
Smilies
您需要登录后才可以回帖
登录
|
立即注册
本版积分规则
发表回复
回帖后跳转到最后一页
浏览过的版块
素材分享
快速回复
返回顶部
返回列表