Dynamic sprite image troubles

i am trying to get a sprite to load in an image as its texture. these image(s) are not able to be built into the game, as they are loose user-made files.

i’ve managed to set the texture of a sprite, however, the result is not the actual image, but the bottom right 1/4th of the image flipped upside down and at double size to fit the actual size of the image.

here is the code that sets the texture:

function on_message(self, message_id, message, sender)

local _=images[message.img]

		local buf =buffer.create(_.width*_.height , {{name=hash("rgba"), type=buffer.VALUE_TYPE_UINT8, count=4 }} )

		local bs= buffer.get_stream(buf, hash"rgba")
		for i=1,#_.buffer do
			bs[i]=_.buffer:sub(i,i):byte() or 0
		end

	local head={ width=_.width, height=_.height, type=resource.TEXTURE_TYPE_2D, format=resource.TEXTURE_FORMAT_RGBA }
	resource.set_texture(go.get("#sprite", "texture0"), head, buf)
end

here is the image i am using:
Untitled3
versus how it actually appers in-game:
Screenshot from 2022-08-22 10-16-06

i’m not really sure what i’m doing wrong here, and the end result is even more confusing to me. the images being loaded in are in a .png format, which the engine can load in (the image table values are the result of an image.load() on the image file). the sprites are given an initial testing texture that is a 64x64 image (stored in an atlas with other unused image sizes), the same size as the image i am trying to load in.

Rather than trying to load the image yourself from a string, I would use Sergey’s image loader native extension. Besides a slight issue with flipped images if you happen to also be dynamically loading images with Defold’s gui functions at the same time, it’s always worked perfectly for me. It should also be 1000+ times faster than what you are doing.

[Edit:] Tested your image with the extension and it loads just fine onto a sprite.

3 Likes

turns out the issue is caused by loading an image into a sprite with an atlas with more than 1 image on it, which applies to both the library and above code (though the upside down thing is caused by image.load being top to bottom, but the buffer being bottom to top)

1 Like

Ahh, yup, that would do it. Sorry, I missed that detail in your post. Glad you found it.

though i might just have to stick with a hack-y fix by using a GUI layer instead of sprites, as gui components can have variable size whereas sprites don’t (at least from my testing) (though layering is an issue)

Sprite “size” is read-only (and doesn’t seem to update when you change the texture), but you can set the scale: API reference (sprite)

-- For example:
go.set("go1#sprite", "scale", vmath.vector3(0.5, 0.5, 0.5))

And even if that were not available (or if you also need to translate or rotate the sprite), you could just put each sprite on a game object and move, rotate, and scale that.

the issue comes when you try to make something with a different size than the preset 64x64, as it will cut off or be stretched to fit the built in size

Hmm, it’s actually getting cut off, not just stretched? Do you have any Margin, Inner Padding, or Extrude Borders set on your atlas? They should all be set to 0 if you are loading your own texture into it (since they will cause the sprite to only use part of the atlas).

But yes, the sprite will normally stay the same size, regardless of what texture you dynamically load into its atlas. But you already get the new width & height when you load the image, so as long as you know the original texture size, you should be able to scale the sprite to correctly match the new image.

i mean if you try to load an image that is too big or too small for the atlas, it will stretch it to fit if too small, or if too large, cut off the extra.
this is an issue if you want a sprite that is, say 128x64, as you would have to compress the image down to fit, which visually leads to poor results compared to having that be the native size

Hmm. Do you have an example project that you can share? Because I’m not getting that at all. I am testing with an atlas with a single 1x1px image in it, so the size of the atlas is only 1x1. At runtime I can load an image of any size into it, and it displays clearly with no cropping or compression.

not sure if it’s a customary way to share a project, but here’s a link to a repository with a 7z in it

That works. You also should be able to upload a zip (maybe not .7z) directly to the forum, and the “.internal” and “build” folders are just things generated by Defold, so you can exclude those and the zip will be much smaller.

Ah, I see what’s happening now. The problem happens when your original texture doesn’t fill the atlas.

When you dynamically load a texture into an atlas, you are replacing the entire atlas texture, not just the texture for one sprite. But the sprite component doesn’t know about that, it just knows about its own piece of the atlas, which is set at build-time (and is done with UV coordinates, so it’s percentage-based, not pixels).

So if you want to load in a texture of a different size, you need the sprite to fill the entire atlas. Since Defold makes atlases stick to power-of-2 sizes, that means your original texture needs to be a power-of-2 size (64, 128, 256, etc.).

2 Likes

ah, ok
thank you for that bit of info

2 Likes