Game Maker question: Tweening
Hi guys :)
If you remember my little dwarf guy (attached), I'm now going to try and animate his bits instead of as a spritesheet, so that that I can build the base animation, and swap out sprites to give him different gear...
So I started looking up tweening in GMS... And found... nothing. Well, nothing that wasn't ancient and/or broken.
So I started writing my own code to stick to my individual objects, one to initialise a tween, one to step through and check if there's a tween to be run, and over time tween the object towards the destination, and another one to call the first one multiple times so that a loop can be achieved... etc.
BUT WAIT! In the spirit of not reinventing the wheel, especially since I'm a noob ass coder, can anyone point me to methods/libraries/code that's already been done for tweening and animating in GMS?
If you remember my little dwarf guy (attached), I'm now going to try and animate his bits instead of as a spritesheet, so that that I can build the base animation, and swap out sprites to give him different gear...
So I started looking up tweening in GMS... And found... nothing. Well, nothing that wasn't ancient and/or broken.
So I started writing my own code to stick to my individual objects, one to initialise a tween, one to step through and check if there's a tween to be run, and over time tween the object towards the destination, and another one to call the first one multiple times so that a loop can be achieved... etc.
BUT WAIT! In the spirit of not reinventing the wheel, especially since I'm a noob ass coder, can anyone point me to methods/libraries/code that's already been done for tweening and animating in GMS?


klepto_01.gif
128 x 128 - 18K
Comments
The problem is that for me, the game I'm making will have many helmets, many weapons, many amulet things, etc. So, I need each part to be dynamically replacable within Game Maker, or else I'd have to make a trillion sprites based on possible configurations @_@
So...... I'm looking for a means to tween within game maker, instead of a means to create sprite sheets.
Make sense?
It seems like what you want is a series of relative-to-parent offsets for "child" sprites per frame of animation of a parent object. That wouldn't be too hard to set up manually, basically you want an x,y co-ordinate per attachment point per frame, which could be done a variety of ways. Then you need a list of sprites to put at those offsets as the animated object moved around and went through frames.
A big part of implementing this right is going to be parenting and inheritance in GM. Basically you don't want to be copy-pasting code into each object, what if that code needs to be changed later on? It's a lot easier and simpler to set up one parent object called TweenedAnimatedObject and then just having your PlayerCharacter inherit from TweenedAnimatedObject (by setting that as its parent) and going from there. You'd probably also need to build a TweenAnimatedSprite that your helmets and axes and shit would inherit from too.
So, the fast code way:
Sets of arrays, tweenOffsetX[anchor point, frame] which stores the x location and tweenOffsetY[anchor point, frame] which stores the y location relative to the main sprite's origin to display the tweened sprite at. Another array for the tweened sprites themselves: tweenedSprites[anchor point] that returns either the sprite_index of the thing to draw (drawing would be handled by the TweenedAnimatedObject in that case), or a pointer to the object itself, if you want the object to be something you can manipulate and do stuff like change colour on, etc. If you want to change the sprite for a helmet, you'd just change what tweenedSprites[whatever the helmet anchor point was] pointed to. Easy!
This isn't super friendly to the animator, because you'd have to sit there and populate the tween offset arrays by hand in the PlayerCharacter's creation event. So any changes to the animation mean sitting there comparing numbers and pixel locations, which would be annoying. Plus you'd have to do this for each thing you wanted to animate extra as well. But it'd work!
The easy animatable way, but slow:
Instead of storing your offsets per frame in hand-populated arrays, you could store them in the sprite itself! Basically, pick a crazy strange pixel colour for each anchor point and then just smack that on the main animation sprite where you want the tweened thing to appear. So each time you draw the main sprite, you'd have to step through it pixel by pixel, checking the colour of each against your list of pixel colours per tweened sprite. So you'd only have the one array: tweenedSprites[anchor pixel colour] - which then draws the same way as above (personally I prefer pointing to objects because it's then really easy to set up offsets for the tweened sprites without having to manage more arrays, so that you could just have the "center" point for your helmet and then different helmet sprites could be different sizes and just set up via their sprite origins).
This has the wonderful benefit of allowing you to change your animations super easilty without going mad and staring at numbers. Going through every pixel of your main sprite every frame is slow though, so you might not want to have a million TweenedAnimatedObjects in your room at the same time ;)
The easy AND fast way:
Step through every pixel of every frame of your main sprite at object creation time, storing the offsets of your coloured anchor point pixels in offset arrays, much like the fast way. Then you're not looking up pixel colours each frame. This is a very easy thing to add on top of the easy animatable way above, so I'd go with easy animatable and then do this if your game got slow :)
Hope that helps. Definitely an interesting toy problem.
The third method looks good!!! I can barely understand it (haven't learned how to read graphics pixel for pixel from a sprite yet) but I think I can get to it if I give it a bash.
Also, what I wanted to achieve in the end was to tween with pixels bigger than the pixels of the art - so for example my art has been made at 4x4 pixels, but I wanted to tween them so they don't jump 4x4 per movement - so that means I'd either have to manually fill in the inbetweens, or create some kind of smoothing tweening method.
But yeah I think I can totally add that to the methods you described :) AWESOME :D Will report back when/if I crack it/hit a brick wall :)
Or, if you're wanting to run slow frames, you could store the anchor position as well as its previous position and then just interpolate between those for the current frame fraction you're drawing... That probably sounds like greek, sorry ;)
So I decided to manually upscale in PS first.
I could make the thing run through the image and pick the top left (first pixel it runs across) of colour x, then use that as anchor point per part.
Apparently draw_getpixel is super slow (according to the help file), but I'd only have to run it once, I think. Gonna have to figure out this array and ds_list type stuff, as well as saving variables to file, etc. Will give it a shot :)
Then the animation will interpolate between the keyframes with linear smoothing. That shouldn't be hard :)
But I don't wanna touch that tween stuff with a ten-foot barge pole. I did *something* like it once or twice before, but the methods I used were always, well, a LOT of work (that, and I realised that I was thoroughly over doing all this animation stuff anyway, because AAARGH art).
I, uhhh ... don't quite know where I'm going with this, sorry. I'm pretty sure this is just a "feel ya, bro" post more than anything actually helpful (though I can at least confirm that drawpixel stuff IS damn slow, but probably quite fine for the frequencies you'll be operating at).
Hey, you wanna know how to do a hex grid in GM? That's all I've got. Found out a couple of nights ago and I was stoked.
I'm currently banging my head against this code. It looks right, and I can say with experience that it runs DAMN slow (I've tried to run it for 1 pixel over 10 frames and it takes like 4-5 seconds @_@
Once it's figured out I'll store the array in a file and read it out of there, or at least try to.
CODE EFFING CODE EFFFFF :P
I've been banging my head against this for the better part of today, and I've reached what I think is an impasse...
Can anyone please throw an eye over this and tell me why it's not giving me values? @_@
I've got anchor_info_step on the 10 frame animation that has the coloured dots.
Then I have code that reads it back and moves the backpack around according to x and y of the array. I know it works cos when comment the above out and manually populate global.twee_backpack_x and y, it does move about.
Problem is when I run this, the code runs, the screen is however black, and when it gets through it to room0 (where the game happens), it immediately throws an error and tells me it can't find global.tween_backpack_y[i]
Error: Are there special rules for using draw_getpixel I'm not understanding? Must it be used a draw event? If so how the heck do I do that... Just call the same code from a draw event?
Do I need to manually let the sprite actually run so that it updates the screen so it works? Cos right now it's not showing the sprite on screen, and I read that draw_getpixel gets the value from the screen.....
The sprite sheet is attached, 128x128 - 10 frames....
HEADACHE @_@ And thanks guys (esp @dislekcia) for helping thus far!
Also I note that you're never going through other frames, only the first one, and exiting straight to room0 after it's done that, so global.tween_backpack_x[1] (because you start i at 1, not 0) is the only array slot that will have data in it when you're running that drawing loop.
Finally, yes you're going to have to draw your sprite to the screen so that draw_getpixel has something to reference. You should be able to draw ok outside the draw event, it'll just never appear because the draw event clears the screen when it starts going through all the objects in your game. But if you do a draw_sprite call and then do the getpixel stuff, it should have colour info on the "screen" memory space for you. Just make sure that your sprite is actually appearing in the right place and not oddly offset or anything. Personally, I'd create a surface (think of surfaces as intermediary textures or "screens" that you can then either draw or do stuff to after the fact) draw the sprite there and use surface_getpixel instead, just to be on the safe side.
Good to know I'm not derping on those things though, then.
Gonna try the draw_sprite call, and if I haven't lost my mind I'll try learn about surfaces. Apparently it's very useful (google finds me nice introduction but never really lets me take them home)
THANKS for the clue! :D
Researching surfaces...
I'm going with manually putting values in @_@ It would have been done by now if I did @_@
Thanks for the assist, I'm not cut out to be a coder @_@
Guess what I saw, 4 lines down, under "Fixed bugs"
I use this really easy one to do a ease out:
x += (destination_x - x)/2
However I can't find a similar simple way to do an ease in... Googled like crazy but I don't understand all that quadratic and whatever stuff. Is there a way to do an ease in simply?
The formula goes like this : y = 1-sqrt(1-t*t)
For EasingIn : 0 <= t <=1
For EasingOut : -1 <= t <= 0
Is that of any help?
y = 1-sqrt(1-t*t)
I assume t = time, but where's destination? Could you ease in/out without knowing the destination?
And I don't understand the greater and less than thing @_@
The whole 0 <= time <= 1 thing means that time is between 0 and 1, and can be either 0 or 1 (we call that inclusive).
So basically, you're doing the following: Calculating a percentage of how far along the path of motion you should be between the start and the end point. That is the same for x, y, z or however many dimensions you happen to be working in... Then you start and the start position and add the distance between the start and end positions, multiplied by the fraction you want to be at now.
It does have some issues, compared to the function you posted above: You have to know how long you want the motion to take and how long since you started the motion; On the flipside though, your formula above will always take the same amount of time to get to the point where the sqrt doesn't make any difference to the perceived distance traveled, so it'll move super fast from one side of the screen to the other and super slow from something 5px away.
Of course, none of this helps you decide how to add easing in and out. For that, you have to split the motion into two distinct steps: start, end and halfway. Calculating halfway is easy: halfwayX = (startX + endX) / 2
I'll give it a shot... When my brain has sufficiently sponged up enough to.
So the halving it all the time method that I had was the lazyman's easing, and that format won't be able to work for anything besides the half-life way of easing? I'm guessing that's so :P