乐趣区

关于unity:Unity学习笔记FPS游戏制作1角色的移动旋转与推进上升

一,什么是 FPS 游戏
第一人称射击类游戏,FPS(First-person shooting game), 严格来说第一人称射击游戏属于 ACT 类游戏的一个分支,但和 RTS 类游戏一样,因为其在世界上的迅速风靡,使之倒退成了一个独自的类型。
FPS(First-person Shooting game)第一人称视角射击游戏顾名思义就是以玩家的主观视角来进行射击游戏。玩家们不再像别的游戏一样操纵屏幕中的虚构人物来进行游戏,而是身临其境的体验游戏带来的视觉冲击,这就大大加强了游戏的主动性和真实感。晚期第一人称类游戏所带给玩家的个别都是的屏幕光线的刺激,简略快捷的游戏节奏。随着游戏硬件的逐步完善,以及各种游戏的一直联合。第一人称射击类游戏提供了更加丰盛的剧情以及精美的画面和活泼的音效。

二,性能实现思路与过程
(1)新建角色
新建我的项目,载入或者新建角色模型与枪械模型,另外,因为我的项目视角是第一人称,所以倡议将枪械设为摄像头的子物体,并为了当前的对战性能的开发,同样倡议将实现之后的角色模型汇合以预制体的形式保留,实现之后的效果图如下

(2)实现挪动性能的思路
要实现键盘输入管制挪动性能,首先必须对键盘的输出进行实时获取,获取的办法,是在脚本的 Update()中进行获取(PS:Update(): 当游戏正在运行,同时脚本是可用的,这个办法会在每帧刷新时调用),获取用户的输出,失去用户挪动矢量的模(挪动矢量的模即挪动间隔)。

/** 获取用户挪动矢量的模的代码如下 **/
float _xMov = Input.GetAxisRaw("Horizontal");// 获取程度方向输出
float _zMov = Input.GetAxisRaw("Vertical");// 获取垂直方向输出

再乘以用户挪动矢量的规范矢量(PS:transform.right,transform.forward 实质为规范矢量,且依据矢量的概念,一个非零矢量除以它的模,可得所需规范矢量。从而反推挪动矢量的规范矢量乘以挪动矢量的模可得出挪动矢量),获取挪动矢量,思考到玩家的挪动受垂直与程度两个挪动矢量的影响,玩家静止的繁难模型如下图

依据矢量三角形法令可知,玩家挪动矢量等于程度矢量 + 垂直矢量(法令简略图如下)

  /** 程度矢量,垂直矢量与玩家挪动矢量的计算代码如下 **/
Vector3 _moveHoruzontal = transform.right * _xMov;// 程度挪动矢量 = 程度方向规范矢量 * 程度方向挪动矢量的模(键盘程度方向输出)
Vector3 _moveVertical = transform.forward * _zMov;// 垂直挪动矢量 = 垂直方向规范矢量 * 垂直方向挪动矢量的模(键盘垂直方向输出)
Vector3 _velocity = (_moveHoruzontal + _moveVertical).normalized * speed;// 将玩家挪动矢量归一化为规范矢量,在乘以

而后将玩家挪动矢量归一化(normalized)为规范矢量,在将归一化的规范矢量乘以速度矢量的模,获取玩家速度矢量,最初将速度矢量乘以工夫加上以后玩家刚体的地位,得出玩家最初应该挪动到的地位。

(3)实现旋转性能的思路
同理在脚本的 Update()中进行获取用户的鼠标输出,将获取的鼠标 X,Y 轴挪动角度乘以用户视线旋转速度,失去摄像头与玩家刚体旋转角度

(4)实现推动回升性能的思路
同理在脚本的 Update()中进行获取用户的键盘输入,当监听到用户输出空格的时候,让角色刚体像正上方挪动。

(5)具体代码
PlayerMoter.cs(代码如下)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerMoter : MonoBehaviour
{[SerializeField]
    private Camera cam;// 摄像头
    private Vector3 velocity = Vector3.zero;
    private Vector3 rotation = Vector3.zero;
    private float cameraRotationX = 0f;
    private float currentCamerRotationX = 0f;
    private Vector3 thrusterForce = Vector3.zero;
    [SerializeField]
    private float cameraRotationLimit = 85f;
    private Rigidbody rb;
    void Start()
    {rb = GetComponent<Rigidbody>();// 获取刚体
    }
    public void Move(Vector3 _velocity)// 挪动
    {velocity = _velocity;}
    public void Rotate(Vector3 _rotation)// 旋转
    {rotation = _rotation;}
    // Update is called once per frame
    void Update()
    { }
    private void FixedUpdate()
    {PerformMovement();
        PeformRotation();}
    public void RotarCamera(float _cameraRotation)// 摄像头旋转
    {cameraRotationX = _cameraRotation;}
    public void ApplyThruster(Vector3 _thrusterForce)// 利用推进力
    {thrusterForce = _thrusterForce;}
    void PerformMovement()// 挪动
    {if(velocity!=Vector3.zero)
        {rb.MovePosition(rb.position + velocity * Time.fixedDeltaTime);//
        }
        if(thrusterForce!=Vector3.zero)
        {rb.AddForce(thrusterForce * Time.fixedDeltaTime,ForceMode.Acceleration);
        }
    }
    void PeformRotation()// 旋转
    {rb.MoveRotation(rb.rotation * Quaternion.Euler(rotation));
        if(cam!=null)
        {
           
            currentCamerRotationX -= cameraRotationX;
            currentCamerRotationX = Mathf.Clamp(currentCamerRotationX,-cameraRotationLimit,cameraRotationLimit);
            cam.transform.localEulerAngles = new Vector3(currentCamerRotationX, 0f, 0f);
        }
    }
}

PlayerControl.cs(代码如下)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(ConfigurableJoint))]
[RequireComponent(typeof(PlayerMoter))]
public class PlayerControl : MonoBehaviour
{[SerializeField]
    private float speed = 5f;
    private PlayerMoter moter;
    [SerializeField]
    private float lookSensitivity = 3f;// 视角旋转速度

    [SerializeField]
    private float thrustserForce = 1000f;// 推动速度

    [Header("Spring setting")]
    [SerializeField]
    private JointDriveMode jointMode=JointDriveMode.Position;
    [SerializeField]
    private float jointSpring = 20f;
    [SerializeField]
    private float jointMaxForce = 40f;
    private PlayerMoter motor;
    private ConfigurableJoint joint;
    // Start is called before the first frame update
    void Start()
    {moter = GetComponent<PlayerMoter>();
        joint = GetComponent<ConfigurableJoint>();}

    // Update is called once per frame
    void Update()
    {float _xMov = Input.GetAxisRaw("Horizontal");// 获取程度方向输出
        float _zMov = Input.GetAxisRaw("Vertical");// 获取垂直方向输出

        //transform.right,transform.forward 实质为规范矢量,即有方向且模为 1 的矢量
        Vector3 _moveHoruzontal = transform.right * _xMov;// 程度挪动矢量 = 程度方向规范矢量 * 程度方向挪动矢量的模(键盘程度方向输出)
        Vector3 _moveVertical = transform.forward * _zMov;// 垂直挪动矢量 = 垂直方向规范矢量 * 垂直方向挪动矢量的模(键盘垂直方向输出)
        Vector3 _velocity = (_moveHoruzontal + _moveVertical).normalized * speed;// 将玩家挪动矢量归一化为规范矢量,在乘以
        moter.Move(_velocity);
        float _yRot = Input.GetAxisRaw("Mouse X");// 获取鼠标 X 轴挪动
        Vector3 _rotation = new Vector3(0f, _yRot, 0f) * lookSensitivity;
        moter.Rotate(_rotation);
        float _xRot = Input.GetAxis("Mouse Y");// 获取鼠标 Y 轴挪动
        float _cameraRotation = _xRot * lookSensitivity;
        moter.RotarCamera(_cameraRotation);
        Vector3 _thrusterForce = Vector3.zero;
        if(Input.GetButton("Jump"))
        {
            _thrusterForce = Vector3.up * thrustserForce;//
            SetJointSettings(0f);
        }
        else
        {SetJointSettings(jointSpring);
        }
        moter.ApplyThruster(_thrusterForce);
    }
    private void SetJointSettings(float _jointSpring)
    {
        joint.yDrive = new JointDrive {
            mode = jointMode,
            positionSpring= _jointSpring,
            maximumForce=jointMaxForce
        };
    }
}

PlayerScript.cs(代码如下)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerScript : MonoBehaviour
{
    private const int Player_Up = 0;
    private const int Player_Right = 1;
    private const int Player_Down = 2;
    private const int Player_Left = 3;

    private int state = 0;
    public int moveSpeed = 2;

    void Awake()
    {state = Player_Up;}

    // Start is called before the first frame update
    void Start()
    { }

    // Update is called once per frame
    void Update()
    {float KeyVertical = Input.GetAxis("Vertical");
        float KeyHorizontal = Input.GetAxis("Horizontal");
        if(KeyVertical==-1)
        {setPlayerState(Player_Down);
        }
        else if(KeyVertical==1)
        {setPlayerState(Player_Up);
        }
        if(KeyHorizontal==1)
        {setPlayerState(Player_Right);
        }
        else if(KeyHorizontal==-1)
        {setPlayerState(Player_Left);
        }

    }
    void setPlayerState(int NewState)
    {int rotateValue = (NewState - state) * 90;
        Vector3 transformValue = new Vector3();
        switch(NewState)
        {
            case Player_Up:
                transformValue = Vector3.forward * Time.deltaTime;
                break;
            case Player_Down:
                transformValue = (-Vector3.forward) * Time.deltaTime;
                break;
            case Player_Left:
                transformValue = Vector3.left * Time.deltaTime;
                break;
            case Player_Right:
                transformValue = (-Vector3.left) * Time.deltaTime;
                break;
        }
        transform.Rotate(Vector3.up, rotateValue);
        transform.Translate(transformValue * moveSpeed, Space.World);
        state = NewState;
    }
}

(5)脚本的应用办法
将 PlayerScript.cs 与 PlayerControl.cs 绑定在玩家角色对象上,并给 PlayerControl 脚本绑定摄像头(如下图)

(6)最终成果

退出移动版