本次将创建玩家控制类,实现玩家基本移动和跑跳蹲等功能
一、定义玩家控制中所需的变量
1.定义常规速度、走跳速度、疾跑速度、跑跳速度、走蹲速度、走蹲跳速度
2.定义变量speed和jumpSpeed来保存玩家当前状态应有的速度
如在疾跑状态时 :speed应为sprintSpeed,jumpSpeed为 sprintJumpSpeed;
在蹲下状态时:speed应为crouchSpeed,jumpSpeed为 crouchJumpSpeed;
public float sprintSpeed = 10;//疾跑速度
public float sprintJumpSpeed = 8;//跑跳速度
public float normalSpeed = 6;//正常速度
public float normalJumpSpeed = 7;//正常走跳速度
public float crouchSpeed = 2;//蹲走速度
public float crouchJumpSpeed = 5;//蹲跳速度
public float crouchDeltaHeight = 0.5f;//蹲下高度
public float gravity = 20;//重力
public float cameraMoveSpeed = 8;//相机移动速度
public AudioClip jumpAudio;//跳跃音频
private float speed;//玩家当前速度
private float jumpSpeed;//玩家跳跃速度
private Transform mainCamera;//相机变换组件
private float standardCamHeight;//正常相机高度
private float crouchingCamHeight;//蹲下相机高度
private bool grounded=false;//玩家是否在地面
private bool walking = false;//玩家是否在行走
private bool crouching = false;//玩家是否在蹲
private bool stopCrouching = false;//玩家是否停止蹲
private bool runing = false;//玩家是否在跑
private Vector3 normaControllerCenter= Vector3.zero;//角色控制器中心点坐标
private float normalControllerHeight = 0;//正常控制器高度
private float timer = 0;//计时器
private CharacterController controller;
private AudioSource audioSource;
private fps_PlayerParameter parameter;
private Vector3 moveDirection= Vector3.zero;//角色移动方向
二、定义玩家状态的枚举enum
将玩家五种基本状态列举
初始化玩家状态为None
用属性对玩家状态的字段进行保护,并加以限制
如当“walking”时 玩家状态state为walk
public enum PlayerState
{
None,
Idle,
Walk,
Crouch,
Run,
}
private PlayerState state = PlayerState.None;
public PlayerState State
{
get
{
if (walking)
state = PlayerState.Walk;
else if (crouching)
state = PlayerState.Crouch;
else if (runing)
state = PlayerState.Run;
else state = PlayerState.Idle;
return state;
}
}
三、定义计算当前速度的方法
利用前面所定义的枚举,用属性进行判断,用对应的状态去定义四种状态(Idle、Walk、Crouch、Run)的Speed和jumpSpeed,方便后面只要角色状态切换,对应的speed和jumpSpeed自动随之切换。将状态进行封装,后面加状态,如趴下等,只需修改状态和计算速度的相关方法。
private void CurrentSpeed()
{ switch (State)
{
case PlayerState.Idle:
speed = normalSpeed;
jumpSpeed= normalJumpSpeed;
break;
case PlayerState.Walk:
speed = normalSpeed;
jumpSpeed = normalJumpSpeed;
break;
case PlayerState.Crouch:
speed = crouchSpeed;
jumpSpeed = crouchJumpSpeed;
break;
case PlayerState.Run:
speed =sprintSpeed;
jumpSpeed =sprintJumpSpeed;
break;
}
}
四、定义脚步音频管理方法
本游戏前提设定为:只有走、跑状态下有脚步声音
audioSource.pitch:来定义两种状态不同的脚步速度
audioSource.isPlaying :判断音频片段是否正在播放
audioSource.Play():播放音频片段
audioSource.Stop():停止播放音频片段
private void AudioManagement()
{
//若状态为走路状态 音频播放速度为1
if (State==PlayerState.Walk)
{
audioSource.pitch = 1.0f;//音频倍速
if (!audioSource.isPlaying)
{
audioSource.Play();
}
}
else if (State == PlayerState.Run)
{
audioSource.pitch = 1.3f;//音频倍速
if (!audioSource.isPlaying)
{
audioSource.Play();
}
}
else audioSource.Stop();//除走和跑两种状态之外 其他状态没有声音
}
五、定义下蹲更新相机方法
用crouching判断两种状态:在下蹲/不在下蹲
用mainCamera.localPosition.y相机当前高度和crouchDeltaHeight*Time.deltaTime*cameraMoveSpeed当前帧相机即将的移动量作比较
来判断相机是否蹲完或者站完。
private void UpdateCrouch()
{ //若crouching为真
if (crouching)
{ //若主摄像机高度大于正在蹲的高度
if (mainCamera.localPosition.y>crouchingCamHeight)
{ //若相机高度减去当前帧相机移动量<蹲后相机高度
if (mainCamera.localPosition.y-(crouchDeltaHeight*Time.deltaTime*cameraMoveSpeed)<crouchingCamHeight)
{
mainCamera.localPosition=new Vector3(mainCamera.localPosition.x,crouchingCamHeight,mainCamera.localPosition.z);
}//否则按帧减小
else { mainCamera.localPosition -= new Vector3(0, (crouchDeltaHeight * Time.deltaTime * cameraMoveSpeed),0); }
}//若不大于,就直接赋值
else { mainCamera.localPosition = new Vector3(mainCamera.localPosition.x, crouchingCamHeight, mainCamera.localPosition.z); }
}else//若crouching不为真
{
if (mainCamera.localPosition.y < standardCamHeight)
{
if (mainCamera.localPosition.y + (crouchDeltaHeight * Time.deltaTime * cameraMoveSpeed) > standardCamHeight)
{
mainCamera.localPosition = new Vector3(mainCamera.localPosition.x, standardCamHeight, mainCamera.localPosition.z);
}
else { mainCamera.localPosition += new Vector3(0, (crouchDeltaHeight * Time.deltaTime * cameraMoveSpeed), 0); }
}
else { mainCamera.localPosition = new Vector3(mainCamera.localPosition.x, standardCamHeight, mainCamera.localPosition.z); }
}
}
六、移动更新方法
当处于地板上时,进行移动。将键盘输入方向inputMoveVector交给移动变量moveDirection
注意这里要将moveDirection转换为自身坐标,因为移动方向要随视角方向改变。
对moveDirection进行操作后 将moveDirection赋值给角色控制器controller
transform.TransformDirection():世界坐标变为自身坐标;
AudioSource.PlayClipAtPoint():在某点播放音频片段;
controller.Move():移动(不自动加grativity);
controller.SimpleMove():移动(自动加grativity);
CollisionFlags.CollidedBelow 底部发生了碰撞"flags & CollisionFlags.CollidedBelow"返回1;
CollisionFlags.CollidedNone 没发生碰撞"flags & CollisonFlags.CollidedNone"返回1;
CollisionFlags.CollidedSides 四周发生碰撞"flags & CollisionFlags.CollidedSides"返回1;
CollisionFlags.CollidedAbove 顶端发生了碰撞"flags & CollisionFlags.CollidedAbove"返回1;
&:按位与运算符 两边都为1 结果才为1;
private void UpdateMove()
{ //若在地板上
if (grounded)
{
//将键盘输入移动交给角色移动变量
moveDirection = new Vector3(parameter.inputMoveVector.x, 0, parameter.inputMoveVector.y);
//将移动的世界坐标转为自身坐标 因为移动方向要随视角方向改变
moveDirection = transform.TransformDirection(moveDirection);
moveDirection *= speed;
//若跳跃
if (parameter.inputJump)
{
moveDirection.y = jumpSpeed;
AudioSource.PlayClipAtPoint(jumpAudio, transform.position);
//跳跃后速度增加 因此重新计算速度
CurrentSpeed();
}
}
//赋予重力
moveDirection.y -= gravity * Time.deltaTime;
//将moveDirection交给Controller
CollisionFlags falgs = controller.Move(moveDirection * Time.deltaTime);
//若有碰撞信息且底部发生了碰撞 grounded为真
grounded = (falgs & CollisionFlags.CollidedBelow) != 0;
//CollisionFlags.CollidedBelow 底部发生了碰撞"flags & CollisionFlags.CollidedBelow"返回1;
//CollisionFlags.CollidedNone 没发生碰撞"flags & CollisonFlags.CollidedNone"返回1;
//CollisionFlags.CollidedSides 四周发生碰撞"flags & CollisionFlags.CollidedSides"返回1;
//CollisionFlags.CollidedAbove 顶端发生了碰撞"flags & CollisionFlags.CollidedAbove"返回1;
//若有移动且在地面
if (Mathf.Abs(moveDirection.x) > 0 && grounded || Mathf.Abs(moveDirection.z) > 0 && grounded)
{
//若输入冲刺
if (parameter.inputSprint)
{
walking = false;
runing = true;
crouching = false;
}
//若输入蹲下
else if (parameter.inputCrouch)
{
walking = false;
runing = false;
crouching = true;
}
//否则为走
else
{
walking = true;
runing = false;
crouching = false;
}
}
//若无移动或者不在地面
else
{ //当前状态为走 那就不走
if (walking) walking = false;
if (runing) runing = false;
if (parameter.inputCrouch) crouching = true;
else crouching = false;
}
if (crouching)
{
controller.height = normalControllerHeight - crouchDeltaHeight;
controller.center = new Vector3(0, (normaControllerCenter.y - crouchDeltaHeight / 2) , 0);
}
else
{
controller.height = normalControllerHeight;
controller.center = normaControllerCenter;
}
UpdateCrouch();
CurrentSpeed();
}