Secondary animations for triggered objects: Experimental version available

Started by namida, May 02, 2019, 09:24:27 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

namida

https://www.youtube.com/watch?v=X4fwdCxnmOc

EDIT: Better video: https://www.youtube.com/watch?v=JVNiNPHUSwM

EDIT: Most up-to-date video: https://youtu.be/wutmgNuo-90

A long overdue feature. I just finished implementing it.

This allows objects like traps, teleporters, locked exits to have a constantly-animating component alongside the triggered animation. The constant component can either stop animating while the object is triggered, or continue regardless - this is a per-object setting. The animation always stops, in the case of a trap, if the trap is disarmed.

Uses for this include:
- Making traps stand out more
- Identifying disarmed traps without relying on clear physics mode
- Combining locked exits and exit decoration flames / lights into a single object
- Probably various artistic uses

The implementation is functionally 100% backwards-compatible. In terms of visuals, it may have oddities on older NL versions but will still be recognizable (unless the object's dual-animation design is really complex, perhaps). If an object with a secondary animation - even one that appears outside the object's original frame borders - is used in an older NL version that doesn't support secondary animations, the secondary animation simply isn't displayed. The primary animation still works fine, and the object will not be repositioned.




The attached experimental version will allow you to see this feature for yourself, as well as get an idea of how it's implemented for your own styles' usage. This experimental also contains several of the other recent fixes / changes, including the Shimmier skill.

To use this experimental build, first, set up a normal copy of (stable) NeoLemmix. Then, extract the contents of this ZIP over the top of it, overwriting any files if prompted. This includes some levels (under the "Single Levels" pack) which demonstrate these new animations.

It includes such animations for traps and locked exits in all official styles (including Sega), as well as all of my styles.

For those preparing their own styles - please see this post for information. Secondary animations are now supported with all visible-in-game object types, but some of the trigger conditions are only supported on certain animation types. Also note that masks are now replaced with secondary animations (which yes, can have the mask-like recoloring property); see default:owa_#### and default:pickup for examples.

Secondaries do not have to be the same size or have the same number of frames as the primary animation, and the primary can have a horizontal strip while the secondary has a vertical one (or vice versa).

As usual, no guarantees that any features in this experimental will remain as-is. I don't see the need for any further changes, though it's not exclusively up to me to make the final call on this. If you like the current state of this feature, make sure to express this, so that the development team as a whole know it's desired.

There is an experimental editor included as well. Please note - it's completely normal for this editor to take a long time to load the first time it runs, if NeoLemmix is also present - it's getting NeoLemmix to pre-render the combined graphic of any objects that have secondary animations.

If you add / remove / modify an object with secondary animations (or with recoloring on the primary animation), the editor does not automatically detect this and request a re-render of the graphic. The easiest way to get a re-render is to exit the editor, delete the "render" folder inside the "editor" folder, then run the editor - this will cause it to request re-rendering of all pieces it needs it for. Alternatively, for advanced users, you can use the command line arguments: "NeoLemmix.exe render -editor <style name> <style name> ...". These will make NeoLemmix re-render all objects in the specified style(s).

Remember, as is often the case: With great power, comes great responsibility. Use this feature in ways that produce neat artistic effects. Use this feature in ways that help clarify exactly what a trap or other object is or isn't doing. Don't use this feature in ways that are intended to be deceptive.
My projects
2D Lemmings: NeoLemmix (engine) | Lemmings Plus Series (level packs) | Doomsday Lemmings (level pack)
3D Lemmings: Loap (engine) | L3DEdit (level / graphics editor) | L3DUtils (replay / etc utility) | Lemmings Plus 3D (level pack)
Non-Lemmings: Commander Keen: Galaxy Reimagined (a Commander Keen fangame)

Crane

The trouble with the example video shown is that it looks like the flashing light is just a second object behind the bear trap.  If it, say, turns off when the trap is disarmed, then it's a little more convincing.

One thing I wondered if was possible is to have a fire trap that only animates if a lemming is being fried by it.  For example, an innocuous nozzle (no animation, or extremely basic) that suddenly emits a menacing blue and green gas flame (think a gas cooker) when a lemming walks on it, without switching off until there are no more lemmings.

namida

QuoteThe trouble with the example video shown is that it looks like the flashing light is just a second object behind the bear trap.  If it, say, turns off when the trap is disarmed, then it's a little more convincing.

It does. See the second video I've added. :)

Also, look closely at the animation when the trap is in use vs when it's idle - it doesn't blink green when the trap is busy. Of course, for some objects / animations it makes more sense if the animation does continue - so see how the boulder trap works in the new video. In both cases, the animation stops if the trap is disarmed.
My projects
2D Lemmings: NeoLemmix (engine) | Lemmings Plus Series (level packs) | Doomsday Lemmings (level pack)
3D Lemmings: Loap (engine) | L3DEdit (level / graphics editor) | L3DUtils (replay / etc utility) | Lemmings Plus 3D (level pack)
Non-Lemmings: Commander Keen: Galaxy Reimagined (a Commander Keen fangame)

Flopsy

Looks really good this does namida :)

I'm excited to make use of this feature in my Sonic tilesets, is there anything I can do to prepare my tilesets for this feature. Like how would I make the image strip now with respect to these changes?

namida

#4
Quote from: Flopsy on May 02, 2019, 11:29:03 PM
Looks really good this does namida :)

I'm excited to make use of this feature in my Sonic tilesets, is there anything I can do to prepare my tilesets for this feature. Like how would I make the image strip now with respect to these changes?

Create the graphic strips as if you were creating two objects that always get placed together. They don't have to be the same size or have the same number of frames, but the relative position needs to always be the same - eg, the second one could be placed at an offset of 10 to the right and 5 down from the first one, as long as it's always placed at that offset (or the flipped / inverted / rotated equivalents, where applicable). The second one can either appear in front of or behind the first one; the way this feature works allows for either, at the object creator's choice.

Old info. See later in this topic for up-to-date info.

In the secondary animation, Frame 0 is always the one that will be displayed if / when the animation is stopped.

In terms of the main object, this is primarily intended for use with traps (including single-use traps), teleporters / receivers and locked exits. However, it's also supported for unlock buttons, splitters, entrances and pickup skills. The secondary animation is always a constant animation, although it always stops if the object is a trap and the trap gets disabled.

That aside, you can configure the following options:
- The relative position of the secondary animation from the first
- "SECONDARY_ALWAYS_ANIMATE" flag - if this is on, the animation doesn't stop while the object is active (eg. a trap killing a lemming, or a teleporter in progress, or a single-use trap that's been used). The animation still always stops if it's a trap that gets disarmed by a disarmer.
- "SECONDARY_IN_FRONT" flag - if this is on, the secondary is drawn in front of the main animation; otherwise it's drawn behind it
- "SECONDARY_INSTANT_STOP" flag - if this is on, then if an event causes the secondary animation to stop, it instantly jumps back to frame 0; otherwise, it completes the current animation cycle then stops upon reaching frame 0

I'll hopefully be able to get an experimental build that includes this feature up soon. I want to prepare secondary animations for at least the offical graphic sets first - I'll cover the Lemmings Plus sets later, probably before any stable release but after an experimental one.

I've implemented this in a way that's pretty forwards and backwards compatible. If you were to take an object that has a secondary animation, and load it in an older version of NL that doesn't support these, the object would be perfectly functional and the main animation would display correctly, you just wouldn't see the secondary animation. If you were to take an object without a secondary animation and load it in the new version that does support secondary animations, the object will work exactly the same way as it would in the older NL versions - secondary animations are optional, so it's no problem if an object doesn't have one.[/sort]
My projects
2D Lemmings: NeoLemmix (engine) | Lemmings Plus Series (level packs) | Doomsday Lemmings (level pack)
3D Lemmings: Loap (engine) | L3DEdit (level / graphics editor) | L3DUtils (replay / etc utility) | Lemmings Plus 3D (level pack)
Non-Lemmings: Commander Keen: Galaxy Reimagined (a Commander Keen fangame)

namida

I've added the experimental version, so you can see how it works in practice, prepare your own styles, report bugs, etc.

Suggestions are still welcome, but keep in mind this isn't intended to do really complicated fancy stuff - you'll still need multiple objects for that.
My projects
2D Lemmings: NeoLemmix (engine) | Lemmings Plus Series (level packs) | Doomsday Lemmings (level pack)
3D Lemmings: Loap (engine) | L3DEdit (level / graphics editor) | L3DUtils (replay / etc utility) | Lemmings Plus 3D (level pack)
Non-Lemmings: Commander Keen: Galaxy Reimagined (a Commander Keen fangame)

IchoTolot

This looks awesome! :thumbsup:

For the L2/L3 tilesets I would request external help though as I don't have time, motivation and nerves to completely draw these from scratch.

I may have some ideas like the cavelem eyes gloing a bit already, but getting ideas and the execution would take a ton of time.

Simon

This is very good to distinguish traps from terrain.

The backwards compatibility to single-animation is a respectable and correct decision: It must be easy to design new tiles, a minimal number of files must suffice, to encourage creativity.




Primary/secondary are bad names, unspecific, is primary the idle animation because levels always start with traps idle, or is primary the busy animation because, as a relic of implementation history, Lemmings 1 had only the busy animation?

Consider idle and busy/activated/... as names.




Be careful to not paint yourself into a corner by restricting the number of possible animations to 2. Triggered traps already have 3 states: idle, busy, and disarmed. You've already seen in Lemmings 1 that it's bad to lump two states into the same animation.

It's fine if the disarmed animation is a single frame, at least it's far less of a problem than with idle traps.

You can even consider (a separate animation/state) (the process of getting disarmed), and after that a loop of the disarmed animation. Doesn't matter if this further separation is practical -- the gist is to not repeat a mistake of history (and maybe in Lix) and restrict number of animations. Otherwise, you'll end with nasty hacks to support future extra states.

-- Simon

namida

Regarding the above: Yeah, I thought about that regarding multiple animations - I realised it would be potentially useful for limited-lemming-count entrances and exits, which especially the latter is a highly-requested feature - so I'm now working on code to allow for any number of additional animations. Even if these never get used, at least they're there.
My projects
2D Lemmings: NeoLemmix (engine) | Lemmings Plus Series (level packs) | Doomsday Lemmings (level pack)
3D Lemmings: Loap (engine) | L3DEdit (level / graphics editor) | L3DUtils (replay / etc utility) | Lemmings Plus 3D (level pack)
Non-Lemmings: Commander Keen: Galaxy Reimagined (a Commander Keen fangame)

namida

With regards to how the animations are actually stored, which layout is preferred?

For this, we'll use an example object that has:

a) A metainfo file, of course (the .nxmo file).
b) A main animation, which we'll call "busy".
c) A secondary animation, which we'll call "idle".
d) A mask (like the default pickup skills do), which we'll call "test_mask".

We'll call this object "namida_example:example_object". (Not sure if this notation is often used publicly; it means graphic set "namida_example", piece "example_object". Internally in NeoLemmix's workings, this notation is often used to specify the combination of graphic set and piece. Whether it refers to a terrain, an object or a background is inferred from context.)

Current setup in stable NL
a) The metainfo file is stored as "styles/namida_example/objects/example_object.nxmo"
b) The main animation is stored as "styles/namida_example/objects/example_object.png"
c) Secondary animations are not currently supported
d) The mask is stored as "styles/namida_example/objects/example_object_mask_test_mask.png"

Option 1 - Subfolder for entire object
a) The metainfo file is stored as either "styles/namida_example/objects/example_object.nxmo" or "styles/namida_example/objects/example_object/<some fixed filename>.nxmo"
b) The main animation is stored as "styles/namida_example/objects/example_object/busy.png"
c) The secondary animation is stored as "styles/namida_example/objects/example_object/idle.png"
d) The mask is stored as "styles/namida_example/objects/example_object/masks/test_mask.png" (Or perhaps the filename is prefixed with "mask", rather than in a "masks" subfolder)

Older pieces would need specific backwards-compatibility code in NeoLemmix to handle them - but this wouldn't be hard to do.

Option 2 - Prefixed filenames
a) The metainfo file is stored as "styles/namida_example/objects/example_object.nxmo"
b) The main animation is stored as "styles/namida_example/objects/example_object.png" (or "styles/namida_example/objects/example_object_busy.png")
c) The secondary animation is stored as "styles/namida_example/objects/example_object_idle.png"
d) The mask is stored as "styles/namida_example/objects/example_object_mask_test_mask.png"

If the alternate name under B isn't used, this is backwards / forwards compatible for the player. The editor has minor issues with the extra images, as it appears to find objects by looking for PNG files, rather than by looking for NXMO files, so new pieces won't work properly in old editor versions.
My projects
2D Lemmings: NeoLemmix (engine) | Lemmings Plus Series (level packs) | Doomsday Lemmings (level pack)
3D Lemmings: Loap (engine) | L3DEdit (level / graphics editor) | L3DUtils (replay / etc utility) | Lemmings Plus 3D (level pack)
Non-Lemmings: Commander Keen: Galaxy Reimagined (a Commander Keen fangame)

IchoTolot

Option 2 - Prefixed filenames would be my choice.

I hate having too many folders. I rather have more files - even more if the pairs of 2-4 have fitting names. :P

I think this is much easier for tileset maintenance.

namida

QuoteI hate having too many folders. I rather have more files - even more if the pairs of 2-4 have fitting names. :P

Yes, they would. All files relating to an object would have filenames starting with the object's name. In the example above, all objects filenames would start with "example_object".
My projects
2D Lemmings: NeoLemmix (engine) | Lemmings Plus Series (level packs) | Doomsday Lemmings (level pack)
3D Lemmings: Loap (engine) | L3DEdit (level / graphics editor) | L3DUtils (replay / etc utility) | Lemmings Plus 3D (level pack)
Non-Lemmings: Commander Keen: Galaxy Reimagined (a Commander Keen fangame)

Simon

Both options 1 or 2 look reasonable, it depends on personal liking. Yeah, the heavy lifters of our maintenance camp should pick. 8-)

-- Simon

namida

I've come up with a more final - and far more powerful - implementation. It uses option 2 in regards to file naming, and allows for... not quite "anything you can imagine", but it's quite flexible. Multiple animations are supported, which can have different (and customizable) conditions. For example, you could have a trap with the normal main animation, a secondary animation that stops (or even disappears - or loops back to frame zero, then stops or disappears) when the trap is in use, another secondary animation that only plays while the trap is in use but continues until reaching frame 0 when the trap finishes... it's probably far more powerful than it needs to be, but it works well.

Currently, it supports telling the animation, based on the supported conditions, to pause, stop (differentiated by that stop also returns to frame 0), return to frame 0 then pause, play, or display the same frame as the primary animation (can be useful eg. for pickup skills). It can also show or hide the animation again based on these conditions - if the animation is told to hide, it remains visible until paused, allowing this to be combined with the "return to frame 0 then pause" feature.

I've decided to keep whatever animation would already currently exist, being defined as the "primary" one. The logic here - the primary one is the one that actually relates directly to physics. To use a trap as an example - how many frames the idling animation has is irrelevant to physics, but how many frames the active animation has is not. Therefore, it should be the primary one. Of course, backwards compatibility is another good reason for this.

The supported conditions, currently, are:
- Primary animation is on frame zero. This is supported on object types where frame zero is significant, eg. traps (including single-use), teleporters.
- Primary animation is on frame one. This is supported on object types where frame one is significant, eg. single-use traps, locked exits.
- Busy. This is supported on traps, teleporters and receivers. A teleporter or receiver will register as busy whenever the pair, as a whole, is in use.
- Triggered. This is supported on all object types that can, but don't always, animate (so it includes eg. entrances, locked exits, etc). The object will register as triggered while the primary animation is animating. Teleporters and receivers are NOT linked for the purpose of this one.
- Disabled. This is supported on traps (including single use, but a used single-use trap does not register as disabled - only a disarmed one does), teleporters and receivers (where it would register if no paired teleporter / receiver exists).

This is quite powerful, and can theoretically get very complex, but the vast majority of real-world use cases would simply need to copy / paste less than 10 lines from a default object that has similar secondary animation behaviour.

This does mean, secondary animation metainfo files from the earlier experimental, won't work as-is (though they're generally very easy to convert). Image files, on the other hand, can most certainly be used as-is under the new system - I didn't have to make a single change to any images I made for the earlier system.

Usage guide for current system

This is obsolete, and is preserved here solely for those who are reading the topic at a later date. See a later post in this topic for up-to-date information.

Define the primary animation the same way as it currently is. If you wish to nine-slice it, use the keywords CUT_LEFT, CUT_TOP, CUT_RIGHT and CUT_BOTTOM with integer values to indicate the border size on each side. Aside from these ones, only fields that were supported prior to this feature, can be used in this way.

Alternatively, you can define the primary animation inside a $PRIMARY_ANIMATION section. If doing this, the section works the same way as a general $ANIMATION section (for secondary animations). However, this is not backwards compatible. You can somewhat subvert this by defining the primary animation in both ways - if you do this, the new version will disregard anything outside $PRIMARY_ANIMATION, while the old version will ignore anything inside it. When defining the primary animation this way, you can use most of the new features supported on secondary animations too, although not all of them work on the primary animation.

You can then define secondary animations inside $ANIMATION sections. The following lines can be used with a secondary animaton:

FRAMES - Indicates the number of frames in the animation.
NAME - Indicates the filename suffix. If this field is "idle", on an object called "trap", the animation will be loaded from "trap_idle.png". If this is empty, on an object called "trap", it will load from "trap.png" (not "trap_.png") - generally, the empty name would be used for the primary animation (but doesn't have to be).
COLOR - Indicates a color name, taken from the current theme, to recolor the animation using. If this is blank, the animation is not recolored.
EDITOR_FALLBACK - This animation will be displayed (together with the primary animation) in the editor, when using the "primary + one secondary rendered by editor" fallback. This cannot be used on the primary animation (always displayed in editor), and if used on multiple secondary animations, priority goes to the animation listed later in the file. If not specified on any animation, the first animation in the file is used.

HORIZONTAL_STRIP - Doesn't need a value. If present, the animation strip will be loaded as a horizontal strip.

Z_INDEX - Indicates the order to draw the animations in. Lower numbers get drawn first. If not specified, it defaults to 0, except on the primary animation, where it defaults to 1.

INITIAL_FRAME * - Indicates the frame number the animation should start on. Set this to -1 to start on a random frame. Doesn't work on primary animation for object types where specific frames are significant to physics, but does on those where it isn't.

OFFSET_X - The animation will be offset horizontally by this amount from the object's position.
OFFSET_Y - Same, but vertical.

CUT_TOP - The size of the top edge when nine-slicing.
CUT_RIGHT - Same, for right edge.
CUT_BOTTOM - Same, for bottom edge.
CUT_LEFT - Same, for left edge.

STOP - If no trigger condition otherwise is fulfilled, the animation will stop (revert to frame 0 and stop animating). Doesn't work on primary animation.
PAUSE - If no trigger condition otherwise is fulfilled, the animation will pause (stop animating, but stay on the current frame). Doesn't work on primary animation.
LOOP_TO_ZERO - If no trigger condition otherwise is fulfilled, the animation will continue until reaching frame 0, then pause. Doesn't work on primary animation.
MATCH_PRIMARY_FRAME - If no trigger condition otherwise is fulfilled, the animation will sit on whatever frame the primary animation is on. Doesn't work on primary animation.

HIDE - If no trigger condition otherwise is fulfilled, the animation will be invisible when not animating (matching the primary animation's frame counts as animating). Doesn't work on primary animation.


* The older keywords PREVIEW_FRAME and RANDOM_START_FRAME still work when defining the primary animation in the main segment, but don't work in $PRIMARY_ANIMATION or $ANIMATION segments.

Inside the $ANIMATION segment (this does not work for the primary animation, even when using a $PRIMARY_ANIMATION segment), you can define trigger conditions which specify when the animation will animate and/or be visible. These are defined inside a $TRIGGER segment, which can contain the following lines.

FRAME_ZERO - Value should be "true" or "false". The trigger will be fulfilled if, on an object that supports it (any where frame zero is significant to physics), the primary animation is on frame zero (or not on frame zero, if "false"). If the keyword is absent altogether, being on frame zero has no effect either way.
FRAME_ONE - Same; condition is that primary animation is on frame one, supported on any object where frame one is significant to physics. Pickup skills instead interpret this as "not frame zero".
BUSY - Same; condition is that object is busy. Supported on traps (including single-use traps), teleporters and receivers.
TRIGGERED - Same; condition is that object is in triggered state. Supported on all objects that have triggered animations (not that simply switch between two frames, eg. splitters).
DISABLED - Same; condition is that object is disabled. Supported on traps (including single-use traps), teleporters and receivers. Note that a single-use trap that has been used, does [b]not[/b] count as disabled - only [i]disarmed[/i] traps do.

HIDE - If present, when conditions are fulfilled [b]and[/b] animation is paused or stopped, the animation will be hidden altogether.

STATE - Defines what animation behaviour the animation should have when the conditions are fulfilled. Options are "PLAY", "PAUSE", "STOP", "LOOP_TO_ZERO" or "MATCH_PRIMARY_FRAME".


A condition on an object that doesn't support the condition, always registers as false.

In regards to the editor's fallback: If you want the fallback for an object to be "don't display any secondary animation", insert the line "EDITOR_FALLBACK_NONE" somewhere in the main segment.

NOTE: Editor fallback indicators don't work in the current exp build, but they've been implemented in the source.

As a side effect of these changes, objects with recoloring masks (which are now implemented as recolored secondary animations) are now slightly more efficient. Previously, these were reloaded any time a new level was loaded, unless the new level used the same theme as the previously loaded level. Now, the "keep existing copy" test is based on the specific color rather than the theme (so two different themes with the same color = no reload), and the unmasked image is kept in memory with the image just being re-masked rather than the entire object reloaded from disk.




On top of that, I added another nice feature for resizable objects - you can now have a "nine-slice" type resize, which recognizes corners and edges. I'm not sure of the normal term for this or how to describe it, so here's an image - notice the lack of the usual roughness updrafts have when resized vertically?

(The squasher trap does indeed lack a secondary animation at this point. The other trap, however, has one - it's just not noticable in a screenshot. The wheel always rotates, unless the trap has been deactivated.)

I have uploaded a new experimental build that includes both these features. Another thing to check out - try using clear physics mode in the new experimental build. :)
My projects
2D Lemmings: NeoLemmix (engine) | Lemmings Plus Series (level packs) | Doomsday Lemmings (level pack)
3D Lemmings: Loap (engine) | L3DEdit (level / graphics editor) | L3DUtils (replay / etc utility) | Lemmings Plus 3D (level pack)
Non-Lemmings: Commander Keen: Galaxy Reimagined (a Commander Keen fangame)

namida

Here's a new video, showing secondary animations in all the official graphic sets, and talking a bit about how they work. It shows off a nice selection of (though not even close to all of) the possibilities the new system has.

https://youtu.be/wutmgNuo-90

If anyone wants to request additional conditions / operations / etc (as long as they're not particularly complex), feel free to ask (though I make no promises) - I would like to hear proposed use cases, so I can consider how worthwhile any ideas are.
My projects
2D Lemmings: NeoLemmix (engine) | Lemmings Plus Series (level packs) | Doomsday Lemmings (level pack)
3D Lemmings: Loap (engine) | L3DEdit (level / graphics editor) | L3DUtils (replay / etc utility) | Lemmings Plus 3D (level pack)
Non-Lemmings: Commander Keen: Galaxy Reimagined (a Commander Keen fangame)