Page 8 of 9

Re: [Library] anim8 - An animation library - v2.3.0 released

Posted: Fri Feb 15, 2019 4:42 am
by yetneverdone
kikito wrote: Tue Jan 22, 2019 12:28 pm The "current frame" is not documented on purpose, because you should not tie your game logic to your animations.

If you must know, it is `anim.position`. Let me explain why you should not use it.

As an example, let's say that your 4-frame animation is a "shooting a gun" animation, with frame durations {0.1, 0.2, 0.3, 0.4 }. Let's say that a bullet should be created a the beginning of the third frame.

The way to properly resolve this is: when you initialize the animation, you also set an independent timer variable to 0. Every time you update the animation with dt, you also increase the timer animation with dt. Then you check if the timer is >= 0.3 seconds (which is the duration of the first and second frames put together, 0.1 + 0.2). If the condition succeeds, you create the bullet and discard the counter.

You should *not* be doing is `if anim.position == 3` on every frame and create the bullet with that if. For two reasons:
  • anim.position is going to be equal to 3 for potentially a lot of frames. You will need an extra variable to control that you have already fired the bullet. So you might as well create the timer variable instead of the `hasAlreadyFired` variable.
  • The game could have spent a lot of time on the previous frame (doing something intensive like physics or pathfinding) and the animation could have gone from frame 1 to frame 4 without going over the intermediate frames. If you only look at the animation frames you won't create a bullet, but if you use a separate timer you will.
How does one get the frame duration of a specific frame in animation? Let's say i have a 6-frame animation:

Code: Select all

	
	local grid = Anim8.newGrid(101, 360, sheet:getWidth(), sheet:getHeight())
	local obj_anim = Anim8.newAnimation(grid('1-6', 1), 0.3)
so each frame has a duration of 0.3, then with what youve explained using a timer

Code: Select all

--every time the animation is played/created
local timer = 0
--on update
timer = timer + dt
obj_anim:update(dt)

if timer >= ???? then
  --do something
end

Re: [Library] anim8 - An animation library - v2.3.0 released

Posted: Thu Feb 21, 2019 3:37 pm
by kikito
How does one get the frame duration of a specific frame in animation?
Those values are not meant to be read by the outside world, because game logic should not depend on animation frames. That info is "set" only - by design. If you need the frame duration for future use, I recommend you store it on its own variable and reuse it for both the animation and the timer:

Code: Select all

local frame_duration = 0.3
local firing_threshold = frame_duration * 2 -- fire at the start of third frame

local function create_player()
  return {
    current_animation = nil,
    firing_timer = nil,
    fire_animation = Anim8.newAnimation(grid('1-6', 1), frame_duration),
  }
end

local function start_firing(player)
  player.firing_timer = 0
  player.fire_animation:gotoFrame(1)
  player.current_animation = fire_animation
end

local function update_player(player, dt)
  if player.firing_timer then
    player.firing_timer = player.firing_timer + dt
    if player.firing_timer >= firing_threshold then
      create_bullet(player)
      player.firing_timer = nil
    end
  end
end
On the example above, the variable firing_threshold is calculated with values that are also used to set up the animation, but the animation itself is not used as an input for the firing timer. You could remove anim8 from the game completely (perhaps using a different animation system, or just static images for everything) and the game logic should still work.

Re: [Library] anim8 - An animation library - v2.3.0 released

Posted: Thu Feb 21, 2019 9:47 pm
by yetneverdone
kikito wrote: Thu Feb 21, 2019 3:37 pm
How does one get the frame duration of a specific frame in animation?
Those values are not meant to be read by the outside world, because game logic should not depend on animation frames. That info is "set" only - by design. If you need the frame duration for future use, I recommend you store it on its own variable and reuse it for both the animation and the timer:

Code: Select all

local frame_duration = 0.3
local firing_threshold = frame_duration * 2 -- fire at the start of third frame

local function create_player()
  return {
    current_animation = nil,
    firing_timer = nil,
    fire_animation = Anim8.newAnimation(grid('1-6', 1), frame_duration),
  }
end

local function start_firing(player)
  player.firing_timer = 0
  player.fire_animation:gotoFrame(1)
  player.current_animation = fire_animation
end

local function update_player(player, dt)
  if player.firing_timer then
    player.firing_timer = player.firing_timer + dt
    if player.firing_timer >= firing_threshold then
      create_bullet(player)
      player.firing_timer = nil
    end
  end
end
On the example above, the variable firing_threshold is calculated with values that are also used to set up the animation, but the animation itself is not used as an input for the firing timer. You could remove anim8 from the game completely (perhaps using a different animation system, or just static images for everything) and the game logic should still work.
Thanks for the explanation.

So to wrap up and, it is

frame_duration * (nth_frame_wanted - 1)

?

Re: [Library] anim8 - An animation library - v2.3.0 released

Posted: Fri Feb 22, 2019 9:55 am
by kikito
yetneverdone wrote: Thu Feb 21, 2019 9:47 pm frame_duration * (nth_frame_wanted - 1)

?
Yes, but don't take it as a law. That code works for the given example: an animation where all frames have the same duration. Other animations can have different duration on different frames, so the equation could be different. Furthermore, they can be set up to wrap around, or to advance at delayed/increased rates (see a previous answer on this thread), or the animation reset with gotoFrame.

Re: [Library] anim8 - An animation library - v2.3.0 released

Posted: Fri Feb 22, 2019 10:05 am
by yetneverdone
kikito wrote: Fri Feb 22, 2019 9:55 am
yetneverdone wrote: Thu Feb 21, 2019 9:47 pm frame_duration * (nth_frame_wanted - 1)

?
Yes, but don't take it as a law. That code works for the given example: an animation where all frames have the same duration. Other animations can have different duration on different frames, so the equation could be different. Furthermore, they can be set up to wrap around, or to advance at delayed/increased rates (see a previous answer on this thread), or the animation reset with gotoFrame.
Why not integrate something like

Code: Select all

obj_anim:onFrame(2, function_callback)
Only for animation with same duration for all frame? That seems necessary for an animation library, what do you think?

Re: [Library] anim8 - An animation library - v2.3.0 released

Posted: Fri Feb 22, 2019 10:27 am
by kikito
yetneverdone wrote: Fri Feb 22, 2019 10:05 am
Why not integrate something like

Code: Select all

obj_anim:onFrame(2, function_callback)
Sorry to sound like a broken record: If I'm understanding you correctly, that would push people into coupling game logic with animation logic. That goes against the design I envisioned for this library and I will not make such change.

If you wish you can fork the library and add such callback, feel free to do it in your fork. I will not take it personally.

Re: [Library] anim8 - An animation library - v2.3.0 released

Posted: Fri Feb 22, 2019 11:59 am
by NotARaptor
I just want to chip in here and say that I really like your stance on the flow of information in the animation library here, even if it seems to be unpopular with (or just strange to) some users.

I totally agree - business logic should dictate animation logic, and not the other way around. After all, the animation is just one visualisation of the game state itself - it should be entirely possible to run a simulation of the game with no graphics (so no animation), and any business logic that executes as a result of the animation state would make that impossible.

As a simple example, an explosion sprite should not despawn because it's just played its last frame, it should despawn because an explosion takes X seconds - defined in the game logic. That timing is passed on to the animation AND to the and despawn event (and to the sound effect), but the despawn (game logic) event should never be triggered from the animation state itself, no more than it would be triggered from the explosion sound effect ending.

I think that this might seem odd to people who are used to working with timeline-based tools such as Flash, where there are events assigned to "Frame X", which always seemed odd and convoluted to me on the few occasions I had to use the Flash Editor (many moons ago); it felt like setting up the entire game flow with gotos instead of proper control structures.

Anyway, I like it, but I can see why it might seem odd to some people. But imo it's the best way to do it. Rock on.

Re: [Library] anim8 - An animation library - v2.3.0 released

Posted: Fri Feb 22, 2019 12:49 pm
by yetneverdone
After reading the thread, ive learned that what kikito stands for is good. It's a good thing to learn something better everyday. Thanks for kindly explaining it

Re: [Library] anim8 - An animation library - v2.3.0 released

Posted: Fri Feb 22, 2019 12:54 pm
by grump
On the other hand, having your game still work flawlessly after your artist changed some animation makes for a good workflow with low turnaround times. I'd argue that having to change hardcoded constants in the code because some graphics has changed is not very good design in most cases and can become hell very quickly, especially in larger games. Compare that to a UI where you have to alter the code because the dimensions or tween timings of some elements have changed - that's not desirable at all.

Animation formats that allow to bind events to certain timecodes are superior in my opinion. The artist should be able to control the timing of those events, not the programmer. You can of course add this on top of an existing animation system and make timing information part of the animation asset's metadata. That's good and flexible design.

Re: [Library] anim8 - An animation library - v2.3.0 released

Posted: Fri Feb 22, 2019 3:07 pm
by NotARaptor
@grump, totally see your point, and so long as kikito doesn't mind his thread being hijacked, it makes for a great discussion. As with everything in development, there is rarely a one-size-fits-all solution that is optimal for every project, team and workflow.
You can of course add this on top of an existing animation system and make timing information part of the animation asset's metadata. That's good and flexible design.
Yes, 100% yes.
The artist should be able to control the timing of those events, not the programmer.
Ehhh.... yes and no. I can see what you mean, but let me give you a simple counterexample.

If you have some kind of... shooter game or something, with different weapons and different enemies, the damage values, rates of fire, and lots of other statistics will be worked out well in advance and thoroughly tested to balance it. If an artists adds a few frames to the reload animation of... a crossbow, for example, it would change its rate of fire, which would mess up the (hopefully) fine-tuned balance of weapons and enemies. It's easy to say "well, if the rate of fire has decreased X% then we'll increase the damage per projectile by Y% to keep the same DPS", but if some enemies have different protection values against different weapons, it obviously becomes more complicated than that.

On top of that, the level designer might have designed a specific wave of enemies to work well (appropriate challenge level) with the weapons found on the level in a certain sequence, in collab with the stats guy who balanced everything... well those few extra frames just killed the level experience, possibly making it way too easy or way too difficult.

So I 100% agree that the animation asset (and everything else) should contain metadata about timings, that metadata should be available to the other systems; in an ideal world (with the above example) the animator would change the animation, and then when the unit tests are run the changes in weapon/enemy balances would flag up as failed tests and it would be resolved in the team.
having your game still work flawlessly after your artist changed some animation makes for a good workflow with low turnaround times
Of course, but I'm not convinced that there's a way to make it still work flawlessly, as in the above example. Of course for some games it wouldn't be an issue, but in the general case I think it would.
I'd argue that having to change hardcoded constants in the code because some graphics has changed is not very good design in most cases
I also agree, I misspoke when I said "X seconds - defined in the game logic" - I meant "defined in the game system". Hardcoded constants have no place in code - code is for logic, data belongs elsewhere (config files, databases) where it can be accessed, consumed and even modified by several components, not just the code itself.
can become hell very quickly, especially in larger games
Absolutely. Complexity grows very quickly and becomes impossible to manage if not planned for. The architecture of any system - game or otherwise - needs to be specced out in advance, and one of the key points is to determine what the source of truth of the data is, and stick to it (anyone who has developed anything dealing with multiple layers of cache will know this!). There are definitely projects where the animation timeline itself is a viable source of truth, but others where it isn't; a simple example would be some cut scene with a background track - if the animator changes the timings of an important character movement, it may then not line up with the relevant part of the background music, which would make the composer justifiably irked.

Huh, that was longer than I intended to write. Oops. Anyway, the point is - there are several animation libraries which allow callbacks and syncs based on the animation system's internal state. This one doesn't and I understand why, and like the approach. It might be convoluted and for some projects or ways of working, but that's fine, there are other animation libraries :)