一、旋转分类
目前游戏中只有绕y轴旋转的需求,因此讨论的都是基于y轴旋转的情况,所以图解都是俯视图。
分单x单、双x双和单x双三类:
- 单x单:不管怎么转占位不变
- 双x双:占位点无法在中心,导致旋转结果偏移,主点位置不变的情况下,旋转会产生“甩动感”
- 单x双:同双x双
二、旋转算法
普遍算法
/// <summary>
/// 绕y轴旋转
/// </summary>
/// <param name="angle">角度</param>
/// <param name="point">单个点位坐标</param>
/// <returns></returns>
private static Vector3 RotatePointAroundY(int angle, Vector3 point)
{
var q = Quaternion.AngleAxis(angle, Vector3.up);
var vector = q * point;
return vector;
}
限制整数点位
/// <summary>
/// 绕y轴旋转(限制整数坐标)
/// </summary>
/// <param name="angle">角度</param>
/// <param name="point">单个点位坐标</param>
/// <returns></returns>
public static Vector3Int RotatePointIntAroundY(int angle, Vector3Int point)
{
var q = Quaternion.AngleAxis(angle, Vector3.up);
var vector = q * point;
var x = McMathUtils.SafeFloorToInt(vector.x);
var y = McMathUtils.SafeFloorToInt(vector.y);
var z = McMathUtils.SafeFloorToInt(vector.z);
return new Vector3Int(x, y, z);
}
压缩至2D平面的算法(这里是只允许转90的算法,如果需要任意角度转,坐标类型从int改成float,且Cos和Sin不取整即可)
/// <summary>
/// 平面旋转算法(只允许90度旋转)
/// </summary>
/// <param name="x">初始x</param>
/// <param name="z">初始z</param>
/// <param name="angle">角度</param>
/// <param name="tx">结果x</param>
/// <param name="tz">结果z</param>
public static void Rotate2d(int x, int z, int angle, out int tx, out int tz)
{
var rad = (360 - angle) * Mathf.Deg2Rad;
var cos = (int)Mathf.Cos(rad);
var sin = (int)Mathf.Sin(rad);
tx = x * cos - z * sin;
tz = x * sin + z * cos;
}
三、修建的旋转修正
为了避免旋转的甩动感,参考了小玩家的设计,思路大致是在旋转后,取玩家朝向的垂直轴(玩家朝z轴正方向,那么就取x轴,反之亦然),计算物件宽度的一半,并向下取整(更小的坐标对齐玩家位置)。图解如下:
四、重点注意
- 游戏中旋转使用的是Unity的旋转方式(左手坐标系),与传统的右手坐标系相反。因此假设一个物体面朝z轴正方向,旋转-90度后其实是面朝x轴负方向,这里需要注意。
- 体素旋转目前的方案是转Scope,也就是只转一个最小点min和一个最大点max,转完根据坐标更新min和max
- 体素旋转的结果和外包围盒的结果不一致,要得到外包围盒的大小,需要对旋转后得到的max坐标加上一个Vector3.one