示例#1
0
    public override void enter(StateParam param)
    {
        // 隐藏静态拖尾,并显示动态拖
        mPlayer.activeTrail(false);

        // 将玩家设置到起点位置
        GameTrackBase curTrack = mRaceSystem.getCurGameTrack();

        ObjectTools.MOVE_OBJECT(mPlayer, curTrack.mStartPointList[mPlayer.getCharacterData().mStartIndex]);

        // 添加休闲状态
        CommandCharacterStop cmdStop = newCmd(out cmdStop);

        cmdStop.mFadeStop     = false;
        cmdStop.mFadeStanding = false;
        pushCommand(cmdStop, mPlayer);

        // 使角色朝向前方
        Vector3 point0 = mWayPointManager.getPoint(0);
        Vector3 point1 = mWayPointManager.getPoint(1);
        float   dir    = MathUtility.getAngleFromVector(point1 - point0) * Mathf.Rad2Deg;

        ObjectTools.ROTATE_OBJECT(mPlayer, new Vector3(0.0f, dir, 0.0f));

        // 启用自行车物理组件,启用硬件速度组件,里程计算组件,手动更新角色位置后再挂接摄像机
        mPlayer.activeFirstComponent <CharacterTrackMileage>();
        mPlayer.activeFirstComponent <CharacterBikePhysics>();
        mPlayer.activeFirstComponent <CharacterSpeedHardware>();
        mPlayer.getFirstComponent <CharacterBikePhysics>().correctTransform();
        if (mPlayer.isType(CHARACTER_TYPE.CT_AI))
        {
            mPlayer.activeFirstComponent <CharacterControllerAI>();
        }
        // 只有主角才挂摄像机
        if (mPlayer.isType(CHARACTER_TYPE.CT_MYSELF))
        {
            CommandCameraLinkTarget cmd = newCmd(out cmd);
            cmd.mTarget       = mPlayer;
            cmd.mLinkerName   = "SmoothFollow";
            cmd.mLookatOffset = GameDefine.CAMERA_LOOKAT_OFFSET;
            cmd.mImmediately  = true;
            cmd.setRelativePosition(GameDefine.CAMERA_RELATIVE);
            pushCommand(cmd, mCameraManager.getMainCamera());
            CameraLinkerSmoothFollow linkerSmoothFollow = mCameraManager.getMainCamera().getComponent("SmoothFollow") as CameraLinkerSmoothFollow;
            linkerSmoothFollow.setCheckGroundLayer(GameUtility.mGroundLayer);
        }
    }
    public override void update(float elapsedTime)
    {
        // 转向判断
        float turnAngle = 0.0f;

        if (mCharacter.getProcessTurn())
        {
            turnAngle = mData.mTurnAngle;
            // 只有转向角度增加时才会判断转弯
            if (Mathf.Abs(turnAngle) > Mathf.Abs(mLastTurnAngle) && Mathf.Abs(turnAngle) >= GameDefine.TURN_ANGLE)
            {
                CommandCharacterTurn cmdTurn = newCmd(out cmdTurn, false);
                cmdTurn.mAngle = turnAngle;
                pushCommand(cmdTurn, mCharacter);
            }
            mLastTurnAngle = turnAngle;
        }
        // 方向
        if (!MathUtility.isFloatZero(turnAngle))
        {
            float angleDelta = turnAngle * elapsedTime * mData.mTurnSensitive;
            // 一帧的旋转角度不能太大
            MathUtility.clamp(ref angleDelta, -GameDefine.MAX_REFLECT_ANGLE, GameDefine.MAX_REFLECT_ANGLE);
            mData.mSpeedRotation.y += angleDelta;
        }
        // 根据运动方向调整角色朝向
        mCharacter.setYaw(mData.mSpeedRotation.y);
        // 位置
        Vector3 moveDir      = MathUtility.getVectorFromAngle(mData.mSpeedRotation.y * Mathf.Deg2Rad);
        Vector3 lastPosition = mCharacter.getPosition();

        if (!MathUtility.isFloatZero(mData.mSpeed) || !MathUtility.isFloatZero(mData.mVerticalSpeed))
        {
            Vector3 delta = (moveDir * mData.mSpeed + Vector3.up * mData.mVerticalSpeed) * elapsedTime;
            mCharacter.setPosition(lastPosition + delta);
        }

        // 检测前方是否有墙,多次检测确保角色不会穿透墙壁,最多只检测3次,避免在极端情况出现无线循环的现象
        const int HIT_WALL_TEST_COUNT = 3;

        for (int i = 0; i < HIT_WALL_TEST_COUNT; ++i)
        {
            // 计算与墙面的碰撞
            // 从运动之前的位置向运动方向发射一条射线
            Vector3 faceDir = MathUtility.getDirectionFromDegreeYawPitch(mData.mSpeedRotation.y, mCharacter.getRotation().x);
            Ray     dirRay  = new Ray(lastPosition, faceDir);
#if UNITY_EDITOR
            Debug.DrawLine(dirRay.origin, dirRay.origin + dirRay.direction * 10.0f, Color.red);
#endif
            RaycastHit dirRet;
            bool       hitWall = false;
            if (Physics.Raycast(dirRay, out dirRet, 1000.0f, 1 << GameUtility.mWallLayer))
            {
                float wallDis = MathUtility.getLength(dirRet.point - lastPosition);
                // 减去上一次的位置与当前位置的差值后,如果小于单车前半部分的长度则认为是碰到了墙,计算出反弹方向
                if (wallDis - MathUtility.getLength(lastPosition - mCharacter.getPosition()) < GameDefine.BIKE_FRONT_LENGTH)
                {
                    Vector3 newDir       = MathUtility.getReflection(faceDir, dirRet.normal);
                    float   reflectAngle = MathUtility.getAngleBetweenVector(newDir, dirRet.normal) * Mathf.Rad2Deg;
                    // 反射角需要限定到一定范围
                    if (reflectAngle < GameDefine.MIN_REFLECT_ANGLE || reflectAngle > GameDefine.MAX_REFLECT_ANGLE)
                    {
                        MathUtility.clamp(ref reflectAngle, GameDefine.MIN_REFLECT_ANGLE, GameDefine.MAX_REFLECT_ANGLE);
                        // 如果法线与反射方向相同,也就是垂直撞墙,则反射角默认为正
                        // 否则通过叉乘的结果来判断反射角的符号
                        if (!MathUtility.isVectorZero(MathUtility.normalize(newDir) - MathUtility.normalize(dirRet.normal)))
                        {
                            Vector3 cross = MathUtility.normalize(Vector3.Cross(newDir, dirRet.normal));
                            if (cross.y > 0.0f)
                            {
                                reflectAngle = -reflectAngle;
                            }
                        }
                        Quaternion quat = new Quaternion();
                        quat.eulerAngles = new Vector3(0.0f, reflectAngle, 0.0f);
                        newDir           = MathUtility.rotateVector3(dirRet.normal, quat);
                    }
                    // 碰撞墙壁后将摄像机的跟随速度减慢
                    if (mCharacter.isType(CHARACTER_TYPE.CT_MYSELF))
                    {
                        CameraLinkerSmoothFollow smoothFollow = mCameraManager.getMainCamera().getCurLinker() as CameraLinkerSmoothFollow;
                        smoothFollow.setFollowPositionSpeed(1.0f);
                    }
                    mData.mSpeedRotation.y = MathUtility.getVectorYaw(newDir) * Mathf.Rad2Deg;
                    // 此处仍然同步设置角色的旋转,暂时保证速度方向与角色的旋转值一致
                    mCharacter.setYaw(mData.mSpeedRotation.y);
                    // 根据碰撞的入射角度,计算出速度损失量
                    float inAngle = MathUtility.getAngleBetweenVector(-faceDir, dirRet.normal) * Mathf.Rad2Deg;
                    CommandCharacterHitWall cmdHitWall = newCmd(out cmdHitWall, false);
                    cmdHitWall.mAngle = inAngle;
                    pushCommand(cmdHitWall, mCharacter);
                    hitWall = true;
                }
            }
            // 已经没有接触墙壁,则退出循环
            if (!hitWall)
            {
                break;
            }
        }
        // 调整自身位置
        correctTransform(elapsedTime);

        // 在地面上时,根据自身的旋转值,判断地面的坡度,计算当前阻力值
        if (!mCharacter.hasState(PLAYER_STATE.PS_JUMP) && mCharacter.isType(CHARACTER_TYPE.CT_MYSELF))
        {
            float pitch = mCharacter.getPitch();
            MathUtility.adjustAngle180(ref pitch);
            float friction = -1.0f;
            // 先将俯仰角限制在一定范围内
            MathUtility.clamp(ref pitch, mMaxUphillAngle, mMaxDownhillAngle);
            // 判断是否在正常范围内
            if (MathUtility.isInRange(pitch, mMinUphillAngle, mMinDownhillAngle))
            {
                friction = mNormalFriction;
            }
            // 是否为上坡,当角度为负时为上坡
            else if (pitch < mMinUphillAngle)
            {
                float percent = (pitch - mMinUphillAngle) / (mMaxUphillAngle - mMinUphillAngle);
                friction = MathUtility.lerp(mMinUphillFriction, mMaxUphillFriction, percent);
            }
            else if (pitch > mMinDownhillAngle)
            {
                float percent = (pitch - mMinDownhillAngle) / (mMaxDownhillAngle - mMinDownhillAngle);
                friction = MathUtility.lerp(mMinDownhillFriction, mMaxDownhillFriction, percent);
            }
            if (mCurFriction != (int)friction)
            {
                mCurFriction = (int)friction;
                SerialPortPacketFriction packet = mUSBManager.createPacket(out packet, COM_PACKET.CP_FRICTION);
                packet.setFriction((byte)mCurFriction);
                mUSBManager.sendPacket(packet);
                if (mCharacter.isType(CHARACTER_TYPE.CT_MYSELF))
                {
                    mScriptDebugInfo.notityFriction(mCurFriction);
                }
            }
            if (mCharacter.isType(CHARACTER_TYPE.CT_MYSELF))
            {
                mScriptDebugInfo.notityPitch(pitch);
            }
        }
        base.update(elapsedTime);
    }