![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=1739309114-LxWcZmtnFlUjUDrCn5hJ3t1CkHR29Lon-0-736751eddf01ac51c99ec0d39ebcafa9)
图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=1739309114-AANBZblE18lcNYg3F8LDyCImjOXfzwHD-0-2e6c65532cf4820a9efec990489e4a9e)
图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=1739309114-8Bj95A8yKf4bhAvAanuXpIgeg0CaHucx-0-10d0d32f9a1ce8d4ea24d021d15eaa79)
图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=1739309114-vYUrctDqfVxIlxjKPgWqRh7LPZkT80w3-0-32d46dd6ba0b8a69ae3bb6ac968a78b7)
图2-12 相机与坦克的位置关系
2.3.2 跟随算法
新建名为CameraFollow.cs的文件,在CameraFollow类中编写相机的跟随功能,代码如下所示。
![](https://epubservercos.yuewen.com/FABC1B/6158700004756901/epubprivate/OEBPS/Images/figure_0056_0001.jpg?sign=1739309114-yZgOjD1Megoz5Ct3MfG7atUarnad0Nlt-0-ecc812bee7b1163100f33744f4b23698)
程序解释如下所示。
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=1739309114-4uohTJsKlZgozFiochX5rE9YLZ3eYnxN-0-c58ba5d2c39e1ca29c79d7703cd65791)
图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=1739309114-Kdewm4bJnNQw1JIqO57dy4RpgPSn2yV9-0-96444ecd5637cd672dbc70318281401d)
图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=1739309114-CYl2zrh0GiE5YlfuuM2qMX6RbqWVHq1R-0-cd20fcee2c74c463cd0a9881fc34b4b3)
图2-15 LookAt方法示意图
现在,将CameraFollow脚本拉到相机身上,调整CameraFollow组件的距离和初始角度(如图2-16所示)。然后绘制一块地形,使玩家能够感受到坦克的移动过程。运行游戏,即可看到相机跟随在坦克后面(如图2-17所示)。
![](https://epubservercos.yuewen.com/FABC1B/6158700004756901/epubprivate/OEBPS/Images/figure_0058_0002.jpg?sign=1739309114-Q3J1oUUlPpsB4YE82jJfpGRvZBNbz9OV-0-f573da2e8ae510e6a1ae41415ce2a7b3)
图2-16 相机跟随组件的属性
![](https://epubservercos.yuewen.com/FABC1B/6158700004756901/epubprivate/OEBPS/Images/figure_0058_0003.jpg?sign=1739309114-rgjytOKuFSwf9Su8s1eUgx9OdK4HZvTx-0-34465b7c5fddb26b793519f0f45ec118)
图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=1739309114-Q5QIL3G2YycOdVgKB13402a3bVaV7GQZ-0-9c2548fcc2f33331901fa272322dc4bb)
图2-18 相机对准不同的中心点
![](https://epubservercos.yuewen.com/FABC1B/6158700004756901/epubprivate/OEBPS/Images/figure_0060_0001.jpg?sign=1739309114-uXNrIuq4qIJoYcaYE7nIkjapwdUGbDqv-0-3598b460f51c77c45fc456f6a93bd242)
图2-19 添加一个名为cameraPoint的方块,精确控制相机对准的点
![](https://epubservercos.yuewen.com/FABC1B/6158700004756901/epubprivate/OEBPS/Images/figure_0060_0002.jpg?sign=1739309114-WvzphdWwcGibWzPHYEvK12rUF4keO4Of-0-6f1f9a84251b6fb586a29f6f11a20689)
图2-20 cameraPoint在不同位置时,相机的视角变化
为了能够指定相机对准的中心点,特制定如下规则。
1)如果对准的物体带有名为cameraPoint的子物体,那么相机对准cameraPoint子物体。
2)如果物体不含名为cameraPoint的子物体,则对准物体中心点。
下面是相应的代码。
![](https://epubservercos.yuewen.com/FABC1B/6158700004756901/epubprivate/OEBPS/Images/figure_0059_0002.jpg?sign=1739309114-H9jtl2qn1yj8IAGBTfkYybkfyLA8VCS1-0-bbf40c48d932834248bbddca8607b172)
可以在炮塔上方添加一个名为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=1739309114-pBDaj73a2vYIiJNPlSUVOYfHL63JyTq9-0-10fe33756f0629db9d00e38403f5b139)
图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=1739309114-JExuUZXsutCORiTCRNCxStE0xZ7WeaRQ-0-4d82307bad0594478cae94da4706e8ea)
最后在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=1739309114-0YQHkilg5vrlksvOC4CMWzosnfYTDK8o-0-6525a3c43ca8ad90c557ef28b448e38c)
图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=1739309114-u1cvLWcvDLsm05q6Fae5LiwcElBIMBuX-0-3ab075df71685cd93321392cc728cdf0)
运行游戏,即可在各个角度观察坦克(如图2-23所示)。
![](https://epubservercos.yuewen.com/FABC1B/6158700004756901/epubprivate/OEBPS/Images/figure_0063_0002.jpg?sign=1739309114-fWVkQ0SgGCJH6Rh8ZIFxryzXojNaxStX-0-2ac363e6365842e4ff1b174b63cfd30c)
图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=1739309114-nPG2Xa3bStXb4P4smFOZS79jO7yr8ZHJ-0-f7fdfc56f5da6838e2eca18156c07141)
运行游戏,滚动鼠标滚轮,相机与坦克的距离就会随之改变(如图2-24所示)。
![](https://epubservercos.yuewen.com/FABC1B/6158700004756901/epubprivate/OEBPS/Images/figure_0064_0002.jpg?sign=1739309114-Ywl3JzREdJ8Qy8nSN9C4n3kAPINnMtho-0-c8f58f5598062948a96b8255ece23efc)
图2-24 在不同距离下观察坦克