【cocos2dx】【iOS工程】如何保存用户在游戏内的绘画数据,并将数据以图像形式展示在预览界面

【cocos2dx】【iOS工程】如何保存用户在应用内的操作数据,并将数据以图像形式展示在预览界面

设备/引擎:Mac(11.6)/Mac Mini

开发工具:Xcode(15.0.1)

开发需求:如何保存用户在应用内的操作数据,并将数据以图像形式展示在预览界面

又到了总结的时候了,之前做过一个涂色类的项目,其中有个技术难点就是怎么保存用户每次的绘画数据,并在预览界面展示用户之前的绘画内容。这几天闲下来就整理整理。

思路:
将用户的绘画数据存储到动态数组中——>每次结束游戏时,遍历动态数组中的数据并将数据存储为一个二进制文件——>用户重新开始游戏时,从保存的二进制文件中加载图像,再用该图像初始化一个CCTexture2D对象,再用该纹理对象创建一个新的精灵,最后将精灵显示在场景中。

简单说就是两步,首先保存好数据,最后将数据提出再展示出来
《获取用户的涂画数据》
根据项目的要求,用户只能在场景内的指定区域来涂色,比如画板上、动物的各部位色块上,为了实现只在指定的区域进行涂色,我们使用了自定义的裁剪节点ColoringClippingNode(CCClippingNode类型)。具体如下:
1.创建背景画布

CCSprite* stencilCanvas = CCSprite::create("DinoColor/canvas.png");
stencilCanvas->setAnchorPoint(ccp(0.0, 0.0));
stencilCanvas->setPosition(CCPointZero);

用来展示用户将要涂色的图像或场景,这个也是基础的背景画布。
2.创建裁剪节点

ColoringClippingNode* clip = ColoringClippingNode::create(stencilCanvas);
clip->setContentSize(CCSizeMake(stencilCanvas->getContentSize().width, stencilCanvas->getContentSize().height));
clip->setAlphaThreshold(0.0f);
clip->setAnchorPoint(ccp(0.5, 0.5));
clip->setPosition(ccp(stencilCanvas->getContentSize().width/2, stencilCanvas->getContentSize().height/2));

以 stencilCanvas 为裁剪模板,设置裁剪节点的大小,设置裁剪的透明度阈值,设置裁剪节点的锚点和位置。
3.用户实际的涂色操作
whiteCanvas为自定义的CCSprite类型的ColorSprite类的实例化对象

whiteCanvas = ColorSprite::CreateColor("DinoColor/canvas.png", ccp(stencilCanvas->getContentSize().width/2, stencilCanvas->getContentSize().height/2), this, m_DrawArray->count());
whiteCanvas->curSprName = "ColoringType_"+std::to_string(ColorManager::shared()->curColorTheme+1)+"_"+std::to_string(ColorManager::shared()->colorAniIndex)+"canvas";
whiteCanvas->showLastSceneImage();
whiteCanvas->initBrushNode();
whiteCanvas->setTag(20);

上述代码依次为:创建画布->设置当前涂色画布的名称(方便后续保存提取对应的数据)->根据保存的涂色数据显示上次的涂色数据->初始化涂色使用的画笔节点->设置一个tag值,方便后续获取。
4.将节点添加到场景中

this->addChild(clip);
m_ClipDrawArray->addObject(clip);
clip->addChild(whiteCanvas);

将裁剪节点添加到当前场景中,将裁剪节点添加到管理裁剪节点的动态数组中,将用户实际的涂色画布作为裁剪节点的子节点,确保涂色操作被裁剪到stencilCanvas指定的区域内。
以上通过使用背景画布、涂色画布和裁剪节点,就可以实现一个用户在指定区域内进行涂色操作的功能。

《保存数据》
我们查了一些iOS工程保存数据内容的方法,最后还是决定用二进制形式(.bin格式)来保存用户的绘画数据。先看保存部分的代码

1.创建渲染对象

CCSize sprSize = _colorSpr->getContentSize();
CCRenderTexture* saverenderTexture = CCRenderTexture::create(sprSize.width, sprSize.height, kCCTexture2DPixelFormat_RGBA8888);

_colorSpr就是传进来的用户绘画内容对象,为什么要将该精灵渲染到CCRenderTexture中,简单说就是为了将用户绘画内容绘制到一个纹理上,以便后续将其保存为图像数据。这个过程类似于在一个虚拟的画布上绘制 _colorSpr,而不是直接在屏幕上显示。具体原因如下:
1)离屏渲染:CCRenderTexture允许在内存中创建一个虚拟的渲染目标,而不是直接显示在屏幕上。通过离屏渲染,可以在不影响屏幕显示的情况下,捕捉和处理精灵的图像内容,更隐蔽更安全更方便。
2)捕捉精灵状态:在游戏中,当我们需要保存当前精灵的状态,就像现在要保存用户的绘画、涂色数据等操作时,将精灵渲染到 CCRenderTexture 中,可以将当前获取的数据内容保存为一个完整的图像数据,方便后续使用和存储。
3)保存为图像文件:一旦将 _colorSpr 的渲染结果存储在 CCRenderTexture 中,接下来就可以将其保存为图像文件(.bin 文件)。这种方式可以将精灵的图像数据永久化存储到文件系统中,以便将来读取、恢复或分享给其他用户。

2.开始渲染并绘制内容

saverenderTexture->begin();	//开始将渲染目标
_colorSpr->visit();			//调用_colorSpr的visit()方法,用于渲染精灵对象到saverenderTexture上
saverenderTexture->end();	//结束渲染

这部分比较简单不再赘述。

3.保存为图像文件

std::string localPath = CCFileUtils::sharedFileUtils()->getWritablePath() + _fileName + ".bin";
CCImage* saveImage = saverenderTexture->newCCImage();
saveImage->saveToFile(localPath.c_str());
saveImage->release();

localPath 是保存文件的本地路径,使用可写入路径加上_fileName(前面自定义的文件名称)加上.bin 扩展名;
saverenderTexture->newCCImage(); 将 saverenderTexture 转换为CCImage对象;
saveImage->saveToFile(localPath.c_str()); 将CCImage对象保存为二进制文件;
saveImage->release(); 释放CCImage对象,避免内存泄漏。
整段内容总结为:将 _colorSpr的渲染内容捕捉并保存为二进制文件
1)为什么要保存为.bin格式
.bin 格式通常是为了将数据以二进制形式存储到文件中,他也不是指定格式,你可以用它来存储图像、音频、视频、数据结构、存档或配置文件、数据库文件、自定义的一些数据格式等等。
2)以此方式存储数据的好处
二进制存储:.bin 文件以二进制形式存储数据,相比文本文件,可以更有效地存储和读取数据。对于像素数据、图像数据等大量的二进制信息,使用二进制格式可以更节省存储空间和提高读写效率。
数据完整性:二进制文件保存数据时,可以直接以字节流形式写入数据,不需要转换为可打印字符(如文本文件)。这样就可以确保数据在存储和读取过程中的完整性,特别是对于图像、音频等复杂数据结构。
适合图像数据:在游戏开发中,如保存精灵的图像状态或游戏中的地图数据或者是绘画内容数据等等,二进制格式通常更为适合。这些数据通常是复杂的结构化数据,直接以二进制形式存储可以减少数据解析和转换的复杂性。

《获取保存的数据》
获取数据简单说就是从指定的**.bin**文件中加载图像数据,并返回一个CCImage对象,然后再在游戏中进一步处理CCImage对象并显示出来。
1.从指定的.bin文件中加载图像数据
1)构建文件路径:

std::string fullPath = CCFileUtils::sharedFileUtils()->getWritablePath() + _fileName + ".bin";

不再赘述
2)打开文件

FILE* file = fopen(fullPath.c_str(), "rb");
if (!file) {// Handle errorreturn nullptr;
}

使用fopen函数以二进制只读模式 (“rb”) 打开文件。如果文件打开失败 (file为nullptr),则返回 nullptr,表示加载失败。
3)获取文件大小

fseek(file, 0, SEEK_END);
long fileSize = ftell(file);
fseek(file, 0, SEEK_SET);

使用fseek和ftell函数来获取文件大小。首先将文件指针移动到文件末尾 (SEEK_END),然后使用 ftell 获取当前文件指针的位置,即文件大小。 一旦获取了文件大小,通常需要将文件指针重新定位到文件的开头,以便进一步读取文件内容或者其他操作,也就是最后一行将文件指针移回文件开头 (SEEK_SET)。
注:获取文件大小是为了在读取文件内容之前,知道文件有多大,以便分配足够大小的内存缓冲区来存储文件内容
4)分配内存并读取文件内容

char* buffer = new char[fileSize];
size_t bytesRead = fread(buffer, 1, fileSize, file);

根据文件大小fileSize分配一个足够大的缓冲区buffer,用于存储文件内容。使用fread函数从打开的文件中读取数据,将文件内容读取到buffer中。
5)创建 CCImage 对象

CCImage* image = new CCImage();
if (!image->initWithImageData(buffer, static_cast<int>(bytesRead))) {// Handle errordelete[] buffer;delete image;return nullptr;
}

使用 CCImage 对象的 initWithImageData 方法,将 buffer 中的二进制数据初始化为 CCImage 对象。如果初始化失败,释放buffer和image对象,然后返回nullptr,表示加载图像数据失败。
6)清理资源

delete[] buffer;
fclose(file);

成功加载图像后,释放 buffer 内存,并关闭文件。
7)返回图像数据

return image;

2.将获取到的图像显示在游戏内
1)构建文件名

std::string canvasFileName = "ColoringType_"+std::to_string(ColorManager::shared()->curColorTheme+1)+"_"+std::to_string(i+1)+"canvas";

目的是为了获取到你保存数据时对应的文件名称,以便加载对应的数据图像。
2)加载二进制图像文件

CCImage* canvasImage = ColorManager::shared()->loadImageFromBinaryFile(canvasFileName);

loadImageFromBinaryFile方法内容就是上面所提到的如何提取数据位图像的内容,不再赘述。
3)初始化纹理对象

if (canvasImage != NULL) {CCTexture2D* canvasTexture = new CCTexture2D();if (canvasTexture && canvasTexture->initWithImage(canvasImage)) {// 创建和设置精灵对象// ...}
}

如果成功加载了 canvasImage,则创建一个 CCTexture2D 对象 canvasTexture,并使用 canvasImage 初始化它。这个步骤是为了将图像数据转换为纹理对象,以便后续在精灵中显示。
4)创建和设置精灵对象

CCSprite* stencilSpr = CCSprite::createWithTexture(CCTextureCache::sharedTextureCache()->addImage("DinoColor/canvas.png"), CCRect(0, 0, 739, 640));
stencilSpr->setAnchorPoint(ccp(0.0, 0.0));
stencilSpr->setPosition(CCPointZero);CCSprite* canvasSpr = CCSprite::createWithTexture(canvasTexture);
canvasSpr->setPosition(ccp(lastscenePos.x+x_x, canvasSpr->getContentSize().height/2));

canvasSpr是加载了从二进制文件中读取的纹理数据的精灵,设置它的位置,这个精灵将显示用户之前涂色的内容。
5)创建裁剪节点并添加精灵

CCClippingNode* clip = CCClippingNode::create(stencilSpr);
clip->addChild(canvasSpr);

CCClippingNode 是一个用于裁剪其子节点显示区域的节点。用stencilSpr也就是画板作为裁剪模板,将canvasSpr作为子节点添加到裁剪节点中。这样做可以确保canvasSpr只在stencilSpr指定的区域内显示。
PS:除了画板之外,游戏内还有各动物的各部位也可以涂画,所以也需要创建他们的精灵对象,方法与上面创建画板的基本一致,不再赘述。

内容有点多,希望能给大家带来帮助!!!有什么问题需要讨论的可以评论私信欢迎讨论~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/42341.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

富格林:抓住正规稳健出金思路

富格林指出&#xff0c;凡事要学会抓住正规思路避繁就简&#xff0c;才会顺利达到终点。在现货黄金市场中&#xff0c;投资者必须学会抓对正规趋势&#xff0c;才是走向盈利出金的根本保障。以下是富格林投资总结的几个观点和建议&#xff0c;希望能帮助投资者实现稳健出金。 …

算法基础之分治法

算法原理 对于一个规模为 n n n 的子问题&#xff0c;若该问题可以容易地解决则直接解决&#xff0c;否则将其分解为 k k k 个规模较小的子问题&#xff0c;这些子问题相互独立且与原问题形式相同。递归地解决这些子问题&#xff0c;然后将各子问题的解合并得到原问题的解&a…

单链表详解(2)

三、函数定义 查找节点 //查找结点 SLTNode* SLTNodeFind(SLTNode* phead, SLTDataType x) {assert(phead);SLTNode* pcur phead;while (pcur){if (pcur->data x){return pcur;}pcur pcur->next;}return NULL; } 查找节点我们是通过看数据域来查找的&#xff0c;查…

Arm64 基础指令集介绍

按照字母排序顺序&#xff1a; ● ADC&#xff1a;带进位加法。 ● ADCS&#xff1a;带进位加法&#xff0c;设置标志位。 ● ADD (extended register)&#xff1a;扩展寄存器加法。 ● ADD (immediate)&#xff1a;立即数加法。 ● ADD (shifted register)&#xff1a;移位寄存…

【MySQL05】【 undo 日志】

文章目录 一、前言二、undo 日志&#xff08;回滚日志&#xff09;1. 事务 id2. undo 日志格式2.1 INSERT 对应的 undo 日志2.2 DELETE 对应的 undo 日志2.3 UPDATE 对应的 undo 日志2.3.1 不更新主键2.3.2 更新主键 2.3 增删改操作对二级索引的影响2.4 roll_pointer 3. FIL_PA…

Windows 网络重置

netsh int ip reset 命令是用于重置 Windows 操作系统中的网络设置和配置的命令。 在网络故障排除、修复网络连接问题以及清除可能存在的网络配置冲突时非常有用。 命令详解&#xff1a; netsh: 用于配置各种网络设置 int: 用于管理网络接口 ip: 用于管理网络接口的 IP 配…

layui项目中的layui.define、layui.config以及layui.use的使用

第一步:创建一个layuiTest项目&#xff0c;结构如下 第二步&#xff1a;新建一个test.js,利用layui.define定义一个模块test,并向外暴露该模块&#xff0c;该模块里面有两个方法method1和method2. 第三步&#xff1a;新建一个test.html&#xff0c;在该页面引入layui.js&#x…

基于FPGA的LDPC编译码算法设计基础知识

基于FPGA的LDPC编译码算法设计基础知识 数字电路&#xff08;数电&#xff09;知识模拟电路&#xff08;模电&#xff09;知识1. 放大器1.1. 晶体管放大器1.2. 运算放大器1.3. 管子放大器&#xff08;真空管放大器&#xff09;微处理器/单片机知识其他相关知识 基于FPGA的算法设…

neo4j 图数据库:Cypher 查询语言、医学知识图谱

neo4j 图数据库&#xff1a;Cypher 查询语言、医学知识图谱 Cypher 查询语言创建数据查询数据查询并返回所有节点查询并返回所有带有特定标签的节点查询特定属性的节点及其所有关系和关系的另一端节点查询从名为“小明”的节点到名为“小红”的节点的路径 更新数据更新一个节点…

python爬虫和用腾讯云API接口进行翻译并存入excel,通过本机的Windows任务计划程序定时运行Python脚本!

项目场景&#xff1a; 提示&#xff1a;这里简述项目相关背景&#xff1a;定时爬取外网的某个页面&#xff0c;并将需要的部分翻译为中文存入excel 接下了的&#xff0c;没学过的最好看一下 基本爬虫的学习 【爬虫】requests 结合 BeautifulSoup抓取网页数据_requests beauti…

Vue CoreVideoPlayer 一款基于 vue.js 的轻量级、优秀的视频播放器组件

大家好,我是程序视点的小二哥!今天小二哥给大家推荐一款非常优秀的视频播放组件 效果欣赏 介绍 Vue-CoreVideoPlayer 一款基于vue.js的轻量级的视频播放器插件。 采用Adobd XD进行UI设计&#xff0c;支持移动端适配,不仅功能强大&#xff0c;颜值也是超一流&#xff01; Vue-…

第一次构建一个对话机器人流程解析(二)

1. 问答机器人的组成-基于知识图谱的搜索 在教育场景下&#xff0c;若学生有关于学习内容的提问&#xff0c;或业务层面的提问&#xff0c;则要求问答机器人的回答必须精准&#xff0c;来满足业务的要求因此需要通过知识图谱来快速检索&#xff0c;所提内容的相关信息&#xf…

数字系统与进制转换

数字系统 数字逻辑是计算机科学的基础&#xff0c;它研究的是如何通过逻辑门电路&#xff08;与门、或门、非门等&#xff09;实现各种逻辑功能。数字系统则是由数字逻辑电路组成的系统&#xff0c;可以实现各种复杂的运算和控制功能。在计算机科学中&#xff0c;数字逻辑和数…

C++ 假设今天是星期日,那么过a^b天之后是星期几?

题目 假设今天是星期日&#xff0c;那么过a^b天之后是星期几&#xff1f; 【输入】 两个正整数a&#xff0c;b&#xff0c;中间用单个空格隔开。0<a≤100,0<b≤10000。 【输出】 一个字符串&#xff0c;代表过a^b天之后是星期几。 其中&#xff0c;Monday是星期一&…

自定义波形图View,LayoutInflater动态加载控件保存为本地图片

效果图: 页面布局: <?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="…

C#多线程并行计算实例

在C#中实现多线程并行计算可以通过使用 Task 和 Parallel 类来实现。这里给出两个简单的示例&#xff0c;一个是使用 Task&#xff0c;另一个是使用 Parallel.ForEach。 使用 Task 进行多线程并行计算 using System; using System.Threading.Tasks;class Program {static voi…

Kubernetes基于helm部署jenkins

Kubernetes基于helm安装jenkins jenkins支持war包、docker镜像、系统安装包、helm安装等。在Kubernetes上使用Helm安装Jenkins可以简化安装和管理Jenkins的过程。同时借助Kubernetes&#xff0c;jenkins可以实现工作节点的动态调用伸缩&#xff0c;更好的提高资源利用率。通过…

MySQL Innodb存储引擎中,当页默认的大小是16K时,页中最多存放多少行的记录?

1、题目引入 Innodb存储引擎是面向行的(row-oriented)&#xff0c;也就是说数据的存放按行进行&#xff0c;每页存放的行记录是有硬性定义的&#xff0c;当页默认的大小是16K时&#xff0c;页中最多存放多少行的记录&#xff1f; A、1600 行B、8192 行C、16383 行D、7992 行 …

基于Python协同过滤的旅游景点推荐系统,采用Django框架,MySQL数据存储,Bootstrap前端,echarts可视化实现

随着旅游业的迅速发展&#xff0c;个性化旅游推荐系统成为提升用户体验和促进旅游市场增长的重要工具。本研究旨在设计并实现一种基于Python协同过滤的旅游景点推荐系统&#xff0c;结合Django框架、MySQL数据库存储、Bootstrap前端框架以及echarts数据可视化技术&#xff0c;为…

Flask发布一个及时止损(止盈)服务(二)

生成可视化的止盈止损结果&#xff08;图片&#xff09; 妈的&#xff0c;还是得用 akshare&#xff0c;还需要指定python版本3.9以上 conda remove -n fonxsys --all conda search pythonconda create -n fonxsys python3.9 conda activate fonxsys python.exe -m pip insta…