目录
一、简介
此文分享个人在项目中实现几种相机控制、并让这几种相机状态共存(可手动切换)的实现。
抛砖引玉,若发现问题或有改进方案还请不吝赐教。
如果你希望你的项目有更多的相机行为,如玩家可以切换几种控制模式,来使用不同的相机,那么这篇文章也许对你有所帮助。
我提供以下几种相机模式供你参考:
1. 正常相机模式(NormalViewMode):
默认模式。定义游玩时的相机的主要行为,类似《ANNO》系列和一些RTS类型游戏。目的是让玩家便捷直观地控制游戏,并和各种模块产生正常游戏行为,观景并不作为主要目的。
a)WASD使相机在固定Y轴高度平移;
b)按住中键拖动,同上;
c)滚轮拉低(约0°)和抬升视角(约90°),略带缩放效果;
d)右键旋转视角(只限上下),直观上跟滚轮有点类似;
e)相机有相对于城镇中心的距离限制。在我的项目中城镇中心设置为(0f,0f,0f);
2. 自由相机模式(FreeViewMode):
玩家可以手动进入的模式。在此模式下会获得类似编辑器内相机的操作手感。
a)按住右键 + WASD 移动
b)Q/E 上升下降
c)按住中键 平移
d)滚轮 缩放(其实是延蓝轴移动)
3.第三人称相机(ThirdPersonViewMode):
灵感来自于ANNO1800,在其游戏中按R可以由上帝视角变成一个第一人称控制器,在城里四处转悠。
我非常喜欢这个功能,希望可以实现出来。并且想搞个第三人称。
a)任何状态下切换到第三人称相机,会设置bool变量“CamCanBeControll”为false来屏蔽本类(相机控制类)的操作。生成一个带第三人称控制脚本的NPC的Prefab到固定的出生点,对相机的控制由该脚本来“接管”;
b)任何时候退出第三人称相机,将会销毁那个生成的Prefab,并将bool变量设为true,恢复类本身对相机的操控;
c)第三人称控制脚本则并不负责相机的操控;
d)(第三人称控制脚本)WASD移动NPC;
e) 鼠标移动 控制视角以NPC身上挂载的一个子对象为中心进行旋转。
4.风景相机(SpotCutViewMode):
本质上就是将cam的parent设置为一些放置好的空对象下,并将localPosition和localRotation设置为000。(其实没必要把相机设置为这些空对象的子对象,只需要赋值transfrom即可,但我在自己的项目中还要对这些点位对象有一些操作,所以将相机作为他们的子对象了。)
a)按键“,” 上一个机位
b)按键“.” 下一个机位
c)按键“/” 退出风景相机,回到正常相机
视频演示:
相机基本功能演示视频
优化效果后的演示视频
二、相机状态机

注:本文中只涉及图中左下角四个状态的定义和切换。
我使用了最基本的enum搭配switch进行状态的定义及状态间的切换,代码不免有点冗余,但好处是很好理解。
三、设置流程
-
设置一个空场景,plan为ground,cube为Building,capsul为NPC

-
设置空对象如以下层级:

-
其中camera、swivel、stick都是空对象,stick的子对象是MainCamera也就是真正的主相机。
此设置主要控制正常视角下的相机。camera负责在xz轴平移,swivel负责旋转,stick负责缩放。 -
设置transfom:




-
制作第三人称视角下控制的NPC,tag必须为“NPC”,设置Animator(非必须),且navmeshAgent启用(必须),有碰撞体(必须);

创建c#脚本"ThirdPersonController.cs"
挂在Npc上。脚本内容如下:
using UnityEngine;
public class ThirdPersonController : MonoBehaviour
{
private float currentVelocity;
public float smoothTime = 0.2f;
public float walkSpeed = 5f;
//private Animator anim;
private Transform cameraTransform;
private void Start()
{
//anim = this.GetComponent<Animator>();
cameraTransform = Camera.main.transform;
}
private void Update()
{
Vector2 input = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
Vector2 inputDir = input.normalized;
float targetRotation = Mathf.Atan2(inputDir.x, inputDir.y) * Mathf.Rad2Deg + cameraTransform.eulerAngles.y;
if (inputDir != Vector2.zero)
{
transform.eulerAngles = Vector3.up * Mathf.SmoothDampAngle(transform.eulerAngles.y, targetRotation,
ref currentVelocity, smoothTime);
transform.Translate(transform.forward * walkSpeed * Time.deltaTime , Space.World);
//anim.SetFloat("Speed",2f);
}
else
{
//anim.SetFloat("Speed",0f);
}
}
}
注:我这里针对胶囊,把Animator相关的操作都注释掉了。如果你游戏里的NPC使用动画,那么需要自己做一个Animator(主要目的是让NPC在不同状态时播放动画),并在这里进行你自己的修改。
- 烘焙navmesh,有了navmesh,在第三人称下可以不必依托实时碰撞来移动角色(前提是角色拥有navmeshAgent组件),从而可以关闭一些碰撞体提升性能。(agent不会到达没有蓝色覆盖的地方)

-
创建脚本“Singleton.cs”
这是一个抽象单例工具类,可供相机控制类和鼠标控制类甚至游戏控制类使用,这些类都可以继承它,而保证游戏中同时只存在一个自己。
这个是麦扣的教程学的…顺便安利一下,如果你跟我一样是个初学者,那么推荐在B站搜M_Studio。
using UnityEngine;
public class Singleton<T> : MonoBehaviour where T : Singleton<T>
{
private static T instance;
public static T Instance
{
get {
return instance; }
}
//protected 只允许继承类访问的方法
//virtual 可以被重写 当重写时,使用“protected override void Awake…”
protected virtual void Awake()
{
if (instance != null)
Destroy(gameObject);
else
instance = (T)this;
}
//返回当前的泛型单例模式是否已经生成了的bool 可以被外部调用
public static bool IsInitialized
{
get {
return instance != null; }
}
//对象销毁时会自动调用OnDestory,可以被重写。在此项目中,当场景里存在多个单例时,就可能要销毁单例
protected virtual void OnDestory()
{
if (instance == this)
{
instance = null;
}
}
}
-
给NPC做一个空子对象,作为第三人称控制下相机旋转的中心点,一般放在人物后背的高度
注:由于要生成这个npc对象,所以不能在界面事先指定,而是要在代码中通过GetChild(0)来获取,这时如果你的人物里面还有骨骼、mesh等等东西,那留意这个获取的下标是否正确。



注:这里的0.399是我刻意手动太高了一点,我希望第三人称下镜头略微有点过肩视角的感觉。 -
在场景里创建空对象“cameraPosition”,此空对象定义切换到第三人称相机时相机的初始位置。
可以在胶囊附近找一个合适的地方,选中此空对象,然后按Ctrl+Shift+F把此空对象摆放到一个合适的地方。
如下图,我觉得在这个位置控制胶囊就差不多:

这时选中cameraPosition对象并Ctrl+Shift+F后:

此对象的位置(Gizimo是local,z轴表示前方) -
使用Ctrl+Shift+F继续创建和摆放4个spot空对象在场景各处,如图
spot1:

spot2:

spot3:

spot4:

这4个位置将作为预定的机位,可以在SpotCutViewMode中被切换 -
创建代码“CameraController.cs”
将如下代码复制:
using UnityEngine;
using System.Collections;
using System;
public class CameraController: Singleton<CameraController>
{
//是否可以控制相机
public static bool camCanBeControl = true;
//相机模式
public enum CamModeEnum {
NormalView,FreeView,ThirdPersonView,SpotCutView};
private CamModeEnum camModeEnum;
//通过这三个东西在正常相机中控制cam,其中cam是camera本身
private Transform swivel, stick, cam;
//保存一些既定按键
private KeyCode forwardKey = KeyCode.W;
private KeyCode backKey = KeyCode.S;
private KeyCode leftKey = KeyCode.A;
private KeyCode rightKey = KeyCode.D;
private KeyCode upKey = KeyCode.E;
private KeyCode downKey = KeyCode.Q;
private KeyCode normalViewKey = KeyCode.P;
private KeyCode freeViewKey = KeyCode.O;
private KeyCode thirdPersonViewKey = KeyCode.I;
private KeyCode spotCamViewKey = KeyCode.Slash;
private KeyCode nextcamSpotKey = KeyCode.Period;
private KeyCode previouscamSpotKey = KeyCode.Comma;
private readonly string mouseXStr = "Mouse X";
private readonly string mouseYStr = "Mouse Y";
[Header("正常相机参数")]
public float stickMinZoom = -90f;//缩放范围
public float stickMaxZoom = -50f;
public float swivelMaxZoom = 90f; //旋转范围
public float swivelMinZoom = 10f;
public float moveSpeedMinZoom = 100;//移动速度范围
public float moveSpeedMaxZoom = 80;
public float pingYiSpeed = 0.1f;//中键拖动速度
public float minimumY = -

本文详细介绍了如何在Unity项目中实现多种相机模式,包括正常视图、自由视角、第三人称视角和风景相机,可通过脚本轻松切换并保持流畅过渡。分享了关键代码和设置流程,适合希望丰富游戏视角的开发者参考。
最低0.47元/天 解锁文章

被折叠的 条评论
为什么被折叠?



