Unity第一人称控制器

第一人称控制

视角移动

基本思路通过读入鼠标的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;
    }
} 

自此关于第一人称玩家控制器的内容基本结束了。

  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值