UnityUI或3D场景如何实现跟随手机陀螺仪的晃动效果

小编给大家分享一下UnityUI或3D场景如何实现跟随手机陀螺仪的晃动效果,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

建网站原本是网站策划师、网络程序员、网页设计师等,应用各种网络程序开发技术和网页设计技术配合操作的协同工作。成都创新互联专业提供网站设计制作、成都网站建设,网页设计,网站制作(企业站、响应式网站、电商门户网站)等服务,从网站深度策划、搜索引擎友好度优化到用户体验的提升,我们力求做到极致!

需求

当游戏显示3d场景及其UI的时候。玩家左右晃动手机的时候,UI界面会随之左右偏移。上下晃动的时候,3D场景会随之上下偏移。手机停止晃动的时候,如若偏移的UI或场景,停顿一会后自动恢复到初始默认位置。

分析

首先本文功能应对的是横屏游戏(竖屏游戏的话也差不多一样,大家自己拓展下),假设当我们拿起手机玩游戏,手机会有四个部位,分别为左手拿的左手边和右手拿的右边,以及屏幕内容的上方和下方(下文中会用左手边,右手边,上方,下方来描述)。每个部位的倾斜都会造成UI或场景的偏移效果

我们可以先用一个枚举来定义这四个部位的倾斜情况

public enum EGyroType{ NoRotate,//不旋转 ToUp,//手机下方向上倾斜 ToDown,//手机下方向下倾斜 ToLeft,//左手边向下倾斜 ToRight,//右手边向下倾斜}

接着我们可以使用Unity的陀螺仪接口Input.gyro的一些属性,来判断当前手机的倾斜状态,Gyroscope有如下属性:

我用到enabled和gravity两个属性,enabled用于打开或者关闭陀螺仪功能,而gravity返回的是一个Vector3变量,具体情况对应的返回值,通过打印Log在android手机上显示如下(横屏游戏,纪录了某种情况下的某个不特定的角度的gravity值):

当手机横着屏幕朝上水平放置在桌上的时候,返回值为:(0.0, 0.0, -1.0)

上下倾斜:

当手机下方向上倾斜时,某个角度(转角小于90度)的返回值为:(0.0, 0.4, -0.9),角度再大的话屏幕的内容会翻转过来。

当手机下方向下倾斜时,某个角度(转角小于90度)的返回值为:(0.0, -0.5, -0.9),转角为90度时:(0.0, -1.0, 0.0),转角在90度到180度中时:(0.0, -0.8, 0.6),180度时即屏幕正朝下为:(0.0, 0.0, 1.0),若角度再大一点为:(0.0, 0.3, 0.9),直至屏幕内容翻转过来。

我们可以发现

1.当 z < 0 , y > 0:当y的值变大则为ToUp,变小则为ToDown

2.当 z < 0 , y < 0:当y的值变大则为ToUp,变小则为ToDown

3.当 z >0 , y < 0:当y的值变大则为ToDown,变小则为ToUp

4.当 z >0 , y >0:当y的值变大则为ToDown,变小则为ToUp

5.当 z <0 变为 z >0,则为ToDown,反之则为ToUp

前四条总结下来就是,当 z < 0,y的值变大则为ToUp,变小则为ToDown。当 z >0,y的值变大则为ToDown,变小则为ToUp

左右倾斜:

当手机左手边向下倾斜时,某个角度(转角小于90度)的返回值为:(-0.2, 0.0, -1.0),转角为90度时:(-1.0, 0.0, 0.0),转角在90度到180度中时:(-0.6, 0.0, 0.8)

当手机右手边向下倾斜时,某个角度(转角小于90度)的返回值为:(0.6, 0.0, -0.8),转角为90度时:(1.0, 0.0, 0.0),转角在90度到180度中时:(0.8, 0.0, 0.5)

可以总结出

1.当 z < 0 , x < 0:当x的值变小则为ToLeft,变大则为ToRight

2.当 z >0 , x < 0:当x的值变大则为ToLeft,变小则为ToRight

3.当 z <0 , x >0:当x的值变大则为ToRight,变小则为ToLeft

4.当 z >0 , x >0:当x的值变小则为ToRight,变大则为ToLeft

即,当 z < 0,x的值变小则为ToLeft,变大则为ToRight。当 z >0,x的值变大则为ToLeft,变小则为ToRight

5.当 z < 0 变为z >0,若 x < 0 则为ToLeft,否则则为ToRight

6.当 z >0 变为z <0,若 x < 0 则为ToRight,否则则为ToLeft

然后我们可以根据这些性质推断出手机的当前状态,然后去执行我们想要执行的操作。

根据需求,无论是移动物体,还是转动摄像机来达到偏移的效果,都会有一个最大偏移值,偏移速度,不转动的时候等待的一个间隔时间,这几个参数需要设置。

具体实现

首先我们写一个脚本GyroManager,挂载在场景的一个GameObject上(也可以处理成为单例,在别处调用里面的Start,Update方法),用来每帧检测当前的手机状态,并调用对应状态的注册事件。

using System;using UnityEngine; public enum EGyroType{ NoRotate,//不旋转 ToUp,//手机下方向上倾斜 ToDown,//手机下方向下倾斜 ToLeft,//左手边向下倾斜 ToRight,//右手边向下倾斜} public class GyroManager : MonoBehaviour{ Gyroscope mGyro;//陀螺仪 Vector2 mCurrentLandscapeGyroValue, mCurrentPortraitGyroValue;//当前的水平垂直的gravity值 Vector2 mLastLandscapeGyroValue, mLastPortraitGyroValue;//上一次的水平垂直的gravity值  public EGyroType LandscapeEGyroType, PortraitEGyroType;//手机的水平垂直状态 float mPrecision = 0.015f;//精度,若前后两次gravity值在精度内,则认为当前没有旋转 public int LandscapeGyroDifference, PortraitGyroDifference;//模拟的一个旋转速度,gravity值差异越大,则该值越大  bool mIsEnable;//是否开启陀螺仪  private void Start() {  mGyro = Input.gyro;  SetGyroEnable(true); }  //每种状态下需要执行的事件 public Action LandscapeTransToDefault; public Action<int> LandscapeTransToAdd; public Action<int> LandscapeTransToReduce;  public Action PortraitTransToDefault; public Action<int> PortraitTransToAdd; public Action<int> PortraitTransToReduce;  public void ResetLandscape() {  LandscapeEGyroType = EGyroType.NoRotate;  SetLandScapeValue();  mLastLandscapeGyroValue = mCurrentLandscapeGyroValue;  LandscapeGyroDifference = 0; }  public void ResetPortrait() {  PortraitEGyroType = EGyroType.NoRotate;  SetPortraitValue();  mLastPortraitGyroValue = Vector2.zero;  PortraitGyroDifference = 0; }  void Update() {  if (mIsEnable)  {   GetEGyroType();    //根据解析出来的手机状态,执行对应事件   if (LandscapeEGyroType == EGyroType.ToLeft)   {    LandscapeTransToReduce?.Invoke(LandscapeGyroDifference);   }   else if (LandscapeEGyroType == EGyroType.ToRight)   {    LandscapeTransToAdd?.Invoke(LandscapeGyroDifference);   }   else   {    LandscapeTransToDefault?.Invoke();   }    if (PortraitEGyroType == EGyroType.ToDown)   {    PortraitTransToReduce?.Invoke(PortraitGyroDifference);   }   else if (PortraitEGyroType == EGyroType.ToUp)   {    PortraitTransToAdd?.Invoke(PortraitGyroDifference);   }   else   {    PortraitTransToDefault?.Invoke();   }  } }  //开启或关闭陀螺仪 public void SetGyroEnable(bool isEnable) {  if (mIsEnable != isEnable)  {   mIsEnable = isEnable;   ResetLandscape();   ResetPortrait();   mGyro.enabled = isEnable;  } }  //解析当前手机状态 public void GetEGyroType() {  SetLandScapeValue();  //Landscape  if (IsEquals(mCurrentLandscapeGyroValue.x, mLastLandscapeGyroValue.x, true))  {   LandscapeEGyroType = EGyroType.NoRotate;   LandscapeGyroDifference = 0;  }  else  {   LandscapeGyroDifference = (int)(Mathf.Abs(mCurrentLandscapeGyroValue.x - mLastLandscapeGyroValue.x) * 60);    if (mCurrentLandscapeGyroValue.y < 0 && mLastLandscapeGyroValue.y < 0)   {    //当 z < 0,x的值变小则为ToLeft,变大则为ToRight    if (mCurrentLandscapeGyroValue.x < mLastLandscapeGyroValue.x)    {     LandscapeEGyroType = EGyroType.ToLeft;    }    else    {     LandscapeEGyroType = EGyroType.ToRight;    }   }   else if (mCurrentLandscapeGyroValue.y > 0 && mLastLandscapeGyroValue.y > 0)   {    //当 z > 0,x的值变大则为ToLeft,变小则为ToRight    if (mCurrentLandscapeGyroValue.x < mLastLandscapeGyroValue.x)    {     LandscapeEGyroType = EGyroType.ToRight;    }    else    {     LandscapeEGyroType = EGyroType.ToLeft;    }   }   else   {    if (mCurrentLandscapeGyroValue.y < mLastLandscapeGyroValue.y)    {     //当 z < 0 变为 z > 0,若 x < 0 则为ToLeft,否则则为ToRight     if (mCurrentLandscapeGyroValue.x > 0)     {      LandscapeEGyroType = EGyroType.ToLeft;     }     else     {      LandscapeEGyroType = EGyroType.ToRight;     }    }    else    {     //当 z > 0 变为 z<0,若 x< 0 则为ToRight,否则则为ToLeft     if (mCurrentLandscapeGyroValue.x < 0)     {      LandscapeEGyroType = EGyroType.ToLeft;     }     else     {      LandscapeEGyroType = EGyroType.ToRight;     }    }   }  }  mLastLandscapeGyroValue = mCurrentLandscapeGyroValue;   SetPortraitValue();  //Portrait  if (IsEquals(mCurrentPortraitGyroValue.x, mLastPortraitGyroValue.x, false))  {   PortraitEGyroType = EGyroType.NoRotate;   PortraitGyroDifference = 0;  }  else  {   PortraitGyroDifference = (int)(Mathf.Abs(mCurrentPortraitGyroValue.x - mLastPortraitGyroValue.x) * 60);    if (mCurrentPortraitGyroValue.y < 0 && mLastPortraitGyroValue.y < 0)   {    //当 z< 0,y的值变大则为ToUp,变小则为ToDown    if (mCurrentPortraitGyroValue.x < mLastPortraitGyroValue.x)    {     PortraitEGyroType = EGyroType.ToDown;    }    else    {     PortraitEGyroType = EGyroType.ToUp;    }   }   else if (mCurrentPortraitGyroValue.y > 0 && mLastPortraitGyroValue.y > 0)   {    //当 z > 0,y的值变大则为ToDown,变小则为ToUp    if (mCurrentPortraitGyroValue.x < mLastPortraitGyroValue.x)    {     PortraitEGyroType = EGyroType.ToUp;    }    else    {     PortraitEGyroType = EGyroType.ToDown;    }   }   else   {    //当 z<0 变为 z > 0,则为ToDown,反之则为ToUp    if (mCurrentPortraitGyroValue.y < mLastPortraitGyroValue.y)    {     //>0 变 <0     PortraitEGyroType = EGyroType.ToUp;    }    else    {     PortraitEGyroType = EGyroType.ToDown;    }   }  }  mLastPortraitGyroValue = mCurrentPortraitGyroValue; }  //读取gravity值 public void SetLandScapeValue() {  mCurrentLandscapeGyroValue.x = mGyro.gravity.x;  mCurrentLandscapeGyroValue.y = mGyro.gravity.z; }  public void SetPortraitValue() {  mCurrentPortraitGyroValue.x = mGyro.gravity.y;  mCurrentPortraitGyroValue.y = mGyro.gravity.z; }  //前后两次是否相等 bool IsEquals(float a, float b, bool isLandscape) {  if ((isLandscape && LandscapeEGyroType == EGyroType.NoRotate) || (!isLandscape && PortraitEGyroType == EGyroType.NoRotate))  {   if (Mathf.Abs(a - b) < 0.025f)   {    return true;   }  }  if (Mathf.Abs(a - b) < mPrecision)  {   return true;  }  return false; }}

接着我们写个脚本GyroBase用于挂载在需要根据手机状态偏移的组件上,用于设置偏移的参数,以及对应状态下计算偏移的量

using System;using UnityEngine; public class GyroBase{ public float MaxValue;//最大偏移值 public float DefaultValue;//初始位置 float mCurrentValue;//当前偏移量  public float Speed;//速度 public float DuringTime;//等待间隔 float mCurrentDuringTime;//当前时间间隔  public Action<float> ValueChanged;//偏移事件  public GyroManager mManager;  float mBackSpeed;//回弹速度(一个减速过程) float BackSpeed {  get  {   if (mBackSpeed > mMinSpeed)   {    mBackSpeed = Mathf.Max(mBackSpeed - Speed * mDeltaTime, mMinSpeed);   }   return mBackSpeed;  } }  float mMinSpeed;//最小速度 float mDeltaTime;//Time.deltaTime  bool mIsLandScape;//检测手机水平转动还是垂直转动 bool mIsResetBackProperty = false;  //初始化赋值 public void Init(float maxValue, float defaultValue, float speed, float duringTime, bool isLandscape, Action<float> action) {  MaxValue = maxValue;  DefaultValue = defaultValue;  Speed = speed;  DuringTime = duringTime;  mMinSpeed = Speed * 0.2f;  mCurrentValue = DefaultValue;  mIsLandScape = isLandscape;   if (mIsLandScape)  {   mManager.LandscapeTransToDefault += TransToDefault;   mManager.LandscapeTransToAdd += TransToAdd;   mManager.LandscapeTransToReduce += TransToReduce;  }  else  {   mManager.PortraitTransToDefault += TransToDefault;   mManager.PortraitTransToAdd += TransToAdd;   mManager.PortraitTransToReduce += TransToReduce;  }   ValueChanged = action; }  //事件清除 public void Clear() {  if (mIsLandScape)  {   mManager.LandscapeTransToDefault -= TransToDefault;   mManager.LandscapeTransToAdd -= TransToAdd;   mManager.LandscapeTransToReduce -= TransToReduce;  }  else  {   mManager.PortraitTransToDefault -= TransToDefault;   mManager.PortraitTransToAdd -= TransToAdd;   mManager.PortraitTransToReduce -= TransToReduce;  } }  //重设回弹参数 void ResetBackProperty() {  if (!mIsResetBackProperty)  {   mIsResetBackProperty = true;   mBackSpeed = Speed * 0.8f;   mCurrentDuringTime = 0;  } }  //手机没转动的时候,超过间隔时间则减速回弹至默认位置 void TransToDefault() {  mIsResetBackProperty = false;  mDeltaTime = Time.deltaTime;  mCurrentDuringTime += mDeltaTime;  if (mCurrentDuringTime > 1)  {   ValueToDefault();   ValueChanged?.Invoke(mCurrentValue);  } }  //偏移增加 void TransToAdd(int difference) {  ResetBackProperty();  ValueAddSpeed(difference);  ValueChanged?.Invoke(mCurrentValue); }  //偏移减小 void TransToReduce(int difference) {  ResetBackProperty();  ValueReduceSpeed(difference);  ValueChanged?.Invoke(mCurrentValue); }  void ValueToDefault() {  if (mCurrentValue > DefaultValue)  {   mCurrentValue = Mathf.Max(mCurrentValue - BackSpeed * mDeltaTime, DefaultValue);  }  else if (mCurrentValue < DefaultValue)  {   mCurrentValue = Mathf.Min(mCurrentValue + BackSpeed * mDeltaTime, DefaultValue);  } }  void ValueAddSpeed(int difference) {  if (mCurrentValue < DefaultValue + MaxValue)  {   mCurrentValue = Mathf.Min(mCurrentValue + Speed * mDeltaTime * difference, DefaultValue + MaxValue);  } }  void ValueReduceSpeed(int difference) {  if (mCurrentValue > DefaultValue - MaxValue)  {   mCurrentValue = Mathf.Max(mCurrentValue - Speed * mDeltaTime * difference, DefaultValue - MaxValue);  } }}

使用

例如,我们3D场景会随手机的垂直转动而上下偏移,我们可以通过旋转摄像机的x轴来实现,我们只需写个简单的脚本挂载在摄像机上即可

public class CameraGyro : MonoBehaviour{ public GyroManager mManager;  Transform mTransform; Vector3 mCameraAngle;  GyroBase mGyroBase;  void Start() {  mTransform = transform;  mCameraAngle = Vector3.zero;   mGyroBase = new GyroBase();  mGyroBase.mManager = mManager;  mGyroBase.Init(5, 0, 5, 1, false, Change); }  void Change(float value) {  mCameraAngle.x = value;  mTransform.localEulerAngles = mCameraAngle; }}

因为自己工程的UI场景并不是所有UI都会随手机水平翻转而转动,所以就不能直接通过摄像头来解决,而需要移动需要偏移的UI部分,所以我们可以写个组件只挂载在需要偏移的UI部分上

public class UIGyro : MonoBehaviour{ public GyroManager mManager;  void Start() {  GyroBase mGyroBase = new GyroBase();  mGyroBase.mManager = mManager;  mGyroBase.Init(80, transform.localPosition.x, 80, 1, true, Change); }  void Change(float value) {  transform.localPosition = new Vector3(value, transform.localPosition.y); }}

这样就大致实现了需要的效果了。

以上是“UnityUI或3D场景如何实现跟随手机陀螺仪的晃动效果”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注创新互联行业资讯频道!

分享文章:UnityUI或3D场景如何实现跟随手机陀螺仪的晃动效果
文章源于:https://www.cdcxhl.com/article2/iidioc.html

成都网站建设公司_创新互联,为您提供电子商务商城网站网站制作营销型网站建设网站导航外贸网站建设

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联

网站托管运营