最近想着手做一个安卓上的 2D 的小游戏。那么首先我有一张背景的图片。但如果是单纯地把它放在底下,那看着就有点单调了。所以我就想,能否同时运用陀螺仪和手指的触控,来让这张背景图左右运动,来增加一些运动感和空间感?

先来看这个陀螺仪的部分。我想实现的功能是当手机在竖直方向上旋转时,背景画面左右运动。

通过查阅可以知道传感器坐标系:

axis_device

那么显然我希望能得到手机关于 y 轴旋转的数据。

翻阅文档发现,Unity 给我们提供了一个 Gyroscope 类,它的属性中有一个 rotationRateUnbiased

Returns unbiased rotation rate as measured by the device’s gyroscope.

The rotation rate is given as a Vector3 representing the speed of rotation around each of the three axes in radians per second. This value has been processed to remove “bias” and give a more accurate measurement. The raw value reported by the gyroscope hardware can be obtained with the rotationRate property.

简单来说这个属性就是一个关于每一轴旋转的角速度的 Vector3。

开始写代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class GyroViewCtrl : MonoBehaviour
{
public bool enable = true;
public float rotateFactor;
public float maxRotateRate;

private ViewCtrlConfig config;

void Start()
{
Input.gyro.enabled = true; //启用手机上的陀螺仪
config = gameObject.GetComponent<ViewCtrlConfig>();
}

void Update()
{
float gyroRotateRate = (enable?1:0) * Mathf.Clamp(rotateFactor * Input.gyro.rotationRateUnbiased.y, -maxRotateRate, maxRotateRate);
if (((transform.position.x >= config.rightPosLimit) && (gyroRotateRate > 0)) ||
((transform.position.x <= config.leftPosLimit) && (gyroRotateRate < 0))) return; //判断是否已经到了边界
transform.Translate(Vector3.right * Time.deltaTime * gyroRotateRate, Space.World);
}
}

接下来再看这个手指触控的部分。这个比陀螺仪的部分要简单一些。我使用了 Touch 类中的 deltaPosition 属性来计算背景的移动:

The position delta since last change in pixel coordinates.

The absolute position of the touch is recorded periodically and available in the position property. The deltaPosition value is a Vector2 in pixel coordinates that represents the difference between the touch position recorded on the most recent update and that recorded on the previous update. The deltaTime value gives the time that elapsed between the previous and current updates; you can calculate the touch’s speed of motion by dividing deltaPosition.magnitude by deltaTime.

代码部分:

1
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
public class TouchViewCtrl : MonoBehaviour
{
public bool enable = true;
public float moveSpeed;
public float maxSpeed;

private ViewCtrlConfig config;
void Start()
{
config = gameObject.GetComponent<ViewCtrlConfig>();
}

void Update()
{
if (Input.touchCount == 0)
{
return; //没有手指触碰直接退出
}
if (Input.GetTouch(0).phase == TouchPhase.Moved)
{
float positionDiff = (enable ? 1 : 0) * Mathf.Clamp(Input.GetTouch(0).deltaPosition.x * moveSpeed, -maxSpeed, maxSpeed);
if (((transform.position.x >= config.rightPosLimit) && (positionDiff > 0)) ||
((transform.position.x <= config.leftPosLimit) && (positionDiff < 0))) return;
transform.Translate(new Vector3(positionDiff, 0, 0), Space.World);
}
}
}

这两段代码除了调用 API 的部分略有不同,其它都是差不多的。

另外,为了边界检测的方便,我另外挂载了一个 ViewCtrlConfig 来规定左右运动的边界:

1
2
3
4
5
public class ViewCtrlConfig : MonoBehaviour
{
public float leftPosLimit;
public float rightPosLimit;
}

最后实现的效果还是挺不错的。

example