• Showcase
  • Code:HARDCORE the mecha game

Related Discussions
...

Hello,everyone
Do you like "Super Robot Taisen"? And you are an action gamer?
Then you should look at this game :devil:

A mech-themed 2D platformer. With action-packed shooting, highly polished animations, driving multiple unique awesome mechs to fly in the air, play your own style based on a fluent set of controls.

The game is now in early stage development, we made this playable demo with only 5 mounths.So there is long way to go.

follow us on twitter: https://twitter.com/rocketpunchgame
and the page: https://www.facebook.com/rocpun
We will show you better things in the futrure

The official runtime of spine is a nightmare, so we rewrote the whole runtime... :bang:

We are the indie game developer in China, we are not good at english, but we are trying our best to connect the world

It looks really awesome! Reminds me of Metal Warriors for SNES.

louiky escreveu

The official runtime of spine is a nightmare, so we rerwote the whole runtime...

Care to explain?

drooooooool!

AWESOME 😃 Thanks for sharing

The official runtime of spine is a nightmare, so we rerwote the whole runtime

😃

Shiu escreveu

drooooooool!

AWESOME 😃 Thanks for sharing

Yep ... drooling :rock:

About time you're done-ish with this! 😃

Niiiice one, they are beautiful.

Spine has truly Mech it's match... sorry

Nate escreveu

Care to explain?

Hi there, I'm the programmer working on the Code: HARECORE project.

There are several problems in the Spine runtime, it becomes really long as I wrote, so I will post them one by one as I complete them one by one 🙂

The most important of all, the method of updating animation timelines are wrong.

Think of the following case. A bone has a rotation of 0 degree in setup pose. We now play animation A and B at the same time, both with weight (or call it 'alpha') of 50%. In one frame, if the bone has a rotation of 20 degree in A, and a rotation of 40 degree in B (as the illustration below), what is expected to happen? After all the updates, the bone should have a rotation of 30 degree, right?

A 

---

 [Bone] 

---

 B
20        30         40

However, check out the code in RotateTimeline.Apply(), which does the following:

float delta = frames[frame + VALUE] - prevFrameValue;
float amount = bone.data.rotation + (prevFrameValue + delta * percent) - bone.rotation;
bone.rotation = bone.rotation + amount * alpha;

In our case, we don't consider interpolation for now, so 'delta' will be 0. OK, then we start updating the rotation timeline of A:

float amount = 0 + (20 + 0 * percent) - 0 = 20;
bone.rotation = 0 + 20 * 0.5 = 10;

Exactly what we want, right? But, when we start updating B:

float amount = 0 + (40 + 0 * percent) - 10 = 30;
bone.rotation = 10 + 30 * 0.5 = 25;

25, not 30. This is exactly where things gone wrong.

Typically, you set your rig to setup pose at the start of each update, and when you updating a timeline, YOU DON'T READ THE CURRENT DATA OF THE BONE. YOU JUST WRITE TO THE CURRENT DATA OF THE BONE.

We modified the update of timeline as the following:

float amount = prevFrameValue + delta * percent;
bone.rotation = bone.rotation + amount * alpha;

We think about the case again.

// Updating A
float amount = 20 + 0 * percent = 20;
bone.rotation = 0 + 20 * 0.5 = 10;

// Updating B
float amount = 40 + 0 * percent = 40;
bone.rotation = 10 + 40 * 0.5 = 30;

We do our aiming animation using a blend tree 1D, like aim-90, aim-45, aim_0, aim_45 and aim_90. We have to care about precision you know 🙁

CDR2003 escreveu

Think of the following case. A bone has a rotation of 0 degree in setup pose. We now play animation A and B at the same time, both with weight (or call it 'alpha') of 50%.

Here is the difference. How Spine does it:

  1. Apply A (rotation = 20) at 100%. Rotation = 20.

  2. Apply B (rotation = 40) at 50%, mixed with current pose. Rotation = 20 + (40 - 20) * 0.5 = 30.

How you do it:

  1. Apply setup pose. Rotation = 0.

  2. Apply A (rotation = 20) at 50%, additive with current pose. Rotation = 0 + 20 * 0.5 = 10.

  3. Apply B (rotation = 40) at 50%, additive with current pose. Rotation = 10 + 40 * 0.5 = 30.

Both arrive at the same result, though your way requires setting an extra pose that may not otherwise be needed. There are differences in how animations are applied and how mix percentages are managed, though it isn't readily apparent where gotchas may be found.

One advantage your way has is that it preserves rotation direction, whereas when mixing between the current pose and the animation pose, the shortest rotation is chosen. This can result in the mix choosing the "wrong" way around, depending on the skeleton's anatomy.

Nate escreveu
  1. Apply A (rotation = 20) at 100%. Rotation = 20.

  2. Apply B (rotation = 40) at 50%, mixed with current pose. Rotation = 20 + (40 - 20) * 0.5 = 30.

The Spine way of doing this does not end up with the sum of alpha equals to 100%, right? In this case, one of the animations is biased to 100%. I know that if A is 50% and B is 100%, the result is also correct. But what if we blend 3 animations together? Which one is the biased one? Or only one animation is not biased? Not having the sum of alpha 100% is kinda weird cuz it does not serve as the meaning of 'alpha' I think.

I've not seen animations blending working this way in other tools. Is there a strong reason for you to do that? If you do, please let me know cuz this should be something to care about. 🙂

Correct, they don't sum to 100%. The alpha mixes between the current pose and the animation pose. Generally the first animation applied (the lowest "track") is 100% and poses the whole skeleton, then animations are applied on top.

Blending 3 animations works, though the outcome is dependent on the order of the 2nd and 3rd (when they both key the same properties):

A = 20, 100% = 20
B = 40, 50% = 20 + (40 - 20) * 0.5 = 30
C = 5, 33% = 30 + (5 - 30) * 0.33 = 21.75

A = 20, 100% = 20
C = 5, 33% = 20 + (5 - 20) * 0.33 = 15.05
B = 40, 50% = 15.05 + (40 - 15.05) * 0.5 = 27.525

It's been this way from the beginning. I only vaguely remember the decisions surrounding it 3+ years ago. Requiring a reset to the setup pose and then applying animations is OK, but removes the possibility of the bones retaining transform state


all the bone state needs to be reapplied every time the skeleton is posed. It's true that is the most common usage, but we should be wary when preventing other usage. I'm open to changing to additive but we'll need to make sure it still allows everything it needs to. I like that it fixes the rotation direction during mixing, and I agree that mixing more than 2 animations is more predictable.

What happens when you want to apply an animation and have it override part of the current pose? If everything is always additive, this would not be possible. Eg, maybe you have your main animations, and you want to mix in another animation to take over control of some bones, then mix it out. With additive you'd need to reset the properties of the bones (not the whole bone) that the overriding animation keys to the setup pose first. Maybe applying an animation could be apply(skeleton, alpha, override) where override is a boolean.

For animation overriding, we have a more complicated way to do that. First, we built a animator controller system similar to Mecanim. In the animator controller, we have several layers, and each layer has several states, and each state can be a plain animation, a blend tree 1D, or a blend tree 2D. A layer can either be additive or override, and we also support weighted override and additive. For a simpler example, let's think about the following case:

Layer 0: Base Layer, plays animation 'idle'
Layer 1: Shooting Layer, Override (weight = 1), plays animation 'aim' (only has keyframes on upper body)

First, we introduce the mask concept. A mask keeps tracking the current weight of all bones in a skeleton. We update the layers like the following:

// In Animator.Update()
skeleton.SetToSetupPose();
mask.Reset(); // Reset weights of all bones to 0.
for( int i = this.layers.Count - 1; i >= 0; i

---

 )
{
    layer[i].Update();
}

// In Layer.Update()
currentState.Update();
currentState.UpdateMask();
mask.ApplyAffectedBoneWeights( layerWeight ); // Weights of all marked bones *= 1 - layerWeight, unmark those bones

// In State.Update()
foreach( timeline in timelines )
{
    timeline.Apply( alpha );
}

// In RotateTimeline.Apply( alpha )
alpha *= mask.GetBoneWeight( boneIndex );

// In RotateTimeline.UpdateMask()
mask.MarkAffectedBone( boneIndex ); // Mark the bone for later 'ApplyAffectedBoneWeights'

So, the idea is to reversely update layers, and for each layer, update the mask so that later layers can apply the bone weight.
I don't know if it is enough for you to understand our practice, just tell me your thought. I kinda like the discussion right now 🙂

I'm having trouble understanding what you mean by "mask" and "weight".

Calling it bone weight is maybe a bit confusing, since we use weights for bone influence on meshes/paths/bounding box vertices. Ignoring that, it's sill confusing. :shake: I also don't understand how "masks" work. It seems you are marking bones, but I expect you'd need to track individual bone properties (scale, rotation, etc), as an animation may key rotation while another keys translation for the same bone.

Let's try a simpler approach:

Say track 0 (you called them layers but let's call them tracks to be consistent with the current AnimationState class) has a run animation which keys legs and arms. Track 1 has an aiming animation which keys arms. I want to mix in the aiming animation from 0-100%, then the aiming animation controls the bones for a while, then mix it out from 100-0% to return to the running animation. How can this work with additive?

If I say track 1 is "override" and that means it resets to the setup pose before applying, the arms won't mix from the running animation to aiming. They would be running, then snap to the setup pose, then mix from setup pose to aiming, etc.

2 meses depois

Hello I'm really interested in this game because of the beautiful 2d animations.
I have a question for the animator Louiky Mu
I saw on your twitter that you also do amazing pixel sprite animations.
So I would like to know what you find more time consuming:
Drawing pixel sprite animations frame by frame?
Or animations done in the Spine software with higher resolution images?

2 meses depois
CDR2003 escreveu

First, we built a animator controller system similar to Mecanim.

Thats your problem. The runtime is fine. If you worked for me and you told me that you created a animator controller system similar to Mecanim, you will have a lot to explain.

I apologize for bringing this old thread back but little disturbed by the fact that these devs in China is making some wild claim about "being broken".

To be fair, AnimationState prior to version 3.5 didn't handle mixing very well in a number of situations. It took quite some effort to get it to work how we want. It ended up surprisingly complicated, but has some really nice features that aren't possible with an additive system.