cocos2dx 大地图分块加载的研究(初)
本文转自http://blog.csdn.net/dinko321/article/details/46739563
项目里面需要加载一个很大的地图,目测最少是4096x4096的分辨率。
先不考虑什么引擎最大支持多大的图啊,大图加载效率啊等等这些问题,光是4k x 4k的分辨率,ARGB8888,加载进去,就是64M的内存,这还只是一个背景。再来点其他七七八八的东西,轻松超过120,这个内存在某些设备上就已经很危险了。
为了实现这个目标,处理的方式大概有2种:
一、资源重用。也就是类似于tiled这种方法,把地图上面的一些图块,反复使用,通过有限的纹理,来拼接出地图。但是这种方法的确定是地图会比较死板
二、分块加载。就是把一个大图切成若干小块,每次只加载需要显示的图块。看上去很美,现在主要说这个。
先说切图,肯定是切小方块,按照二的幂的原则,一般有这么几个备选:32、64、128、256、512。
用切的图块来铺满一个屏幕,不一定能恰好填满,可能会多出一部分(少一部分就会留黑,肯定不行),从这个上面上来说,肯定是切的越小越好,因为这样就算有浪费,最多也就浪费一个图块的宽度。但是图切的太碎,会对渲染效率产生影响。从调试信息可以看到有个GLVerts,verts越多,显示效率就越低。
但是如果切太大,内存又会有影响,比如我切个512的方块,假设屏幕是960X640,那么极限情况下,最多会同时显示4块(请自行想象在田字格的中间放一个方框,方框就是屏幕),这样就达不到节约内存的目的。
一般128或者256应该就差不多了。
然后把大图切小,分别命名。这图怎么切,当然是叫美工用PS切啊,命名,手动啊。。。当然这是开玩笑,你要真这么弄,美工不把你砍死。。。作为程序员,就是要会偷懒嘛,写个程序就切了,python的。虽然我也不是很会python,只会基本语法。但是python库多啊
- import Image
- import sys
- import os.path
- from datetime import *
- import random
- import time
- IMAGE_PATH = "map.png"
- xIndex = 0
- yIndex = 0
- cropSize = 256
- xNum = 0
- yNum = 0
- im = Image.open(IMAGE_PATH) #打开图片句柄
- pSize = im.size
- xNum = pSize[0]/cropSize
- yNum = pSize[1]/cropSize
- print "size " ,xNum,' ',yNum
- for yIndex in range(yNum):
- for xIndex in range(xNum):
- print "pic : " , xIndex , "_" , yIndex
- box = (xIndex*cropSize,yIndex*cropSize,(xIndex+1)*cropSize,(yIndex+1)*cropSize) #设定裁剪区域
- region = im.crop(box) #裁剪图片,并获取句柄region
- name = "/Users/apple/Desktop/result/map%s_%s.png" % (xIndex,yNum-1-yIndex)
- region.save(name) #保存图片
- # xIndex = xIndex+1
- # yIndex = yIndex+1
- print int(time.time());
这样图就切好了,名字也起好了,而且还不用和美工撕逼或者装孙子。。。
再说铺砖,也就是把图块放在地图上的方法。
这里会有2个需要区别的东西,一个是坐标,就是offset的那个坐标。一个是index,就是图块的index。因为按照上面的图块命名,坐标是map0_0 ,map0_1这样的规则。
大概思路应该是这样的,首先,获取winSize,计算横竖2个方向需要多少图块才能铺满,向上取整。
- xTileNum = winSize.width/tileSize;
- yTileNum = winSize.height/tileSize;
- xTileNum+=1;
- yTileNum+=1;
然后,根据scrollView的offset,取得左下角那个图块的index,知道这一点应该用那一块来铺。
- Vec2 offset = view->getContentOffset();
- offset.x = fabsf(offset.x);
- offset.y = fabsf(offset.y);
- int xStartIdx = offset.x/tileSize;
- int yStartIdx = offset.y/tileSize;
然后,在横方向,和竖方向上,铺满
- for (int i = 0 ; i<xTileNum ; i++)
- {
- for (int j=0; j<yTileNum; j++)
- {
- int xIdx = xStartIdx+i;
- int yIdx = yStartIdx+j;
- char name[128];
- sprintf(name, "result/map%d_%d.png",xIdx,yIdx);
- Sprite* tile = Sprite::create(name);
- tile->ignoreAnchorPointForPosition(true);
- int posX = tileSize*xIdx;
- int posY = tileSize*yIdx;
- tile->setPosition(posX,posY);
- contentLayer->addChild(tile);
- }
- }
这样,就铺满一屏幕了。
然后来说说滚动的时候的处理方法。
- #include <algorithm>
- #include <unordered_map>
- #include <set>
- using namespace std;
- unordered_map<string, Sprite*> curTiles;
- set<string> curKeys;
- set<string> newKeys;
其实想法和简单,就是把每次加入的图块sprite,放入一个map,然后把移动过后,需要加入的图块,是全部的,不是新增的那一部分,放入一个map,然后对2个map取差集,就可以得出需要添加的部分,和需要删除的部分。
- // 统计需要显示的图块
- newKeys.clear();
- for (int i = 0 ; i<xTileNum ; i++)
- {
- for (int j=0; j<yTileNum; j++)
- {
- int xIdx = xStartIdx+i;
- int yIdx = yStartIdx+j;
- if (xIdx>15 || yIdx>15)
- {
- continue;
- }
- char name[128];
- sprintf(name, "result/map%d_%d.png",xIdx,yIdx);
- string key = string(name);
- newKeys.insert(key);
- }
- }
- set<string> results;
- //在旧集合中去掉新的集合的元素,得到该移除的部分
- set_difference(curKeys.begin(), curKeys.end(), newKeys.begin(), newKeys.end(), inserter(results, results.begin()));
- for (auto it=results.begin(); it!=results.end(); it++)
- {
- curTiles[*it]->removeFromParent();
- curTiles.erase(*it);
- }
- //在新集合中去掉旧的集合的元素,得到该添加的部分
- results.clear();
- set_difference(newKeys.begin(), newKeys.end(), curKeys.begin(), curKeys.end(), inserter(results, results.begin()));
- for (auto it=results.begin(); it!=results.end(); it++)
- {
- Sprite* tile = Sprite::create((*it).c_str());
- tile->ignoreAnchorPointForPosition(true);
- int xIdx = 0;
- int yIdx = 0;
- sscanf((*it).c_str(), "result/map%d_%d.png",&xIdx,&yIdx);
- int posX = tileSize*xIdx;
- int posY = tileSize*yIdx;
- tile->setPosition(posX,posY);
- contentLayer->addChild(tile);
- curTiles[*it] = tile;
- }
- curKeys = newKeys;
- newKeys.clear();
然后还有最重要的一步
- Director::getInstance()->getTextureCache()->removeUnusedTextures();
再说效果。代码这就写完了,然后上甄姬测试吧。
- bool Sprite::initWithFile(const std::string& filename)
- {
- ... ...
- Texture2D *texture = Director::getInstance()->getTextureCache()->addImage(filename);
- ... ...
- }
- Texture2D * TextureCache::addImage(const std::string &path)
- {
- ... ...
- image = new (std::nothrow) Image();
- ... ...
- }
从这里看,在创建新纹理的时候,用到了fileUtil,也就是说读了文件,也就是说这个地方有IO,然而IO是很容易卡的。
- -------1--------
- ------- time stamp : 1386769052
- -------2--------
- ------- time stamp : 1386769053
- -------a--------
- ------- time stamp : 1386769053
- -------b--------
- ------- time stamp : 1386769084
- -------a--------
- ------- time stamp : 1386769084
- -------b--------
- ------- time stamp : 1386769115
- -------a--------
- ------- time stamp : 1386769115
- -------b--------
- ------- time stamp : 1386769147
- -------a--------
- ------- time stamp : 1386769147
- -------b--------
- ------- time stamp : 1386769182
- cocos2d: TextureCache: removing unused texture: /private/var/mobile/Containers/Bundle/Application/C85C52CF-396B-4630-A4AE-11A412D8C060/Hello iOS.app/result/map10_3.png
- cocos2d: TextureCache: removing unused texture: /private/var/mobile/Containers/Bundle/Application/C85C52CF-396B-4630-A4AE-11A412D8C060/Hello iOS.app/result/map10_2.png
- cocos2d: TextureCache: removing unused texture: /private/var/mobile/Containers/Bundle/Application/C85C52CF-396B-4630-A4AE-11A412D8C060/Hello iOS.app/result/map10_0.png
- cocos2d: TextureCache: removing unused texture: /private/var/mobile/Containers/Bundle/Application/C85C52CF-396B-4630-A4AE-11A412D8C060/Hello iOS.app/result/map10_1.png
- -------3--------
- ------- time stamp : 1386769184
1是开始计算新图块的地方,2是开始添加新图库的地方。3是整个完成。a是创建图库sprite前,b是创建图库sprite之后。
所以我觉得这种方式暂时是不靠谱的,还是乖乖用tiled去拼,或者分层来做吧。
PS:
其实还有一种做法,类似google map和百度地图那样,在滚动的时候不加载,滚动停了之后再加载。但是人家那是APP啊,游戏这样做你看效果如何。分分钟删游戏
转载于:https://www.cnblogs.com/liuqing0328/p/4900903.html
cocos2dx 大地图分块加载的研究(初)相关推荐
- 【笔记】unity大地图分块加载
1.大地图分块加载 chunk的大小可动态调整 写工具做地块拆分 如果地块是由1个个小格子单元组成的,则可按位置进行划分保存成多个chunk预设 如果地 ...
- unity-大地图分块加载研究
title: unity-大地图分块加载研究 categories: Unity3d tags: [unity, 大地图, 分块, lightmap] date: 2018-12-12 11:22:1 ...
- android游戏大地图如何加载失败,求助:android studio第一次加载百度地图api失败
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 activity.xml xmlns:tools="http://schemas.android.com/tools" android ...
- swift 百度地图加载与百度地图电子围栏加载
最近在写百度地图电子围栏加载,研究源码,也花了一些时间. 1.百度电子围栏整体思路 1)用户创造实体,赋予监控权限 2)对该实体创造电子围栏,有服务端和本地端两种方式,先创造实体,上传到的服务器中.在 ...
- Unity 分帧加载和分块加载
#分帧加载和分块加载 在我们实际做项目的时候,往往会遇见需要创建大量数据的时候,这时如果在一帧里面大量创建数据,那我们的游戏就会发生卡顿从而降低了用户的体验. 为了解决这种情况,可以使用使用分帧加载使 ...
- 数据分块加载——BigPipe 技术【类似facebook】
一.原理 分块加载,加载完一块,就先把页面数据刷给用户,再加载下面的,直到加载完毕 二.基础需知: 三.服务端和php的相应配置 如果想实现分块加载[bigpipe技术],还需要对nginx.conf ...
- fread读取整个文件_qt如何实现大文件的加载和显示
最近研究了下如何用qt的原生控件来加载和显示大文件(>1G),分享下一些摸索经验. 下文源码: compilelife/loginsightgithub.com 文件的内存映射 在开始qt部分 ...
- 高德地图 sdk 加载 geoserver 发布的瓦片地图服务
前言 最近,碰到了需求,想要在高德 sdk 中使用在 geoserver 里面发布的瓦片地图. 还好经过一番研究,在其官方文档中发现,从新版本(v1.4.3开始)的高德 sdk 开始,已经支持加载第三 ...
- OGC标准WMTS服务概念与地图商的瓦片编号流派-web地图切片加载
还不知道地图栅格化切片等相关GIS原理的,推荐阅读<webGIS底图栅格化与实时数据合成处理原理,地图API设计,xyz加载> OGC概念 OGC全称--开放地理空间信息联盟(Open G ...
最新文章
- CodeBlocks集成Objective-C开发 Windows下学习Objective-C
- Oracle 11g sys,system 密码忘记设置解决办法
- bzoj 2406: 矩阵 ——solution
- 使用撤回流RetractStream的场景
- 关于SAP Spartacus Routing 页面上下文切换机制的实现
- 201409-5 拼图
- 再见,Springboot和SpringCloud
- 来自百度的一篇如何关闭Centos7的防火墙以及如何永久关闭防火墙的指令教学【转载】
- 为什么spring中的controller跳转出错_你的业务代码中Spring声明式事务处理正确了吗?
- win2003安装邮箱服务器,windows2003邮件服务器
- HDU 1500(经典的恶心DP)
- c mysql 数据更新_MySQL数据更新
- ai背景合成_视频素材不好找!图片也能生成视频啦,AI剪辑助力原创短视频创作...
- 怎么多台计算机网络同传,使用网刻工具进行局域网内的网络同传
- jsp汽车4S店维修管理系统
- 【历史上的今天】5 月 1 日:AMD 成立;第一个 BASIC 程序跑通;世界上第一封垃圾邮件
- 腾讯元老辞职后,回到老家每天对着200亩地发呆
- arm linux开发板自动配置IP地址
- Foobar 2000 EIKO 增强版 取消“最小化到托盘”设置
- 一行代码实现自制炫酷二维码