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); }