国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 編程 > C# > 正文

Unity UI或3D場(chǎng)景實(shí)現(xiàn)跟隨手機(jī)陀螺儀的晃動(dòng)效果

2020-01-23 20:46:55
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

需求

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

分析

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

我們可以先用一個(gè)枚舉來(lái)定義這四個(gè)部位的傾斜情況

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

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

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

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

上下傾斜:

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

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

我們可以發(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é)下來(lái)就是,當(dāng) z < 0,y的值變大則為ToUp,變小則為ToDown。當(dāng) z > 0,y的值變大則為ToDown,變小則為ToUp

左右傾斜:

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

當(dāng)手機(jī)右手邊向下傾斜時(shí),某個(gè)角度(轉(zhuǎn)角小于90度)的返回值為:(0.6, 0.0, -0.8),轉(zhuǎn)角為90度時(shí):(1.0, 0.0, 0.0),轉(zhuǎn)角在90度到180度中時(shí):(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)?nbsp;z > 0,若 x < 0 則為ToLeft,否則則為ToRight

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

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

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

具體實(shí)現(xiàn)

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

using System;using UnityEngine; public enum EGyroType{ NoRotate,//不旋轉(zhuǎn) ToUp,//手機(jī)下方向上傾斜 ToDown,//手機(jī)下方向下傾斜 ToLeft,//左手邊向下傾斜 ToRight,//右手邊向下傾斜} public class GyroManager : MonoBehaviour{ Gyroscope mGyro;//陀螺儀 Vector2 mCurrentLandscapeGyroValue, mCurrentPortraitGyroValue;//當(dāng)前的水平垂直的gravity值 Vector2 mLastLandscapeGyroValue, mLastPortraitGyroValue;//上一次的水平垂直的gravity值  public EGyroType LandscapeEGyroType, PortraitEGyroType;//手機(jī)的水平垂直狀態(tài) float mPrecision = 0.015f;//精度,若前后兩次gravity值在精度內(nèi),則認(rèn)為當(dāng)前沒(méi)有旋轉(zhuǎn) public int LandscapeGyroDifference, PortraitGyroDifference;//模擬的一個(gè)旋轉(zhuǎn)速度,gravity值差異越大,則該值越大  bool mIsEnable;//是否開(kāi)啟陀螺儀  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ù)解析出來(lái)的手機(jī)狀態(tài),執(zhí)行對(duì)應(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();  } } }  //開(kāi)啟或關(guān)閉陀螺儀 public void SetGyroEnable(bool isEnable) { if (mIsEnable != isEnable) {  mIsEnable = isEnable;  ResetLandscape();  ResetPortrait();  mGyro.enabled = isEnable; } }  //解析當(dāng)前手機(jī)狀態(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; }}

接著我們寫個(gè)腳本GyroBase用于掛載在需要根據(jù)手機(jī)狀態(tài)偏移的組件上,用于設(shè)置偏移的參數(shù),以及對(duì)應(yīng)狀態(tài)下計(jì)算偏移的量

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)前時(shí)間間隔  public Action<float> ValueChanged;//偏移事件  public GyroManager mManager;  float mBackSpeed;//回彈速度(一個(gè)減速過(guò)程) float BackSpeed { get {  if (mBackSpeed > mMinSpeed)  {  mBackSpeed = Mathf.Max(mBackSpeed - Speed * mDeltaTime, mMinSpeed);  }  return mBackSpeed; } }  float mMinSpeed;//最小速度 float mDeltaTime;//Time.deltaTime  bool mIsLandScape;//檢測(cè)手機(jī)水平轉(zhuǎn)動(dòng)還是垂直轉(zhuǎn)動(dòng) 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; } }  //手機(jī)沒(méi)轉(zhuǎn)動(dòng)的時(shí)候,超過(guò)間隔時(shí)間則減速回彈至默認(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場(chǎng)景會(huì)隨手機(jī)的垂直轉(zhuǎn)動(dòng)而上下偏移,我們可以通過(guò)旋轉(zhuǎn)攝像機(jī)的x軸來(lái)實(shí)現(xiàn),我們只需寫個(gè)簡(jiǎn)單的腳本掛載在攝像機(jī)上即可

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; }}

因?yàn)樽约汗こ痰腢I場(chǎng)景并不是所有UI都會(huì)隨手機(jī)水平翻轉(zhuǎn)而轉(zhuǎn)動(dòng),所以就不能直接通過(guò)攝像頭來(lái)解決,而需要移動(dòng)需要偏移的UI部分,所以我們可以寫個(gè)組件只掛載在需要偏移的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); }}

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

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持武林網(wǎng)。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 千阳县| 延安市| 应城市| 札达县| 伽师县| 武定县| 海淀区| 周至县| 新和县| 大悟县| 阜新| 天等县| 乌兰浩特市| 景洪市| 芒康县| 绩溪县| 镇雄县| 南郑县| 屏山县| 西盟| 五寨县| 云和县| 临武县| 天全县| 开远市| 云浮市| 明溪县| 扎赉特旗| 青田县| 林芝县| 增城市| 平阴县| 油尖旺区| 陆河县| 建昌县| 商水县| 大英县| 玉溪市| 日喀则市| 色达县| 石台县|