让我们克隆PokémonGO一样的游戏 - 1、AR模式
2016-12-05 16:59
417 查看
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下载
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下载
相关文章推荐
- 让我们克隆PokémonGO一样的游戏 - 0、大纲
- 让我们克隆PokémonGO一样的游戏 - 2、利用uGUI图形接口,泛型状态控制
- 我们这些普通的网民需要知道什么是 WEB2。0吗??【WEB2。0详解 预测模式】
- 游戏和工作的本质一样,都是为了让人快乐.
- 为J2ME开发移动3D游戏之立即模式
- 休闲棋牌游戏大厅的基本模式及特点
- 我们不能没有游戏
- 用窗口模式运行游戏
- 为J2ME开发移动3D游戏之保留模式
- 翻译:在我们使用的NET FRAMEWORK类库中发现设计模式(1)
- 翻译:在我们使用的NET FRAMEWORK类库中发现设计模式(3)
- 我们的游戏!
- 我们需要一种新的商业模式
- 解密手机网络游戏收费模式 2005.11.23
- 深度分析:盛大实施游戏新收费模式为哪般
- 改造我们的action -----template method模式的完美应用
- 我们需要刘翔一样的英雄
- 看完仙剑,心里不太舒服,和当年玩完游戏一样............
- 设计模式之Bridge——游戏篇
- 在游戏全屏模式下,使用远程调试的设置方法