实现的功能
- 在两个相机(一人称和三人称)之间切换
- 鼠标显示隐藏
- 角色位移旋转(视野限制)距离缩放
- 第三人称相机防遮挡(将遮挡物体透明化)
前期准备
创建地形(示例使用长宽为100的Terrain)
创建主角(示例使用Sphere)
在主角物体下创建两个相机分别命名为FPS和TPS表示第一人称和第三人称的相机(注意将两个相机的局部坐标修改为(0,0,0),可以将三人称的相机设置为(0,5,-5)旋转x45,确保对着主角即可)
在地形上创建一些遮挡物,便于测试防遮挡功能脚本
编写脚本
在编写脚本之前,在主角上添加Character Controller组件
下图脚本控制相机的旋转角度和TPS相机缩放
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Mouse_Controller : MonoBehaviour
{
//第三人称相机设置缩放
public Camera TpsCamera;
// 水平视角移动的敏感度
public float sensitivityHor = 5f;
// 垂直视角移动的敏感度
public float sensitivityVer = 5f;
// 视角向上移动的角度范围,该值越小范围越大 X轴向上
public float upVer = -45;
// 视角向下移动的角度范围,该值越大范围越大 X轴向下
public float downVer = 45;
// 垂直旋转角度
private float rotVer;
// 水平旋转角度
private float rotHor;
//缩放速度
public float wheelSpeed = 1f;
//最小距离
public float MinDistince = 5f;
//最大距离
public float MaxDistince = 20f;
void Start()
{
// 初始化当前的垂直角度和水平角度
rotVer = transform.eulerAngles.x;
rotHor = transform.localEulerAngles.y;
}
void Update()
{
changeRotate();
changeDistinces();
}
void changeRotate() {//旋转
// 获取鼠标Y轴移动 垂直
float mouseVer = Input.GetAxis("Mouse Y");
// 获取鼠标X轴移动 水平
float mouseHor = Input.GetAxis("Mouse X");
// 鼠标往上移动,视角其实是往下移,所以要想达到视角也往上移的话,就要减去它
rotVer -= mouseVer * sensitivityVer;
// 水平视角
rotHor += mouseHor * sensitivityHor;
// 限定上下移动的视角范围,即垂直方向不能360度旋转
rotVer = Mathf.Clamp(rotVer, upVer, downVer);
// 设置视角的移动值
transform.localEulerAngles = new Vector3(rotVer, rotHor, 0);
}
void changeDistinces() {//TPS相机的距离
if (TpsCamera.isActiveAndEnabled) {
if (Input.GetAxis("Mouse ScrollWheel") > 0 && Vector3.Distance(TpsCamera.transform.position, transform.position) >= MinDistince)
{
TpsCamera.transform.Translate(Vector3.forward * wheelSpeed);
}
if (Input.GetAxis("Mouse ScrollWheel") < 0 && Vector3.Distance(TpsCamera.transform.position, transform.position) <= MaxDistince)
{
TpsCamera.transform.Translate(Vector3.forward * -1f * wheelSpeed);
}
}
}
}
下图脚本控制角色移动跳跃以及显示隐藏鼠标(M)和镜头切换(V),快捷键可以自行更改
脚本中包含空气墙的功能,如果需要请按照自身场景的地形大小设置参数.
若不需要此功能可以在脚本组件里将is Open Boundray取消勾选.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Key_Controller : MonoBehaviour
{
public float speed = 6.0F;
public float jumpSpeed = 8.0F;
public float gravity = 20.0F;
private Vector3 moveDirection = Vector3.zero;
// 角色控制器
CharacterController controller;
public Camera FpsCamera, TpsCamera;
public bool isFps = true;
public bool isOpenBoundray = true;
void Start()
{
// 获取组件
controller = GetComponent<CharacterController>();
//TpsCamera.enabled = false;
//相机初始化
FpsOrTpsInit();
//鼠标显示隐藏初始化
MouseVisibleInit();
}
void Update()
{
// 移动
Key_Move();
// 鼠标显示隐藏
Change_MouseIsVisible();
//相机切换
Change_FpsOrTps();
}
void Key_Move() {
// 当角色在地面上
if (controller.isGrounded)
{
moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
moveDirection = transform.TransformDirection(moveDirection);
moveDirection *= speed;
if (Input.GetButton("Jump"))
moveDirection.y = jumpSpeed;
}
moveDirection.y -= gravity * Time.deltaTime;
controller.Move(moveDirection * Time.deltaTime);
boundray();
}
//空气墙
void boundray() {
//Vector3 pos = transform.position;
if(isOpenBoundray)
transform.position = new Vector3(Mathf.Clamp(transform.position.x, 0.5f, 99.5f), transform.position.y, Mathf.Clamp(transform.position.z, 0.5f, 99.5f));
}
void MouseVisibleInit() {
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
void Change_MouseIsVisible() {
if (Input.GetKeyDown(KeyCode.M))
{
if (Cursor.visible == false)
{
Cursor.lockState = CursorLockMode.Confined;
Cursor.visible = true;
}
else
{
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
}
}
void FpsOrTpsInit() {
FpsCamera.gameObject.SetActive(isFps);
TpsCamera.gameObject.SetActive(!isFps);
}
void Change_FpsOrTps() {
if (Input.GetKeyDown(KeyCode.V)) {
if (isFps)
{
FpsCamera.gameObject.SetActive(false);
TpsCamera.gameObject.SetActive(true);
//FpsCamera.enabled = false;
//TpsCamera.enabled = true;
isFps = false;
}
else {
FpsCamera.gameObject.SetActive(true);
TpsCamera.gameObject.SetActive(false);
//FpsCamera.enabled = true;
//TpsCamera.enabled = false;
isFps = true;
}
}
}
}
此时主角的检视面板应如下图状态
Key_Controller脚本的属性为:
Speed:移动速度
Jump Speed:跳跃高度
gravity:重力
FPS Camera:一人称相机,将创建的FPS相机拖入
TPS Camera:三人称相机,将创建的TPS相机拖入
is FPS:勾选在game视图默认是一人称,否则为三人称
is Open Boundray:启用空气墙(我设置的范围为x和z轴从0到100,如开启勿忘记根据自身情况修改)
Mouse_Controller脚本属性为:
TPS Camera:三人称相机,用于缩放功能,将创建的TPS相机拖入
sensitivityHor:水平视角移动的敏感度
sensitivityVer:垂直视角移动的敏感度
upVer:视角向上移动的角度范围
downVer:视角向下移动的角度范围
wheelSpeed:缩放速度
MinDistince:最小缩放距离
MaxDistince:最大缩放距离
此时运行场景,对于主角的位移旋转缩放,相机的切换等功能都是可以正常使用,如果失败,请按照上述仔细核对
防遮挡处理
我采取的方案是将遮挡的物体透明化,引用一位老哥的大部分代码,只针对自己的项目做了一点小小的修改.
原文地址:
https://blog.csdn.net/wl1611102/article/details/86550243
将下面的代码添加到TPS相机
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CollisionHandling : MonoBehaviour
{
public Transform player_Transform;//玩家角色
private List<GameObject> collideredObjects;//本次射线hit到的GameObject
private List<GameObject> bufferOfCollideredObjects;//上次射线hit到的GameObject
void Start()
{
collideredObjects = new List<GameObject>();
bufferOfCollideredObjects = new List<GameObject>();
}
// Update is called once per frame
void Update()
{
bufferOfCollideredObjects.Clear();
for (int temp = 0; temp < collideredObjects.Count; temp++)
{
bufferOfCollideredObjects.Add(collideredObjects[temp]);//得到上次的
}
collideredObjects.Clear();
//发射射线
Vector3 dir = (player_Transform.position-transform.position ).normalized;
RaycastHit[] hits;
hits = Physics.RaycastAll(transform.position, dir, Vector3.Distance(player_Transform.position, transform.position));
Debug.DrawLine(transform.position, player_Transform.position, Color.red);//让其显示以便观测
for (int i = 0; i < hits.Length; i++)
{
if (hits[i].collider.gameObject.name != "Terrain" && hits[i].collider.gameObject.name != "Sphere")
{
collideredObjects.Add(hits[i].collider.gameObject);//得到现在的
}
}
//把上次的还原,这次的透明
for (int i = 0; i < bufferOfCollideredObjects.Count; i++)
{
SetMaterialsColor(bufferOfCollideredObjects[i].GetComponent<Renderer>(), false);
}
for (int i = 0; i < collideredObjects.Count; i++)
{
SetMaterialsColor(collideredObjects[i].GetComponent<Renderer>(), true);
}
}
//是否搞透明
void SetMaterialsColor(Renderer r, bool isClear)
{
if (isClear)
{
int materialsNumber = r.sharedMaterials.Length;
for (int i = 0; i < materialsNumber; i++)
{
r.materials[i].shader = Shader.Find("Transparent/Diffuse");
Color tempColor = r.materials[i].color;
tempColor.a = 0.3f;
r.materials[i].color = tempColor;
}
}
else
{
int materialsNumber = r.sharedMaterials.Length;
Debug.Log("materialsNumber=" + materialsNumber);
for (int i = 0; i < materialsNumber; i++)
{
r.materials[i].shader = Shader.Find("Transparent/Diffuse");
Color tempColor = r.materials[i].color;
tempColor.a = 1f;
r.materials[i].color = tempColor;
}
}
}
}
这样当有物体挡在镜头和主角之间时,遮挡物会透明.
结语
酌贪泉而觉爽,处涸辙以犹欢