Unity:类RTS相机+类编辑器相机+第三人称相机+风景相机的实现 附详细设置步骤和代码

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

一、简介

此文分享个人在项目中实现几种相机控制、并让这几种相机状态共存(可手动切换)的实现。
抛砖引玉,若发现问题或有改进方案还请不吝赐教。

如果你希望你的项目有更多的相机行为,如玩家可以切换几种控制模式,来使用不同的相机,那么这篇文章也许对你有所帮助。

我提供以下几种相机模式供你参考:
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进行状态的定义及状态间的切换,代码不免有点冗余,但好处是很好理解。

三、设置流程

  1. 设置一个空场景,plan为ground,cube为Building,capsul为NPC
    在这里插入图片描述

  2. 设置空对象如以下层级:在这里插入图片描述

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

  4. 设置transfom:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  5. 制作第三人称视角下控制的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在不同状态时播放动画),并在这里进行你自己的修改。
  1. 烘焙navmesh,有了navmesh,在第三人称下可以不必依托实时碰撞来移动角色(前提是角色拥有navmeshAgent组件),从而可以关闭一些碰撞体提升性能。(agent不会到达没有蓝色覆盖的地方)
    在这里插入图片描述
  2. 创建脚本“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;
        }
    }
}
  1. 给NPC做一个空子对象,作为第三人称控制下相机旋转的中心点,一般放在人物后背的高度
    注:由于要生成这个npc对象,所以不能在界面事先指定,而是要在代码中通过GetChild(0)来获取,这时如果你的人物里面还有骨骼、mesh等等东西,那留意这个获取的下标是否正确。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    注:这里的0.399是我刻意手动太高了一点,我希望第三人称下镜头略微有点过肩视角的感觉。

  2. 在场景里创建空对象“cameraPosition”,此空对象定义切换到第三人称相机时相机的初始位置。
    可以在胶囊附近找一个合适的地方,选中此空对象,然后按Ctrl+Shift+F把此空对象摆放到一个合适的地方。
    如下图,我觉得在这个位置控制胶囊就差不多:
    在这里插入图片描述
    这时选中cameraPosition对象并Ctrl+Shift+F后:
    在这里插入图片描述
    此对象的位置(Gizimo是local,z轴表示前方)

  3. 使用Ctrl+Shift+F继续创建和摆放4个spot空对象在场景各处,如图
    spot1:
    spot1
    spot2:
    在这里插入图片描述
    spot3:
    在这里插入图片描述
    spot4:
    在这里插入图片描述
    这4个位置将作为预定的机位,可以在SpotCutViewMode中被切换

  4. 创建代码“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 = -
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值