ID:1886490
 
(See the best response by Kaiochao.)
Hey everyone, I'm trying to figure out how to get these animations working properly, but I'm completely stumped.


So, I've got hud bars that I'll be using in the game to show various values for the player to watch, and I'd like them to animate with easing from one position to the next.


Normally, this shouldn't be an issue at all, but I'm running into some problems...


The bars are rotated to a 45 degree angle for stylistic reasons, and look like this:




Now, the green is the bar that'll fill things in, and I have a few ways of displaying it (Three bars are shown here, each will be modified separately).

A: Use an image that's basically just the bar at the angle it's currently at.

B: Use an upright bar, then rotate it 45 degrees

C: Use a strip of pixels, stretch it to match the height, and then rotate it 45 degrees.


Here's the issues I'm running into....

A: I can't seem to figure out how to appropriately shrink the Y axis of the bar, when everythings already tilted. I've tried a few different matrix transforms (including skew) but nothing ends up looking right.

B and C: I can get it to go to the right value, only problem is that animating the process ends up with some crazy animations going on where the bar skews wildly then snaps into the position it's supposed to be in. I'm using Scale(), Translate() then Turn() for B and C (B is scaled to be smaller, while C is Scaled larger).

Not sure exactly what to do, I've been wracking my mind over it for a day or so now and I just can't get it to look right during the animation, even though the end result is fine.

Any help is appreciated!
http://www.byond.com/forum/?post=1881276

I had this same problem with my Lightning library when I tried to make line datum to create beams, eventually I resorted to placing line segments.

That workaround works well since it solves any out of view problems as well but for screen objects animate would definitely be cool.

If you want, I can send you modified "beam" to work as a bar as a screen object for your exact purpose.
All you really need to do is modify Draw() to display it elsewhere.
It's probably a solution you've already tried, but I'm just throwing it out there: have you tried just interpolating the animation manually?

It's probably not the most optimal solution, but all things considered, it should be able to get it working, I would think.

Yeah, after looking, the Interpolate() proc would be right up your alley. How about that.
@Rotem12: Sounds like the same issue, the animation works, it's just janky and the end result is correct, but doesn't match the animation. I'd very much like to see the beam code you have.

@Kats: I tried it out myself but it's not so much an issue with interpolation and more the fact that the order at which different portions of the matrix are modified during the animate() proc seem to be other than how I'd set up the bar in the first place. Maybe I'm notunderstanding what you mean, but perhaps if you could demonstrate it I'd have a better understanding. Interpolate() may have potential to fix the problem that I, and apparently Rotem12, are having.

With all of the trouble you are going through, it seems as though you'd be better off using the tried and true method of stat baring; which is to use icon_states.

I'm assuming the only reason you aren't using icon_states is because you don't feel like drawing each individual frame for each individual state. Well, the good news is you don't have to. With some icon manipulation, user input, then file saving; you can simply draw what your bar would look like full (at 100%) then generate different states as it goes down to 0%.

I've actually took the liberty of scripting something that will do just that. My suggestion, just generate all of the states and use them instead of matrixes.
mob/verb/Generate_Icon_States(f as file)
var/icon/i = icon(f,"")
var/xy = parseNumberSet(input("Please enter the coords for the bottom left hand corner of the filled area. \nExample: 4,4") as text, ",")

if(xy[1]==null)
src<<"The parser was unable to read your x and y coordinates. Please try again and read the instructions carefully."
return

var/hw = parseNumberSet(input("Please enter the enter the size of the filled area in the format of widthxheight. \nExample: 10x4") as text, "x")

if(hw[1]==null)
src<<"The parser was unable to read your size. Please try again and read the instructions carefully."
return

var/xStart = xy[1]
var/yStart = xy[2]
var/width = hw[1]
var/height = hw[2]
var/emptyColor = input("What would you like the empty color to be?") as color
var/stateNumber = width
var/fillEdge = xStart+width

while(stateNumber >= 0)
set background = 1//Let's not let this lock the game up
var/icon/i2 = icon(f,"")
i2.DrawBox(emptyColor,fillEdge,yStart,xStart+stateNumber,yStart+height)
i.Insert(i2,"[stateNumber]")
stateNumber--

i.Turn(-45)
alert("When you save your file, be sure to type .dmi after you enter the name.")
src << ftp(i)


proc/parseNumberSet(string, symbol)//Just a parser for a double value set.
var/list/returnVal[2]
var/spot = findtext(string,symbol)
if(spot)
returnVal[1] = text2num(copytext(string,1,spot))
returnVal[2] = text2num(copytext(string,spot+1,length(string)+1))
return returnVal


Incase you aren't familiar with using icon_states to display stat bars, I'll explain. In order to have the icon_state accurately represent a value, you must first get the percent of their value. For example, when displaying HP, you would take their HP and divide it by their max HP to get the percentage. Afterwards, you would then multiply the value by the highest state number (for example, if my icon_states are from 32 to 0, I would multiply 32 by the percentage), and then round to make sure you only get whole numbers.

Example which assumes a 30 is the maximum filled icon_state.
var/value = round((HP/maxHP)*30)
icon_state = "[value]"


Using icon manipulation, you can quickly create a large amount of images within moments. Because the process is CPU intensive, it is advised to also save the results so that servers which house players will never have to do the same process.
In response to Bravo1
It's in Lightning library, beam.dm

I'm thinking to make it so if z passed in Draw is a client it'll draw in client.screen.

It's made as a beam because it was requested as such but I might change it into 'bar' and add a function to change it. I basically expand the library as need arises, I put it on github to allow people to do so as well.

Look into the updated demo with beams in the library, I'll work on the library to make the required adjustments so you could use as a screen bar.
I updated Lightning library with bars and the option to draw on client.screen.

Looking backwards it might've been a little silly to call it lightning since it's more about line effects.

    var/beam/bar/b = new(start, dest, 50)
b.Draw(usr.z, color = c)

bars inheirt from beams, they're basically the same, the Draw() changes and bars can also adjust.

After you've drawn a full hp bar on their screen all you'll have to do to adjust it is
    b.Adjust(78) // 78%


beams/bars are basically just a collection of line segments, this is how you give the illusion. If you want smaller segments in beam/line constructor you can use placement parameter, it's 32 by default, smaller segments will give you better animate (but result in more objects, it can be either good or bad depending on what you use it for).

Let me know if you need anything else related to this, I actually enjoy expanding that library.
Well, turns out that I was wrong, actually. The standard interpolation doesn't come close to editing the matrices how they should. I rewrote an interpolate function that is exactly what you're looking wanting to do.

(Thank you, Kaiochao for catching my egregious interpretation of matrices and removing the clutter.)
proc/interpolateMatrix(matrix/start, matrix/end, scale)
return (end-start)*scale + start

scale, like the t variable from the original Interpolate() function, does the exact same thing. A value of 0 means that nothing will change and the start matrix will be returned and a value of 1 means that it's transitioned completely and will return the end matrix. 0.5 will give you the value exactly between the two and this time it does it correctly.


This is what a 5 second animation looks like and here is the code I used to animate it:
obj
icon = 'i.dmi'
screen_loc = "3,3"
var/matrix/m = new
proc
updateMatrix(matrix/m) transform = m
outline
icon_state = "outline"
New()
..()
m.Turn(45)
m.Translate(48,0)
updateMatrix(m)
fill
icon_state = "fill"
New()
..()
m.Turn(45)
m.Translate(48,0)
updateMatrix(m)
proc
setScale(seconds)
seconds *= FPS
var/matrix/M = new
var/matrix/z = new
M.Scale(1,0)
M.Turn(45)
M.Translate(3,-45)
for(var/frame=1,frame<=seconds,frame++)
z = interpolateMatrix(m,M,(frame/seconds))
updateMatrix(z)
sleep(world.tick_lag)


Basically the only thing I had to determine was what the end matrix should look like, which is matrix M in the code. A bit of simple mathematics nailed that one down, though, but you can probably play with the Translate() to get it where you need it. Either way works.

Hope this helps.
Best response
The issue here is that animate() interpolates matrices in such a way that the desired effect is impossible, not that it can't be done through a loop. Lummox has mentioned that matrix interpolation could be more flexible in the future by being able to specify which method to use. It's just that doing it with a loop misses the point of animate(), which is to move the processing from the server to the clients and remove network lag from the animation.

And this should in theory work just as well:
proc/interpolate_matrix(matrix/start, matrix/end, scale)
return (end-start)*scale + start

These arithmetic operators are defined for matrices, after all.
In response to Kaiochao
Well, I guess that's true. Idk why I wrote out each variable... God, I'm such a ditz. But thank you, yes, it works exactly the same.

I personally think that just using basically a gradient between two matrices like in my example works great. I'm trying all sorts of animations with it and really aren't finding any transitions that look bad.

The animate looks great with other stuff, but with matrices, I think a more direct approach is a better option.
Nix that last statement. My version of interpolation can return some weird behavior when adjusting the Turn() of a matrix. It does this weird ducking when it's working. Well, I guess it's an option for when animate() and interpolate() are misbehaving. Not really a great catch-all.
Thanks a bunch everyone! I think Kats' solution probably fits best but Rotem12 definitely gets props for adding even more functionality to their library.

Kats, I love you. All of you keep being magnificent.

I'll use this until the matrix interpolation gains some flexibility, as LummoxJR stated would likely be around 509, which is fine by me, I only need something working in the meantime rather than something super ultra efficient.

Thanks!
Yeah, linear matrix interpolation would be the ideal solution for this, but at the moment animate() doesn't support it. I'll consider that for 509, although for 508 it won't be feasible.

My ideal for a matrix interpolation would be to be able to specify formulas for each and every value in the matrix if you wanted to tweak it manually, but dang is that impractical.
In response to Lummox JR
That seems a bit overkill, yes. Linear interpolation alone sounds pretty nice.

Should someone make a Feature request for it or is this already on the list?
In response to Lummox JR
It would be horribly impractical to try to include all possible methods of distorting a matrix. It's some ridiculous number over 800,000, but having a list of useful distortions might be interesting.

I was actually thinking about going through and finding more useful ones and posting them somewhere else. It may take a bit, though...
I don't think there is a feature request for linear matrix interpolation, so that'd be worth posting.
In response to Kats
Kats wrote:
It would be horribly impractical to try to include all possible methods of distorting a matrix. It's some ridiculous number over 800,000, but having a list of useful distortions might be interesting.

I was actually thinking about going through and finding more useful ones and posting them somewhere else.

If you did that I'd love to see the result.

Though I'm also interested in which methods of interpolation would be most useful. The current default is, I think, appropriate as the default, and linear interpolation makes sense for certain contexts. If there are others that might be relevant, I'd like to know about them.

One thing it kinda bothers me that animation can't do with transforms is spiral patterns, which would at least require multiple matrices to multiply together.