效果图
传入的时gif图,帧率有所降低,不过能看清楚
这是旋转时的效果
这是背后有墙时的效果
一、简介
全部代码放上来太麻烦了,就只把挂载核心的代码拿出来,代码中不知道是干什么的请自动忽略,其余用到与主题相关的代码都有注释,有需要解释的地方可以留言,废话不多说,客官往下看。
二、基本层级
这个脚本是挂在人物下面用来设置摄像机位置的一个空物体上的,层次结构是这样的:
在代码中的位置:
物体:脚本变量名称
1.Main Camera :private GameObject MainCamera
- 摄像机身上没有脚本,使用vector3.SmoothDamp()来跟随cameraPos,这样设置,就可以达到很多游戏上摄像平滑移动的效果。
//设置摄像机跟随
MainCamera.transform.position = Vector3.SmoothDamp(MainCamera.transform.position, transform.position, ref RefCameraForWorld,CameraWorldSpreed);
2.PlayerHand :private GameObject PlayerHand
这个是人物的父物体
3.cameraHand : private GameObject CameraHand
这个是摄像机的父物体,放在人物的头顶,以下几个作用:
- 使用LookAt函数使摄像机啊始终看向这个物体
- MainCamera.transform.LookAt(CameraHand.transform); //摄像机始终看向人物
- 再有就是用来控制摄像机的上下角度的限制和旋转,因为摄像机的位置是这个物体的子物体,所以只要旋转这个物体的X轴也就是旋转了上下的视角,这样更方便的去限制上下角度的大小,代码中用来pi点的所有代码可以理解为键盘操作部分,比如WSAD,详细可以去看[傅老師/Unity教學][4/27中午更新] DarkSouls複刻經典教程#第一季,剩下的horizontalSpeed、verticalSpeed就是鼠标的X轴和Y轴的速度,鼠标XY轴的设置不在这个脚本,就是这两条,请记住这两个命名下面会用pi点出来:
Jup = Input.GetAxis("Mouse Y") * MouseSpeedX;
Jright = Input.GetAxis("Mouse X") * MouseSpeedY;
- 这个就是鼠标控制左右和上下旋转的代码
//计算摄像机的旋转
private void CalculateCameraRotate()
{
PlayerHand.transform.Rotate(Vector3.up, pi.Jright * horizontalSpeed * Time.fixedDeltaTime);
tempEulerX -= pi.Jup * verticalSpeed * Time.fixedDeltaTime;
tempEulerX = Mathf.Clamp(tempEulerX, CameraXMin, CameraXMax);
CameraHand.transform.localEulerAngles = new Vector3(tempEulerX, 0, 0);
}
4.CameraPos : 基本都是this.transfrom
接下来就是挂载下面脚本的物体
三、核心脚本
```csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class CameraController : MonoBehaviour {
[Header("===== 摄像机输入值 =====")]
public float horizontalSpeed = 100f; //摄像机水平移动速度
public float verticalSpeed = 80f; //摄像机垂直移动速度
public float MouseCenterSpeed = 200f; //摄像机中键速度
public float tempEulerX; //限制摄像机上下角度
public float CameraWorldSpreed; //摄像机跟随速度
public float SaveCameraCenter; //储存摄像机的中键值
public float CameraRotateMax = 3; //最远的摄像机距离
public float CameraRotateMin = 0.7f; //最近的摄像机距离
public float CameraXMin = -25; //最大向下角度
public float CameraXMax = 30; //最大向上角度
public float distance = 3; //当前使用的距离
private float Mouse2Distance = 3; //鼠标中键滚动了多少
private bool IsMouse2 = true; //是否允许缩放摄像机
[Header("===== 组件 =====")]
public LineRenderer line; //画出射线
//public LineRenderer line2; //画出射线
private PlayerInput pi; //人物输入脚本
private GameObject PlayerHand; //玩家碰撞体积
private GameObject CameraHand; //摄像机父亲
private GameObject MainCamera; //主摄像机
public GameObject model; //玩家自身
private Vector3 RefCameraForWorld; //暂存摄像机的缓动值
private Vector3 TempVec; //射线时暂存摄像机位置
private Vector3 Offsetpos; //摄像机与人物之间的差值
private void Awake()
{
CameraHand = transform.parent.gameObject;
PlayerHand = CameraHand.transform.parent.gameObject;
pi = PlayerHand.GetComponent<PlayerInput>();
MainCamera = GameObject.FindGameObjectWithTag("MainCamera");
model = pi.GetComponent<ActorController>().model;
}
private void Start()
{
//计算出摄像机与人物之间的差值
Offsetpos = transform.position - CameraHand.transform.position;
}
private void FixedUpdate()
{
//获取人物的欧拉角
//(这个是和FixedUpdate最后一句//当前人物的欧拉角对应的,
//作用是现将人物的方向保存下来,在做完摄像机的一切操作后在赋值给人物)
Vector3 TempModleEuluer = model.transform.eulerAngles;
//设置摄像机跟随
MainCamera.transform.position = Vector3.SmoothDamp(MainCamera.transform.position, transform.position, ref RefCameraForWorld, CameraWorldSpreed);
MainCamera.transform.LookAt(CameraHand.transform); //摄像机始终看向人物
//调用射射线计算摄像机与人物之间的距离
CalculateCameraDistance();
//设置这个的原因:为了保存人物滚轮滚动的距离,也在不滚动滚轮时不用一直去计算摄像机与人物之间的距离
if (Input.GetAxis("Mouse ScrollWheel") != 0)
{
CalculateCameraCenter();
}
//限制摄像机的自动旋转,这个设置成input.mouseButton(1)也可以
if (pi.IsCameraMouseEnable)
{
//按下鼠标右键后去调用摄像机旋转的方法
//并且重新计算差值
CalculateCameraRotate();
Offsetpos = transform.position - CameraHand.transform.position;
}
//否则就将当前位置确定下来
else
{
transform.position = Offsetpos + CameraHand.transform.position;
}
//当前人物的欧拉角(与FixedUpdate第一行代码呼应,上面写了)
model.transform.eulerAngles = TempModleEuluer;
}
//计算摄像机的旋转
private void CalculateCameraRotate()
{
PlayerHand.transform.Rotate(Vector3.up, pi.Jright * horizontalSpeed * Time.fixedDeltaTime);
tempEulerX -= pi.Jup * verticalSpeed * Time.fixedDeltaTime;
tempEulerX = Mathf.Clamp(tempEulerX, CameraXMin, CameraXMax);
CameraHand.transform.localEulerAngles = new Vector3(tempEulerX, 0, 0);
}
//发射射线计算摄像机与人物之间的距离
private void CalculateCameraDistance()
{
Ray ray2 = new Ray(CameraHand.transform.position, (this.transform.position - CameraHand.transform.position).normalized);
RaycastHit hit2;
int laymask = ~(1 << 10);
if (Physics.Raycast(ray2, out hit2, 100, laymask))
{
IsMouse2 = false;
if (hit2.distance < Mouse2Distance )
{
line.SetPositions(new Vector3[] { CameraHand.transform.position, hit2.point });
if (hit2.distance < CameraRotateMin)
{
CrameraPosition(CameraRotateMin);
return;
}
CrameraPosition(hit2.distance);
}
else
{
CrameraPosition(Mouse2Distance);
IsMouse2 = true;
}
}
else if ( hit2.distance == 0)
{
CrameraPosition(Mouse2Distance);
IsMouse2 = true;
}
transform.position = Offsetpos + CameraHand.transform.position;
}
//计算摄像机应该拥有的位置
private void CrameraPosition( float theDistance)
{
//1.设置摄像机的位置
this.Offsetpos = this.Offsetpos.normalized * theDistance;
}
//计算鼠标中键调整摄像机的远近
private void CalculateCameraCenter()
{
if (EventSystem.current.IsPointerOverGameObject() != false)
{
return;
}
distance = Offsetpos.magnitude;
distance -= Input.GetAxis("Mouse ScrollWheel") * MouseCenterSpeed;
distance = Mathf.Clamp(distance, CameraRotateMin, CameraRotateMax);
if (Input.GetAxis("Mouse ScrollWheel") != 0)
{
Mouse2Distance = distance;
}
Offsetpos = Offsetpos.normalized * distance;
}
}
四、寒暄
呃………………没啥说的了,代码逻辑还不是很好,看不懂给我留言,有更好的建议也希望能留言。谢谢。
对这块比较感兴趣的推荐学习[傅老師/Unity教學][4/27中午更新] DarkSouls複刻經典教程#第一季,大部分逻辑都是跟着学的。