LOADING

加载过慢请开启缓存 浏览器默认开启

空洞武士笔记1

处理不同形式角色动画资产的Animation管理器

对于一些美术资产我们常常会遇到俩种形式

一张动画包含连续的多个帧 一帧一张动画 image-20240917144347379

1.首先处理的每帧一张图片的动画资源

我们选择了使用atlas类进行整合

#include <vector>
#include <graphics.h>

class Atlas
{
public:
    Atlas()=default;
    ~Atlas()=default;

    //根据路径名字和图片数量加载图片,同时存入img_list(IMAGE的vector)中
    void load(LPCTSTR path_template, int num)
    {
        img_list.clear();
        img_list.resize(num);

        TCHAR path_file[256];
        for (int i = 0; i < num; i++)
        {
            _stprintf_s(path_file, path_template,i + 1);
            loadimage(&img_list[i], path_file);
        }
    }
    
    //清空数组
    void clear()
    {
        img_list.clear();
    }
    
    //获取帧动画的数量
    int get_size()const
    {
        return (int)img_list.size();
    }
    
    //获取对应帧索引的图片指针(IMAEG*)
    IMAGE* get_image(int idx)
    {
        if (idx < 0 || idx >= img_list.size())
            return nullptr;

        return &img_list[idx];
    }
    
    //添加对应的图片,用于翻转图片的时候生成翻转图集
    void add_image(const IMAGE& img)
    {
        //复制构造函数放入,所以放入的可以是临时变量
        img_list.push_back(img);
    }
private:
    std::vector<IMAGE> img_list;
};

利用规律的命名以及图片数组将一个动画所需的所有帧整合到一个atlas类中,并且给出了对应的工具函数

在本项目中我们将atlas视为管理动画资产的一种方式

在渲染上我们进一步使用Animation去管理对应的动画帧,在Animation中我们对于美术资产形式atlas(散装的动画帧)和连续帧动画的美术资产统一了绘制形式Frame类

我们都将其转换为Frame然后统一绘制

2.对于一张图片的连续动画集我们选择从绘制方法和frame类解决

首先在绘制方法上我们首先选择裁剪图片的方式绘画
inline void putimage_ex(IMAGE* img, const Rect* rect_dst, const Rect* rect_src = nullptr)
{
    static BLENDFUNCTION blend_func = { AC_SRC_OVER ,0,255,AC_SRC_ALPHA };
    AlphaBlend(GetImageHDC(GetWorkingImage()), rect_dst->x, rect_dst->y, rect_dst->w, rect_dst->h,
        GetImageHDC(img), rect_src ? rect_src->x : 0, rect_src ? rect_src->y : 0,
        rect_src ? rect_src->w : img->getwidth(), rect_src ? rect_src->h : img->getheight(), blend_func);
}

形参(绘制的image指针,绘制在对应窗口的矩形位置,对应图片的裁剪位置)

当裁剪位置的Rect指针为空时候表明不需要裁剪

然后对于帧动画形式我们创建统一的类Frame

struct Frame
{
    Rect rect_src;							//需要的裁剪矩形	
    IMAGE* image = nullptr;					//对应的图片资源

    Frame() = default;
    Frame(IMAGE* image,const Rect& rect_src)//构造函数
        :image(image),rect_src(rect_src){}
    ~Frame() = default;
};

统一了每一帧动画的绘制形式,表明了我们需要将atlas(散装动画集合)和image(一张图片的动画图集)都转换为frame,然后放入

Animation的frame_list统一绘制

将atlas转换为frame

    void add_frame(Atlas* atlas)
    {
        for (int i = 0; i < atlas->get_size(); i++)
        {
            IMAGE* image = atlas->get_image(i);
            int width = image->getwidth();
            int height = image->getheight();

            Rect rect_src;
            rect_src.x = 0, rect_src.y = 0;
            rect_src.w = width, rect_src.h = height;

            frame_list.emplace_back(image, rect_src);
        }
    }

显然由于atlas是散装的不需要裁剪,所以rect_src就是图片的原点和宽高

将image(连续帧的图)转换为frame

void add_frame(IMAGE* image, int num_h)
{
    int width = image->getwidth();
    int height = image->getheight();
    int width_frame = width / num_h;

    for (int i = 0; i < num_h; i++)
    {
        Rect rect_src;
        rect_src.x = i * width_frame, rect_src.y = 0;
        rect_src.w = width_frame, rect_src.h = height;
        frame_list.emplace_back(image, rect_src);
    }
}

显然就是我们需要传入其动画帧的数量,然后根据序号然后计算出对应的裁剪矩形生成对应的fraem

这里补充一点

vector的push_back实现是调用复制构造函数,复制一份传入的对象然后放入vector

而emplace_back是调用构造函数放入vector,所以传入对应实例化类的构造函数参数就可以了

通过这样的方法,俩种不同的美术资产形式就统一成一种frame形式,也就方便了后面绘制的使用也是十分方便。