cocos2d-x学习之路(三)——精灵与动作

这里我们来看看所有游戏引擎中都会出现的一个重要的概念——精灵???♀?,及其使用方法。我还将介绍如何通过“动作”来控制精灵。



精灵的概念

在各大游戏引擎中,精灵一般都是载有图像,可以实现某些动作(比如移动,旋转,甚至高级一点的跳跃和碰撞)的一种类。在cocos2dx里面,官方文档对精灵的定义是:精灵是您在屏幕上移动的对象,它能被控制。你喜欢玩的游戏中主角可能就是一个精灵,我知道你在想是不是每个图形对象都是一个精灵,不是的,为什么? 如果你能控制它,它才是一个精灵,如果无法控制,那就只是一个节点(Node)。

简单的说就是“可以被玩家控制的就是精灵(比如游戏里的角色),不能被玩家控制的就是节点”。



精灵的创建

cocos2dx里创建精灵的方法有多种。首先最简单的是使用create静态函数来创建:

auto spriteName = Sprite::create(path);

这里path是图像所在的路径名称。这里提示一下:要加载的图片最好是png格式的。我常常加载jpg和bmp加载失败。

你可能会有疑问:为什么使用的是create静态方法?我在其他游戏引擎里(比如pygame)都是使用构造函数创建啊,这里使用create函数不是加多了代码量吗?

这里我觉得有着两种原因:

  • 这里使用静态方法可以方便Sprite类管理已经生成的精灵。而如果使用构造函数的话类对产生的对象的管理会比较麻烦(我以前就是常常使用构造函数,的确会加大管理的难度(也可能是我本身水平的问题吧??))。
  • 不知道你有没有发现,这个create函数使得Sprite像是工厂一样。没错,这里的Sprite的create是工厂方法(工厂方法详见设计模式),这样实现对拓展开放,对修改关闭,在代码设计上也是杠杠的。

后面我们还会接触到很多很多的工厂函数。可以说cocos2dx创建所有的东西都是使用工厂方法。后面我们会看到工厂方法在cocos里的广泛运用。

这里你还可以使用一些优化的方法来通过图像导入精灵,首先是使用图集(.plist)文件来导入图片并创建精灵。要是想使用图集,你可以将图集加载到“精灵帧隐藏区(SpriteFrameCache)”,SpriteFrameCache是一个单例对象。在cocos2dx里面获得单例对象基本上都是使用getInstance()方法:

auto spritecache = SpriteFrameCache::getInstance();
spritecache->addSpriteFramesWithFile("sprites.plist");

第一行获得SpriteFrameCache的单例,第二行读取了sprites.plist图集。

那么什么是图集呢?

图集就是将多个图片变成一个大图片,就和图片合并差不多。将最后生成图片的信息放到plist文件里面。

那么这有什么好处呢?

好处有以下几点:

  • 读取图片的次数少了。以前要读取一大堆图片,现在只需要读取一个plist文件就行了。
  • 减少OpenGL的渲染次数

这样就加快了图像的读取速度。

那么怎么制作一个图集呢?

cocos官方提供了四种工具来创建图集。其中比较推荐的是Texture Packer和Zwoptex两个工具。

我使用的是Texture Packer工具。这个是官方最推荐的一个:

这里是将我自己绘制的主角生成了图集(不要吐槽主角的画风以及主角衣服上的字......)。然后点击上面菜单栏的“Publish sprite sheet”来生成一个.plist和一份图像文件(默认png文件,就是在软件里面显示的这个图片)。

生成的.plist文件是使用xml作为描述的文件。打开可以看到如下信息:

这里因为我的是mac系统,打开之后直接就帮我解析了XML文件了。

可以看到文件里面分为frames和metadata两个标签。其中frames是保存我们图片信息的。其中图片的名字信息是图片的名字,也就是说如果你对OKNinja.bmp生成图集,那么OKNinja.png对应的名字就是OKNinja.bmp了。

然后比较重要的是textureRect标签,里面有一个列表,表示的是这个图片在总图片中的左上角坐标以及大小,即{{x,y},{w,h}}。

然后textureRotated表示是否旋转了图片

metadata包含了一些关于图片格式的信息。可以看出产生的图片名字在标签realTextureFileName下,我这里是test.png。然后总图片的大小在size标签下,我这里是740*200的。然后还可以在pixelFromat标签下看到颜色类型,这里是RGBA8888。

有了图集,并且加载了之后,我们就可以从图集里面读取图片了。使用createWithSpriteFrameName()函数。

auto mysprite = Sprite::createWithSpriteFrameName(spriteName);

这里spriteName为你的图片名称,而且必须是带后缀的全称。名称可以在.plist文件里面看到(其实就是原图图像名)。我这里加载一个OKNinja.bmp出来:

SpriteFrameCache::getInstance()->addSpriteFramesWithFile("res/test.plist");
auto sprite = Sprite::createWithSpriteFrameName("OKNinja.bmp");

这里我将总图像和.plist放在res文件夹下了,所以路径里面要加上"res/"。



控制精灵和放置精灵

创建了精灵之后我们当然是将其显示在我们的窗口上了啦。首先介绍一些控制精灵的函数,这些函数都是精灵对象的成员函数:

  • setPosition()设置精灵的位置
  • setScale()设置精灵缩放,你也可以使用setScaleX(),setScaleY()来只针对x或y方向缩放
  • setOpacity()设置精灵的透明度。0为完全透明,255位完全不透明。
  • setRotateion()设置精灵旋转。顺时针为正角度
  • setSkew()设置倾斜,你也可以使用setSkewX(),setSkewY()
  • setColor()设置颜色。里面的参数是Color3B指定的颜色。

比如说我们想要放置精灵在窗口,就得先设置精灵的坐标:

sprite->setPosition(Vec2(200,200));

这里通过Vec2类的构造函数来创建一个二维向量指定位置为(200,200)。后面在进入3D的时候我们还会遇到Vec3来表示空间向量。

这里还要着重说一下的是锚点的概念。你可以使用成员函数setAnchorPoint()函数来设置锚点。

所谓锚点就是图像的参考点,用物理的话说就像是质点一样。锚点代表了真个图像的坐标信息,所有需要图像坐标的函数都是获取锚点来作为图像的坐标的。比如我们旋转的时候,你说它是按照图像上那个点进行旋转呢?答案就是按照锚点旋转。默认锚点在图像的中心,也就是(0.5,0.5)处。这里说明一下,你在设置锚点时(1,1)是图像最右上方,(0,0)为图像的最左下方。所以(0.5,0.5)就是图像中心啦。

最后我们需要放置精灵到场景中。使用场景的addChild方法:

this->addChild(sprite);

这里由于我是在场景HelloWorldScene里面直接放置精灵的,所以使用this来指代HelloWorldScence场景。

最后效果如下:



cocos坐标系

在说控制之前首先要了解cocos的坐标系。首先是UI坐标,也就是一般软件设计UI界面时使用的坐标。这种坐标X轴向右,Y轴向下,原点在窗口的左上角,是个常见的计算机坐标系。

接下来是OpenGL坐标。这是标准的笛卡尔坐标系:X轴向右,Y轴向上,原点在窗口左下角。

你可以使用convertToGL()函数将UI坐标转换为OpenGL坐标。

cocos默认为OpenGL坐标。



精灵的动作控制

有了精灵,也将精灵放在窗口中了,接下来我们就要对精灵进行一些控制了。这一部分的函数都是大同小异的,比较简单。

首先来看看如何移动精灵。这里有两种移动方式:

  • 使用MoveBy对象
  • 使用MoveTo对象

你说,诶这里使用对象来移动是什么意思?我以前都是使用类的成员函数移动的呀。

这个也可以说是cocos的特点了吧。cocos将所有的动作(移动,旋转,缩放,跳跃等)封装成对象,当你要使用这些动作的时候你可以通过对象构建动作序列来让人物完成一系列动作。是不是很机智?

那你又说,MoveBy和MoveTo有什么区别呢?

所有的动作函数里,后缀为By的都是指从当前状态开始执行,而To则是不管当前状态直接执行。也就是说如果我MoveBy(200,200),这样是从当前的位置向上移动200,向右移动200。但是如果是MoveTo的话,就移动到坐标为(200,200)的地方去了(直线移动)。

那么如何创建一个动作呢?答案就是使用对应类的工厂方法啦:

auto mv=MoveBy::create(time,Vec2(x,y));

这里time是需要在多长时间内完成这个动作,单位为秒。 然后是由Vec2生成的目的地坐标(或者是偏移坐标,这个取决于你是使用By还是To)。

很多动作都是这样创建的,而且也都有time参数(都是在第一个),例如旋转:

auto rotate1=RotateBy::create(time,angle);

这里angle为角度值,以角度制衡量。

这里介绍一个比较特殊的动作:DelayTime。这个动作只是延时一段时间而已:

auto delay=DelayTime(time);

有了一些方法对象之后,我们还需要将方法对象变成序列(Sequence)。那么怎么变成序列呢?想必你已经想到了:使用工厂方法呗;

atuo seq=Sequence::create(action1,action2,...,nullptr);

这里动作序列里面可以传入任意长的动作对象。最后使用nullptr或者NULL来表示序列结束。

我这里创建了这样的动作序列:

auto delay=DelayTime::create(1);
auto rotate1=RotateBy::create(2, 90.0f);
auto scale1=ScaleBy::create(2,2);
auto seq=Sequence::create(delay(),rotate1,rotate1->clone()->reverse(),delay->clone(),MoveTo::create(2, Vec2(500,300)),delay->clone(),scale1, nullptr);

这里的FadeIn是淡入,FadeOut是淡出。需要注意的是,如果你要使用FadeIn的话,精灵必须是有一些透明的(透明值的大小会影响你的FadeIn效果)。同样FadeOut的话必须是不完全透明的。ScaleBy是缩放啦。

还要注意的一点是:所有的动作对象都有自己的内部状态。如果你在一个序列里面使用多次同一个动作对象的话,可能会因为内部状态导致奇葩的错误。这个时候你需要调用动作对象的成员函数clone()来获得其副本。这里序列的delay就做到了这一点。

然后rotate1的reverse()函数是用来将动作反转的。

最后需要让精灵使用这个动作序列,以此来完成动作。使用精灵的成员函数runAction()就行:

sprite->runAction(seq);

结果如下:

这里还要介绍一个结构,是和Sequence同样用来存储动作的结构:Spawn。与Sequence不一样的是,Spawn会同时执行其存储的动作对象。Spawn的创建和使用方法完全和Sequence一模一样。

最后要说的是,Sequence里可以嵌套Spawn或者Sequence。Spawn里面也可以嵌套Sequence和Spawn。只要将其传入参数就行了:

auto spawn = Spawn(Moveby::create(2,Vec2(10,l00),Rotate::create(2,30)));
auto seq=Sequence::create:(ScaleBy::create(2,2,2),DelayTime::create(3),spawn,Flipx::create(2));


总结

最后我们来总结一下创建精灵以及控制精灵的方法:

原文地址:https://www.cnblogs.com/learn-program/p/9643554.html

时间: 2024-08-03 00:11:27

cocos2d-x学习之路(三)——精灵与动作的相关文章

Jquery学习之路(三) 实现弹出层插件

弹出层的应用还是比较多的,登陆,一些同页面的操作,别人的总归是别人的,自己的才是自己的,所以一直以来想写个弹出层插件.不多废话,直接开始吧! 不想看可以在这里直接下载源码xsPop.zip 1:遮罩层 要弹出层,先要用一个遮罩层挡在下面的页面,此遮罩层是全屏的,页面滚动也要有,所以设置 position: fixed;还要有透明效果,下面是我定义的遮罩层css,取名mask .mask { position: fixed; width: 100%; height: 100%; backgroun

Redis——学习之路三(初识redis config配置)

我们先看看config 默认情况下系统是怎么配置的.在命令行中输入 config get *(如图) 默认情况下有61配置信息,每一个命令占两行,第一行为配置名称信息,第二行为配置的具体信息. 我们就从上到下来理解一下这些配置信息中的某些配置: 1.dbfilename是本地持久化存储数据库文件名,默认为dump.rdb.我可以在安装目录文件夹下找到这个文件. 2.requirepass是密码,即连接服务器的密码,默认为空.下面我来设置一个密码然后用带密码的命令连接一遍. 3.msterauth

JAVA学习之路三 编程英文汇总学习

JAVA学习中的每个章节中都有许多英文,不断熟悉工作英语也是提高编程能力很重要的一块,对于IT行业,英语才是通用语. 记在<JAVA语言程序设计>学习的第一章英文学习之后 .class file(.class文件)                       javac command(javac命令)          .java file(.java文件)            java Development Toolkit(JDK,java开发工具包)          assembl

linux学习之路三------指令篇

为什么要学习Linux命令? 1.Linux的图形界面虽然使用简单.直观,但是会占用大量系统资源,降低运行效率,增加安全的隐患. 2.学习和工作中,应尽可能使用Linux系统的命令运行界面,通过命令来完成操作. 3.要熟练掌握Linux操作系统,就必须熟练运用Linux下的各种命令,因此介绍Linux命令的基本功能和使用. 命令的使用方式,在Linux系统中打开终端的方式有以下两种: 一种是在桌面上依次单击"主程序→系统工具→终端"可打开如下图的终端窗口: 另一种是在Linux桌面上单

rabbitmq学习之路(三)

今天继续学习rabbitmq 了解一下AMQP的一些基本概念 交换机: Direct exchange(直连交换机) Fanout exchange(扇型交换机) Topic exchange(主题交换机) Headers exchange(头交换机) 交换机有两个状态 持久和暂存,区别就是持久话的交换机在消息代理也就是broker重启后依旧存在 队列: 队列需要被声明之后才能使用,如果声明时,该队列不存在,就会新建,如果已经存在,且属性无变化,则没有关系,不影响,若属性有变化,则报错 队列和交

Spring学习之路三——第一个Spring程序(体会IoC)

体会IoC:Spring通过一种称作控制反转(IoC)的技术促进了松耦合.当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象.你可以认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它. 步骤如下: 1.建立web功能 2.导入jar包 3.配置applicationContext.xml 4.创建类 5.测试 具体代码如下 UserDao.java 1 package cn.itcast

Java学习之路(三):Java中的数组

数组的概述和定义的格式 数组的作用: 用来存储同种数据类型的多个值 数组的基本概念: 数组是存储同一种数据类型多个元素的集合.就相当于一个容器. 注意:数组既可以存储基本数据类型,也可以存储引用数据类型. 数组的定义格式: 数据类型[] 数组名 = new 数据类型[指定数组的长度] 数组的初始化 概念: 就是为数组开辟连续的内存空间,并且为每个数的元素赋值 如何初始化: 动态初始化:指定长度,有系统给出初始化值  int[] arr = new int[5] 静态初始化:给出初始化的值 格式:

【python3的学习之路三】字符串和编码

字符串编码 由于计算机是美国人发明的,因此,最早只有127个字符被编码到计算机里,也就是大小写英文字母.数字和一些符号,这个编码表被称为ASCII编码,比如大写字母A的编码是65,小写字母z的编码是122. 但是要处理中文显然一个字节是不够的,至少需要两个字节,而且还不能和ASCII编码冲突,所以,中国制定了GB2312编码,用来把中文编进去. 但是全世界有上百种语言,各国有各国的标准就,会不可避免地出现冲突,结果就是,在多语言混合的文本中,显示出来会有乱码.因此,Unicode应运而生.Uni

springcloud学习之路: (三) springcloud集成Zuul网关

网关就是做一下过滤或拦截操作 让我们的服务更加安全 用户访问我们服务的时候就要先通过网关 然后再由网关转发到我们的微服务 1. 新建一个网关服务Module 2. 依然选择springboot工程 3. 老规矩起个名字 4. 勾选注册中心客户端 5. 勾选zuul网关模块 6. 编写配置文件 server: # 服务端口号 port: 8085 spring: application: # 服务名称 - 服务之间使用名称进行通讯 name: service-zuul eureka: client

Redis——学习之路四(初识主从配置)

首先我们配置一台master服务器,两台slave服务器.master服务器配置就是默认配置 端口为6379,添加就一个密码CeshiPassword,然后启动master服务器. 两台slave服务器配置如下: 1.先找到配置redis.windows-service.config文件修改port 6379 为port 6380 2.然后配置master服务器地址:slaveof 127.0.0.1 6379,然后主服务有密码还需要配置密码 masterauth CeshiPassword(本