Dynamic Sprite Sheet Files

Hi,

I’ve been looking though the forums and docs trying to figure out a way to do dynamic content scaling for my sprite sheets. Here’s where I’m at:

I have content scaling working using display.newImageRect() for regular images and an @HD suffix set up in the config. This works fine. The problem is since I can’t get the sprites to autoscale they look blurry on high res devices compared to the rest of the images.

I thought of using a boolean to keep track of whether the app is running in an HD mode and then loading the sprite sheet file based on that but then the sprite appears too large (because it’s not being dynamically scaled.)

What is the recommended approach to handling multiple resolutions for sprite sheets?

Thanks, [import]uid: 92392 topic_id: 19979 reply_id: 319979[/import]

@ngotch, if you use Flash to generate your animation, then you can use Spriteloq to export SWF to spritesheet, and great news is, Spriteloq supports dynamic content scaling. From a single SWF file, it can spit out higher resolution spritesheet for you. I was sooooo happy when Spriteloq released this new feature.

http://developer.anscamobile.com/forums/spriteloq-flash-corona®-exporter

http://www.loqheart.com/spriteloq/

Naomi (just a happy customer & user of Spriteloq) [import]uid: 67217 topic_id: 19979 reply_id: 77792[/import]

Thanks Naomi, but how do you tie the dynamic sprite sheets generated from Spriteloq into Corona? I already have my sprite sheet files at both a LD and HD resolution, I’m just not sure how to load it into Corona and make it display properly.

For example, say I have a sprite sheet called “foo.png” that is 20x10, containing two 10x10 images. I also have a “foo@HD.png” file at 40x20 res with two 20x20 images. I can tell Corona to load the HD file with sprite.newSpriteSheet("foo@HD.png", 20, 20) but the problem is the image is now too large compared to the rest of the images on screen.

If I then scale it by 50%, will that show correctly on the HD devices or will it just display a scaled-up version of the scaled-down original image? Does this make sense? [import]uid: 92392 topic_id: 19979 reply_id: 77805[/import]

Hey, @ngotch, I’m sorry I won’t be able to help you (because I don’t have the answer to your question). Spriteloq automatically spits out pairing lua files with spritesheets for dynamic resolution, and Corona compiles both spritesheets and lua files. Spriteloq API tells Corona what to do with these files, and I don’t worry about it.

Naomi [import]uid: 67217 topic_id: 19979 reply_id: 77857[/import]

Ah, ok. Actually this might be helpful to me; I’m going to try Spriteloq and see what the lua files it generates look like and maybe that will give me an idea how to solve this problem.

Thanks! [import]uid: 92392 topic_id: 19979 reply_id: 77860[/import]

@ ngotch , I hope it will work out for you.

Naomi [import]uid: 67217 topic_id: 19979 reply_id: 77945[/import]

@Naomi Thanks for the praise!

@ngotch Here’s info on how to use Spritesheet Scales in Spriteloq to handle dynamic spritesheet resolution http://developer.anscamobile.com/forum/2011/10/14/spriteloq-v130-out

Check out the loq_sprite.lua file from the api and you can see the code with regards to how spritesheets are loaded.
[import]uid: 27183 topic_id: 19979 reply_id: 77977[/import]

Ok, that did it, I figured out enough to get this working for me. It’s not the prettiest thing in the world, requiring a special call after every sprite initialization to handle the scaling, but it is working.

For who might also be trying to do a similar thing, I basically do the following:

First I should mention that in my case all HD images are exactly double resolution so this process would require tweaking if yours are different. Also, and this is important, this code requires that every sprite initialized with loadSpriteSheet() below MUST have both a regular (foo.png) file and an HD equivalent ([text]foo@HD.png[/text].)

I created these functions:

local isHd = false  
function determineHd()  
 if display.contentScaleX \<= 0.5 then isHd = true end  
end  
  
function loadSpriteSheet(filename, unitWidth, unitHeight)  
 local newSpriteSheet  
 if not isHd then  
 newSpriteSheet = sprite.newSpriteSheet(filename, unitWidth, unitHeight)  
 else  
 filename = string.sub(filename, 1, string.find(filename, "%.")-1).."@HD.png"  
 newSpriteSheet = sprite.newSpriteSheet(filename, unitWidth\*2, unitHeight\*2)  
 end  
 return newSpriteSheet  
end  
  
function scaleFactor(value)  
 if isHd then return value \* 0.5 else return value end  
end  

Once that’s in place every sprite I want to use HD is loaded by calling loadSpriteSheet() immediately followed by a line setting the scaling for the sprite (something like: mySprite.x = scaleFactor(1); mySprite.y = scaleFactor(1). Then the only other thing to remember is if you do any scaling of sprites in code you wrap them in scaleFactor() calls.

This isn’t an ideal solution but it does work. If anyone has suggestions for improvement, I’d love to hear them.

I really hope Corona adds a better way to do this in the future [import]uid: 92392 topic_id: 19979 reply_id: 78372[/import]

For our project i wrote a function that generates a sprite sheet instance by checking the set imageSuffix inside the config.lua.

Not the most sophisticated way yet (since i did not spend too much time on it), but maybe it will help you guys.

It is (so far) not a very dynamic code. So sadly you need to fullfill some requirements:

  • the spritesheet need to have a 1px spacing between the frame pictures (but you can edit the function to conform your project)
  • the spritesheet must be in the png format for every set resolution (config.lua) since i do not check wether the file exists (should be easy to implement too, actually ;-))
    first the string split file (save as str.lua)
module(..., package.seeall)  
  
split = function(str, pat)  
 local t = {}  
 local fpat = "(.-)" .. pat  
 local last\_end = 1  
 local s, e, cap = str:find(fpat, 1)  
 while s do  
 if s ~= 1 or cap ~= "" then  
 table.insert(t,cap)  
 end  
 last\_end = e+1  
 s, e, cap = str:find(fpat, last\_end)  
 end  
 if last\_end \<= #str then  
 cap = str:sub(last\_end)  
 table.insert(t,cap)  
 end  
 return t   
end  

and here is the function:

require("config")  
require "sprite"  
str = require("str")  
  
function create\_spritesheet(\_file, \_w, \_h, \_frames, \_duration)  
 -- split to append suffix  
 local file = str.split(\_file,".png")  
  
 local scalex = display.contentScaleX  
 local scaley = display.contentScaleY  
 local upscalex = 1/scalex  
 local upscaley = 1/scaley  
  
 -- searching for the right scale factor and suffix  
 local chosen\_scale = 1  
 local chosen\_suffix = ""  
 for k,v in pairs(application.content.imageSuffix) do  
 if math.abs(upscalex-v) \< math.abs(upscalex-chosen\_scale) then  
 chosen\_scale = v  
 chosen\_suffix = k  
 end  
 end  
  
 local chosen\_w = ((\_w-1)\*chosen\_scale)+1  
 local chosen\_h = ((\_h-1)\*chosen\_scale)+1  
 local new\_file = file[1]..chosen\_suffix..".png"  
  
 local sheet = sprite.newSpriteSheet( new\_file, chosen\_w, chosen\_h )  
 local spriteSet = sprite.newSpriteSet(sheet, 1, \_frames)  
 sprite.add( spriteSet, "animation", 1, \_frames, \_duration, 0 )  
 local instance = sprite.newSprite( spriteSet )  
  
 -- down or upscale the sprite  
 instance:scale(1/chosen\_scale,1/chosen\_scale)  
 instance:prepare("animation")  
  
 return instance  
end  

example how to call the function

 local my\_sprite = create\_spritesheet("images/spaceship.png", 40, 40, 15, 1000)  
 my\_sprite:play()  

hf
Robin
[import]uid: 102950 topic_id: 19979 reply_id: 78720[/import]

I’ve implement the code @GbCStudios posted. It seems to work except that my sprite sheets are scaling opposite of how I need them to work (they display at half the size they need to be). The reason is that the config.lua created by the Kwik plug-in defines the scaling in the opposite way most configs are set up (the @2 suffix refers to the non-retina displays). I have so many pages with animations using sprite sheets that it would very impractical to recode each page if I changed the config.lua, so I’m trying to figure out what to change about @GbCStudios code to make this work.

My config looks like this:

[lua]local kScale = “zoomStretch”
application =
{
content =
{
width = 1536,
height = 2048,
fps = 30,
scale = kScale,
imageSuffix = {
["@2"] = .5,
}
},
} [/lua]

The closest fix I’ve found is to change line 18 of @GbCStudio’s code to:
[lua]if math.abs(upscalex-v) > math.abs(upscalex-chosen_scale) then[/lua]

All I did was change the < to >. That works for my iPad 2 but crashes the app on iPad 1. Any ideas on how to fix this would be much appreciated. Thanks! [import]uid: 97058 topic_id: 19979 reply_id: 121352[/import]

I’ve implement the code @GbCStudios posted. It seems to work except that my sprite sheets are scaling opposite of how I need them to work (they display at half the size they need to be). The reason is that the config.lua created by the Kwik plug-in defines the scaling in the opposite way most configs are set up (the @2 suffix refers to the non-retina displays). I have so many pages with animations using sprite sheets that it would very impractical to recode each page if I changed the config.lua, so I’m trying to figure out what to change about @GbCStudios code to make this work.

My config looks like this:

[lua]local kScale = “zoomStretch”
application =
{
content =
{
width = 1536,
height = 2048,
fps = 30,
scale = kScale,
imageSuffix = {
["@2"] = .5,
}
},
} [/lua]

The closest fix I’ve found is to change line 18 of @GbCStudio’s code to:
[lua]if math.abs(upscalex-v) > math.abs(upscalex-chosen_scale) then[/lua]

All I did was change the < to >. That works for my iPad 2 but crashes the app on iPad 1. Any ideas on how to fix this would be much appreciated. Thanks! [import]uid: 97058 topic_id: 19979 reply_id: 121352[/import]