游戏角色动画:从入门到商用(一)

您所在的位置:网站首页 全国车用尿素液排名 游戏角色动画:从入门到商用(一)

游戏角色动画:从入门到商用(一)

2024-07-08 05:02| 来源: 网络整理| 查看: 265

2D游戏中的角色由两种方案,第一种是骨骼动画,骨骼动画的好处是节省资源,减少空间占用;但是缺点是表现力差,一般只做侧面2方向,主要用于横板过关类的游戏。

第二种是逐帧动画,逐帧动画理论上来将可以做任意多个方向,但每1个方向就是1套序列帧,会占用大量的内存,因此一般是采用1方向,4方向和8方向。其中1方向的一般是npc,只是正面朝向玩家,4方向和8方向的一般位普通角色,细节要求不高的话,可以利用翻转节省对称方向的资源。传统的经典2D游戏,梦幻,大话,神武,传奇等都是采用这种方式实现的。

一 动画资源设计 方向(5方向模拟8方向) 上:0右上:1右:2右下:3下:4左下(右上1翻转):5左(右2翻转):6左上(右下3翻转):7 换装 身体武器翅膀其他 动作: idle 空闲war 战斗状态run 跑步die 死亡struck 受击spell 持续施法appear 出现disappear 消失sneak 潜行attack 普攻skill0、skill1、skill2、… 技能动作 输出资源命名: 目录命名:角色编号,角色编号由6位组成,包括: 类型2位,如: 10 表示角色20 小怪30 boss40 npc 职业2位 01 战士02 法师03 道士 序号2位:从00开始递增,表示该角色的资源数 图片资源命名:部位_角色编号_动作名_方向_帧序号 部位: 身体:body武器:weapon翅膀:wing 角色编号:即目录名动作名,idle、run 等方向:1位,0-7帧序号:2位,从00开始,按播放顺序递增 二 编辑器实现帧动画

介绍下 demo 环境:

系统 win10游戏引擎 cocos creator 2.4脚本语言 typescript

首先入门版,使用 cocos creator 动画编辑器,做一个帧动画,为了节省时间,只做下角色身体 body 空闲动作 idle 下方向 4 的动画。cocos create 帧动画制作参考文档,通过以下步骤:

导入帧动画资源添加一个 Sprite 节点在 Sprite 节点添加 Animation 组件创建动画剪辑 idle,添加 cc.Sprite.spriteFrame 属性设置每个关键帧的纹理WrapMode 改为 Loop将新建的 Clip 设为 Default Clip勾选 Play On Load运行场景

edit 这样不需要任何代码,就可以快速在场景中制作一个角色的帧动画。

三 动态创建动画

一般游戏角色都会有很多动作,如果每个动作的动画都需要在编辑器中编辑实现,会耗费大量时间。因此需要使用代码创建帧动画,参考文档,动态添加动画组件,创建动画剪辑。

在脚本中声明一个 Sprite 变量 spPlayer,保存玩家节点声明一个 cc.SpriteFrame 变量 spPlayerFrames,保存帧动画每帧的纹理在编辑器中创建一个精灵,对上述两个变量赋值使用代码创建动画剪辑,然后循环播放动画

ani

@property(cc.Node) ndActor: cc.Node = null;// 角色节点 @property([cc.SpriteFrame]) spPlayerFrames: cc.SpriteFrame[] = []; animation: cc.Animation = null; onLoad() { let clipName: string = "idle"; // 动画剪辑名字,播放动画时使用 let sample: number = 6; // 帧率,即一秒钟播放多少帧 this.ceateAnimation(clipName, sample, this.spPlayerFrames); this.playAnimation(clipName, cc.WrapMode.Loop); } ceateAnimation(clipName: string, sample: number, spriteFrames: cc.SpriteFrame[]) { // 创建动画剪辑 let animation = this.ndActor.addComponent(cc.Animation); let clip: cc.AnimationClip = cc.AnimationClip.createWithSpriteFrames(spriteFrames, sample); animation.addClip(clip, clipName); } playAnimation(clipName: string, mode: cc.WrapMode) { // 播放动画 let aniState: cc.AnimationState = this.ndActor.getComponent(cc.Animation).play(clipName);// 播放动画 aniState.wrapMode = mode;// 循环播放 }

代码中,动态创建了动画剪辑,然后播放动态创建的动画剪辑。修改之后,将动画创建从编辑器移到了代码中,此时需要运行之后才能看到效果。

四 动态加载资源

上述过程中动画虽然动态创建了,但是精灵帧还是手动从编辑器中拖,将精灵帧也改为动态加载。

删除关联的中的 spPlayer 节点和 spPlayerFrames 序列帧动态创建角色节点 ndPlayer动态加载序列帧动画资源到 spPlayerFrames 中使用代码创建动画剪辑循环播放动画 ndActor: cc.Node = null; // 角色节点 spPlayerFrames: cc.SpriteFrame[] = []; // 保存加载的帧动画资源 loadIndex: number = 0; // 已加载下标 loadTotalCnt: number = 6; // 动画帧数 animation: cc.Animation = null; // 角色动画组件 onLoad() { this.ndActor = this.createActorNode(); this.loadSpriteFrames(); } loadSpriteFrames() { // 加载帧动画资源 if (this.loadIndex >= this.loadTotalCnt) { this.ceateAnimation(); return; } let url = "demo/piece/100354/body_100354_idle_4_0" + this.loadIndex; cc.resources.load(url, cc.SpriteFrame, (error: Error, spriteFrame: cc.SpriteFrame) => { if (error) { console.error(error.message || error); } this.loadIndex += 1; this.spPlayerFrames.push(spriteFrame); this.loadSpriteFrames(); }); } ceateAnimation() { // 资源加载完成,播放动画 let clipName = "idle"; // 动画剪辑名字,播放动画时使用 let sample = 6; // 帧率,即一秒钟播放多少帧 this.createAnimationClip(clipName, sample, this.spPlayerFrames); this.playAnimation(clipName, cc.WrapMode.Loop); } createActorNode() { // 创建角色节点 let node = new cc.Node(); node.parent = this.node; node.addComponent(cc.Sprite); node.addComponent(cc.Animation); return node; }

代码中新增了方法 loadSpriteFrames,动态的从图片中加载动画纹理。经过这两步,将动画创建完全从编辑器移到了代码中。

五 将资源打成图集

一般游戏中将会有很多帧动画资源,为了降低 Draw Call,优化 IO,需要将帧动画得资源打成一个图集,打图集一般使用得是 TexurePacker,然后在代码中动态加载图集,获取其中的精灵帧创建动画。步骤如下:

使用 TexturePacker 将动画序列帧打包成图集动态创建角色节点 ndPlayer动态加载图集用图集中的精灵帧创建动画剪辑循环播放动画

导出图集资源: tp

loadSpriteFrames() { let url = "demo/sheet/simple/100354"; cc.resources.load(url, cc.SpriteAtlas, (error: Error, spriteAtlas: cc.SpriteAtlas) => { if (error) { console.error(error.message || error); return; } this.spPlayerFrames = spriteAtlas.getSpriteFrames(); this.spPlayerFrames.sort(); this.ceateAnimation(); }); }

代码跟上一节的差不多,只改了 loadSpriteFrames 这个方法,精灵帧由从图片一张一张加载,改到从图集中一次性加载。

六 加载多图集资源

一个角色有多个动作,每个动作又有不同方向。在打图集时,为了兼容性,一般最大尺寸都设置成 1024 * 1024,因此一个角色就会产生多张图集。之前的都只加载了 idle 动作方向 4 的资源,本节把角色身体的全部动作和方向的动画都加载进游戏。

打包多图集资源: 在 TexturePacker 中勾选 Multipack最大尺寸 Max-size 设为 1024,尺寸限制 Size Constraints 改为 AnySize修改导出 png 和 plist,在后缀前加上 -{n},例如 player-{n}.png、player-{n}.plist生成图集,可以看到命名是 player-0.png、player-0.plist, player-1.png、player-1.plist等 动态创建角色节点 ndPlayer动态加载多个图集根据动作和方向,缓存图集中的精灵帧创建动画剪辑循环播放动画载界面上添加两个按钮在按钮的响应事件中,分别切换动画方向和动作

TexurePacker设置: tp 导出资源: mul_tp 编辑器: edit

ndActor: cc.Node = null; // 角色节点 spPlayerFrameMap: {[actionName: string]: {[actionName_dir: string]: cc.SpriteFrame[]}} = {}; // 保存加载的帧动画资源 loadIndex: number = 0; // 已加载下标 loadTotalCnt: number = 5; // 图集数量 dir: number = 4; // 方向 actions: string[] = [];// 动作列表 actionIndex: number = 0;// 动作下标 onLoad() { this.ndActor = this.createActorNode(); this.loadSpriteFrames(); } loadSpriteFrames() { // 加载帧动画资源 if (this.loadIndex >= this.loadTotalCnt) { // 排序以保证帧序列顺序 for (let actionName in this.spPlayerFrameMap) { if (this.actions.indexOf(actionName) { if (error) { console.error(error.message || error); return; } this.onLoadAtlas(spriteAtlas); this.loadIndex += 1; this.loadSpriteFrames(); }); } onLoadAtlas(spriteAtlas: cc.SpriteAtlas) { let frames = spriteAtlas.getSpriteFrames(); let name, actionName, dir; for (let i = 0; i


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3