예제 #1
0
 internal StateController(StateControllerBuilder <T, TContext> builder)
 {
     DefaultState = builder.DefaultState;
     CurrentState = DefaultState;
     _states      = builder._states;
     States       = new ReadOnlyCollection <T>(builder.States.ToArray());
 }
        /// <inheritdoc/>
        public override StateController BuildController()
        {
            Initialize();
            var builder  = new StateControllerBuilder();
            var stateMap = BuildStates(builder, _states);

            var typeOrder = new Type[] {
                typeof(StateAsset),
                typeof(StateGroupAsset),
            };
            var transitions = _states.OrderBy(s => Array.IndexOf(typeOrder, s.GetType()))
                              .SelectMany(s => s.Transitions);

            BuildTransitions(transitions, stateMap);

            return(builder.Build());
        }
        static Dictionary <StateAsset, State> BuildStates(StateControllerBuilder builder,
                                                          List <BaseStateAsset> stateAssets)
        {
            var  stateMap = new Dictionary <StateAsset, State>();
            uint id       = 0;

            foreach (var stateAsset in stateAssets.OfType <StateAsset>().OrderBy(s => s.name))
            {
                if (stateAsset == null || stateMap.ContainsKey(stateAsset))
                {
                    continue;
                }
                State state = stateAsset.BuildState(id);
                builder.AddState(state);
                stateMap[stateAsset] = state;
                id++;
            }
            return(stateMap);
        }
        public StateController <CharacterState, CharacterContext> BuildCharacterControllerImpl(StateControllerBuilder <CharacterState, CharacterContext> builder)
        {
            Builder = builder;
            InjectState(this);

            // Declare Smash Attacks
            SmashUp.Charge.Data.SmashAttack   = SmashAttack.Charge;
            SmashSide.Charge.Data.SmashAttack = SmashAttack.Charge;
            SmashDown.Charge.Data.SmashAttack = SmashAttack.Charge;

            SmashUp.Attack.Data.SmashAttack   = SmashAttack.Attack;
            SmashSide.Attack.Data.SmashAttack = SmashAttack.Attack;
            SmashDown.Attack.Data.SmashAttack = SmashAttack.Attack;

            // Ground Attacks
            new [] { Idle, Walk, CrouchStart, Crouch, CrouchEnd }
            // Smash Attacks
            .AddTransitions <CharacterState, CharacterContext>(context => {
                var input = context.Input;
                if (!input.Attack.WasPressed)
                {
                    return(null);
                }
                switch (input.Smash.Direction)
                {
                case Direction.Right:
                case Direction.Left:
                    return(SmashSide.Charge);

                case Direction.Up:
                    return(SmashUp.Charge);

                case Direction.Down:
                    return(SmashDown.Charge);
                }
                return(null);
            })
            // Tilt Attacks
            .AddTransitions <CharacterState, CharacterContext>(context => {
                var input = context.Input;
                if (!input.Attack.WasPressed)
                {
                    return(null);
                }
                switch (input.Movement.Direction)
                {
                case Direction.Right:
                case Direction.Left:
                    return(TiltSide);

                case Direction.Up:
                    return(TiltUp);

                case Direction.Down:
                    return(TiltDown);
                }
                return(Neutral);
            });
            SmashUp.Charge.AddTransitionTo(SmashUp.Attack);
            SmashDown.Charge.AddTransitionTo(SmashDown.Attack);
            SmashSide.Charge.AddTransitionTo(SmashSide.Attack);
            TiltDown.AddTransitionTo(Crouch, Input(i => i.Movement.Direction == Direction.Down));
            new[] { Neutral, TiltUp, TiltDown, TiltSide, SmashUp.Attack, SmashDown.Attack, SmashSide.Attack }
            .AddTransitionTo(Idle);

            new [] { Fall, Jump, JumpAerial }
            .AddTransitions(Land, ctx => ctx.IsGrounded)
            // Aerial Attacks
            .AddTransitions <CharacterState, CharacterContext>(context => {
                var input = context.Input;
                if (!input.Attack.WasPressed)
                {
                    return(null);
                }
                switch (input.Movement.Direction)
                {
                case Direction.Right:
                    return(context.Direction >= 0f ? AerialForward : AerialBackward);

                case Direction.Left:
                    return(context.Direction >= 0f ? AerialBackward : AerialForward);

                case Direction.Up:
                    return(AerialUp);

                case Direction.Down:
                    return(AerialDown);
                }
                return(AerialNeutral);
            });
            new[] { AerialForward, AerialBackward, AerialDown, AerialUp, AerialNeutral }
            .AddTransitions(AerialAttackLand, ctx => ctx.IsGrounded)
            .AddTransitionTo(Fall);
            AerialAttackLand.AddTransitionTo(Idle);

            // Aerial Movement
            new [] { Idle, Walk, Dash, Run, RunTurn, RunBrake, CrouchStart, Crouch, CrouchEnd, Shield.Main }
            .AddTransitions(JumpStart, ctx => ctx.Input.Jump.WasPressed && ctx.CanJump);
            new[] { JumpStart, JumpAerial }.AddTransitionTo(Jump);
            new[] { Jump, Fall }.AddTransitions(JumpAerial, ctx => ctx.Input.Jump.WasPressed && ctx.CanJump)
            .AddTransitions(EscapeAir, Input(i => i.Shield.WasPressed));
            Jump.AddTransition(Idle, ctx => ctx.NormalizedStateTime >= 1.0f && ctx.IsGrounded)
            .AddTransition(Fall, ctx => ctx.NormalizedStateTime >= 1.0f && !ctx.IsGrounded);
            EscapeAir.AddTransitionTo(FallHelpless);
            new[] { Fall, FallHelpless, EscapeAir }.AddTransitions(Land, ctx => ctx.IsGrounded);
            Land.AddTransitionTo(Idle);

            Func <Func <PlayerInputContext, DirectionalInput>, Func <CharacterContext, bool> >
            movementContext = func => {
                return(ctx => !DirectionInput(Direction.Down)(ctx) &&
                       Input(i => Mathf.Abs(func(i).Value.x) > DirectionalInput.DeadZone)(ctx));
            };

            // Running States
            Idle.AddTransition(Dash, movementContext(i => i.Smash));
            Dash.AddTransitionTo(Idle, DirectionInput(Direction.Neutral));
            new[] { Dash, RunTurn }.AddTransitionTo(Run);
            Run.AddTransition(RunBrake, DirectionInput(Direction.Neutral));
            Run.AddTransition(RunTurn,
                              ctx => !Mathf.Approximately(Mathf.Sign(ctx.Input.Movement.Value.x), Mathf.Sign(ctx.Direction)));
            RunBrake.AddTransitionTo(Idle);

            // Ground Movement
            new[] { Idle, Walk, Run }
            .AddTransitions(CrouchStart, DirectionInput(Direction.Down))
            .AddTransitions(Fall, ctx => !ctx.IsGrounded);

            Idle.AddTransition(Walk, movementContext(i => i.Movement));
            Walk.AddTransition(Idle, DirectionInput(Direction.Neutral));

            // Crouching States
            CrouchStart.AddTransitionTo(Crouch);
            CrouchEnd.AddTransitionTo(Idle);
            new[] { CrouchStart, Crouch, CrouchEnd }.AddTransitions(Fall, ctx => !ctx.IsGrounded);
            Crouch.AddTransition(CrouchEnd, Input(i => i.Movement.Direction != Direction.Down));

            // Ledge States
            new[] { Idle, Fall, FallHelpless }.AddTransitions(LedgeGrab, ctx => ctx.State.IsGrabbingLedge);
            LedgeGrab.AddTransitionTo(LedgeIdle);
            LedgeIdle.AddTransition(LedgeRelease, ctx => !ctx.State.IsGrabbingLedge)
            .AddTransition(LedgeClimb, DirectionInput(Direction.Up))
            .AddTransition(LedgeJump, ctx => ctx.Input.Jump.WasPressed && ctx.CanJump)
            .AddTransition(LedgeAttack, Attack());
            LedgeJump.AddTransitionTo(Jump);
            new[] { LedgeRelease, LedgeClimb, LedgeEscape, LedgeAttack }
            .AddTransitions(Idle, ctx => ctx.NormalizedStateTime >= 1.0f && ctx.IsGrounded)
            .AddTransitions(Fall, ctx => ctx.NormalizedStateTime >= 1.0f && !ctx.IsGrounded);

            // Shielding
            Idle.AddTransition(Shield.On, Input(i => i.Shield.Current));
            Shield.On.AddTransition(Shield.Perfect, ctx => ctx.State.IsHit)
            .AddTransitionTo(Shield.Main);
            Shield.Main.AddTransition(Shield.Broken, ctx => ctx.State.ShieldDamage <= 0)
            .AddTransition(Shield.Off, Input(i => !i.Shield.Current));
            Shield.Off.AddTransitionTo(Idle);
            new[] { Shield.Broken, Shield.Stunned, Idle }.Chain();

            // Rolls/Sidesteps
            Shield.Main
            .AddTransition(EscapeForward, ctx => {
                if (ctx.Direction > 0f)
                {
                    return(DirectionalSmash(Direction.Right)(ctx));
                }
                else
                {
                    return(DirectionalSmash(Direction.Left)(ctx));
                }
            })
            .AddTransition(EscapeBackward, ctx => {
                if (ctx.Direction > 0f)
                {
                    return(DirectionalSmash(Direction.Left)(ctx));
                }
                else
                {
                    return(DirectionalSmash(Direction.Right)(ctx));
                }
            })
            .AddTransition(Escape, DirectionInput(Direction.Down));
            new[] { Escape, EscapeForward, EscapeBackward }.AddTransitionTo(Shield.Main);

            Builder.WithDefaultState(Idle);
            BuildCharacterController();
            return(Builder.Build());
        }
예제 #5
0
 internal StateController(StateControllerBuilder builder)
 {
     DefaultState = builder.DefaultState;
     States       = new ReadOnlyCollection <State>(builder.States.ToArray());
 }