第一人称控制
视角移动
基本思路通过读入鼠标的X、Y轴的位移,从而对人物的方向和摄像机进行角度调整。
public class Player_move : MonoBehaviour
{
public Rigidbody Player;
private float Camera_speed;
void Start()
{
Camera_speed =2.0f;
rotation_y = 0f;
rotation_x = 0f;
}
void Update()
{
rotation_y += Input.GetAxis("Mouse Y");
rotation_x_player = Input.GetAxis("Mouse X");
rotation_x += Input.GetAxis("Mouse X");
Vector3 rotation_player = new Vector3(0, rotation_x_player*Camera_speed, 0);
Vector3 rotation_camare = new Vector3(-rotation_y * Camera_speed, 0, 0);
Player.transform.eulerAngles += rotation_player; //旋转玩家的X轴
Camera.main.transform.localEulerAngles = rotation_camare; //旋转相机的Y轴
}
}
但以上程序无法控制摄像机在Y轴上的旋转上限,会出现人向后看的情况。
eg:
因此需要对从鼠标中输入的数据进行处理(对上面的程序进行添加,后面有//*为添加的代码)
public class Player_move : MonoBehaviour
{
public Rigidbody Player;
private float Camera_speed;
private float y_max; //*
private float y_min; //*
void Start()
{
Camera_speed =2.0f;
rotation_y = 0f;
rotation_x = 0f;
y_max = 25f; //*
y_min = -25f; //*
}
void Update()
{
rotation_y += Input.GetAxis("Mouse Y");
rotation_x_player = Input.GetAxis("Mouse X");
rotation_x += Input.GetAxis("Mouse X");
rotation_y = anger_overflow(y_min, rotation_y, y_max); //*
Vector3 rotation_player = new Vector3(0, rotation_x_player*Camera_speed, 0);
Vector3 rotation_camare = new Vector3(-rotation_y * Camera_speed, 0, 0);
Player.transform.eulerAngles += rotation_player; //旋转玩家的X轴
Camera.main.transform.localEulerAngles = rotation_camare; //旋转相机的Y轴
}
float anger_overflow (float min,float anger,float max) //* 对角度进行处理
{
if (anger < min) return min;
if (anger > max) return max;
return anger;
}
}
通过这样角色的视角就会跟随着鼠标移动
将上诉程序封装
public class Player_move : MonoBehaviour
{
public Rigidbody Player;
private float Camera_speed;
private float y_max; //*
private float y_min; //*
void Start()
{
Camera_speed =2.0f;
rotation_y = 0f;
rotation_x = 0f;
y_max = 25f; //*
y_min = -25f; //*
}
float anger_overflow (float min,float anger,float max) //* 对角度进行处理
{
if (anger < min) return min;
if (anger > max) return max;
return anger;
}
void rotate_camera()
{
rotation_y += Input.GetAxis("Mouse Y");
rotation_x_player = Input.GetAxis("Mouse X");
rotation_x += Input.GetAxis("Mouse X");
rotation_y = anger_overflow(y_min, rotation_y, y_max); //*
Vector3 rotation_player = new Vector3(0, rotation_x_player*Camera_speed, 0);
Vector3 rotation_camare = new Vector3(-rotation_y * Camera_speed, 0, 0);
Player.transform.eulerAngles += rotation_player; //旋转玩家的X轴
Camera.main.transform.localEulerAngles = rotation_camare; //旋转相机的Y轴
}
}
移动
基础思路通过从键盘读入的数据对角色的坐标进行修改。
public class Player_move : MonoBehaviour
{
public float move_speed; //定义移动速度
void Start()
{
move_speed=12.5f;
}
void Update()
{
moveVertical = Input.GetAxis("Vertical");
moveHorizontal = Input.GetAxis("Horizontal");
Vector3 move = new Vector3(moveVertical , 0, moveHorizontal);
move = move * Time.deltaTime * move_speed;
Player.MovePosition(transform.position + move);
}
}
但是这其中也存在这一个问题,角色的移动方向是与世界轴的坐标相绑定,而不是与角色的视角相绑定。
eg:
(蓝色为玩家,淡蓝色为玩家视角,此时玩家视角即为x轴的正方向,因此此时当玩家按下W的时候,移动方向与视角方向相同,所以并不会出现问题)
(但当玩家将视角转过90°后,当玩家按下W时角色将会向左移动)
因此该方法玩家的移动与视角无关,所以要对输入进来的数据进行三角计算,首先要从视角的程序中获取玩家X轴的旋转值
但在unity角度是这么被表示的
所以要进行一定的换算。
public class Player_move : MonoBehaviour
{
public float Forward_speed; //定义移动速度
private float Shift_speed; //侧移的速度
void Start()
{
Forward_speed=12.5f;
Shift_speed = 5.0f;
}
void Update()
{
rotate_camera(); //之前封装的程序
turn_rotation = anger(rotation_x); //这块要使用上面视角旋转的值
moveVertical = Input.GetAxis("Vertical");
moveHorizontal = Input.GetAxis("Horizontal");
Acutal();
Vector3 move = new Vector3(Acutally_moveVertical, 0, Acutally_moveHorizontal);
move = move * Time.deltaTime;
Player.MovePosition(transform.position + move);
}
float anger(float a) //换算
{
a =a * 2;
a=a*Mathf.Deg2Rad; //这里是因为Mathf.sin(cos)是对弧度进行计算,所以要将角度转换成弧度。
return a;
}
void Acutal() //向量分解
{
Acutally_moveVertical = moveVertical * Forward_speed * Mathf.Sin(turn_rotation) + moveHorizontal * Shift_speed * Mathf.Cos(turn_rotation);
Acutally_moveHorizontal = moveVertical * Forward_speed * Mathf.Cos(turn_rotation) - moveHorizontal * Shift_speed * Mathf.Sin(turn_rotation);
}
}
将上诉程序封装
public class Player_move : MonoBehaviour
{
public Rigidbody Player;
private float Camera_speed;
private float y_max; //*
private float y_min; //*
void Start()
{
Camera_speed =2.0f;
rotation_y = 0f;
rotation_x = 0f;
y_max = 25f; //*
y_min = -25f; //*
}
void Update()
{
rotate_camera();
turn_rotation = anger(rotation_x); //这块要使用上面视角旋转的值
player_movement();
}
float anger_overflow (float min,float anger,float max) //* 对角度进行处理
{
if (anger < min) return min;
if (anger > max) return max;
return anger;
}
void Acutal() //向量分解
{
Acutally_moveVertical = moveVertical * Forward_speed * Mathf.Sin(turn_rotation) + moveHorizontal * Shift_speed * Mathf.Cos(turn_rotation);
Acutally_moveHorizontal = moveVertical * Forward_speed * Mathf.Cos(turn_rotation) - moveHorizontal * Shift_speed * Mathf.Sin(turn_rotation);
}
void player_movement()
{
moveVertical = Input.GetAxis("Vertical");
moveHorizontal = Input.GetAxis("Horizontal");
Acutal();
Vector3 move = new Vector3(Acutally_moveVertical, 0, Acutally_moveHorizontal);
move = move * Time.deltaTime;
Player.MovePosition(transform.position + move);
}
}
跳跃(施工结束)
本质上使角色可以跳跃并不困难。只要让程序从键盘上读入Space时给角色一个向上的力,并且禁用此时的移动就可以了(防止玩家在空中凌车漂移)。
具体实现:在每帧中加入一个检测判断角色是否在地面上(可以通过调节距离来实现简单的二段跳),之后依据这个来开启移动
public class Player_move : MonoBehaviour
{
public LayerMask floorLayer;
public float Forward_speed;
private float Shift_speed;
private float Camera_speed;
private float jump_power;
public Rigidbody Player;
private float rotation_x_player;
private float rotation_x;
private float rotation_y;
private float turn_rotation;
private float moveVertical;
private float moveHorizontal;
private float Acutally_moveVertical;
private float Acutally_moveHorizontal;
private float y_max;
private float y_min;
private bool Whether_jump; //是否可以跳
private bool jump; //是否跳起
private bool shift;
private bool OnGound; //是否在地板上
// Start is called before the first frame update
void Start()
{
Forward_speed=10f;
Shift_speed = 5.0f;
Camera_speed =2.0f;
jump_power = 300f;
rotation_y = 0f;
rotation_x = 0f;
y_max = 25f;
y_min = -25f;
jump = false;
shift = false;
OnGound = true;
Whether_jump = true;
}
void Update()
{
isGound(); //判断是否在地板并返回OnGound
if (OnGound == true)
{
Whether_jump = true;
jump = false;
}
else Whether_jump = false;
if (Input.GetKeyDown(KeyCode.Space) && Whether_jump == true)
{
jump = true;
Player.AddForce(0, jump_power, 0);
}
rotate_camera(); //之前封装的代码块
turn_rotation = anger(rotation_x); //这块要使用上面视角旋转的值
if(jump==false)
{
player_movement(); //之前封装的代码块+1
}
}
void isGound()
{
Ray ray = new Ray(transform.position, Vector3.down);
RaycastHit hit;
if (Physics.Raycast(ray,out hit,0.1f,floorLayer))
{
OnGound = true;
}
else
{
OnGound = false;
}
}
...........//省略之前写的一堆函数
但这仍存在一个问题,就是当按下Space时,玩家就会停止移动,并向上跳。这样就不存在跑跳这种操作,因此要在玩家跳跃前记录玩家速度,并在跳跃后再将这个速度赋值给玩家。但也因为如此,我之前所演示的移动脚本是直接移动坐标,因此Unity不能直接读取到玩家速度,所以需要人工计算速度。
public class Player_move : MonoBehaviour
{
public LayerMask floorLayer;
public float Forward_speed;
private float Shift_speed;
private float Camera_speed;
private float jump_power;
public Rigidbody Player;
private float rotation_x_player;
private float rotation_x;
private float rotation_y;
private float turn_rotation;
private float moveVertical;
private float moveHorizontal;
private float Acutally_moveVertical;
private float Acutally_moveHorizontal;
private float y_max;
private float y_min;
private bool Whether_jump; //是否可以跳
private bool jump; //是否跳起
private bool shift;
private bool OnGound; //是否在地板上
private Vector3 Player_v;
private Vector3 recent_position; //记录角色的上一个位置
// Start is called before the first frame update
void Start()
{
Forward_speed=10f;
Shift_speed = 5.0f;
Camera_speed =2.0f;
jump_power = 300f;
rotation_y = 0f;
rotation_x = 0f;
y_max = 25f;
y_min = -25f;
jump = false;
shift = false;
OnGound = true;
Whether_jump = true;
}
void Update()
{
isGound(); //判断是否在地板并返回OnGound
if (OnGound == true)
{
Whether_jump = true;
jump = false;
}
else Whether_jump = false;
Velocity(); //计算物体当前速度
if (Input.GetKeyDown(KeyCode.Space) && Whether_jump == true)
{
jump = true;
Player.AddForce(0, jump_power, 0);
Player.velocity = Player_v;
}
rotate_camera(); //之前封装的代码块
turn_rotation = anger(rotation_x); //这块要使用上面视角旋转的值
if(jump==false)
{
recent_position = Player.transform.position;
player_movement(); //之前封装的代码块+1
}
}
void isGound()
{
Ray ray = new Ray(transform.position, Vector3.down);
RaycastHit hit;
if (Physics.Raycast(ray,out hit,0.1f,floorLayer))
{
OnGound = true;
}
else
{
OnGound = false;
}
}
void Velocity()
{
Player_v = (Player.transform.position - recent_position);
Player_v.x = Player_v.x / 0.032f;
Player_v.z = Player_v.z / 0.032f;
Player_v.y = 0f;
}
...........//省略之前写的一堆函数
最终成果(移植到其他地方应该没有问题,只要给地板添加一层Layer,并调试好参数应该就没问题了)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player_move : MonoBehaviour
{
public LayerMask floorLayer;
public float Forward_speed;
private float Shift_speed;
private float Camera_speed;
private float jump_power;
public Rigidbody Player;
private float rotation_x_player;
private float rotation_x;
private float rotation_y;
private float turn_rotation;
private float moveVertical;
private float moveHorizontal;
private float Acutally_moveVertical;
private float Acutally_moveHorizontal;
private float y_max;
private float y_min;
private bool Whether_jump;
private bool jump;
private bool shift;
private bool OnGound;
private Vector3 Player_v;
private Vector3 recent_position;
// Start is called before the first frame update
void Start()
{
Forward_speed=10f;
Shift_speed = 5.0f;
Camera_speed =2.0f;
jump_power = 300f;
rotation_y = 0f;
rotation_x = 0f;
y_max = 25f;
y_min = -25f;
jump = false;
shift = false;
OnGound = true;
Whether_jump = true;
}
// Update is called once per frame
private void FixedUpdate()
{
if(Input.GetKeyDown(KeyCode.Escape))
{
Application.Quit();
}
if (Input.GetKeyDown(KeyCode.F7))
{
Player.transform.position=new Vector3(-24.0f,1.1f,-30.8f);
}
}
void Update()
{
isGound();
if (OnGound == true)
{
Whether_jump = true;
jump = false;
}
else Whether_jump = false;
Debug.Log(Player_v);
Velocity();
if (Input.GetKeyDown(KeyCode.Space) && Whether_jump == true)
{
jump = true;
Player.AddForce(0, jump_power, 0);
Player.velocity = Player_v;
}
rotation_y += Input.GetAxis("Mouse Y");
rotation_x_player = Input.GetAxis("Mouse X");
rotation_x += Input.GetAxis("Mouse X");
rotation_y = anger_overflow(y_min, rotation_y, y_max);
Vector3 rotation_player = new Vector3(0, rotation_x_player*Camera_speed, 0);
Vector3 rotation_camare = new Vector3(-rotation_y * Camera_speed, 0, 0);//rotation_x * Camera_speed
Player.transform.eulerAngles += rotation_player;
Camera.main.transform.localEulerAngles = rotation_camare;
turn_rotation = anger(rotation_x);
if(jump==false)
{
recent_position = Player.transform.position;
moveVertical = Input.GetAxis("Vertical");
moveHorizontal = Input.GetAxis("Horizontal");
Acutal();
Vector3 move = new Vector3(Acutally_moveVertical, 0, Acutally_moveHorizontal);
move = move * Time.deltaTime;
Player.MovePosition(transform.position + move);
}
}
float anger_overflow (float min,float anger,float max)
{
if (anger < min) return min;
if (anger > max) return max;
return anger;
}
float anger(float a)
{
a =a * 2;
a=a*Mathf.Deg2Rad;
return a;
}
void Acutal()
{
Acutally_moveVertical = moveVertical * Forward_speed * Mathf.Sin(turn_rotation) + moveHorizontal * Shift_speed * Mathf.Cos(turn_rotation);
Acutally_moveHorizontal = moveVertical * Forward_speed * Mathf.Cos(turn_rotation) - moveHorizontal * Shift_speed * Mathf.Sin(turn_rotation);
}
void isGound()
{
Ray ray = new Ray(transform.position, Vector3.down);
RaycastHit hit;
if (Physics.Raycast(ray,out hit,0.1f,floorLayer))
{
OnGound = true;
}
else
{
OnGound = false;
}
}
void Velocity()
{
Player_v = (Player.transform.position - recent_position);
Player_v.x = Player_v.x / 0.032f;
Player_v.z = Player_v.z / 0.032f;
Player_v.y = 0f;
}
}
自此关于第一人称玩家控制器的内容基本结束了。