/// <summary>
        /// Jump or second jump
        /// </summary>
        /// <param name="parameters"></param>
        private void Jump(TemporaryParameters parameters)
        {
            // Clear jump flags
            if (parameters.IsGrounded)
            {
                this.JumpTrigger.Clear();
                this.LandingTrigger.Clear();
                this.SecondJumpTrigger.Clear();
                parameters.VerticalVelocity = 0f;
            }

            //if (this.AnimatorStates.IsNotInTransition
            //    && this.AnimatorStates.HasTag(clearSecondJumpFlagTag))
            //{
            //    this.isSecondJumpDone = false;
            //}

            //if (this.AnimatorStates.IsNotInTransition
            //    && !this.JumpState.IsActive)
            //{
            //    this.JumpRequest = false;
            //}

            // Jump
            if (parameters.Desired.Jump)// && !this.JumpRequest)
            {
                if (parameters.IsGrounded)
                {
                    if (this.IdleState.IsActive || this.LocoState.IsActive)
                    {
                        this.JumpRequest = true;

                        this.JumpTrigger.Set();
                        this.LandingTrigger.Clear();
                        this.SecondJumpTrigger.Clear();

                        this.isSecondJumpDone = false;

                        //Debug.Log("jump");
                    }
                }
                else
                {
                    if ((this.JumpState.IsActive || this.LandState.IsActive)
                        && !this.isSecondJumpDone)
                    {

                        var vd = parameters.Desired.HorizontalVelocity * velocityCoefficient * this.moveDirection * 0.3f;

                        if (parameters.HorizontalVelocity * vd < -0.0001)
                        {
                            parameters.HorizontalVelocity = vd;

                        }
                        else if (parameters.HorizontalVelocity * vd > 0.0001)
                        {
                            parameters.HorizontalVelocity += vd * 0.3f;
                        }
                        else
                        {
                            parameters.HorizontalVelocity += vd;
                        }

                        parameters.VerticalVelocity = jumpSpeed * 0.8f;

                        this.SecondJumpTrigger.Set();
                        this.LandingTrigger.Clear();
                        this.JumpTrigger.Clear();

                        this.isSecondJumpDone = true;

                    }
                }
            }

            // Jump
            if (this.JumpState.IsActive && this.JumpRequest && this.JumpStartTrigger)
            {
                parameters.VerticalVelocity = jumpSpeed;
                this.JumpRequest = false;
            }
        }
        /// <summary>
        /// Walk or run
        /// </summary>
        /// <param name="parameters"></param>
        private void HorizontalMove(TemporaryParameters parameters)
        {
            var velocity = parameters.Desired.HorizontalVelocity
                * (parameters.Desired.IsRunning ? 1f : 0.4f);

            // Change Direction
            if (velocity * this.moveDirection < 0 && parameters.IsGrounded)
            {
                this.ToggleMoveDirection();
                parameters.HorizontalVelocity *= -1f;
            }

            this.targetObject.transform.rotation = Quaternion.Euler(0, this.moveDirection * -70 + 10, 0);

            velocity *= this.moveDirection;

            // Horizontal Move
            if (parameters.IsGrounded)
            {
                if (this.Lan2State.IsActive)
                {
                    velocity = 0;
                }

                this.AnimatorSpeed.Value = velocity * 5f;
                //this.Animator.SetFloat("Speed", velocity * 5f);

                var desiredSpeed = velocity > 0 ? velocity * 0.6f : 0;

                this.currentSpeed = desiredSpeed
                    - (desiredSpeed - this.currentSpeed) * this.accelTime;// / Time.deltaTime;

                if (this.AnimatorStates.HasTag(inAttackTag))
                {
                    parameters.HorizontalVelocity *= 0.9f;
                }
                else
                {
                    parameters.HorizontalVelocity = this.currentSpeed * velocityCoefficient;
                }
            }
            else
            {
                this.currentSpeed = 0f;
            }
        }
        /// <summary>
        /// attack, damage
        /// </summary>
        /// <param name="parameters"></param>
        private void AttackOrDamage(TemporaryParameters parameters)
        {
            // not in attack
            if (this.AnimatorStates.IsNotInTransition
                //&& this.AnimatorStates.HasTag(damagedTag))
                && !this.AnimatorStates.HasTag(inAttackTag))
            {
                this.isInAttack = false;
                this.AttackCollider.ClearCollider();

                this.Attack2.Value = false;
                this.Attack2Finish.Value = false;
                this.isRushHit = false;
                this.rushHitCount = 0;
                this.finishPunchFlag = false;

                if (this.AnimatorStates.HasTag(damagedTag))
                {
                    this.Attack1.Value = false;
                    this.Attack2.Value = false;
                    this.Attack2_0.Value = false;
                    //this.Animator.SetBool("Attack1", false);
                    //this.Animator.SetBool("Attack2", false);
                    //this.Animator.SetBool("Attack2_0", false);
                    this.finishPunchFlag = false;
                    this.Attack2Finish.Value = false;
                }

            }

            if(this.finishPunchFlag
                && this.AnimatorStates.IsNotInTransition
                && this.AnimatorStates.HasTag(finishPunchAttackTag))
            {
                this.AttackCollider.Information.Power = 2;
                //this.AttackCollider.Information.StayingTime = 1f;
                this.AttackCollider.Information.Type = AttackType.Fly;
                this.AttackCollider.Information.Effect = EffectType.Burst;

                this.Attack2Finish.Value = false;
                this.finishPunchFlag = false;
            }

            this.VitalBody.IsEnabled = this.Invincible.Value < 0.6;

            var attackEnabled = this.AttackEnabled.Value > 0.5f;// this.Animator.GetFloat("AttackEnabled") > 0.5f;

            if (attackEnabled && this.AnimatorStates.HasTag(punchAttackTag))
            {
                this.StartAttack("Punch");
                //Debug.Log("p1");
            }
            else if (attackEnabled && this.AnimatorStates.HasTag(punch2AttackTag))
            {
                var attackStarted = this.StartAttack("Punch2");
                if (attackStarted)
                {
                    if (this.isRushHit)
                    {
                        this.rushHitCount++;
                    }
                    else {
                        this.rushHitCount = 0;
                    }

                    this.isRushHit = false;

                    Debug.Log(this.rushHitCount.ToString());
                }
            }
            else if (attackEnabled && this.AnimatorStates.HasTag(kickAttackTag))
            {
                this.StartAttack("Kick");
            }
            else if (!attackEnabled)
            {
                this.AttackCollider.ClearCollider();
                this.previousAttackState = 0;
            }

            //if (this.AnimatorStates.IsNotInTransition)
            {
                if (this.StrongDamageState.IsActive)
                {
                    parameters.HorizontalVelocity = (parameters.Desired.BackDamage ? 1f : -1f)
                        * this.DamageMove.Value * (parameters.IsGrounded ? 1f : 0.2f);
                    //Debug.Log(parameters.HorizontalVelocity.ToString());
                }
                else if (this.WeakDamageState.IsActive)
                {
                    parameters.HorizontalVelocity = (parameters.Desired.BackDamage ? 1f : -1f)
                        * this.DamageMove.Value * (parameters.IsGrounded ? 0.1f : 0.02f);
                }
                else if (this.FlyDamageState.IsActive)
                {
                    parameters.HorizontalVelocity = (parameters.Desired.BackDamage ? 1f : -1f)
                        * this.DamageMove.Value * 5.0f;
                }
                else if (this.AnimatorStates.HasTag(damagedTag))
                {
                    parameters.HorizontalVelocity = 0f;
                }
            }

            var attack2Flag = false;

            if (parameters.Desired.IsDamaged)
            {
                // Damage

                switch (parameters.Desired.DamageType)
                {
                    case AttackType.Weak:
                        this.WeakDamageTrigger.Set();
                        break;
                    case AttackType.Strong:
                        this.StrongDamageTrigger.Set();
                        break;
                    //case AttackType.Stun:
                    //    this.StunDamageTrigger.Set();
                    //    break;
                    case AttackType.Fly:
                        parameters.VerticalVelocity += 3f;
                        this.FlyDamageTrigger.Set();
                        break;
                    default:
                        this.WeakDamageTrigger.Set();
                        break;
                }
                this.isSecondJumpDone = false;
                this.AttackCollider.ClearCollider();
            }
            else
            {

                // Attack

                if (this.AnimatorStates.HasTag(attackableTag)
                    && !this.AnimatorStates.IsInTransition
                    && !this.isInAttack)
                {
                    if (parameters.Desired.Kick)
                    {
                        this.isInAttack = true;
                        this.Attack1.Value = true;
                        //this.Animator.SetBool("Attack1", true);
                        parameters.VerticalVelocity += jumpSpeed * 0.2f;
                        this.AttackCollider.Information.Power = 3;
                        this.AttackCollider.Information.Type = AttackType.Strong;
                        this.AttackCollider.Information.Effect = EffectType.Burst;
                    }
                    else if (parameters.Desired.Punch)
                    {
                        this.isInAttack = true;
                        this.Attack2_0.Value = true;
                        //this.Animator.SetBool("Attack2_0", true);
                        this.AttackCollider.Information.Power = 2;
                        this.AttackCollider.Information.Type = AttackType.Weak;
                        this.AttackCollider.Information.Effect = EffectType.Burst;

                        this.isRushHit = false;
                        this.rushHitCount = 0;
                    }
                    else if (parameters.Desired.RushPunch)
                    {
                        this.isInAttack = true;
                        attack2Flag = true;
                        this.AttackCollider.Information.Power = 1;
                        this.AttackCollider.Information.Type = AttackType.Weak;
                        this.AttackCollider.Information.Effect = EffectType.BurstSmall;

                        this.isRushHit = false;
                        this.rushHitCount = 0;
                    }
                    else if ((this.IdleState.IsActive || this.RestState.IsActive) && parameters.Desired.Rest)
                    {
                        this.RestTrigger.Set();
                    }
                }
                else if (this.isInAttack
                    && this.AnimatorStates.HasTag(inRushAttackTag))
                {
                    if (parameters.Desired.RushPunch)
                    {
                        attack2Flag = true;
                        this.Attack2_0.Value = false;
                        //this.Animator.SetBool("Attack2_0", false);
                        this.AttackCollider.Information.Power = 1;
                        this.AttackCollider.Information.Type = AttackType.Weak;
                        this.AttackCollider.Information.Effect = EffectType.BurstSmall;
                    }
                }
            }

            if (!attack2Flag && this.rushHitCount >= 10 && this.Attack2.Value)
            {
                attack2Flag = true;
                //this.Attack2.Value = true;
                this.Attack2Finish.Value = true;

                this.isRushHit = false;
                this.rushHitCount = 0;

                this.finishPunchFlag = true;

                //this.AttackCollider.Information.Power = 2;
                ////this.AttackCollider.Information.StayingTime = 1f;
                //this.AttackCollider.Information.Type = AttackType.Fly;
                //this.AttackCollider.Information.Effect = EffectType.Burst;
            }

            this.Attack2.Value = attack2Flag;
            //this.Animator.SetBool("Attack2", attack2Flag);
        }
        /// <summary>
        /// gravity effect
        /// </summary>
        /// <param name="parameters"></param>
        private void Gravity(TemporaryParameters parameters)
        {
            // Gravity
            var gravity
                = (!isInAttack) ? 20f
                : (parameters.VerticalVelocity > 0) ? 10f
                : 5f;
            parameters.VerticalVelocity -= gravity * Time.deltaTime;

            // Landing
            var jumping = this.JumpState.IsActive || this.Jmp2State.IsActive;
            var stability = jumping ? this.JumpStability.Value : 0f;// this.Animator.GetFloat("JumpStability") : 0f;

            if ((parameters.VerticalVelocity < -jumpSpeed * 0.6f && jumping)
                || (jumping && parameters.IsGrounded && (stability > 0.8)))
            // || (Math.Abs(desiredVelocity.y) > 0.1 && desiredVelocity.y < this.jumpspeed * 0.5))))
            //!this.JumpRequest))// // || (!jumping && !isIdle && cc.isGrounded))
            {
                if (!this.LandState.IsActive)
                {
                    this.LandingTrigger.Set();
                    this.isSecondJumpDone = true;
                }
            }

            this.OnGrond.Value = parameters.IsGrounded;
            //this.Animator.SetBool("OnGround", parameters.IsGrounded);

            if (parameters.IsGrounded && !this.AnimatorStates.IsInTransition)
            {
                this.isSecondJumpDone = false;
            }
        }
        /// <summary>
        /// Use this for initialization
        /// </summary>
        protected override void OnStart()
        {
            base.OnStart();

            var model = AppCore.GetEnvironment(null).Characters.GetModel<HumanoidModel>();

            var children = this.transform.AsEnumerable().Where(y => y.tag.Equals("HumanoidModel")).ToArray();
            //var children = GameObject.FindGameObjectsWithTag("HumanoidModel");

            var index = model.ColorIndex;

            children.ForEach((c, i) => c.gameObject.SetActive(i == index));

            this.targetObject = children[index];

            var offsetCorrection = this.GetComponent<OffsetCorrection>();
            if (offsetCorrection != null)
            {
                offsetCorrection.TargetObject = this.targetObject;
            }

            this.Controller = gameObject.GetComponent<CharacterController>();
            this.Animator = this.targetObject.GetComponent<Animator>();

            this.InitializeAnimator();

            this.AttackCollider = this.transform.AsEnumerable()
                .First(x => x.name.Equals("AttackColliders")).GetComponent<AttackColliderController>();
            this.AttackCollider.Id = model.Id;

            this.AttackCollider.Guarding.Subscribe(y =>
            {
                model.OnGuarding(y);
                //Debug.Log(y.Id.ToString());
            }).AddTo(this.Disposables);

            this.AttackCollider.Hit
                .Subscribe(y =>
                {
                    this.isRushHit = true;
                })
                .AddTo(this.Disposables);

            var vital = this.transform.AsEnumerable()
                .First(x => x.name.Equals("VitalBody")).GetComponent<VitalController>();
            vital.Id = model.Id;
            vital.Damaged.Subscribe(y =>
            {
                model.OnDamaged(y);
                //Debug.Log(y.Id.ToString());
            }).AddTo(this.Disposables);
            this.VitalBody = vital;

            this.previousAttackState = 0;

            /*
            var prevIsRush = false;
            this.AnimatorStates.StateChanged
                .Subscribe(current =>
                {
                    var isRush = this.AnimatorStates.HasTag(inRushAttackTag);

                    if (isRush && prevIsRush)
                    {
                        //this.AttackCollider.Information.Power = 1;
                        //this.AttackCollider.ActivateCollider("Punch");
                    }

                    prevIsRush = isRush;
                })
                .AddTo(this.Disposables);
                */

            this.Velocity = Vector3.zero;
            this.currentSpeed = 0f;

            this.JumpRequest = false;
            this.isSecondJumpDone = false;
            this.isInAttack = false;

            this.isRushHit = false;
            this.rushHitCount = 0;
            this.finishPunchFlag = false;

            var pos = this.transform.position;
            pos.x = model.InitialPosition * -3f;
            this.transform.position = pos;

            this.moveDirection = 1f;// this.Model.Mirror ? -1f : 1f;
            if (model.Mirror)
            {
                this.ToggleMoveDirection();
            }

            var parameters = new TemporaryParameters(model.DesiredParameters);

            model.Updated.Subscribe(_ =>
            {
                // Check state
                this.AnimatorStates.CheckAll(this.Animator.IsInTransition(0),
                this.Animator.GetCurrentAnimatorStateInfo(0).fullPathHash);

                // store parameters
                //parameters.Input = model.DesiredParameters;
                parameters.IsGrounded = this.Controller.isGrounded;
                parameters.HorizontalVelocity = this.Velocity.z;
                parameters.VerticalVelocity = this.Velocity.y;

                this.AttackCollider.Information.SourcePositionHorizontal = this.transform.position.x;
                this.AttackCollider.Information.SourcePositionVertical = this.transform.position.y;

                // Action
                this.HorizontalMove(parameters);
                this.Jump(parameters);
                this.AttackOrDamage(parameters);
                this.Gravity(parameters);

                // Commit
                this.Velocity = new Vector3(0, parameters.VerticalVelocity, parameters.HorizontalVelocity);

                // Move character
                this.Controller.Move(transform.TransformDirection(this.Velocity) * Time.deltaTime);

                // update model
                model.ViewParameters.HorizontalPosition = this.transform.position.x;
                model.ViewParameters.VerticalPosition = this.transform.position.y;
                model.ViewParameters.Direction = this.moveDirection;
                //Debug.Log(this.transform.position.x.ToString() + ", " + this.transform.position.y.ToString() + ", " + this.transform.position.z.ToString());
            })
            .AddTo(this.Disposables);

            //this.Model = model;
        }