Unity 官方教学链接
B站双语机翻链接
using UnityEngine;
public class CameraControl : MonoBehaviour
{
// 相机移动的延时
public float m_DampTime = 0.2f;
// 屏幕四周的缓冲,防止坦克压在屏幕的边缘
public float m_ScreenEdgeBuffer = 4f;
// 相机景观的最小尺寸
public float m_MinSize = 6.5f;
// [HideInInspector]
public Transform[] m_Targets;
private Camera m_Camera;
private float m_ZoomSpeed;
private Vector3 m_MoveVelocity;
// 相机试图触及的位置,应该在所有坦克的中间
private Vector3 m_DesiredPosition;
private void Awake()
{
m_Camera = GetComponentInChildren<Camera>();
}
private void FixedUpdate()
{
Move();
Zoom();
}
private void Move()
{
FindAveragePosition();
// SmoothDamp 用于平滑移动
transform.position = Vector3.SmoothDamp(transform.position, m_DesiredPosition, ref m_MoveVelocity, m_DampTime);
}
private void FindAveragePosition()
{
Vector3 averagePos = new Vector3();
int numTargets = 0;
for (int i = 0; i < m_Targets.Length; i++)
{
if (!m_Targets[i].gameObject.activeSelf)
continue;
averagePos += m_Targets[i].position;
numTargets++;
}
if (numTargets > 0)
averagePos /= numTargets;
averagePos.y = transform.position.y;
m_DesiredPosition = averagePos;
}
private void Zoom()
{
float requiredSize = FindRequiredSize();
m_Camera.orthographicSize = Mathf.SmoothDamp(m_Camera.orthographicSize, requiredSize, ref m_ZoomSpeed, m_DampTime);
}
private float FindRequiredSize()
{
Vector3 desiredLocalPos = transform.InverseTransformPoint(m_DesiredPosition);
float size = 0f;
for (int i = 0; i < m_Targets.Length; i++)
{
if (!m_Targets[i].gameObject.activeSelf)
continue;
Vector3 targetLocalPos = transform.InverseTransformPoint(m_Targets[i].position);
Vector3 desiredPosToTarget = targetLocalPos - desiredLocalPos;
size = Mathf.Max (size, Mathf.Abs (desiredPosToTarget.y));
size = Mathf.Max (size, Mathf.Abs (desiredPosToTarget.x) / m_Camera.aspect);
}
size += m_ScreenEdgeBuffer;
size = Mathf.Max(size, m_MinSize);
return size;
}
public void SetStartPositionAndSize()
{
FindAveragePosition();
transform.position = m_DesiredPosition;
m_Camera.orthographicSize = FindRequiredSize();
}
}
接着在 CameraRig 中,通过拖动将 Tank 添加到 Targets 数组中。(需要在代码中注释掉[HideInInspector]
)
可以测试发现实现了相机跟随。
为 HealthSlider 添加脚本 UIDirectionControl.cs,作用是在坦克旋转时让血条不要跟着旋转,代码如下:
using UnityEngine;
public class UIDirectionControl : MonoBehaviour
{
public bool m_UseRelativeRotation = true;
private Quaternion m_RelativeRotation;
private void Start()
{
m_RelativeRotation = transform.parent.localRotation;
}
private void Update()
{
if (m_UseRelativeRotation)
transform.rotation = m_RelativeRotation;
}
}
为 Tank 添加脚本 TankHealth.cs,代码如下:
using UnityEngine;
using UnityEngine.UI;
public class TankHealth : MonoBehaviour
{
public float m_StartingHealth = 100f;
public Slider m_Slider;
public Image m_FillImage;
public Color m_FullHealthColor = Color.green;
public Color m_ZeroHealthColor = Color.red;
public GameObject m_ExplosionPrefab;
private AudioSource m_ExplosionAudio;
private ParticleSystem m_ExplosionParticles;
private float m_CurrentHealth;
private bool m_Dead;
private void Awake()
{
m_ExplosionParticles = Instantiate(m_ExplosionPrefab).GetComponent<ParticleSystem>();
m_ExplosionAudio = m_ExplosionParticles.GetComponent<AudioSource>();
m_ExplosionParticles.gameObject.SetActive(false);
}
private void OnEnable()
{
m_CurrentHealth = m_StartingHealth;
m_Dead = false;
SetHealthUI();
}
public void TakeDamage(float amount)
{
// Adjust the tank's current health, update the UI based on the new health and check whether or not the tank is dead.
m_CurrentHealth -= amount;
SetHealthUI();
if (m_CurrentHealth <= 0f && !m_Dead)
{
OnDeath();
}
}
private void SetHealthUI()
{
// Adjust the value and colour of the slider.
m_Slider.value = m_CurrentHealth;
m_FillImage.color = Color.Lerp(m_ZeroHealthColor, m_FullHealthColor, m_CurrentHealth / m_StartingHealth);
}
private void OnDeath()
{
// Play the effects for the death of the tank and deactivate it.
m_Dead = true;
m_ExplosionParticles.transform.position = transform.position;
m_ExplosionParticles.gameObject.SetActive(true);
m_ExplosionParticles.Play();
m_ExplosionAudio.Play();
gameObject.SetActive(false);
}
}