Animated GUISprites
These functions can be used to create spritesheets and set animation frames to a GUISprite. Includes example and explanation below.
/**
* Create a spritesheet.
*
* @param {number} imageWidth Image width.
* @param {number} imageHeight Image height.
* @param {number} width Frame width.
* @param {number} height Frame height.
* @param {number} x Horizontal unit position (from 0.0 to 1.0).
* @param {number} y Vertical unit position (from 0.0 to 1.0).
*
* @return {table[]}
*/
function createSpritesheet(imageWidth, imageHeight, width, height, x, y) {
// Spritesheet to be returned:
local spritesheet = [];
// Rows and columns of spritesheet:
local rows = imageHeight / height;
local columns = imageWidth / width;
// Frame index counter:
local index = 0;
// Iterate through rows and columns...
for(local row = 0; row < rows; row += 1) {
for(local column = 0; column < columns; column += 1) {
spritesheet.push({
// Frame index.
index = index,
// Initial cut position (unit value).
topLeft = {
x = x * column,
y = y * row
},
// Final cut position (unit value).
bottomRight = {
x = (x * column) + x,
y = (y * row) + y
}
});
index += 1;
}
}
return spritesheet;
}
/**
* Set animation frame for one image.
*
* @param {GUISprite} image Image.
* @param {table} frame Animation frame.
*/
function setSpriteFrame(image, frame) {
image.TopLeftUV.X = frame.topLeft.x;
image.TopLeftUV.Y = frame.topLeft.y;
image.BottomRightUV.X = frame.bottomRight.x;
image.BottomRightUV.Y = frame.bottomRight.y;
}
Example
This will animate a GUISprite using this image (name it "spritesheet.png"): https://i.ibb.co/8dVGb3v/spritesheet.png
// Animated sprite. It does have a size of 256x320:
local sprite_anim = GUISprite("spritesheet.png", VectorScreen(0, 0));
sprite_anim.Size = VectorScreen(128, 128);
// Create spritesheet:
local spritesheet = createSpritesheet(256, 320, 64, 64, 0.25, 0.2);
setSpriteFrame(sprite_anim, spritesheet[0]);
// Animation control:
local sprite_frame = 0;
local sprite_delay = 0;
/**
* @event Script::ScriptProcess
*/
function Script::ScriptProcess() {
// Increase delay counter...
sprite_delay += 1;
// Change the frame and reset the delay:
if(sprite_delay >= 20) {
sprite_delay = 0;
// Set and advance frame:
setSpriteFrame(sprite_anim, spritesheet[sprite_frame]);
sprite_frame += 1;
// Loop frames:
if(sprite_frame >= spritesheet.len()) {
sprite_frame = 0;
}
}
}
How it works?
While GUISprites are typically used to display one full-sized image, it's possible to display only one part of it, too. If used alongside a spritesheet or texture atlas, this can be used for a lot of things, like animations. This is achieved by using "TopLeftUV" and "BottomRightUV". So how does that work?
Basically, these properties determine where your image starts and ends. When a GUISprite is created, the default properties are always set to display the whole image. The first position (the top-left) will always be at "0,0", and the last position (the bottom-right) will always be at "<width of image - 1>,<height of image - 1>".
Let's take the "logo.png" from Blank Server, for example:
(https://i.ibb.co/2jZHPBS/logo-sizes.png)
The image size is 500x247, so the top-left position will be at "0,0" and the bottom-right position will be at "499,246". Simple enough, right?
Well, not really. Take a look at this:
// This is the logo image from Blank Server (500x247):
sprite_logo <- GUISprite("logo.png", VectorScreen(0, 0));
// Top-left position: 0,0 (as expected).
Console.Print("Top-left position: " + sprite_logo.TopLeftUV.X + "," + sprite_logo.TopLeftUV.Y);
// Bottom-right position: 1,1 (wait... what?).
Console.Print("Bottom-right position: " + sprite_logo.BottomRightUV.X + "," + sprite_logo.BottomRightUV.Y);
What went wrong?
Turns out the "UV" properties don't use pixels, but percentages! These properties will go from 0.0 (0%) to 1.0 (100%) and can wrap or flip the image if you go lower or greater than these values. This can give you interesting effects, like a moving background.
We can also take advantage of percentages to make simple cuts. Let's say we want to display only half of the logo:
// This is the logo image from Blank Server (500x247):
sprite_logo <- GUISprite("logo.png", VectorScreen(0, 0));
sprite_logo.BottomRightUV.X = 0.5; // This will remove 50% of the right side.
// The image has been cut, but the original size of the GUISprite will
// remain the same. We need to resize it too if we don't want the result
// to appear stretched:
sprite_logo.Size.X = 250;
Remember to resize the image, too. You should see something like this:
(https://i.ibb.co/1X77ccg/logo-result.png)
Spritesheets
Now let's move onto spritesheets. Take this image, and name it "spritesheet.png":
(https://i.ibb.co/8dVGb3v/spritesheet.png)
The image size is 256x320, but every frame is 64x64. Just to get started, let's split this into rows and columns and see if we can figure something out:
(https://i.ibb.co/Lh4v91W/spritesheet-sizes.png)
We have 5 rows (20% per row) and 4 columns (25% per column). If we want to display the frame where the character is scared, we need to move the top-left position to "25%,80%" and the bottom-right position to "50%,100%".
// This is the sprite (256x320):
sprite_anim <- GUISprite("spritesheet.png", VectorScreen(0, 0));
sprite_anim.Size = VectorScreen(64, 64);
// Set the top-left position:
sprite_anim.TopLeftUV.X = 0.25;
sprite_anim.TopLeftUV.Y = 0.8;
// Set the bottom-right position:
sprite_anim.BottomRightUV.X = 0.5;
sprite_anim.BottomRightUV.Y = 1.0;
And that's how it's done.
Just wow! You have explained everything very well, and the discovery itself is wow!
I didn't even know we have such UV functions.
I remember some guys asking for such a thing back in the days, where client-side just came in.
Well, it was always here, after all.
Good job,
@DMWarrior !
Fucking beast. A job well fucking done!
Looks great!
Awesome!
A prime example of a well documented and coded snippet. Just like very few other people on this forum have done it this way.
Damn, you nailed it. Awesome!