0x001 前言:什么是Aprite?它能做什么?
Aprite是一个基于Sprite升级的精灵库,它的出现其目的是为了简化脚本工作者(后面简称 程序狗)的工作量,使得开发效率适当的提升。Aprite创建的初衷是使得在创建简易动画特效,简单事件触发上更加容易。目前Aprite还处在一个弱demo时期,正在一步步升级强大起来,它现在的主要构成是由 感知精灵管理器 和 感知精灵库 一同构成,管理器是为了更加方便和集中化的管理我们使用Aprite创建出来的精灵。那么,说这么多也是口水话,不如让我们来尝试学习一下Aprite的使用吧!
0x002 让我们从显示开始
尝试输入如下代码:
player = Aprite.new
loop do
$Aprite_manager.update
Graphics.update
Input.update
end
此时保存并且运行游戏,就会发现在屏幕上有一个 100*100 的红色矩形。
我们可以看出,在创建默认Aprite的时候,它自动为你初始化了一个 100*100 红色矩形 Bitmap对象,如果你不满意这个红色方块,你可以通过它的bitmap属性替换,在上述代码中,你可以再起一行写上: player.bitmap = Bitmap.new(xxxx),这样你就可以为Aprite对象更换材质了。我们发现,和传统意义上不同了,这里我们没有再单独的为Aprite对象进行更新了,我们直接使用$Aprite_manager.update来做精灵的更新,这样,无论你有多少个Aprite,它都会帮你集群化的更新,是不是很方便?其实,Aprite是继承于Sprite的,所以Sprite存在的方法属性,在Aprite上同样适用。
0x003 从一句单行动画命令开始
Aprite的主要特性是简易动画,那我们看看它---到底可以有多简单?我们在上述代码的基础上写下了如下代码:
player.run( "width 0 100 50" )
我们来测试一下运行效果,可以看出,启动后那个小红块的宽度正在慢慢变长... ...
没错,只要这么简单就能实现一个简易的线性动画,run方法一次性只能执行一句 动画指令,对于动画指令来说,其实很容易理解,就上述动画指令解释( width 0 100 50 ),可以直观的翻译成:让宽度从0到100,用时30帧。是不是很容易理解?同理,现在我随便写一句动画指令,都可以被这么直观的翻译出来,比如 moveX 0 20 10,这一句就是指x方向移动从0到20,用时10帧... ...很简单的就实现了我们要的线性动画效果,那么,目前都有哪些动画指令呢?请看下表:
sx | 在前面带有s是指精灵的位图传送矩阵,sx就是该传送矩阵的x(传送矩阵是相对bitmap位图进行变换的,用它可以实现人物行走动画) |
sy | 传送矩阵的x |
sheight | 传送矩阵的高 |
swidth | 传送矩阵的宽 |
fade | 淡入淡出效果(也指透明度),变换的值最小为0,最大255 |
width | 宽度(这个的变换是会拉升图像) |
height | 高度(这个的变换是会拉升图像) |
moveX | x坐标 |
moveY | y坐标 |
rotate | 旋转角度(这个比较影响性能) |
wait | 只接受一个参数,就是等待的帧数 |
autowait | 无参数,自动判断等待时间 |
do | 设置一个标记点 |
loop | 跳转到最近的一个do标记点,如果没有,则跳转到第一条指令 |
0x004 利用scan来写动画脚本
上述的动画利用run来实现的,但是前面也说了,run一次只执行一行指令,那么一些复杂点的线性动画都是很多简单线性动画合成的,一行是肯定不行,所以这里我们使用多行扫描指令scan,现在我们写下下列代码:
animate = "width 0 100 50\n"
animate = animate + "autowait\n"
animate = animate + "do\n"
animate = animate + "fade 255 160 20\n"
animate = animate + "autowait\n"
animate = animate + "fade 160 255 20\n"
animate = animate + "autowait\n"
animate = animate + "loop"
player.scan( animate )
看,通过多行命令,就成功的实现了一个多组合的动画效果,而且后面的闪烁效果会一直循环下去。讲到这里,我们必须要尝试了解一下Aprite代码的机制问题,这样能让你更加清楚它的运行流程,从而减少错误或者bug的产生。刚刚写的多行语句,被scan扫描后变成了单行语句存放在任务列表里,精灵每次刷新都会执行任务列表里的值,本让列表指针加一,如果任务列表指针下一次指向空,就不会再运行了,注意,此时动画虽然不再运行,但是任务列表里的任务还存在,这时候只要将指针更改到有效值,它依然会运行,这就是do-loop循环的机制,同时这也意味着一件事,如果我想更换动画效果怎么办?这里给开发者提供了一个方法就是
clear_task,并且将指针指向起点。
0x005 人物行走帧动画的实现
我们来尝试实现一下人物行走的帧动画,这里我们用的character里的4*4行走图,意味着每行有四个动作,四个动作完成一次行走,我们首先要将bitmap给Aprite,然后我们现在要限制显示区域了,不然它一次性显示全部就没有意义了。限制区域用src_rect来限制,这个是sprite对象的原生方法。对与动画,我们每一帧只需要让 sx 移动到下一个状态的雪碧图位置,即一次4帧行走可以看作sx从 width*0 ---> width*1 ---> width*2 ---> width*3 的这么一个过程,具体代码实现可以看下图:
说实话,作为作者的我来说,我觉得这样实现确实有一点点麻烦,所以下一改版我会尝试变得让它更容易实现。
0x006 它,是一个事件触发性的精灵
没有听错,它带有基本的事件感知,由于是demo版,目前仅提供四种基本事件 聚焦 / 失焦 / 碰撞 / 控制器,每一个事件都有相应的事件触发方法,这一章将一一为大家讲解。
⚪focused与on_focus / on_blur事件
所有刚创建出来的精灵,都没有被聚焦即 focused = false,当focused从false --> true的时候,将会触发on_focus,并且会将管理器里其他所有精灵全部失焦(因为我们每次聚焦只能对一个精灵,不然就会歧义),我们来看看它能做什么吧。
⚪on_collision碰撞事件和点对点判断collisionInxy(x, y)函数
这里的碰撞事件与Unity3D不太相同,这里没有所谓的刚体,这里的碰撞也是矩形碰撞(考虑到RM性能问题),所有不支持多边形碰撞,当然由于demo版本,目前不止持精灵的绑定,后期可以通过精灵绑定实现多边形碰撞。什么情况下会触发on_collision事件呢?只要collisionInxy(x, y)方法了,它检测到碰撞物体了,就会触发,所有这里的碰撞是一种 可预知的碰撞, 这种碰撞机制是为了让开发者更容易的实现PID算法下的矫正。
⚪player玩家与control控制器
要想使控制器生效,你需要做两件事情,第一件事情,将它的player属性设置为true,第二件事情,它的状态应该被聚焦。这种情况下,刷新的时候,每一帧都会触发控制器,即control事件,如果你不想写control事件,没关系,每个Aprite都内置一个control默认事件,下面是内核代码,可以看一下内置的control事件长什么样。
可以看出,默认控制器已经帮我们实现了方向的控制和碰撞的检测,这意味我们只需要写很少一部分代码就能实现控制了,快来看看如何做吧!
可以看见,就这么简单实现了控制,如果我们觉得它动的太慢,可以将speed属性写高一点就好了。
0x007 一些细节
1)关于Aprite是否影响性能?答案是肯定的,但是你把它当sprite使用,它的性能和sprite相当,用它做丰富的动画,随之而来的也是性能问题,这个问题就RM渲染机制暂时没办法解决,你可以尝试用论坛大佬的 RGD引擎 ,目前暂时没做对RGD引擎的兼容,也没做测试。
2)每次转换场景时,我们应该销毁我们创建的精灵,因为有管理器的存在,可以直接使用$Aprite_manager.dispose。
3)管理器的存在是为了更加好的集群化管理,我们应该善用管理器。
0x008 关于未来版本的展望
其实我最讨厌立flag,我希望能有更多的开发者协同开发和维护Aprite,毕竟它是开源的。有兴趣可以去我的 github 给个小星星。
未来可能会加入:
1)键盘和鼠标触发事件
2)样式表和对应的样式语法
3)component组件化
4)内置更多便于开发的小方法
5)基于bitmap上的图像处理
6)精灵绑定和继承
7)精灵计算,+ - * /
0x009 代码Code
GitHub晚一些再上传。
本帖来自P1论坛作者antilmid,因Project1站服务器在国外有时候访问缓慢不方便作者交流学习,经联系P1站长fux2同意署名转载一起分享游戏制作经验,共同为国内独立游戏作者共同创造良好交流环境,原文地址:
https://rpg.blue/forum.php?mod=viewthread&tid=477782 若有侵权,发帖作者可联系底部站长QQ在线咨询功能删除,谢谢。