AR 版超级玛丽

本文将简要介绍下图这个 AR 小游戏的制作过程,完整项目可见 GitHub 仓库。原本还设计了一个复杂得多得游戏,碍于 Placenote 的各种奇怪的 Bug 而无法实现,希望以后能有更成熟的 AR SDK 出现。

首先用 Vuforia 插件的 Image Target 制作一个地板。由于涉及到图像识别,所以选择一个辨识度较高的杂志封面作为目标图像:

Image Target 创建一个 Plane 子对象,并添加草地的材质:

为了在地图上放置蘑菇,需要创建一个 Vuforia 中的 Ground Plane Stage 组件,并添加一个蘑菇子对象,表示在平面上创建蘑菇。然后再添加一个 Plane Finder 组件,并将 Content Positioning Behaviour 脚本中的 Anchor Stage 设置为刚刚创建的 Ground Plane Stage 对象,用于蘑菇的定位。结合这两个组件就能实现点击手机屏幕时在地面放置蘑菇。

Image Target 中添加一个马里奥对象,并创建四个虚拟按钮,分别用于控制马里奥的前进、后退、左转和右转。虚拟按钮默认是不显示的,此处用 Cube 对象为虚拟按钮添加外观:

为马里奥添加控制脚本,处理马里奥的移动及其与蘑菇的碰撞:

using UnityEngine;

public class MarioController : MonoBehaviour {
    private enum MarioState {
        Stop,
        Forward,
        Backward,
        Left,
        Right
    };
    private MarioState currentState;

    private readonly float moveSpeed = 0.05f;
    private readonly float turnSpeed = 80f;

    private Animation idleAnimation;
    private Animation runAnimation;

    private void Start() {
        idleAnimation = GetComponent<Animation>();
        runAnimation = GetComponent<Animation>();
        Stop();
    }

    private void Update() {
        if (currentState == MarioState.Stop) {
            return;
        }
        runAnimation.Play("run");
        switch (currentState) {
            case MarioState.Forward:
                transform.Translate(Vector3.forward * moveSpeed * Time.deltaTime);
                break;
            case MarioState.Backward:
                transform.Translate(Vector3.back * moveSpeed * Time.deltaTime);
                break;
            case MarioState.Left:
                transform.Rotate(Vector3.up, -turnSpeed * Time.deltaTime);
                break;
            case MarioState.Right:
                transform.Rotate(Vector3.up, turnSpeed * Time.deltaTime);
                break;
        }
    }

    public void MoveForward() {
        currentState = MarioState.Forward;
    }

    public void MoveBackward() {
        currentState = MarioState.Backward;
    }

    public void RotateLeft() {
        currentState = MarioState.Left;
    }

    public void RotateRight() {
        currentState = MarioState.Right;
    }

    public void Stop() {
        idleAnimation.Play("idle");
        currentState = MarioState.Stop;
    }

    private void OnCollisionEnter(Collision collision) {
        if (collision.collider.gameObject.CompareTag("Meshroom")) {
            Destroy(collision.collider.gameObject);
        }
    }
}

Image Target 添加虚拟按钮的处理脚本。当某个虚拟按钮被点击时,调用 MarioController 控制马里奥执行相应的动作。

using UnityEngine;
using Vuforia;

public class VirtualButtonEventHandler : MonoBehaviour, IVirtualButtonEventHandler {
    public MarioController marioController;

    private void Start() {
        VirtualButtonBehaviour[] vbbs = GetComponentsInChildren<VirtualButtonBehaviour>();
        foreach (VirtualButtonBehaviour vbb in vbbs) {
            vbb.RegisterEventHandler(this);
        }
    }

    public void OnButtonPressed(VirtualButtonBehaviour vb) {
        switch (vb.VirtualButtonName) {
            case "ForwardButton":
                marioController.MoveForward();
                break;
            case "BackwardButton":
                marioController.MoveBackward();
                break;
            case "LeftButton":
                marioController.RotateLeft();
                break;
            case "RightButton":
                marioController.RotateRight();
                break;
        }
    }

    public void OnButtonReleased(VirtualButtonBehaviour vb) {
        marioController.Stop();
    }
}

在 Unity 中构建 iOS 项目,在手机中启动该程序,然后将摄像头对准之前选用的杂志封面即可开始游戏:点击手机屏幕放置蘑菇,点击虚拟按钮移动马里奥。

Updated: