五、进阶处理方法
如果你只想做个小型游戏的话,以下章节可以直接越过。否则不但解决不了您的问题,可能还会增加疑惑。
通过咱们讨论完上述内容后,大家可能会觉得GMS对精灵资源的处理机制很不灵活。例如,工程中所有纹理页面不能按需加载。游戏过程中只能同步加载并创建精灵(因为同步的原因,会引起cpu中断导致游戏短暂卡顿)。本章节我们的目的就是如何避免这两个问题。
我们的目的很明确,就是如何在不影响效率的情况下尽可能占用少的内存资源。首先来说,纹理页面以bitmap的方式加载到显存中是无法避免的,这是GMS内部机制。当然这样做的目的是可以绘制出无损的2D精美画面。所以我们所能做只有按需加载纹理页面,以及如何在游戏过程中异步加载精灵资源这两方面了。
六、精灵按需加载
本章要解决的问题是纹理页面动态组合并加载。理想情况是在进入某关卡前,将本关卡的精灵资源通过included files的方式动态加载,随后通过某种算法将其所有精灵资源组整合成一张纹理页面中,这样我们就不用担心某关卡里避免出现其他关卡的精灵了。
实现方式很简单,15年时YoYoGames社区里一个叫Braffolk的朋友发布了一个Custom Sprite Framework插件。目前插件已发布在marketplace中,并且为免费。下载地址(需注册账户):
https://marketplace.yoyogames.com/assets/4543/custom-sprite-framework此插件的功能非常强大,在这里就不做详细介绍了。下面只举个简单的例子,便于大家认识与理解:
1、系统初始化。游戏执行中只需做一次。
image_system_init();
2、创建纹理页面组
image_group_create("main");
//创建页面组
image_stream_start("main",2048,2048,0);
//设置页面组大小为2048x2048,且精灵之间无间隔像素。
3、加载精灵资源
image_stream_add("main","player","player.png",subimg,xorg,yorg);
//读取精灵。其中subing为帧数,如果设置为2插件会将player.png图片横向等分切开,分成两帧存储。而xorg和yorg是精灵的中心。
image_stream_finish("main");
//读取结束并将所有精灵整合到纹理页面中。
4、存储精灵索引
img_player = image_group_find_image("main","player");
//提取纹理页面中精灵资源。
5、绘制精灵
draw_image(img_player,image_index,x,y);
//绘制精灵。
6、回收资源
image_group_clear("main");
//清除并不删除组。下次可以直接用组名加载精灵资源。
image_group_destroy("main");
//不用时可以删除组。下次需要重新创建。
七、精灵异步加载
当游戏背景不能通过tile展现时,异步加载背景就显得很重要了。再或者对于tile和个性化背景相结合的工程,我们不想在游戏开始时将整个关卡的背景资源一股脑的加载到内存里。不幸的是GMS的sprite_add函数为同步加载函数(只有在加载http资源时为异步),加载时会引擎短暂的卡顿。所以我们需要想办法来实现精灵的异步加载。
下面我直奔主题,简单介绍下目前可以解决的方法与流程:
1、首先将sprite精灵以bitmap的格式存储为bin文件,然后通过IDE放置到included files里作为外部精灵资源,以便动态加载。
2、当游戏过程中需要绘制外部精灵资源时,首先通过buffer_load_async异步读取资源到buffer里。
3、然后通过buffer_set_surface将buffer转换为surface。
4、最后再利用sprite_create_from_surface将surface转换为精灵。是否将表面转换为精灵看个人需求,也可直接绘制表面。
下面我再举个简单例子介绍下如何实现:
1)先对精灵进行二进制格式转换。
可以将这部分内容写成一个脚本scr_sprite_save(精灵索引,帧数,文件存储位置)。
var width = sprite_get_width(argument0);
var height = sprite_get_height(argument0);
var xoffset = sprite_get_xoffset(argument0);
var yoffset = sprite_get_yoffset(argument0);
var sur = surface_create(width, height);
surface_set_target(sur)
draw_sprite(argument0, argument1, -xoffset, -yoffset);
surface_reset_target()
buff = buffer_create(width * height * 4, buffer_fixed, 1);
buffer_get_surface(buff, sur, 0, 0, 0);
buffer_save(buff, argument2);
surface_free(sur);
buffer_delete(buff);
return buff;
2)异步加载精灵
surf = surface_create(1024,1024);
buff = buffer_create(4194304,buffer_fast,1);
loadid =buffer_load_async (buff,"sprite.bin",0,4194304);
3)生成精灵(异步Save/Load事件)
if ds_map_find_value(async_load, "id") == loadid
{
if ds_map_find_value(async_load, "status") == false
{
buffer_set_surface(buff,surf,0,0,0);
//经测试除WIN与PS4平台外IOS平台也可用(android未测试)
myspr = sprite_create_from_surface(surf,x,y,w,h,removeback,smooth,xorig,yorig);
}
}