让我们克隆PokémonGO一样的游戏 - 1、AR模式

PokémonGO中的AR效果相对简单,用一个章节简单介绍。当然也可以用第三方SDK实现AR效果,如kudan、wikitude等都是自带SLAM功能的。

LZ的实际项目稍微复杂,用了真实的经纬度信息,在后台可以管理每个小精灵的刷新信息。相关内容还没有写到,暂时不作介绍。

资源来自于PokemonGO.apk,提取完发现没有Assets里没有一只小精灵,恐怕是做了AB,于是去国外网站找了一只没有动作的皮神。有兴趣的同学可以在这里下载更多。

如上图所示,我们要在一个场景中制作平面模式与AR模式的切换。确保层级遮挡关系正确,然后从最底层开始依次编辑每个层。

平面背景层 切分好的背景和树木用image组件拼好效果,填满画布。


AR背景层

新建RawImage,编辑脚本CameraAsBackground.cs,再挂上自带的组件AspectRatioFitter,控制画布比例。

using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class CameraAsBackground : MonoBehaviour
{
    private RawImage image;
    private WebCamTexture cam;
    private AspectRatioFitter arf;

	void Start ()
    {
        arf = GetComponent<AspectRatioFitter>();

        image = GetComponent<RawImage>();
        cam = new WebCamTexture(Screen.width,Screen.height);
        image.texture = cam;
        image.enabled = false;
    }

    void Update ()
    {
        if (cam.width < 100)
        {
            return;
        }

        float cwNeeded = -cam.videoRotationAngle;
        if (cam.videoVerticallyMirrored)
        {
            cwNeeded += 180f;
        }

        image.rectTransform.localEulerAngles = new Vector3(0f,0f,cwNeeded);

        float videoRatio = (float)cam.width / (float) cam.height;
        arf.aspectRatio = videoRatio;

        if (cam.videoVerticallyMirrored)
        {
            image.uvRect = new Rect(1, 0, -1, 1);
        }
        else
        {
            image.uvRect = new Rect(0,0,1,1);
        }
	}
    

    public void videoTexture(bool isOpen)
    {
        if (isOpen)
        {
            image.enabled = true;
            cam.Play();
        }
        else
        {
            cam.Stop();
            image.enabled = false;
        }
    }
}
模型层
把皮神模型拖放到摄像机前合适的位置。Game面板中把比例调到Portrait方向预览。

UI层

新建CatchManager.cs。因为 “AR背景层” 是叠在 “平面背景层” 上面的,所以用AR切换开关控制该层显示与隐藏。ARMode挂上AR On/Off 两张状态图。

using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class CatchManager : MonoBehaviour
{
    private bool isARMode = false;
    public Sprite[] ARMode;
    public Image ARModeImage;
    private GyroController gyroController;
    private CameraAsBackground cameraAsBackground;

    void Start ()
    {
        gyroController = GameObject.FindObjectOfType<GyroController>();
        cameraAsBackground = GameObject.FindObjectOfType<CameraAsBackground>();
    }
	
	public void SwitchARMode ()
    {
        isARMode = !isARMode;
        if (!isARMode)
        {
            ARModeImage.sprite = ARMode[0];
            gyroController.DetachGyro();
            cameraAsBackground.videoTexture(false);
        }
        else
        {
            ARModeImage.sprite = ARMode[1];
            gyroController.AttachGyro();
            cameraAsBackground.videoTexture(true);
        }
    }
}
摄像机层
在MainCamera挂上新建脚本GyroController.cs。这是一个调用陀螺仪,并平滑处理抖动的脚本。

using UnityEngine;

public class GyroController : MonoBehaviour
{
	#region [Private fields]

	private bool gyroEnabled = true;
	private const float lowPassFilterFactor = 0.2f;

	private readonly Quaternion baseIdentity =  Quaternion.Euler(90, 0, 0);
	private readonly Quaternion landscapeRight =  Quaternion.Euler(0, 0, 90);
	private readonly Quaternion landscapeLeft =  Quaternion.Euler(0, 0, -90);
	private readonly Quaternion upsideDown =  Quaternion.Euler(0, 0, 180);
	
	private Quaternion cameraBase =  Quaternion.identity;
	private Quaternion calibration =  Quaternion.identity;
    private Quaternion baseOrientation =  Quaternion.Euler(90, 0, 0);
    private Quaternion baseOrientationRotationFix =  Quaternion.identity;

	private Quaternion referanceRotation = Quaternion.identity;

	#endregion

	#region [Unity events]

	void Start () 
	{
        DetachGyro();
	}

	void Update() 
	{
		if (!gyroEnabled)
			return;
		transform.rotation = Quaternion.Slerp(transform.rotation, cameraBase * ( ConvertRotation(referanceRotation * Input.gyro.attitude) * GetRotFix()), lowPassFilterFactor);
	}


    #endregion

    #region [Public methods]

    // Attaches gyro controller to the transform.
    public void AttachGyro()
	{
        Input.gyro.enabled = true;
        gyroEnabled = true;
		ResetBaseOrientation();
		UpdateCalibration(true);
		UpdateCameraBaseRotation(true);
		RecalculateReferenceRotation();
	}

	// Detaches gyro controller from the transform
	public void DetachGyro()
    {
        transform.rotation = Quaternion.identity;
        gyroEnabled = false;
        Input.gyro.enabled = false;
    }

	#endregion

	#region [Private methods]

	/// Update the gyro calibration.
	private void UpdateCalibration(bool onlyHorizontal)
	{
		if (onlyHorizontal)
		{
			var fw = (Input.gyro.attitude) * (-Vector3.forward);
			fw.z = 0;
			if (fw == Vector3.zero)
			{
				calibration = Quaternion.identity;
			}
			else
			{
				calibration = (Quaternion.FromToRotation(baseOrientationRotationFix * Vector3.up, fw));
			}
		}
		else
		{
			calibration = Input.gyro.attitude;
		}
	}
	
	/// Update the camera base rotation.
	/// Only y rotation.
	private void UpdateCameraBaseRotation(bool onlyHorizontal)
	{
		if (onlyHorizontal)
		{
			var fw = transform.forward;
			fw.y = 0;
			if (fw == Vector3.zero)
			{
				cameraBase = Quaternion.identity;
			}
			else
			{
				cameraBase = Quaternion.FromToRotation(Vector3.forward, fw);
			}
		}
		else
		{
			cameraBase = transform.rotation;
		}
	}
	
	// Converts the rotation from right handed to left handed.
	// The result rotation.
	// The rotation to convert.
	private static Quaternion ConvertRotation(Quaternion q)
	{
		return new Quaternion(q.x, q.y, -q.z, -q.w);	
	}

    // Gets the rot fix for different orientations.
    // The rot fix.
    private Quaternion GetRotFix()
	{
		return Quaternion.identity;
	}
	
	// Recalculates reference system.
	private void ResetBaseOrientation()
	{
		baseOrientationRotationFix = GetRotFix();
		baseOrientation = baseOrientationRotationFix * baseIdentity;
	}

	// Recalculates reference rotation.
	private void RecalculateReferenceRotation()
	{
		referanceRotation = Quaternion.Inverse(baseOrientation)*Quaternion.Inverse(calibration);
	}

	#endregion
}

附上本节完整 Demo下载

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值