溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

UnityUI或3D場景如何實現(xiàn)跟隨手機陀螺儀的晃動效果

發(fā)布時間:2021-09-28 14:27:32 來源:億速云 閱讀:139 作者:小新 欄目:編程語言

小編給大家分享一下UnityUI或3D場景如何實現(xiàn)跟隨手機陀螺儀的晃動效果,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

需求

當(dāng)游戲顯示3d場景及其UI的時候。玩家左右晃動手機的時候,UI界面會隨之左右偏移。上下晃動的時候,3D場景會隨之上下偏移。手機停止晃動的時候,如若偏移的UI或場景,停頓一會后自動恢復(fù)到初始默認(rèn)位置。

分析

首先本文功能應(yīng)對的是橫屏游戲(豎屏游戲的話也差不多一樣,大家自己拓展下),假設(shè)當(dāng)我們拿起手機玩游戲,手機會有四個部位,分別為左手拿的左手邊和右手拿的右邊,以及屏幕內(nèi)容的上方和下方(下文中會用左手邊,右手邊,上方,下方來描述)。每個部位的傾斜都會造成UI或場景的偏移效果

我們可以先用一個枚舉來定義這四個部位的傾斜情況

public enum EGyroType{ NoRotate,//不旋轉(zhuǎn) ToUp,//手機下方向上傾斜 ToDown,//手機下方向下傾斜 ToLeft,//左手邊向下傾斜 ToRight,//右手邊向下傾斜}

接著我們可以使用Unity的陀螺儀接口Input.gyro的一些屬性,來判斷當(dāng)前手機的傾斜狀態(tài),Gyroscope有如下屬性:

我用到enabled和gravity兩個屬性,enabled用于打開或者關(guān)閉陀螺儀功能,而gravity返回的是一個Vector3變量,具體情況對應(yīng)的返回值,通過打印Log在android手機上顯示如下(橫屏游戲,紀(jì)錄了某種情況下的某個不特定的角度的gravity值):

當(dāng)手機橫著屏幕朝上水平放置在桌上的時候,返回值為:(0.0, 0.0, -1.0)

上下傾斜:

當(dāng)手機下方向上傾斜時,某個角度(轉(zhuǎn)角小于90度)的返回值為:(0.0, 0.4, -0.9),角度再大的話屏幕的內(nèi)容會翻轉(zhuǎn)過來。

當(dāng)手機下方向下傾斜時,某個角度(轉(zhuǎn)角小于90度)的返回值為:(0.0, -0.5, -0.9),轉(zhuǎn)角為90度時:(0.0, -1.0, 0.0),轉(zhuǎn)角在90度到180度中時:(0.0, -0.8, 0.6),180度時即屏幕正朝下為:(0.0, 0.0, 1.0),若角度再大一點為:(0.0, 0.3, 0.9),直至屏幕內(nèi)容翻轉(zhuǎn)過來。

我們可以發(fā)現(xiàn)

1.當(dāng) z < 0 , y > 0:當(dāng)y的值變大則為ToUp,變小則為ToDown

2.當(dāng) z < 0 , y < 0:當(dāng)y的值變大則為ToUp,變小則為ToDown

3.當(dāng) z >0 , y < 0:當(dāng)y的值變大則為ToDown,變小則為ToUp

4.當(dāng) z >0 , y >0:當(dāng)y的值變大則為ToDown,變小則為ToUp

5.當(dāng) z <0 變?yōu)?z >0,則為ToDown,反之則為ToUp

前四條總結(jié)下來就是,當(dāng) z < 0,y的值變大則為ToUp,變小則為ToDown。當(dāng) z >0,y的值變大則為ToDown,變小則為ToUp

左右傾斜:

當(dāng)手機左手邊向下傾斜時,某個角度(轉(zhuǎn)角小于90度)的返回值為:(-0.2, 0.0, -1.0),轉(zhuǎn)角為90度時:(-1.0, 0.0, 0.0),轉(zhuǎn)角在90度到180度中時:(-0.6, 0.0, 0.8)

當(dāng)手機右手邊向下傾斜時,某個角度(轉(zhuǎn)角小于90度)的返回值為:(0.6, 0.0, -0.8),轉(zhuǎn)角為90度時:(1.0, 0.0, 0.0),轉(zhuǎn)角在90度到180度中時:(0.8, 0.0, 0.5)

可以總結(jié)出

1.當(dāng) z < 0 , x < 0:當(dāng)x的值變小則為ToLeft,變大則為ToRight

2.當(dāng) z >0 , x < 0:當(dāng)x的值變大則為ToLeft,變小則為ToRight

3.當(dāng) z <0 , x >0:當(dāng)x的值變大則為ToRight,變小則為ToLeft

4.當(dāng) z >0 , x >0:當(dāng)x的值變小則為ToRight,變大則為ToLeft

即,當(dāng) z < 0,x的值變小則為ToLeft,變大則為ToRight。當(dāng) z >0,x的值變大則為ToLeft,變小則為ToRight

5.當(dāng) z < 0 變?yōu)閦 >0,若 x < 0 則為ToLeft,否則則為ToRight

6.當(dāng) z >0 變?yōu)閦 <0,若 x < 0 則為ToRight,否則則為ToLeft

然后我們可以根據(jù)這些性質(zhì)推斷出手機的當(dāng)前狀態(tài),然后去執(zhí)行我們想要執(zhí)行的操作。

根據(jù)需求,無論是移動物體,還是轉(zhuǎn)動攝像機來達到偏移的效果,都會有一個最大偏移值,偏移速度,不轉(zhuǎn)動的時候等待的一個間隔時間,這幾個參數(shù)需要設(shè)置。

具體實現(xiàn)

首先我們寫一個腳本GyroManager,掛載在場景的一個GameObject上(也可以處理成為單例,在別處調(diào)用里面的Start,Update方法),用來每幀檢測當(dāng)前的手機狀態(tài),并調(diào)用對應(yīng)狀態(tài)的注冊事件。

using System;using UnityEngine; public enum EGyroType{ NoRotate,//不旋轉(zhuǎn) ToUp,//手機下方向上傾斜 ToDown,//手機下方向下傾斜 ToLeft,//左手邊向下傾斜 ToRight,//右手邊向下傾斜} public class GyroManager : MonoBehaviour{ Gyroscope mGyro;//陀螺儀 Vector2 mCurrentLandscapeGyroValue, mCurrentPortraitGyroValue;//當(dāng)前的水平垂直的gravity值 Vector2 mLastLandscapeGyroValue, mLastPortraitGyroValue;//上一次的水平垂直的gravity值  public EGyroType LandscapeEGyroType, PortraitEGyroType;//手機的水平垂直狀態(tài) float mPrecision = 0.015f;//精度,若前后兩次gravity值在精度內(nèi),則認(rèn)為當(dāng)前沒有旋轉(zhuǎn) public int LandscapeGyroDifference, PortraitGyroDifference;//模擬的一個旋轉(zhuǎn)速度,gravity值差異越大,則該值越大  bool mIsEnable;//是否開啟陀螺儀  private void Start() {  mGyro = Input.gyro;  SetGyroEnable(true); }  //每種狀態(tài)下需要執(zhí)行的事件 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();    //根據(jù)解析出來的手機狀態(tài),執(zhí)行對應(yīng)事件   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();   }  } }  //開啟或關(guān)閉陀螺儀 public void SetGyroEnable(bool isEnable) {  if (mIsEnable != isEnable)  {   mIsEnable = isEnable;   ResetLandscape();   ResetPortrait();   mGyro.enabled = isEnable;  } }  //解析當(dāng)前手機狀態(tài) 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)   {    //當(dāng) 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)   {    //當(dāng) z > 0,x的值變大則為ToLeft,變小則為ToRight    if (mCurrentLandscapeGyroValue.x < mLastLandscapeGyroValue.x)    {     LandscapeEGyroType = EGyroType.ToRight;    }    else    {     LandscapeEGyroType = EGyroType.ToLeft;    }   }   else   {    if (mCurrentLandscapeGyroValue.y < mLastLandscapeGyroValue.y)    {     //當(dāng) z < 0 變?yōu)?z > 0,若 x < 0 則為ToLeft,否則則為ToRight     if (mCurrentLandscapeGyroValue.x > 0)     {      LandscapeEGyroType = EGyroType.ToLeft;     }     else     {      LandscapeEGyroType = EGyroType.ToRight;     }    }    else    {     //當(dāng) z > 0 變?yōu)?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)   {    //當(dāng) 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)   {    //當(dāng) z > 0,y的值變大則為ToDown,變小則為ToUp    if (mCurrentPortraitGyroValue.x < mLastPortraitGyroValue.x)    {     PortraitEGyroType = EGyroType.ToUp;    }    else    {     PortraitEGyroType = EGyroType.ToDown;    }   }   else   {    //當(dāng) z<0 變?yōu)?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用于掛載在需要根據(jù)手機狀態(tài)偏移的組件上,用于設(shè)置偏移的參數(shù),以及對應(yīng)狀態(tài)下計算偏移的量

using System;using UnityEngine; public class GyroBase{ public float MaxValue;//最大偏移值 public float DefaultValue;//初始位置 float mCurrentValue;//當(dāng)前偏移量  public float Speed;//速度 public float DuringTime;//等待間隔 float mCurrentDuringTime;//當(dāng)前時間間隔  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;//檢測手機水平轉(zhuǎn)動還是垂直轉(zhuǎn)動 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;  } }  //重設(shè)回彈參數(shù) void ResetBackProperty() {  if (!mIsResetBackProperty)  {   mIsResetBackProperty = true;   mBackSpeed = Speed * 0.8f;   mCurrentDuringTime = 0;  } }  //手機沒轉(zhuǎn)動的時候,超過間隔時間則減速回彈至默認(rèn)位置 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場景會隨手機的垂直轉(zhuǎn)動而上下偏移,我們可以通過旋轉(zhuǎn)攝像機的x軸來實現(xiàn),我們只需寫個簡單的腳本掛載在攝像機上即可

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都會隨手機水平翻轉(zhuǎn)而轉(zhuǎn)動,所以就不能直接通過攝像頭來解決,而需要移動需要偏移的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); }}

這樣就大致實現(xiàn)了需要的效果了。

以上是“UnityUI或3D場景如何實現(xiàn)跟隨手機陀螺儀的晃動效果”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI