This document is about: QUANTUM 3
SWITCH TO

此頁面正在修正中,可能有待更新。

Project Architecture

Introduction

This page provides an overview of the project's overall structure. The game is designed around the idea that animation-driven motion is the central element that defines gameplay behavior. Rather than using abstract Animator Mecanim state transitions or manually scripted KCC movement, the system derives the character's actions and movement primarily from animation-driven data.

The core gameplay systems implementing this approach are:

Animated Bone Sampler: responsible for sampling colliders motion and extracting frame-accurate position and rotation data.

Root Motion: drives character displacement and rotation directly from animation data.

FadeTo Animations: a controlled transition system that blends animations with precise timing.

This project makes extensive use of the Animator Addon, it is important to read the Animator documentation prior to exploring this sample source.
This project is under development, and structural changes may occur during the process.

Animated Bone Sampler

Some games require extracting specific information from animations so it can be used in the Quantum simulation, for example, determining the hitbox of a sword that moves quickly during an attack animation.

The technique referred to here as Animated Bone Sampler demonstrates how the Quantum Animator can be extended to extract custom data from an animation clip. It is important to note that this is not a one-size-fits-all solution; depending on the project, additional adjustments or modifications may be required to support different scenarios or more complex animation setups.

Usage and DSL Layout

In the DSL (game state), the sampled data is stored in the dictionary contained within the AnimationBoneSamplerComponent.
The BoneSamplerType enum must be extended whenever new bone slots need to be tracked. Each enum value is used as a direct array index into the per-frame data, so values must remain stable and contiguous across bakes. Currently defined types are Sword, Shield, and Head.

For more details on how the solution works internally, refer to AnimationBoneSamplerSystem, which is responsible for advancing the bone data according to the current animation state, creating or updating the corresponding bone child entities each tick, and making the data ready to be polled by custom systems.
The image below illustrates how the stored data can be retrieved from the dictionary and used to perform Shape Queries, as demonstrated in this sample's AttackCollisionSystem.

Bone Entity Lifecycle

Each BoneSamplerType slot is backed by a dedicated child entity that carries a Transform3D and a trigger PhysicsCollider3D. These entities are created lazily on the first tick a given bone slot is encountered and are registered in the AnimationBoneSamplerComponent dictionary keyed by their BoneSamplerType. On subsequent ticks the system updates the existing entity's transform in-place rather than recreating it. The BoneHolder component on each bone entity references its owning character entity and its type, allowing other systems to identify the source of a physics overlap.

Custom Graph Baker

The AnimatorGraph's ReferenceModel is the GameObject used to extract animation data during the bake process. To enable bone tracking, add the AnimationBoneSamplerMarker component anywhere in the hierarchy of the ReferenceModel, positioned and oriented to match the bone or attachment point you want to track.
The figure below shows how this configuration was applied for the sword's collider. The Shape Config used for sampling can be of type Box, Sphere, or Capsule. The shape is automatically scaled by the bone's world-space lossy scale during baking, so the authored shape should be defined in the marker's local space.

For each state defined in the Unity AnimatorController, the baking process generates an AnimationBoneSamplerSpec asset located at: Assets/QuantumUser/AnimationBoneSampler/Resources/<AnimatorGraphAsset-Name>. For blend-tree states the spec contains one motion entry per child clip, in the same order as the blend tree's child list. Re-baking always replaces existing assets to prevent stale data from persisting between bakes.

Root Motion

This sample project includes a fully functional 3D Root Motion implementation within the MovementSystem. It also provides a motion-cancellation mechanism that interrupts the current state whenever the character collides with or stumbles over an obstacle.
How motion is applied is entirely a design choice. In this case, the decision was made to disable the KCC and move the transform directly. The code below demonstrates how the animator's root motion is processed.

C#

    /// <summary>
    /// Signal handler invoked by the Quantum Animator addon once per tick after root motion
    /// has been computed. Applies the animation-driven positional and rotational delta to
    /// the character's <see cref="Transform3D"/> directly, bypassing the KCC.
    /// <para>
    /// The displacement is rotated from animation-local space into world space using the
    /// inverse of the accumulated root motion rotation. A forward raycast slightly longer
    /// than the displacement vector is then cast from chest height to detect static obstacles:
    /// <list type="bullet">
    ///   <item>If no hit is detected the KCC is teleported to the new position.</item>
    ///   <item>
    ///     If an obstacle is hit, displacement is cancelled and
    ///     <see cref="ISignalOnChangeState"/> is raised with <see cref="StateType.HitStun"/>
    ///     to interrupt the current animation state.
    ///   </item>
    /// </list>
    /// </para>
    /// </summary>
    /// <param name="f">The current simulation frame.</param>
    /// <param name="entity">The entity whose animator produced a root motion delta this tick.</param>
    /// <param name="deltaFrame">The incremental root motion delta for this tick.</param>
    /// <param name="currentFrame">The accumulated root motion frame used to derive the current orientation.</param>
    public void OnAnimatorRootMotion3D(Frame f, EntityRef entity, AnimatorFrame deltaFrame,
        AnimatorFrame currentFrame)
    {
        if (deltaFrame.Position == FPVector3.Zero && deltaFrame.RotationY == FP._0) return;
        if (f.Unsafe.TryGetPointer<Transform3D>(entity, out var transform))
        {
            transform->Rotate(FPVector3.Up, deltaFrame.RotationY * FP.Rad2Deg);
            transform->Teleport(f, transform->Rotation);

            if (f.Unsafe.TryGetPointer<KCC>(entity, out var kcc))
            {
                kcc->Data.SetLookRotation(transform->Rotation);
            }

            var currentFrameRotation = FPQuaternion.CreateFromYawPitchRoll(currentFrame.RotationY, 0, 0);
            currentFrameRotation = FPQuaternion.Inverse(currentFrameRotation);
            var newPosition = currentFrameRotation * deltaFrame.Position;
            var displacement = transform->Rotation * newPosition;

            var targetHitPosition = (displacement.XOZ.Normalized * FP._0_33 * 2) + displacement;
            var hitOrigin = transform->Position + FPVector3.Up;
            Draw.Ray(hitOrigin, targetHitPosition);
            var hits = f.Physics3D.RaycastAll(hitOrigin, targetHitPosition.XOZ, targetHitPosition.Magnitude, -1,
                QueryOptions.HitStatics);

            if (hits.Count <= 0)
            {
                if (kcc != null)
                {
                    kcc->Teleport(f, transform->Position + displacement);
                }
            }
            else
            {
                f.Signals.OnChangeState(entity, StateType.HitStun);
            }
        }
    }

Note that AnimationBoneSamplerSystem also implements ISignalOnAnimatorRootMotion3D and updates all bone child entity transforms on the same signal, ensuring bone colliders remain in sync with the character's post-root-motion position within the same tick.

FadeTo Animations

This project primarily uses FadeTo transitions. This means the AnimatorController is not the main driver for deciding which animation state should play.
Instead, state selection and transition logic are handled programmatically. For more information on how this system works, refer to the AnimationSystem.

Third Party Assets

This sample includes third-party free and CC0 assets. The full packages can be acquired for your own projects at their respective site:

More Quantum Samples

Visit our Samples page to discover more example projects.

Back to top