Unity向量运算详解
1. 向量基础概念
1.1 什么是向量?
向量是一个同时具有大小和方向的量,在Unity中主要使用Vector2、Vector3和Vector4。向量可以表示:
- 位置和位移
- 速度和加速度
- 力和方向
- 旋转和缩放
1.2 基本运算
public class VectorBasics : MonoBehaviour
{
void Example()
{
Vector3 a = new Vector3(1, 2, 3);
Vector3 b = new Vector3(4, 5, 6);
// 向量加法
Vector3 sum = a + b; // (5, 7, 9)
// 向量减法
Vector3 diff = a - b; // (-3, -3, -3)
// 向量乘以标量
Vector3 scaled = a * 2f; // (2, 4, 6)
// 向量的长度(大小)
float magnitude = a.magnitude; // √(1² + 2² + 3²)
}
}
2. 向量点乘(Dot Product)
2.1 点乘的数学定义
点乘是两个向量的对应分量相乘后求和:
A·B = Ax×Bx + Ay×By + Az×Bz
另一种表达方式:
A·B = |A|×|B|×cos(θ)
其中θ是两个向量之间的夹角。
2.2 点乘的几何意义
-
判断两个向量的夹角:
- 结果为正:夹角小于90度
- 结果为零:夹角等于90度(垂直)
- 结果为负:夹角大于90度
-
计算投影:
- 一个向量在另一个向量上的投影长度
- 判断物体的前后位置关系
2.3 实际应用示例
public class DotProductExample : MonoBehaviour
{
// 判断物体是否在前方
public bool IsInFront(Transform target)
{
Vector3 directionToTarget = (target.position - transform.position).normalized;
float dotProduct = Vector3.Dot(transform.forward, directionToTarget);
return dotProduct > 0; // 大于0表示在前方
}
// 计算投影长度
public float GetProjectedLength(Vector3 vector, Vector3 onNormal)
{
onNormal = onNormal.normalized;
return Vector3.Dot(vector, onNormal);
}
// 视野检测示例
public bool IsInFieldOfView(Transform target, float maxAngle)
{
Vector3 directionToTarget = (target.position - transform.position).normalized;
float angle = Mathf.Acos(Vector3.Dot(transform.forward, directionToTarget)) * Mathf.Rad2Deg;
return angle <= maxAngle;
}
}
3. 向量叉乘(Cross Product)
3.1 叉乘的数学定义
叉乘的结果是一个新的向量,垂直于原来的两个向量:
A×B = (Ay×Bz - Az×By, Az×Bx - Ax×Bz, Ax×By - Ay×Bx)
3.2 叉乘的几何意义
-
方向判断:
- 结果向量垂直于两个输入向量
- 遵循右手法则
- 可用于判断左右方向
-
面法线计算:
- 计算平面的法线方向
- 构建坐标系
3.3 实际应用示例
public class CrossProductExample : MonoBehaviour
{
// 判断目标是在左边还是右边
public bool IsOnRight(Vector3 targetPosition)
{
Vector3 directionToTarget = targetPosition - transform.position;
Vector3 cross = Vector3.Cross(transform.forward, directionToTarget);
return cross.y < 0; // y分量小于0表示在右边
}
// 计算平面法线
public Vector3 CalculateSurfaceNormal(Vector3 v1, Vector3 v2)
{
return Vector3.Cross(v1, v2).normalized;
}
// 构建局部坐标系
public void BuildCoordinateSystem()
{
Vector3 forward = transform.forward;
Vector3 up = Vector3.up;
Vector3 right = Vector3.Cross(up, forward).normalized;
up = Vector3.Cross(forward, right); // 重新计算up确保正交
}
}
4. 向量归一化(Normalization)
4.1 归一化的数学定义
归一化是将向量的长度调整为1的过程:
normalized = vector / vector.magnitude
4.2 归一化的意义
-
方向表示:
- 保持方向不变
- 统一向量长度
- 便于比较方向
-
计算优化:
- 简化后续计算
- 避免大小影响
- 提高精度
4.3 实际应用示例
public class NormalizationExample : MonoBehaviour
{
// 移动方向归一化
public void NormalizedMovement()
{
Vector3 moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
// 归一化确保各个方向移动速度一致
if (moveDirection.magnitude > 0)
{
moveDirection = moveDirection.normalized;
transform.Translate(moveDirection * Time.deltaTime * moveSpeed);
}
}
// 插值计算示例
public Vector3 SmoothLookAt(Vector3 target, float smoothSpeed)
{
Vector3 direction = (target - transform.position).normalized;
Quaternion targetRotation = Quaternion.LookRotation(direction);
return Quaternion.Slerp(transform.rotation, targetRotation, smoothSpeed * Time.deltaTime).eulerAngles;
}
// 反射向量计算
public Vector3 CalculateReflection(Vector3 incoming, Vector3 normal)
{
normal = normal.normalized; // 确保法线为单位向量
return Vector3.Reflect(incoming, normal);
}
}
5. 综合应用示例
5.1 角色控制器
public class CharacterController : MonoBehaviour
{
[SerializeField] private float moveSpeed = 5f;
[SerializeField] private float turnSpeed = 180f;
private void Update()
{
// 移动输入
Vector3 moveInput = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
// 归一化移动方向
if (moveInput.magnitude > 0)
{
moveInput = moveInput.normalized;
// 计算移动
transform.Translate(moveInput * moveSpeed * Time.deltaTime, Space.World);
// 计算朝向
Quaternion targetRotation = Quaternion.LookRotation(moveInput);
transform.rotation = Quaternion.RotateTowards(
transform.rotation,
targetRotation,
turnSpeed * Time.deltaTime
);
}
}
}
5.2 碰撞检测和反弹
public class CollisionHandler : MonoBehaviour
{
private void OnCollisionEnter(Collision collision)
{
// 获取碰撞点法线
Vector3 normal = collision.contacts[0].normal;
// 计算入射向量
Vector3 incomingVelocity = GetComponent<Rigidbody>().velocity;
// 计算反弹方向(使用点乘和叉乘)
Vector3 reflectDirection = Vector3.Reflect(incomingVelocity, normal);
// 应用反弹力
GetComponent<Rigidbody>().velocity = reflectDirection;
}
}
向量运算是游戏开发中的基础数学工具,正确理解和使用这些运算可以帮助我们实现更多复杂的游戏功能。在实际开发中,要根据具体需求选择合适的向量运算方法。