![Unity3D网络游戏实战](https://wfqqreader-1252317822.image.myqcloud.com/cover/295/847295/b_847295.jpg)
2.3 相机跟随
相机是场景中不可缺少的元素,它就像是人的眼睛,三维场景的呈现方式,最后还是要通过相机来确定。图2-9展示了相机的视野范围。
![](https://epubservercos.yuewen.com/FABC1B/6158700004756901/epubprivate/OEBPS/Images/figure_0054_0001.jpg?sign=1739349931-OXaBMJiwhtx0gushQZaT2wLrYMDMXC1m-0-a46b4bbd2a9179e7cb4d7381d2ca5702)
图2-9 相机及其视野范围
通常第一人称或第三人称的游戏,相机会跟随角色移动,故而要实现下面3个功能。
1)相机跟随坦克移动。
2)鼠标控制相机的角度。
3)鼠标滚轮调整相机与坦克的距离。
下面实现的是一套通用的第三人称相机组件,除了可以用于坦克游戏中,还可以把它抽出来,用在更多的游戏中。
2.3.1 数学原理
复习三角函数:因为本节会涉及sin、cos等三角函数,如果遗忘了这些知识,可以参阅相关资料(如百度百科的“三角函数公式”词条)。在图2-10所示的三角形中,角A的角度为θ,假设已知边AB的长度,那么由公式AC=AB·cosθ, BC=AB·sinθ即可求出边AC和BC的长度。
![](https://epubservercos.yuewen.com/FABC1B/6158700004756901/epubprivate/OEBPS/Images/figure_0054_0002.jpg?sign=1739349931-k5kLddUs4uhd3Y4Tjy0552bAWVMLF7wi-0-164e8f2b8d73e0cb7e4c2964a83786c0)
图2-10 三角函数示意图
要想让相机跟随坦克移动,就要明白在一定角度下相机与坦克的位置关系。如图2-11所示,设相机与坦克的距离为distance,相机与xz平面的角度为roll。根据三角关系即可求得映射在xz平面的距离d为distance·cos(roll),相机高度为distance·sin(roll)。
![](https://epubservercos.yuewen.com/FABC1B/6158700004756901/epubprivate/OEBPS/Images/figure_0055_0001.jpg?sign=1739349931-ZJoJWTsAJietCR56zs6l5YOO8zt2yhYW-0-ee5c0307b028f960252380284c4abec7)
图2-11 在yz平面,相机与坦克的位置关系
在图2-12所示的xz平面中,设相机与坦克的距离为d(即图2-11中distance映射在xz平面的长度),相机的旋转角度为rot。由图2-12可知,相机与坦克的连线与x轴的角度为rot-180。根据三角函数,即可得出x轴的位移为d·sin(rot), z轴的位移为d·cos(rot)。所以,只要用坦克的坐标减去相对位移,便能求出相机的坐标。
![](https://epubservercos.yuewen.com/FABC1B/6158700004756901/epubprivate/OEBPS/Images/figure_0055_0002.jpg?sign=1739349931-EyLXrqfLuH4vMGa5ZEo5WtkZYBJiYr44-0-ffb0027455d41432d9f944241218a0ff)
图2-12 相机与坦克的位置关系
2.3.2 跟随算法
新建名为CameraFollow.cs的文件,在CameraFollow类中编写相机的跟随功能,代码如下所示。
![](https://epubservercos.yuewen.com/FABC1B/6158700004756901/epubprivate/OEBPS/Images/figure_0056_0001.jpg?sign=1739349931-vXC2lCn4MwTIGkLwzfwNmFBBb1gs3rAq-0-da3b9588e7e2a41305ffb4bc57109efe)
程序解释如下所示。
1)定义3个变量distance、rot、roll分别代表距离、横向角度和纵向角度(参见图2-11和图2-12)。由于Mathf.Sin和Mathf.Cos使用弧度作为单位,因此这里的角度都用弧度来表示。根据“弧度=角度*2π/360”可以得知,30f*Mathf.PI*2/360便是30度所对应的弧度值。
2)定义变量target表示相机要跟随的物体。然后在Start()方法中通过Game-Object.Find找到场景中的坦克,赋值给target(或通过2.3.3节实现的SetTarget方法)。注意Find方法的参数要与场景中的坦克名相同。
GameObject.Find(名字)会根据参数所指定的名字,在场景中查找物体。如果场景中存在对应名字的物体,那么它将会返回该游戏对象,否则返回null。层次面板中显示了场景中所有游戏物体的名字,读者可以右键该物体,选择Rename菜单项来修改名字(如图2-13所示)。
![](https://epubservercos.yuewen.com/FABC1B/6158700004756901/epubprivate/OEBPS/Images/figure_0057_0001.jpg?sign=1739349931-7wFcCrJWuzGxVhP83XbRfD7ULaUpIpUY-0-e2cadd5e3e7c2f122a2e4cebec37345f)
图2-13 修改游戏对象的名字
3)在LateUpdate()中通过上文得出的位置关系计算出相机的新位置,最后使用transform. LookAt方法使相机对准目标。读者还记得前面提及的Update方法吗?Unity3D会在每一帧中调用它,那么LateUpdate又是什么呢?这里将涉及Unity3D的生命周期。
图2-14描述了Unity3D组件的生命周期。当组件被创建时(进入场景后,场景里的所有游戏对象和组件都会被创建), Unity3D会依次调用它们的Awake和Start方法,然后在每一帧中依次调用Update和LateUpdate方法。也就是说Unity3D会在调用所有组件的Update方法后再调用LateUpdate。通过Update和LateUpdate可以控制脚本的执行顺序,例如在Update里编写移动物体的代表,在LateUpdate中实现跟随物体的相机。
![](https://epubservercos.yuewen.com/FABC1B/6158700004756901/epubprivate/OEBPS/Images/figure_0057_0002.jpg?sign=1739349931-PVGrc4Evb4f7KtPDT0DQsnF02btYpuQP-0-17657754de9634696ca6b34156baa6d8)
图2-14 简化版的组件生命周期
4) Camera.main表示场景中的主相机,它是第一个启用的被标记为“Main-Camera”的相机。只需要给Camera.main.transform.position赋值即可设置相机位置。下列几行代码将根据2.3.1节中所叙述的数学原理,计算相机的位置,并保存到Vector3类型的cameraPos中。
Vector3 cameraPos; float d = distance *Mathf.Cos (roll); float height = distance * Mathf.Sin(roll); cameraPos.x = targetPos.x +d * Mathf.Cos(rot); cameraPos.z = targetPos.z + d * Mathf.Sin(rot); cameraPos.y = targetPos.y + height;
5)Camera.main.transform.LookAt()使相机旋转,对准它所跟随的物体。图2-15 (左)展示了相机与立方体的初始位置关系,在调用LookAt方法后,相机的旋转角度如图2-15(右)所示,对准了立方体。
![](https://epubservercos.yuewen.com/FABC1B/6158700004756901/epubprivate/OEBPS/Images/figure_0058_0001.jpg?sign=1739349931-CTdBU98BbLZnqCO3HEmngkX6DauQqrE1-0-2018bdbe0246df7c2bebd4f73784592c)
图2-15 LookAt方法示意图
现在,将CameraFollow脚本拉到相机身上,调整CameraFollow组件的距离和初始角度(如图2-16所示)。然后绘制一块地形,使玩家能够感受到坦克的移动过程。运行游戏,即可看到相机跟随在坦克后面(如图2-17所示)。
![](https://epubservercos.yuewen.com/FABC1B/6158700004756901/epubprivate/OEBPS/Images/figure_0058_0002.jpg?sign=1739349931-LxcPlh5tOe1MH1x1A5IgYfBFfM3LM0Gx-0-67a7fa66a8c7437f2ff4039fa14d8de7)
图2-16 相机跟随组件的属性
![](https://epubservercos.yuewen.com/FABC1B/6158700004756901/epubprivate/OEBPS/Images/figure_0058_0003.jpg?sign=1739349931-gILTeLH7SZ220qxRrBqJDij2SIrIcTHA-0-85ff35daa86cb9a7a35c87437715c00d)
图2-17 相机跟随在坦克后面
2.3.3 设置跟随目标
在CameraFollow类中添加SetTarget方法,设置相机对准的目标。不同的三维模型其中心点会有所不同,图2-18展示的是相机对准不同中心点的情况。中心点不同,玩家所看到的视角也就不同(如图2-20所示)。
![](https://epubservercos.yuewen.com/FABC1B/6158700004756901/epubprivate/OEBPS/Images/figure_0059_0001.jpg?sign=1739349931-n8fi278fjVTp1axd0v3dU5SdsP9KjRTK-0-f71f19c01e085c962664f2a7ad19a270)
图2-18 相机对准不同的中心点
![](https://epubservercos.yuewen.com/FABC1B/6158700004756901/epubprivate/OEBPS/Images/figure_0060_0001.jpg?sign=1739349931-dcisgfUNIw7jImAEw68LDOpn8GfXAhya-0-8c90e4c239663a90493eb30de5d0dd1b)
图2-19 添加一个名为cameraPoint的方块,精确控制相机对准的点
![](https://epubservercos.yuewen.com/FABC1B/6158700004756901/epubprivate/OEBPS/Images/figure_0060_0002.jpg?sign=1739349931-yXeo9uLfr36kOr4Pjvmt3aZY2hxO7TuK-0-f407946ca177374e2fe1fe76b0f7bf5d)
图2-20 cameraPoint在不同位置时,相机的视角变化
为了能够指定相机对准的中心点,特制定如下规则。
1)如果对准的物体带有名为cameraPoint的子物体,那么相机对准cameraPoint子物体。
2)如果物体不含名为cameraPoint的子物体,则对准物体中心点。
下面是相应的代码。
![](https://epubservercos.yuewen.com/FABC1B/6158700004756901/epubprivate/OEBPS/Images/figure_0059_0002.jpg?sign=1739349931-UmTvnB2emaBVTPXAe0Y0c6phMoaPWtNf-0-8461cfa3e83c7840ddc9cc8f97a41aa3)
可以在炮塔上方添加一个名为cameraPoint的方块(作为Tank的子物体),精确控制相机对准的中心点(如图2-19所示)。
图2-20展示了cameraPoint在不同位置时,相机的视角的变化。
2.3.4 横向旋转相机
本节将实现通过鼠标来控制相机旋转的功能,当鼠标向左移动时,相机随之“左转”;当鼠标向右移动时,相机随之“右转”。这样,玩家便可以从各个方向查看坦克模型(如图2-21所示)。
![](https://epubservercos.yuewen.com/FABC1B/6158700004756901/epubprivate/OEBPS/Images/figure_0061_0001.jpg?sign=1739349931-WXmWAY1uvCCCYANRRd6ZX9OApIT23PD6-0-55769407e6575ee443cf921a2814616a)
图2-21 相机随着鼠标的移动而旋转,玩家可以从各个方向查看模型
Unity3D的输入轴Mouse X和Mouse Y代表着鼠标的移动增量,也就是说当鼠标向左移动时,Input.GetAxis ("Mouse X")的值会增大,向右则减小。只要让旋转角度rot与Mouse X成正比关系,便能够通过鼠标控制相机的角度。
在CameraFollow类中新增变量rotSpeed,表示相机旋转的速度。然后编写Rotate()方法,使相机的横向角度rot随着Input.GetAxis ("Mouse X")的改变而改变,代码如下所示。
![](https://epubservercos.yuewen.com/FABC1B/6158700004756901/epubprivate/OEBPS/Images/figure_0061_0002.jpg?sign=1739349931-RZPXK4ewBE9iso4UBhW4cPLcixIQc2Up-0-14c78417781383aa3659d4de4ea30d05)
最后在LateUpdate ()中调用Rotate()。运行游戏后,镜头将随着鼠标的移动而转动,玩家便可以从各个角度观察坦克了。
void LateUpdate ()
{
//一些判断
if (target == null)
return;
if (Camera.main == null)
return;
//横向旋转
Rotate();
……
Unity3D的输入轴请参见InputManager面板(可通过Edit→Project Settings→Input打开,面板如图2-22所示),默认包含Mouse X、Mouse Y、Mouse ScrollWheel(鼠标滚轮)、Horizontal(水平轴)、Vertical(垂直轴)等多个参数项。我们会在使用到具体的输入轴时再做说明。
![](https://epubservercos.yuewen.com/FABC1B/6158700004756901/epubprivate/OEBPS/Images/figure_0062_0001.jpg?sign=1739349931-YmZtYEwZiB0Xd498pp9GdARIKfvPLdrV-0-f94ae3bd3bb6365e1825064df1b7bb30)
图2-22 Unity3D的输入项
2.3.5 纵向旋转相机
除了操控相机的横向角度,玩家还可以调整相机的高度。下面的代码通过maxRoll和minRoll定义了相机的纵向旋转范围(以弧度表示),通过rollSpeed给出旋转的速度。
//纵向角度范围 private float maxRoll = 70f * Mathf.PI * 2 / 360; private float minRoll = -10f * Mathf.PI * 2 / 360; //纵向旋转速度 private float rollSpeed = 0.2f;
在CameraFollow类中编写Roll方法,使相机纵向角度roll随着Input.GetAxis ("Mouse Y")的改变而改变。最后在LateUpdate()里调用它,代码如下所示。
![](https://epubservercos.yuewen.com/FABC1B/6158700004756901/epubprivate/OEBPS/Images/figure_0063_0001.jpg?sign=1739349931-BWH179hz1RpeTmEz2PdHCfLPYIios9zx-0-a8d83e25e7743ec2f6c60b424b970db8)
运行游戏,即可在各个角度观察坦克(如图2-23所示)。
![](https://epubservercos.yuewen.com/FABC1B/6158700004756901/epubprivate/OEBPS/Images/figure_0063_0002.jpg?sign=1739349931-RarwMdlFqo3icbkAIenh5N8ZQnfCrMsq-0-4cb4da99d4305a0b348627283d4e995b)
图2-23 在不同角度观察坦克
2.3.6 滚轮调节距离
本节将会实现用鼠标滚轮调节相机与坦克之间距离的功能。输入轴Mouse Scroll-Wheel代表鼠标滚轮,即通过Input.GetAxis ("Mouse ScrollWheel")可以获取鼠标滚轮的增量,当滚轮向上滚动时该值减少,向下滚动时该值增加。添加maxDistance、minDistance和zoomSpeed这3个调整距离的变量,其中maxDistance和minDistance表示距离的范围,zoomSpeed表示缩放的速度,代码如下所示。
//距离范围 public float maxDistance = 22f; public float minDistance = 5f; //距离变化速度 public float zoomSpeed = 0.2f;
在CameraFollow类中添加Zoom方法实现距离缩放,并在LateUpdate ()里调用它,代码如下所示。
![](https://epubservercos.yuewen.com/FABC1B/6158700004756901/epubprivate/OEBPS/Images/figure_0064_0001.jpg?sign=1739349931-FCNy6KYKQgEHo5i6uqkwdJnFNHdlWplP-0-f7290bd81aa3c5c058f1d45b240d0716)
运行游戏,滚动鼠标滚轮,相机与坦克的距离就会随之改变(如图2-24所示)。
![](https://epubservercos.yuewen.com/FABC1B/6158700004756901/epubprivate/OEBPS/Images/figure_0064_0002.jpg?sign=1739349931-kChWoxa9onjpaA7OVNGpfD4zT7rq9XS9-0-8d42e5287f68d44e3a0a2237a817408c)
图2-24 在不同距离下观察坦克