正则
--协议替换
^\s*--id\s*:([0-9]*)\s*.*\s+msg:(([A-Z]|[a-z]|_)*)((.|\n)*?)^\}
$2=$1,--扭蛋
android stiduo
常用的gradle版本6.3
常用的gradle plugin版本 4.0.2
手游笔记
动画类型
{
SFANIM_TYPE_PLAYER = 1, -- 玩家
SFANIM_TYPE_MONSTER = 2, -- 怪物
SFANIM_TYPE_NPC = 3, -- NPC
SFANIM_TYPE_SKILL = 4, -- 技能
SFANIM_TYPE_SFX = 5, -- 特效
SFANIM_TYPE_WEAPON = 6, -- 武器
SFANIM_TYPE_SHIELD = 7, -- 盾牌
SFANIM_TYPE_WINGS = 8, -- 翅膀
SFANIM_TYPE_HAIR = 9, -- 发型
SFANIM_TYPE_FABAO = 10, -- 法宝
SFANIM_TYPE_NUM = 10, -- max
}
动画id
{
animRawID 动画原生id
}
动画性别
{
ACTOR_DEFAULT_SEX = 0, -- 默认性别
ACTOR_PLAYER_SEX_M = 1, -- 男
ACTOR_PLAYER_SEX_F = 2, --女
}
动画动作
{
ANIM_INVALID = 0xFF,
ANIM_DEFAULT = 0, -- npc、skill、sfx default act:idle
ANIM_IDLE = 0,
ANIM_WALK = 1,
ANIM_ATTACK = 2,
ANIM_SKILL = 3,
ANIM_DIE = 4, --死亡
ANIM_RUN = 5,
ANIM_SHOWSTAND = 6, --内观
ANIM_STUCK = 7, --受击
ANIM_SITDOWN = 8, --采集
ANIM_BORN = 9, --出生
ANIM_MINING = 10, --挖矿
ANIM_READY = 11, --预备
ANIM_CHANGESHAPE = 12, --变身
ANIM_DEATH = 13, --挖完的尸体
ANIM_HORSE_IDLE = 14, --坐骑待机
ANIM_HORSE_WALK = 15, --坐骑走
ANIM_HORSE_RUN = 16, --坐骑跑
ANIM_HORSE_DIE = 17, --坐骑上死亡
ANIM_HORSE_STUCK = 18, --坐骑上受击
ANIM_COUNT = 19, -- max(ANIM, ORIENT)
}
动画方向
{
0 1 2 3 4 5 6 7
}
动画帧数
{
0000 0001 0002 0003 ... 一般四帧
}
anim/类型/类型_id_性别_动作.png
anim/类型/类型_id_性别_动作.plist
类型_d_性别_动作_方向_帧数00
类型_d_性别_动作_方向_帧数01
类型_d_性别_动作_方向_帧数02
类型_d_性别_动作_方向_帧数03
可以看出:一个性别的一个动作就是一整张图
意味着八个方向上的每个方向上n帧动画被合并到一张图里面
序列帧动画管理类
actorSequFrameAnimManager:_animDataMgr
{
指定动作的某一个方向上的动画数据
动画配置id 对应cfg_model_info
animID = (animRawID * 0xF + animType ) * 0xF + sex
动画索引
frameAniIndex = act * 8 + dir
frames = _animDataMgr[animType][animID].animData[frameAniIndex]
拿到frames就可以按照帧率一帧一帧的切换播放这些图片了
}
gameActorPlayer
{
创建动作
{
}
更新动作
{
每一帧都会更新
}
创建某一个类型动画的时候 会先用9999这个id对应的类型数据给其赋值
即便加载不到对应的衣服 也会默认使用9999这个衣服的数据
持有若干个序列帧动画控制器:actorSequenceFrameAnim
{ 主要包含这些类型:衣服 武器 影子 翅膀 法宝 盾牌 坐骑 特效 龙魂
他们都是actorSequenceFrameAnim
_animDataDesc:动画数据描述
{
loadStateUnit[act]:动画的下载状态
{
--******* 动画资源加载状态 *******
LOAD_STATE_UNLOADING = -1, 资源从没加载过 初始化的时候使用的值
LOAD_STATE_LOADING = 1, 放入到加载队列中时 标记为第一次加载资源
LOAD_STATE_RELOADING = 2, 放入到加载队列中时 标记因为加载失败以后 再次触发加载资源
LOAD_STATE_UNUSED = 3, 当一个角色从屏幕中移除的时候 并且此时它的动画数据也没有被引用 就会标记为没有使用的状态
LOAD_STATE_INUSED = 4, 资源加载成功以后 记录该标志
LOAD_STATE_FAILED = 5, 资源加载失败 记录该标志
LOAD_STATE_FOREVER = 6, 标记该资源为永久存在 对于那些预加载的资源 直接跳过什么的资源 比如9999这种模版资源
在启动加载的时候 这个资源的加载的状态必须是-1或者5 其它状态禁止加载
}
animType:动画类型(武器 衣服 等)
animID:动画的配置id 动态变化的 计算方式 (animRawID * 0xF + animType ) * 0xF + sex
rawAnimID:动画的原生id
timeStamp:
animData:动画数据
{
动画索引计算方式 frameAniIndex = act * 8 + dir
animFrameVecs[index]:数据帧
animDelays[index]:帧率
animIDInUnit[act]:判断动画是否加载过 key是act 如果动画类型是skill 则是key就是dir
{
如果动画加载成功 此处会以act为key存入animID
}
}
}
{
actorSequenceFrameAnim本身继承LuaSprite
cfg_model_info调整节点的偏移缩放基于这个节点,其配置表的id对应就是animID
就是播放个普通动画
利用序列帧 创建Animation
利用Animation创建Animate
利用CallFunc和Animate创建Sequence
判断是否是循环播放 利用Sequence创建RepeatForever
最后基于这个节点播放上面创建的action
action里面包含了若干张动画 播放这个动画 就是根据帧率来切换这些动画
}
unloadSequAnim()
{
卸载序列帧动画
将动画帧引用计数减一 下一帧释放纹理数据
移除内存中加载的plist文件信息
移除内存中纹理的数据
}
Sync(old动画播放器)
{
该函数的主要作用就是同步上个动画播放器的一些状态 做到完美衔接
}
}
}
动画加载管理员:actorSequFrameAnimManager
{
各种类型序列帧动画:(衣服 武器 怪物 盾牌 翅膀 特效 发型 法宝 技能 npc)
_animRefsMgr:会为每种序列帧类型初始化一个
_animDataMgr:会为每种序列帧类型初始化一个
_animLoads:动画加载器 每一种序列帧类型都对应一种加载器
资源初始化:
{
会为各种序列帧类型初始化一个rawAnimID为9999的动画
将来加载其他rawAnimID时 会以9999的动画数据为模版数据
}
createSequFrmAnimAsync(rawAnimID, sex, animType, act, extCB, immediate, scale)
{
创建一个序列帧动画播放器 将返回给GameActor使用
_animDataMgr[animType][animID] = 一套动画数据
_animRefsMgr[animType][animID] = {播放器1,播放器2,播放器3,,......}
屏幕中有若干个GameActor每一个GameActor都有若干个动画播放器
对于相同animType和rawAnimID和sex和act 共用一套动画数据
比如长得一模一样的小怪 有100只 那么他们共用一套动画数据 但动画播放器的实例会有100个
动画数据的底层原理就是加载到内存中的一张张纹理 通过action组合一下看啥时候切换当前播放节点的纹理
屏幕中若干节点共用一张纹理的时候,画图是基于树的结构一次一次向GPU提交渲染数据的
}
unloadUnusedAnimData()
{
该函数10帧由系统触发一次
卸载所有类型的动画数据
判断当前动画数据是否被引用
对于没有被引用的动画资源 判断是否过期 一般是30s 如果是大图则是150s
主要就是释放内存中的plist和png的解析数据 以及在GPU端存储的纹理数据
}
}
1:地图改变加载大图 一次性加载18个动作的动画
actorSequFrameAnimManager:ChangeMap()
2:动画只会在加载第一次失败的时候 给出错误提示 后续则仍然继续下载 如果还是继续下载失败的话不会给出错误提示