前言
  当角色在场景中移动时,为了更好地观察角色,我们需要让摄像机跟随角色移动。又因为场景的大小一般是有限的,为了避免穿帮,我们还需要限制摄像机移动的范围。也就是说,我们需要让摄像机在可移动的范围内跟随角色进行移动。
跟随角色移动
  首先,我们在Assets\Scripts
下创建一个名为Utility
的文件夹,并在Utility
下创建一个名为CameraFollow
的C#脚本。然后我们将CameraFollow.cs
添加到Main Camera
上。
CameraFollow.cs1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| using System.Collections; using System.Collections.Generic; using UnityEngine;
public class CameraFollow : MonoBehaviour { [Tooltip("水平方向上最大偏移量")] public float HorizontalMargin = 2f; [Tooltip("竖直方向上最大偏移量")] public float VerticalMargin = 2f; [Tooltip("水平方向上跟随角色的速度")] public float HorizontalFollowSpeed = 2f; [Tooltip("竖直方向上跟随角色的速度")] public float VerticalFollowSpeed = 2f;
private Transform m_Player;
private void Awake() { m_Player = GameObject.FindGameObjectWithTag("Player").transform;
if(m_Player == null) { Debug.LogError("请添加Tag为Player的GameObject"); } }
private void LateUpdate() { TrackPlayer(); }
private void TrackPlayer() { float targetX = transform.position.x; float targetY = transform.position.y;
if (CheckHorizontalMargin()) { targetX = Mathf.Lerp(transform.position.x, m_Player.position.x, HorizontalFollowSpeed * Time.deltaTime); }
if (CheckVerticalMargin()) { targetY = Mathf.Lerp(transform.position.y, m_Player.position.y, VerticalFollowSpeed * Time.deltaTime); }
transform.position = new Vector3(targetX, targetY, transform.position.z); }
private bool CheckHorizontalMargin() { return Mathf.Abs(transform.position.x - m_Player.position.x) > HorizontalMargin; }
private bool CheckVerticalMargin() { return Mathf.Abs(transform.position.y - m_Player.position.y) > VerticalMargin; } }
|
代码说明:
  GameObject.FindGameObjectWithTag("Player")
表示获取场景中Tag为Player
的GameObject的引用,如果场景中没有Tag为Player
的GameObject,那么它会返回null。这里,我们使用GameObject.FindGameObjectWithTag("Player").transform
来获取角色的Transform
组件的引用。
  编辑完CameraFollow.cs
之后,我们设置Player
的Tag为Player
,并将修改Apply到Player
的Prefab上。接着,我们运行游戏,可以看到摄像机已经可以跟随角色进行移动。
获取摄像机的视口大小
  在Unity里,正交摄像机(Orthographic Camera)
的视口高度是固定的,大小为Camera的Size属性的值 * 2
个Unity基本单位。而视口的宽度则是根据输出画面的高度分辨率和宽度分辨率的比例
,乘上视口的高度
得到的。在CameraFollow.cs
加入以下代码。
CameraFollow.cs1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class CameraFollow : MonoBehaviour { ...
private void Start() { Camera camera = this.GetComponent<Camera>(); Vector3 cornerPos = camera.ViewportToWorldPoint(new Vector3(1f, 1f, Mathf.Abs(transform.position.z)));
float cameraWidth = 2 * (cornerPos.x - transform.position.x); float cameraHeight = 2 * (cornerPos.y - transform.position.y);
Debug.Log("Width: " + cameraWidth); Debug.Log("Height: " + cameraHeight); }
... }
|
  修改完成后,运行游戏,因为此时摄像机的Size为11
,设置的屏幕分辨率为1920 * 1080
,所以输出的Height应为22
,输出的Weight应为1920 / 1080 * 22 = 39.11
,也就是我们计算得到的结果正确。
定义摄像机的移动范围
  接下来,我们来限制摄像机的移动范围。因为摄像机的移动范围大小和场景的大小有关,为了能清晰地在游戏场景里看到我们当前编辑的边界大小,我们不使用数值来定义摄像机移动的范围,我们使用Box Collider2D组件来定义摄像机的移动范围
。
  首先,我们在Hierarchy
中创建一个名为SceneBounds
的Empty GameObject,并Reset
它的Transform
组件。接着,我们给SceneBounds
添加一个Box Collider2D
组件,并调整Box Collider2D
组件的Offset
和Size
属性,直到Box Collider2D
覆盖整个游戏场景为止。
Box Collider2D的属性为:
Offset
: (0, 0)
Size
: (48, 27)
  调整完毕之后,为了避免SceneBounds
的Box Collider2D
组件对游戏场景里的其他物体产生影响,我们需要新建一个名为Setting
的Layer,将SceneBounds
的Layer设置为Setting
之后,在Layer Collision Matrix
里设置Setting
与所有其他Layer不产生任何交互。
限制摄像机的移动
  最后,我们在CameraFollow.cs
加入以下代码。
CameraFollow.cs1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| using System.Collections; using System.Collections.Generic; using UnityEngine;
public class CameraFollow : MonoBehaviour { ... [Tooltip("摄像机可移动的范围")] public BoxCollider2D Region;
private Vector2 m_HorizontalRegion; private Vector2 m_VerticalRegion;
...
private void Start() { ...
Vector2 regionPosition = new Vector2( Region.transform.position.x + Region.offset.x, Region.transform.position.y + Region.offset.y );
float halfDeltaWidth = (Region.size.x - cameraWidth) / 2; float halfDeltaHeight = (Region.size.y - cameraHeight) / 2;
if(halfDeltaWidth < 0) { Debug.LogError("Box Collider2D的宽度小于摄像机视口的宽度"); }
if(halfDeltaHeight < 0) { Debug.LogError("Box Collider2D的高度小于摄像机视口的高度"); }
m_HorizontalRegion = new Vector2( regionPosition.x - halfDeltaWidth, regionPosition.x + halfDeltaWidth );
m_VerticalRegion = new Vector2( regionPosition.y - halfDeltaHeight, regionPosition.y + halfDeltaHeight ); }
...
private void TrackPlayer() { ...
targetX = Mathf.Clamp(targetX, m_HorizontalRegion.x, m_HorizontalRegion.y); targetY = Mathf.Clamp(targetY, m_VerticalRegion.x, m_VerticalRegion.y);
transform.position = new Vector3(targetX, targetY, transform.position.z); } }
|
代码说明:
  Box Collider2D
组件的Offset
属性表示Box Collider2D
中心点和Transform
组件中心点的偏移量。我们想获得Box Collider2D
组件的中心点,就需要在Transform
组件的Position
属性的值上加上Box Collider2D
组件的Offset
属性的值。
  编辑完CameraFollow.cs
之后,我们将SceneBounds
拖拽到Region
属性的赋值框,运行游戏,可以看到摄像机被正确限制在Box Collider2D
组件定义的范围内。
完整代码
  本篇文章涉及到的所有完整代码如下:
CameraFollow.cs1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
| using System.Collections; using System.Collections.Generic; using UnityEngine;
public class CameraFollow : MonoBehaviour { [Tooltip("水平方向上最大偏移量")] public float HorizontalMargin = 2f; [Tooltip("竖直方向上最大偏移量")] public float VerticalMargin = 2f; [Tooltip("水平方向上跟随角色的速度")] public float HorizontalFollowSpeed = 2f; [Tooltip("竖直方向上跟随角色的速度")] public float VerticalFollowSpeed = 2f; [Tooltip("摄像机可移动的范围")] public BoxCollider2D Region;
private Vector2 m_HorizontalRegion; private Vector2 m_VerticalRegion;
private Transform m_Player;
private void Awake() { m_Player = GameObject.FindGameObjectWithTag("Player").transform;
if(m_Player == null) { Debug.LogError("请添加Tag为Player的GameObject"); } }
private void Start() { Camera camera = this.GetComponent<Camera>(); Vector3 cornerPos = camera.ViewportToWorldPoint(new Vector3(1f, 1f, Mathf.Abs(transform.position.z)));
float cameraWidth = 2 * (cornerPos.x - transform.position.x); float cameraHeight = 2 * (cornerPos.y - transform.position.y);
Vector2 regionPosition = new Vector2( Region.transform.position.x + Region.offset.x, Region.transform.position.y + Region.offset.y );
Debug.Log(regionPosition.x); Debug.Log(regionPosition.y);
float halfDeltaWidth = (Region.size.x - cameraWidth) / 2; float halfDeltaHeight = (Region.size.y - cameraHeight) / 2;
if(halfDeltaWidth < 0) { Debug.LogError("Box Collider2D的宽度小于摄像机视口的宽度"); }
if(halfDeltaHeight < 0) { Debug.LogError("Box Collider2D的高度小于摄像机视口的高度"); }
m_HorizontalRegion = new Vector2( regionPosition.x - halfDeltaWidth, regionPosition.x + halfDeltaWidth );
m_VerticalRegion = new Vector2( regionPosition.y - halfDeltaHeight, regionPosition.y + halfDeltaHeight ); }
private void LateUpdate() { TrackPlayer(); }
private void TrackPlayer() { float targetX = transform.position.x; float targetY = transform.position.y;
if (CheckHorizontalMargin()) { targetX = Mathf.Lerp(transform.position.x, m_Player.position.x, HorizontalFollowSpeed * Time.deltaTime); }
if (CheckVerticalMargin()) { targetY = Mathf.Lerp(transform.position.y, m_Player.position.y, VerticalFollowSpeed * Time.deltaTime); }
targetX = Mathf.Clamp(targetX, m_HorizontalRegion.x, m_HorizontalRegion.y); targetY = Mathf.Clamp(targetY, m_VerticalRegion.x, m_VerticalRegion.y);
transform.position = new Vector3(targetX, targetY, transform.position.z); }
private bool CheckHorizontalMargin() { return Mathf.Abs(transform.position.x - m_Player.position.x) > HorizontalMargin; }
private bool CheckVerticalMargin() { return Mathf.Abs(transform.position.y - m_Player.position.y) > VerticalMargin; } }
|
后言
  至此,我们已实现了摄像机跟随角色移动的功能,摄像机跟随的速度、摄像机与角色的偏移量以及摄像机可移动的范围可以根据自己的喜好进行设置。最后,本篇文章所做的修改,可以在PotatoGloryTutorial这个仓库的essay8
分支下看到,读者可以clone这个仓库到本地进行查看。
参考链接
- Unity的Camera