void ReadAnimAttribute(XmlNode node)
    {
        Dictionary <string, PlayerAnimAttribute.AnimAttr> attrs = new Dictionary <string, PlayerAnimAttribute.AnimAttr>();

        foreach (XmlNode animItem in node.ChildNodes)
        {
            PlayerAnimAttribute.AnimAttr animAttr = new PlayerAnimAttribute.AnimAttr();
            XmlElement animItemElem = animItem as XmlElement;
            animAttr.strAnim = animItemElem.GetAttribute("name");
            XmlNodeList lstNodes = animItemElem.SelectNodes("./KeyEvent");
            animAttr.keyFrame = new List <PlayerAnimAttribute.KeyFrame>();

            PlayerAnimAttribute.KeyFrame kf = new PlayerAnimAttribute.KeyFrame("null", 0);
            foreach (XmlNode keyEvent in lstNodes)
            {
                XmlElement keyEventElem = keyEvent as XmlElement;
                string     id           = keyEventElem.GetAttribute("id");
                int        frame        = int.Parse(keyEventElem.GetAttribute("x"));

                if (id == "MoveToStartPos")
                {
                    IM.Vector3 param = IM.Vector3.zero;
                    XmlElement elem  = keyEventElem.SelectSingleNode("./Param") as XmlElement;
                    if (elem != null)
                    {
                        param = XmlUtils.XmlGetAttr_Vec3(elem);
                    }
                    kf = new PlayerAnimAttribute.KeyFrame_MoveToStartPos(id, frame, param);
                }
                else if (id == "OnLayupShot")
                {
                    IM.Vector3 param = IM.Vector3.zero;
                    XmlElement elem  = keyEventElem.SelectSingleNode("./Param") as XmlElement;
                    if (elem != null)
                    {
                        param = XmlUtils.XmlGetAttr_Vec3(elem);
                    }
                    kf = new PlayerAnimAttribute.KeyFrame_LayupShootPos(id, frame, param);
                }
                else if (id == "blockable")
                {
                    int        blockframe = 0;
                    XmlElement elem       = keyEventElem.SelectSingleNode("./Param") as XmlElement;
                    if (elem != null)
                    {
                        blockframe = XmlUtils.XmlGetAttr_Int(elem, "x");
                    }
                    kf = new PlayerAnimAttribute.KeyFrame_Blockable(id, frame, blockframe);
                }
                else if (id == "DefenderSpasticity")
                {
                    int        length = 0;
                    XmlElement elem   = keyEventElem.SelectSingleNode("./Param") as XmlElement;
                    if (elem != null)
                    {
                        length = XmlUtils.XmlGetAttr_Int(elem, "x");
                    }
                    kf = new PlayerAnimAttribute.KeyFrame_DefenderSpasticity(id, frame, length);
                }
                else if (id == "RotateToBasketAngle")
                {
                    IM.Number  angle = IM.Number.zero;
                    XmlElement elem  = keyEventElem.SelectSingleNode("./Param") as XmlElement;
                    if (elem != null)
                    {
                        angle = XmlUtils.XmlGetAttr_Number(elem, "x");
                    }
                    kf = new PlayerAnimAttribute.KeyFrame_RotateToBasketAngle(id, frame, angle);
                }
                else
                {
                    kf = new PlayerAnimAttribute.KeyFrame(id, frame);
                }

                animAttr.keyFrame.Add(kf);
            }
            if (attrs.ContainsKey(animAttr.strAnim))
            {
                Debug.LogError("AnimInfo same key: " + animAttr.strAnim);
            }
            attrs.Add(animAttr.strAnim, animAttr);
        }
        playerAttributes.animItemAttrs.Add(node.Name, attrs);
    }
    override public void OnEnter(PlayerState lastState)
    {
        base.OnEnter(lastState);

        m_skillArea = m_match.mCurScene.mGround.GetArea(m_player);

        m_movingTime = IM.Number.zero;
        m_bMoving    = false;

        m_player.mStatistics.ReadyToCountShoot();
        IM.RootMotion rootMotion = m_player.animMgr.Play(m_curAction, !m_bMoving).rootMotion;
        rootMotion.Reset();

        PlayerAnimAttribute.AnimAttr layupAttr = m_player.m_animAttributes.GetAnimAttrById(Command.Layup, m_curAction);
        if (layupAttr == null)
        {
            Debug.LogError("Current action: " + m_curAction + " in layup, skill id: " + m_curExecSkill.skill.id);
        }

        IM.Number frameRate = m_player.animMgr.GetFrameRate(m_curAction);
        m_player.m_blockable.Init(layupAttr, frameRate);

        IM.Number  fTimeFromRotateToBasket = new IM.Number(layupAttr.GetKeyFrame("RotateToBasket").frame) / frameRate;
        IM.Vector3 vPlayer2Basket          = m_basket.m_vShootTarget - m_player.position;
        vPlayer2Basket.y = IM.Number.zero;
        IM.Vector3 dirPlayerToBasket = vPlayer2Basket.normalized;
        rootMotion.dirMove = dirPlayerToBasket;

        IM.Number fAngle = IM.Vector3.Angle(m_player.forward, dirPlayerToBasket);
        m_turningSpeed = IM.Math.Deg2Rad(fAngle) / fTimeFromRotateToBasket;
        m_rotateTo     = RotateTo.eBasket;
        m_rotateType   = RotateType.eSmooth;
        m_bMoveForward = false;

        PlayerAnimAttribute.KeyFrame_MoveToStartPos kf_mts = layupAttr.GetKeyFrame("MoveToStartPos") as PlayerAnimAttribute.KeyFrame_MoveToStartPos;
        IM.Vector3 vToBasketOffset = kf_mts.toBasketOffset;
        IM.Vector3 vRimHPos        = m_basket.m_vShootTarget;
        vRimHPos.y = IM.Number.zero;

        m_vMovePos = -dirPlayerToBasket * vToBasketOffset.x + vRimHPos;
        //Debugger.Instance.DrawSphere("layup", m_vMovePos, 0.1f);
        m_movingTimeThreshold = kf_mts.frame / frameRate;

        IM.Vector3 vMovePos2Bakset = m_vMovePos - m_player.position;
        IM.Number  fSameDir        = IM.Vector3.Dot(vMovePos2Bakset, vPlayer2Basket);
        bool       bRush           = false;

        if (vPlayer2Basket.magnitude < vMovePos2Bakset.magnitude || fSameDir < 0 || kf_mts.frame == 0)
        {
            m_speed = IM.Vector3.zero;
        }
        else
        {
            m_speed = (m_vMovePos - m_player.position) / (kf_mts.frame / frameRate);
            bRush   = true;
        }

        PlayerAnimAttribute.KeyFrame_LayupShootPos kf_layup = layupAttr.GetKeyFrame("OnLayupShot") as PlayerAnimAttribute.KeyFrame_LayupShootPos;
        PlayerAnimAttribute.KeyFrame kf_leaveGround         = layupAttr.GetKeyFrame("OnLeaveGround") as PlayerAnimAttribute.KeyFrame;

        IM.Vector3 vPosLayup, vPosLeaveGround;
        m_player.GetNodePosition(SampleNode.Root, m_curAction, kf_layup.frame / frameRate, out vPosLayup);
        m_player.GetNodePosition(SampleNode.Root, m_curAction, kf_leaveGround.frame / frameRate, out vPosLeaveGround);
        if (vPosLayup == vPosLeaveGround)
        {
            Debug.LogError("PlayerState_Layup, vPosLayup equals vPosLeaveGround");
        }

        //TODO 这段代码不知道什么意思,待探讨
        //IM.Vector3 posLeaveGround = m_player.position + IM.Vector3.DotForNumber(vPosLeaveGround, dirPlayerToBasket) * dirPlayerToBasket;
        //IM.Vector3 posLayup = m_player.position + IM.Vector3.DotForNumber(vPosLayup, dirPlayerToBasket) * dirPlayerToBasket;

        IM.Number deltaDistance = (vPosLayup - vPosLeaveGround).magnitude;
        if (deltaDistance == IM.Number.zero)
        {
            Debug.LogError("PlayerState_Layup, DeltaDistance is 0.");
        }
        IM.Number fOrigLayupOffset = vToBasketOffset.x - deltaDistance;
        IM.Number fPlayer2Basket   = vPlayer2Basket.magnitude;
        IM.Number fLayupDist       = fPlayer2Basket - fOrigLayupOffset;

        if (bRush)
        {
            rootMotion.scale = IM.Number.one;
        }
        else
        {
            rootMotion.scale = IM.Math.Max(fLayupDist / deltaDistance, IM.Number.zero);
        }

        m_bRateFixed = false;
        _CalcLayupRate();

        if (m_speed != IM.Vector3.zero)
        {
            m_bMoving = true;
            m_player.animMgr.GetPlayInfo(m_curAction).enableRootMotion = false;
        }

        GameSystem.Instance.mClient.mPlayerManager.IsolateCollision(m_player, true);

        if (m_skillArea == Area.eNear)
        {
            ++m_player.mStatistics.data.layup_near_shoot;
        }
        else if (m_skillArea == Area.eMiddle)
        {
            ++m_player.mStatistics.data.layup_mid_shoot;
        }
    }