从0开发游戏引擎之游戏引擎中2D序列帧动画控制器的实现

简介: 需要的图片类似图1.1 会把所有的动作拼接到一张图上,这样做也是为了节省内存和减少DrawCall,切换动作的时候只需要重新计算图片的UV,然后把算出来的UV作为新区域贴在原来的那张纹理面片上即可。

这种序列帧动画要求每一帧的宽高必须一一致,否则动画播起来会出问题。


需要的图片类似图1.1 会把所有的动作拼接到一张图上,这样做也是为了节省内存和减少DrawCall,切换动作的时候只需要重新计算图片的UV,然后把算出来的UV作为新区域贴在原来的那张纹理面片上即可。

 

395331a801714fe892f7d7be1fad33db.jpg


Animate是Animation的一部分,一个Animation可以保存多个Animate,也可以理解成Animate只是一个动画片段,Animation是完整的动画控制器。这个结构其实有点像Unity的AnimateClip和Animator。


不多说,上代码,自己去看代码吧,实现不是很难,不多讲了。


程序1.1


Animate.h 完整代码


#pragma once
namespace U2D
{
  enum MovementEvent
  {
    START,
    COMPLETE,
  };
  class CAnimate :public CPicture
  {
    friend class CAnimation;
  private:
    char name[50];  //动画名
    UINT frameWidth;//每一帧的宽度
    UINT frameHeight;//每一帧的高度
    UINT rows;//总行数
    UINT cols;//总列数
    UINT startIndex;//开始的索引编号
    UINT endIndex;//结束的索引编号
    UINT curIndex;//当前的索引编号
    bool isPlaying;//是否播放
    bool isLoop;//是否循环
    UINT frameSpeed;//每一帧的速度
    UINT frameTimer;//每一帧的时间
    animate_selector listener_movementEvent; //动画事件监听
  public:
    CAnimate(char *fileName, UINT rows, UINT cols);
    CAnimate(CCTexture *tex, UINT rows, UINT cols);
    void setName(char *name) { strcpy(this->name, name); }
    char *getName() { return this->name; }
    void setFrameIndex(UINT startIndex, UINT endIndex)
    {
      this->startIndex = startIndex;
      this->endIndex = endIndex;
      curIndex = startIndex;
    }
    Vector2 getFrameSize() { return Vector2(frameWidth, frameHeight); }
    UINT getFrameCount() { return endIndex - startIndex + 1; }
    void play() { isPlaying = true; }
    void stop() { isPlaying = false; }
    void repeat(bool isLoop) { this->isLoop = isLoop; }
    void setDelayUnit(UINT frameSpeed) { this->frameSpeed = frameSpeed; }
    void setMovementEventCallFunc(animate_selector fun_movementEvent);//设置动画事件的回调函数
    void draw();
    void all_Anchor_0_0();
    void all_Anchor_05_05();
  public:
    CAnimate() {}
    ~CAnimate(void) {}
  };
}


程序1.2


Animate.cpp 完整代码


#include "Engine.h"
namespace U2D
{
  CAnimate::CAnimate(char *fileName, UINT rows, UINT cols)
    :CPicture(fileName)
  {
    strcmp(this->name, fileName);
    this->rows = rows;
    this->cols = cols;
    isPlaying = true;
    isLoop = true;
    frameSpeed = 10;
    frameWidth = pTexture->getWidth() / cols;
    frameHeight = pTexture->getHeight() / rows;
    startIndex = 0;
    endIndex = rows*cols - 1;
    curIndex = startIndex;
  }
  CAnimate::CAnimate(CCTexture *tex, UINT rows, UINT cols)
    :CPicture(tex)
  {
    strcmp(this->name, tex->getName());
    this->rows = rows;
    this->cols = cols;
    isPlaying = true;
    isLoop = true;
    frameSpeed = 10;
    frameWidth = tex->getWidth() / cols;
    frameHeight = tex->getHeight() / rows;
    startIndex = 0;
    endIndex = rows*cols - 1;
    curIndex = startIndex;
  }
  void CAnimate::setMovementEventCallFunc(animate_selector fun_movementEvent)
  {
    this->listener_movementEvent = fun_movementEvent;
  }
  void CAnimate::draw()
  {
    if (isPlaying)
    {
      if (frameTimer++%frameSpeed == 0)
      {
        if (curIndex == startIndex)
        {
          if (listener_movementEvent)
          {
            if (parent != NULL)
            {
              (parent->*listener_movementEvent)(this, MovementEvent::START);
            }
            else
              (parent->*listener_movementEvent)(this, MovementEvent::START);
          }
        }
        if (curIndex == endIndex + 1)
        {
          if (isLoop)
          {
            curIndex = startIndex;
          }
          else
          {
            isPlaying = false;
            curIndex = endIndex;
          }
          if (listener_movementEvent)
          {
            if (parent != NULL)
            {
              if (parent != NULL)
              {
                (parent->*listener_movementEvent)(this, MovementEvent::COMPLETE);
              }
              else
                (parent->*listener_movementEvent)(this, MovementEvent::COMPLETE);
            }
          }
        }
        (&srcRect,
          curIndex%cols*frameWidth,
          curIndex / cols*frameHeight,
          (curIndex%cols + 1)*frameWidth,
          (curIndex / cols + 1)*frameHeight);
        curIndex++;
      }
    }
    CPicture::draw();
  }
  void CAnimate::all_Anchor_0_0()
  {
  }
  void CAnimate::all_Anchor_05_05()
  {
  }
}


程序1.3


Animation.h 完整代码


#pragma once
namespace U2D {
  class CAnimation :public CElement
  {
    friend class CPlayer;
  protected:
    list<CAnimate*> animateList;          //动画的链表用名字判断,有就返回,没有就new一个
    CAnimate *animate;                //当前播放的动画
    animation_selector listener_movementEvent;    //动画集事件监听
    void animateEvent(CAnimate* animate, MovementEvent type);
    UINT rows;
    UINT cols;
    char*fileName;
  public:
    CAnimation();
    CAnimate* addAnimate(char *animName, char *fileName, UINT rows, UINT cols);
    CAnimate* addAnimate(char *animName, CCTexture *tex, UINT rows, UINT cols);
    CAnimate* findAnimate(char *animName);
    void setAnimate(char *animName);
    Vector2 getFrameSize() { return animate->getFrameSize(); }
    UINT getFrameCount() { return animate->getFrameCount(); }
    void play() { animate->play(); }
    void stop() { animate->stop(); }
    void repeat(bool isLoop) { animate->repeat(isLoop); }
    void setDelayUnit(UINT frameSpeed) { animate->setDelayUnit(frameSpeed); }
    void setFrameIndex(UINT startIndex, UINT endIndex) { return animate->setFrameIndex(startIndex, endIndex); }
    void setName_FrameIndex(char*name, UINT startIndex, UINT endIndex);
    void setMovementEventCallFunc(animation_selector fun_movementEvent);
    void draw();
    void setCurrentAllanchor(float ox, float oy);
    RECT getBoundBox();
    ~CAnimation();
  };
}


程序1.4


Animation.cpp 完整代码


#include "Engine.h"
namespace U2D
{
  CAnimation::CAnimation()
  {
    animate = NULL;
    listener_movementEvent = NULL;
  }
  CAnimate* CAnimation::addAnimate(char *animName, char *fileName, UINT rows, UINT cols)
  {
    CAnimate* anim = findAnimate(animName);//查找这个动画名字,如果找不到就会返回空
    if (anim != NULL)           //如果不为空就说明找到了
      return anim;
    this->rows = rows;
    this->cols = cols;
    this->fileName = fileName;
    anim = new CAnimate(fileName, rows, cols); //如果为空就new一个对象压进去。并且把名字设置好
    anim->setName(animName);          //设置动画名字
    anim->setParent(this);            //CAnimation是CNode的子类,子类拥有父类的所有数据,所以压入CAnimation就等同于压入了CNode。
    anim->setMovementEventCallFunc(animate_selector(&CAnimation::animateEvent));//回调函数
    animateList.push_back(anim);        //压进链表
    setAnimate(animName);           //设置动画
    return animateList.back();          //把刚压进去的对象返回出去
  }
  CAnimate* CAnimation::addAnimate(char *animName, CCTexture *tex, UINT rows, UINT cols)
  {
    CAnimate* anim = findAnimate(animName);
    if (anim != NULL)
      return anim;
    this->rows = rows;
    this->cols = cols;
    anim = new CAnimate(tex, rows, cols);
    anim->setName(animName);
    anim->setParent(this);
    anim->setMovementEventCallFunc(animate_selector(&CAnimation::animateEvent));
    animateList.push_back(anim);
    setAnimate(animName);
    return animateList.back();
  }
  CAnimate* CAnimation::findAnimate(char *animName)
  {
    list<CAnimate*>::iterator iter;
    for (iter = animateList.begin(); iter != animateList.end(); iter++)
    {
      if (strcmp((*iter)->name, animName) == 0)
      {
        return *iter;
      }
    }
    return NULL;
  }
  void CAnimation::setAnimate(char *animName)
  {
    animate = findAnimate(animName);
    animate->startIndex = animate->startIndex;//这个写的不对、明天去参考伟哥的写法
    //animate->curIndex = animate->startIndex;
    animate->isPlaying = true;
    animate->frameTimer = 0;
  }
  void CAnimation::setName_FrameIndex(char*name, UINT startIndex, UINT endIndex)
  {
    CAnimate* anim = findAnimate(name);
    anim = new CAnimate(fileName, rows, cols);        //如果为空就new一个对象压进去。并且把名字设置好
    anim->setName(name);                //设置动画名字
    anim->setParent(this);                //CAnimation是CNode的子类,子类拥有父类的所有数据,所以压入CAnimation就等同于压入了CNode。
    anim->setFrameIndex(startIndex, endIndex);
    anim->frameTimer = 0;
    animateList.push_back(anim);            //压进链表
    setAnimate(name);                 //设置动画
  }
  void CAnimation::draw()
  {
    Matrix3 scaleMatrix;
    Matrix3 rotateMatrix;
    Matrix3 transMatrix;
    //放缩图片
    Scale(scaleMatrix, scale.x, scale.y);
    //水平翻转
    if (flip == true)
      scaleMatrix._11 *= -1;
    //旋转图片
    Rotate(rotateMatrix, angle);
    // 平移图片到我们的指定位置
    Translate(transMatrix, pos.x, pos.y);
    local_matrix = scaleMatrix*rotateMatrix*transMatrix;
    if (parent == NULL)
    {
      world_color = local_color;
      world_matrix = local_matrix;
    }
    else
    {
      sColor col = parent->getWorldColor();
      world_color.r = local_color.r*col.r;
      world_color.g = local_color.g*col.g;
      world_color.b = local_color.b*col.b;
      world_color.a = local_color.a*col.a;
      world_matrix = local_matrix*parent->getWorldMatrix();
    }
    if (visible == false)
      return;
    animate->draw();
  }
  void CAnimation::setCurrentAllanchor(float ox, float oy)
  {
  }
  void CAnimation::setMovementEventCallFunc(animation_selector fun_movementEvent)
  {
    listener_movementEvent = fun_movementEvent;
  }
  void CAnimation::animateEvent(CAnimate* animate, MovementEvent type)
  {
    if (listener_movementEvent)
    {
      if (parent != NULL)
        (parent->*listener_movementEvent)(this, type, animate->name);
      else
        (this->*listener_movementEvent)(this, type, animate->name);
    }
  }
  RECT CAnimation::getBoundBox()
  {
    return animate->getBoundBox();
  }
  CAnimation::~CAnimation()
  {
  }
}


谢谢大家

相关文章
|
23天前
动画控制器在 Flutter 中的工作原理
【10月更文挑战第18天】总的来说,动画控制器 `AnimationController` 在 Flutter 中起着关键的作用,它通过控制动画的数值、速度、节奏和状态,实现了丰富多彩的动画效果。理解它的工作原理对于我们在 Flutter 中创建各种精彩的动画是非常重要的。
|
11月前
|
人工智能 算法 图形学
Unity 动画系统基本概念
Unity 动画系统基本概念
109 0
|
Dart Android开发
Flutter(十七)——组合动画
Flutter(十七)——组合动画
330 2
Flutter(十七)——组合动画
|
前端开发 JavaScript UED
封装库/工具库中重要概念之动画
前端开发中,动画是一个非常重要的技术特性。它可以提升用户体验,增加页面交互性,并且让网站看起来更加生动活泼。然而,在实现复杂动画时,手写代码往往会变得繁琐且容易出错。因此,前端工具库和封装库的出现为我们提供了便利。在本文中,我们将探讨前端中的封装库和工具库以及它们在实现动画效果方面的作用。
100 0
|
存储 传感器 C#
从零开始做一款Unity3D游戏<二>——移动,相机控制与碰撞(一)
从零开始做一款Unity3D游戏<二>——移动,相机控制与碰撞
从零开始做一款Unity3D游戏<二>——移动,相机控制与碰撞(一)
|
存储 安全 C#
从零开始做一款Unity3D游戏<二>——移动,相机控制与碰撞(二)
从零开始做一款Unity3D游戏<二>——移动,相机控制与碰撞
从零开始做一款Unity3D游戏<二>——移动,相机控制与碰撞(二)
|
负载均衡 安全 vr&ar
【Unity渲染】一文看懂!Unity通用渲染管线URP介绍
Unity 的渲染管线包含内置渲染管线、SRP、URP和HDRP。自从Unity2019.3开始,Unity将轻量级渲染管线修改为了通用渲染管线,这是一种快速、可扩展的渲染管线,支持所有的移动设备,适用于 2D、3D、虚拟现实 (VR) 和增强现实 (AR) 项目。
从0开发游戏引擎之碰撞检测模块底层实现
从0开发游戏引擎之碰撞检测模块底层实现
|
iOS开发 开发者
iOS开发CoreGraphics核心图形框架之六——梯度渐变(二)
iOS开发CoreGraphics核心图形框架之六——梯度渐变
217 0
iOS开发CoreGraphics核心图形框架之六——梯度渐变(二)
|
iOS开发 开发者
iOS开发CoreGraphics核心图形框架之六——梯度渐变(一)
iOS开发CoreGraphics核心图形框架之六——梯度渐变
291 0
iOS开发CoreGraphics核心图形框架之六——梯度渐变(一)