Legacy GM Large dynamic resources loading method not blocking the game

G

Giovanni Felice F

Guest
Hi,
I would like to ask, if there is a method (included or extension) to load external resources without blocking the game pipeline. (I use sprite_add function in the code)

To be clear, I am working on a game that would preferrably use sprite sheets for character actions (composed layered as torso, hand, head etc.) changing as the character changes equipment - pretty much like
http://www.kevinbertel.com/diablo2/...1=HVY&s2=HVY&rh=JAV&lh=0&sh=TOW&&submit=Build

They are pretty large however and, while loading (switching) just one 2 MB image for another, the game freezes for about 0.5 s on older machines. I'd much rather have the old sprite (sheet) that would be used until the new one is loaded in the background while playing.

I am using GM:S 1.4.

As I understand the Async events (e.g. image_loaded) just detect when an event. which is by nature async happens and will not be of any help to me. (e.g. it will not tell GM now this image is to be loaded in some other way). Correct me if I am wrong, please.


thanks for any hints
Giovanni F
 
J

Joshua Allen

Guest
The only way to load a sprite asynchronously (that I know of) is to use buffers.
I made a few scripts to help you out:
Code:
/// sprite_save_buffer(sprite, subimg, fileName);

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;
Code:
/// sprite_load_start()
sprite_load_map = ds_map_create();
sprite_map = ds_map_create();
Code:
/// sprite_load_add(fileName, spriteName, width, height, removeback, smooth, xorig, yorig)
var file = argument0;
var spr = argument1;
var width = argument2;
var height = argument3;
var removeback = argument4;
var smooth = argument5;
var xorig = argument6;
var yorig = argument7;

//
var buff = buffer_create(width * height * 4, buffer_fixed, 1);

//
var map = ds_map_create();
ds_map_add(map, "name", spr);
ds_map_add(map, "width", width);
ds_map_add(map, "height", height);
ds_map_add(map, "removeback", removeback);
ds_map_add(map, "smooth", smooth);
ds_map_add(map, "xorig", xorig);
ds_map_add(map, "yorig", yorig);
ds_map_add(map, "buffer", buff);

//
ds_map_add_map(sprite_load_map, buffer_load_async(buff, file, 0, width * height * 4), map);
Code:
/// sprite_load_async()
var _id = ds_map_find_value(async_load, "id");

var sprLoadMap = ds_map_find_value(sprite_load_map, _id);
if (!is_undefined(sprLoadMap)) {
    var name = ds_map_find_value(sprLoadMap,"name");
    var width = ds_map_find_value(sprLoadMap,"width");
    var height = ds_map_find_value(sprLoadMap,"height");
    var removeback = ds_map_find_value(sprLoadMap,"removeback");
    var smooth = ds_map_find_value(sprLoadMap,"smooth");
    var xorig = ds_map_find_value(sprLoadMap,"xorig");
    var yorig = ds_map_find_value(sprLoadMap,"yorig");
    var buff = ds_map_find_value(sprLoadMap,"buffer");
 
    var sur = surface_create(width, height);
    buffer_set_surface(buff, sur, 0, 0, 0);
 
    var spr = sprite_create_from_surface(sur, 0, 0, width, height, removeback, smooth, xorig, yorig);
 
    surface_free(sur);
    buffer_delete(buff);
    ds_map_delete(sprite_load_map, _id);
 
    ds_map_add(sprite_map, name, spr);
}
Code:
/// sprite_get(name);

var spr = ds_map_find_value(sprite_map, argument0);
if (is_undefined(spr)) {
    return -1;
} else {
    return spr;
}

First, you need to save all your sprites with "sprite_save_buffer".
Now with your loading object put "sprite_load_start();" in its create event and in the Save/Load async event add "sprite_load_async();".

Now all that is left is to load all your sprites. Anytime after you added sprite_load_start, you can put sprite_load_add. The spriteName argument should be a unique string and anytime you want to use that sprite just call sprite_get with that spriteName.

I haven't really tested it with a lot of large sprites but I think it should work.

--Update--
After more testing, I found that I was unable to remove the hitching caused by loading a buffer with a large file (95MB). If "buffer_load_async" truly was asynchronous, I really don't know what is causing that slowdown. Also, it might be better for you to put all your sprites into texture pages each with the same dimension and then use one buffer and one surface to load them. The code I gave creates a buffer and a surface for each image you want to load, that probably isn't the smartest thing to do.

Sorry, I can't be of more help.
 
Last edited by a moderator:
G

Giovanni Felice F

Guest
Thank you for the effort :) I'll give it a shot with buffers, but if it doesnt't work out, this reload happens fortunately only in the inventory screen (or at room transition) so small text box "Please wait, I'm changing." won't disturb as much as in actual game.
 
@Joshua Allen
it's something wrong with my project,could you help me?
my project have a picture which named bck0.png(included files),and only one object with the code:
create event:
sprite_load_start();
sprite_load_add("bck0.png","sprite0",1024,1024,0,0,0,0);
bck = -1;

save/load event:
sprite_load_async();

draw event:
if bck = -1
{
bck = sprite_get("sprite0");
}
if bck
{
draw_sprite(bck,0,0,0);
}

when i run the project(i put the object in the room),this is nothing happened.
when i debug the project,i find object.bck equal 0,what's wrong?
 
Last edited:
J

Joshua Allen

Guest
@Joshua Allen
it's something wrong with my project,could you help me?
my project have a picture which named bck0.png(included files),and only one object with the code:
create event:
sprite_load_start();
sprite_load_add("bck0.png","sprite0",1024,1024,0,0,0,0);
bck = -1;

save/load event:
sprite_load_async();

draw event:
if bck = -1
{
bck = sprite_get("sprite0");
}
if bck
{
draw_sprite(bck,0,0,0);
}

when i run the project(i put the object in the room),this is nothing happened.
when i debug the project,i find object.bck equal 0,what's wrong?
try:
if (bck != -1) {
draw_sprite(bck,0,0,0);
}
 

zbox

Member
GMC Elder
Try profiling that code and see what is causing the hitch? Don't see why it shouldn't work (maybe the function that copies data to the buffer is doing it - buffer load async has always worked perfectly for me)
 
Top