- 投稿日:2020-07-05T23:03:00+09:00
PicoNeo2EyeのEyeTrackingを試してみる
始めに
BtoB用VRHMDでスタンドアローン機の「PicoNeo2 Eye」を個人用で手に入れましたので、もし同じ様に個人開発しようと思う人向けの内容になります。
PicoNeo2 Eyeについて
この「PicoNeo2 Eye」はTobiiのアイトラッキングセンサーが入ったスタンドアローンアイトラッキング機能付きのVRHMDです。
大体の操作はOculusQuestに似てますが、コントローラーは光学式ではなく電磁式なので後頭部の方へ持っていっても操作できます。
ただ、Questのコントローラーと比べると若干ふわっとした感じがあります。見えにくいですが、レンズの両サイドにTobiiのセンサーがあります
デモ確認の環境
Untiy2019.4.1f
(デモプロジェクトは2019.2.20fで開発されています)デモプロジェクト
https://github.com/picoxr/EyeTrackingプロジェクトをclone、またはDLして来たらUnityで起動してみましょう
初回起動時こちらのウインドウが出ますが
Applyを押してしばらく待ちます。
ちなみに、最初からビルド対象をAndroidにしておくとこちらのウィンドウは出ません。
ビルドする
最初から設定されてますが、ビルドシーンを番号で管理してるみたいなので番号変えると起動しなかったりします。
自分の視線情報を登録する
デモを始める前にホーム画面にある専用アプリ「Tobii User Calibration」で自分の視線情報を登録する必要があります。
デモを体験する
「PicoEyeTracking」とアプリがホーム画面に出るのでコントローラーで選択してください。
わかりやすい「Eye Tracking」から始めるといいです。
自分でアプリを作る場合について
視線情報と瞼の開閉レベルならPvr_Unity_SDKだけで対応可能です。
EyeManagerの EyeTrackingのチェックを入れれば動作します。
視線が向いてるところ以外の解像度を下げる「Foveated Rendering」もここで設定できます。TobiiのSDKを使う場合
細かい機能を使う場合はTobiiXR SDKが必要です。
こちらについては、後日簡単なデモの確認と動作まで書きたいと思います。
https://vr.tobii.com/sdk/develop/今までBtoB向けであまり表に出てきてないPicoでしたが、G2はヨドバシでも買えるようになったり、G3がソフトバンクさんのイベントで出たりとしているのでNeo系も今後購入できるようになるので期待してます。
制限がそんなに多くないけど、自前で作らないと無いという状態なのでそういったデバイスをいじるのが好きな人にはおすすめのVRHMDです(ΦωΦ)
- 投稿日:2020-07-05T21:59:55+09:00
【Unity】2019.4 Android実機でTextureが透過してしまう
- 投稿日:2020-07-05T19:46:26+09:00
Unityでベクターファイル(SVG)を扱う(導入編)
はじめに。
いつもラスター(ドットデータの)画像を使用しているので、ベクター画像は扱えないの?と思い調べたところ、プレビュー版ながら使用できることがわかりましたので、ちょっと触ってみました。
動作環境
2018.1以上の環境が必要です
導入
Window >> Package Managerから、Advancedを押して、Show preview packagesを押す
VectorGrapicsを検索して、インポート
以上。
次回は実際にSVGファイルをインポート&表示してみます。
参考
- VectorGrapics
https://github.com/Unity-Technologies/vector-graphics-samples/blob/master/Documentation/vectorgraphics.md- 【Unity道場】VectorGraphicsで作る エモい表現
https://www.slideshare.net/UnityTechnologiesJapan/62-152340248
- 投稿日:2020-07-05T18:39:56+09:00
Unity_スクリプトがVisualStudioじゃない。
- 投稿日:2020-07-05T11:57:02+09:00
Unity オブジェクトをボタンから拡大縮小するときはOnClickでthis.transform.localScale = new Vector3(1, 1, 1);をしよう!
やること
2分でオブジェクトを拡大するボタンを作る
完成イメージ
使用したバージョン
Unity 2019 3.14
さっそくやってみる!
スクリプトをつくろう!
名前はなんでもいいです。
私はScaleChengeというスクリプト名にしました。コードをかこう!
ScaleChange.csusing System.Collections; using System.Collections.Generic; using UnityEngine; public class ScaleChenge : MonoBehaviour { void Start() { } public void OnClick() { this.transform.localScale = new Vector3(1, 1, 1); } }スクリプトを拡大したいオブジェクトにアタッチしよう!
ボタンをつくろう!
(ボタンの作り方は割愛します)
つくったボタンのインスペクター > Buttonに下の画像のようなOnClickという場所があるのでそこの左下のところに先ほどスクリプトをアタッチしたオブジェクトをつけましょう!すると、右側のところから選択できるので
ScaleChange(スクリプト名) > OnClickを選択しましょう!これであとは再生ボタンをおして、ボタンをクリックすれば完成です!
縮小したいとき
もう1つボタンをつくってあげて
スクリプト内の
Vector3(0.3,0.3,0.3)とかにしてあげれば縮小できると思います!
- 投稿日:2020-07-05T11:34:20+09:00
【Unity(C#),PUN2】ルームの入退室に対応したプレイヤー生成位置、色の振り分け
やりたかったこと
まず前提として、ネットワーク経由で同期し、
ルームに好きなタイミングで出入りできるアプリケーションの
作成を行っていました。その中で、プレーヤーごとの生成位置及び色をどうきめるか
という問題に直面しました。位置も色も被ることなくプレイヤーに反映したい!
というのが今回の記事の本筋です。試行錯誤
最初に思いついたのは
ルームの人数に応じたプレイヤーの生成位置、色の振り分けです。ルーム入室時の人数が
0人なら1番目の位置、色
1人なら2番目の位置、色
2人なら3番目の位置、色
3人なら4番目の位置、色 といった具合です。ただし、このやり方だと再入室時に位置被り、色被りが発生してしまいます。
既に部屋に3人いる場合
4人目の場所と色を割り当てる
3人目のプレイヤーが退出
再入室(もしくは別プレイヤーの入室)
部屋の人数は4人なので既に存在する4番目の位置、色に割り当てられたプレイヤーと被るそこで別のやり方を考えました。
プレイヤーごとに番号を割り当てる
入室時にプレイヤーごとに番号を割り当てるという方法を思いつきました。
まずはルーム内で使われていない番号をチェックします。使われていない番号が
1なら番号1をプレイヤーに割り当て
2なら番号2をプレイヤーに割り当て
3なら番号3をプレイヤーに割り当て
4なら番号4をプレイヤーに割り当て といった具合です。あとは各々のクライアントが各自のローカルで生成された
同期オブジェクトの所有権を持つプレイヤーを取得し、
番号をチェックして処理を行います。番号ごとに各々のクライアントが自分のローカル環境で処理を行うので、
位置情報や色を送る必要がなくなります。つまり、この方法を使えば通信に必要な情報は
プレイヤーごとに割り振られた番号のみで済みます。カスタムプロパティ
入室時にプレイヤーごとに番号を割り当てるという実装を行う上で便利そうな
カスタムプロパティというPUN2の機能に関する記事を発見しました。【参考リンク】:PUN2で始めるオンラインゲーム開発入門【その5】
今回はカスタムプロパティを利用して、
ルームの入退室に対応したプレイヤー生成位置、色の振り分けを
実装していきます。カスタムプロパティはプレイヤー単位で設定できて、
必要な時に通信して呼び出す形で利用できるので
今回の目的との親和性は高いと思います。拡張メソッド
カスタムプロパティを使う上で拡張メソッドは必須かなと個人的には思ってます。
理由としては、カスタムプロパティの定義には文字列のキーが必要だからです。
とある文字列Xに対して、値を紐づけていくようなイメージです。
コードに落とし込むとこんな感じになります。
using Photon.Realtime; using UnityEngine; using Hashtable = ExitGames.Client.Photon.Hashtable; public static class PlayerPropertyExtensions { private const string PLAYER_ASSIGN_NUMBER = "n"; private static Hashtable _hashtable = new Hashtable(); //========================================= // プレイヤーの番号 //========================================= //Hashtableにプレイヤーに割り振られた番号があれば取得する private static bool TryAndGetPlayerNum(this Hashtable hashtable, out int playerAssignNumber) { if (hashtable[PLAYER_ASSIGN_NUMBER] is int value) { playerAssignNumber = value; return true; } playerAssignNumber = 0; return false; } //プレイヤー番号を取得する public static int GetPlayerNum(this Player player) { player.CustomProperties.TryAndGetPlayerNum(out int playerNum); return playerNum; } //プレイヤーの割り当て番号のカスタムプロパティを更新する public static void UpdatePlayerNum(this Player player, int assignNum) { _hashtable[PLAYER_ASSIGN_NUMBER] = assignNum; player.SetCustomProperties(_hashtable); _hashtable.Clear(); } }任意の文字列キーのカスタムプロパティを取得、更新する拡張メソッドを用意することで、
利用側は文字列キーの誤りを気にする必要性がなくなります。使用済みの番号を特定し、未使用の番号でプレイヤーの番号を更新
拡張メソッドで利用側はかなり楽になったので、
プレイヤーごとの番号割り当ての実装に移ります。順番としては見出し通り下記です。
①使用済みの番号を特定
②未使用の番号でプレイヤーの番号を更新コードは以下です。
private const int _PLAYER_UPPER_LIMIT = 4; /// <summary> /// プレイヤーに番号を与える /// </summary> private void SetMyCustomProperties() { //自分のクライアントの同期オブジェクトにのみ if (photonView.IsMine) { List<int> playerSetableCountList = new List<int>(); //制限人数までの数字のリストを作成 //例) 制限人数 = 4 の場合、{0,1,2,3} int count = 0; for (int i = 0; i < _PLAYER_UPPER_LIMIT; i++) { playerSetableCountList.Add(count); count++; } //他の全プレイヤー取得 Player[] otherPlayers = PhotonNetwork.PlayerListOthers; //他のプレイヤーがいなければカスタムプロパティの値を"0"に設定 if (otherPlayers.Length <= 0) { //ローカルのプレイヤーのカスタムプロパティを設定 int playerAssignNum = otherPlayers.Length; PhotonNetwork.LocalPlayer.UpdatePlayerNum(playerAssignNum); return; } //他のプレイヤーのカスタムプロパティー取得してリスト作成 List<int> playerAssignNums = new List<int>(); for (int i = 0; i < otherPlayers.Length; i++) { playerAssignNums.Add(otherPlayers[i].GetPlayerNum()); } //リスト同士を比較し、未使用の数字のリストを作成 //例) 0,1にプレーヤーが存在する場合、返すリストは2,3 playerSetableCountList.RemoveAll(playerAssignNums.Contains); //ローカルのプレイヤーのカスタムプロパティを設定 //空いている場所のうち、一番若い数字の箇所を利用 PhotonNetwork.LocalPlayer.UpdatePlayerNum(playerSetableCountList[0]); } }コメントの通りの処理を行っています。
番号に応じた生成位置と色を割り当て
未使用の番号でプレイヤーの番号を更新 することができたので、
割り当てられた番号を利用した処理を追加していきます。PhotonViewがアタッチされたアバターオブジェクトにアタッチusing System.Collections.Generic; using Cysharp.Threading.Tasks; using ExitGames.Client.Photon; using Photon.Pun; using Photon.Realtime; using UnityEngine; /// <summary> /// プレーヤー生成時に行う初期設定処理 /// </summary> public class PlayerInitializeSetting : MonoBehaviourPunCallbacks { [SerializeField] private MeshRenderer _avatarObjectMeshRenderer; [SerializeField] private Material[] _playerMaterials; [SerializeField] private Transfrom[] _playerInitTransform; private bool _isUpdateCustomProperty; private async void Start() { //プレーヤーのカスタムプロパティ更新 SetMyCustomProperties(); //通信完了(カスタムプロパティ更新)まで待つ await UniTask.WaitUntil(() => _isUpdateCustomProperty); //自分のクライアントの同期オブジェクトの設定 if (photonView.IsMine) { this.gameObject.transform.rotation = _playerInitTransform[PhotonNetwork.LocalPlayer.GetPlayerNum()].rotation; this.gameObject.transform.position = _playerInitTransform[PhotonNetwork.LocalPlayer.GetPlayerNum()].position; _avatarObjectMeshRenderer.sharedMaterial = _playerMaterials[PhotonNetwork.LocalPlayer.GetPlayerNum()]; } //他のクライアントの同期オブジェクトの設定 else { this.gameObject.transform.rotation = _playerInitTransform[photonView.Owner.GetPlayerNum()].rotation; this.gameObject.transform.position = _playerInitTransform[photonView.Owner.GetPlayerNum()].position; _avatarObjectMeshRenderer.sharedMaterial = _playerMaterials[photonView.Owner.GetPlayerNum()]; } } /// <summary> /// カスタムプロパティ更新時のコールバック /// </summary> /// <param name="target">更新されたカスタムプロパティを持つプレーヤー</param> /// <param name="changedProps">更新されたカスタムプロパティ</param> public override void OnPlayerPropertiesUpdate(Player target, Hashtable changedProps) { _isUpdateCustomProperty = true; } }
OnPlayerPropertiesUpdateという
通信完了後、カスタムプロパティが更新し終えたことを
通知するコールバック内でフラグを立てています。このフラグを利用し、Start関数内でasync/awaitして完了待ちを実装しています。
デモ
実際に今回メモした実装方法でプレイヤーの生成位置、色の設定を
入退室に対応させたアプリのデモムービーです。【参考リンク】:Sign Language in VR
オンライン上でVR空間内での手話トレーニングを実施し、
動画やビデオ通話以上のトレーニング効果を期待したVRアプリです。最後に
カスタムプロパティで初期設定を行う方法を使用しましたが、
完全に同じタイミングでカスタムプロパティを
更新した場合のエラーハンドリングなどが未実装です。私が把握している問題は現状これだけですが、
まだまだ対策しないといけないことが多そうです。引き続き深みにはまっていこうと思います。。。
参考リンク
- 投稿日:2020-07-05T09:03:49+09:00
DOTween完全に理解するその3 UIアニメーション編
前回:DOTween完全に理解するその2 Transformアニメーション編
今回解説するアニメーション
今回は主にUIに関するアニメーションを解説していきます。
UI系のアニメーションもかなりお手軽にアニメーションが作れるのでとても便利です。
メソッド名 メモ DOColor Colorの変更(Graphic,Image,Text,Outline) DOGradientColor Gradientを使用してColorを変更(Image) DOFade Alphaの変更(CanvasGroup,Graphic,Image,Text,Outline) DONormalizedPos系 ScrollViewのスクロール位置を変更 DOText Textに表示される文字列を表示していく DOValue Slider.Valueを変更 DOFillAmount ImageのFilled設定を変更する 開発環境
Unity:2019.4.0f1
DOTween:v1.2.335DOColor
var image = GetComponent<Image>(); image.DOColor(Color.Red, duration);現在値から設定したColorへRGBの値を変更します。
対象コンポーネントはImage,Text,Outline,Graphicです。ImageとTextはGraphicの派生クラスなので
例えばDOColor()をターゲットに対して行うコンポーネントを作る場合、
GraphicをGetConponent()してDOColor()を実行するように実装すれば、
Image用とText用を別々に作らなくて済みます。
Outlineは派生クラスではないので別に必要があります。
また、DOBlendableColor()というAPIがあります。
意味はDOBlendableMove()とかと同じで、値の代入ではなく加算を行います、DOGradientColor
[SerializeField] private Gradient to; void Start(){ var image = GetComponent<Image>(); image.DOGradientColor(to, duration); }対象コンポーネントはImageのみです。
Gradientを引数に渡してグラデーションでColorの変更を行います。
ゲーミング的な表現に使えると思います。カードの枠をオーラみたいにするのとかにも良さそう。DOFade
var image = GetComponent<Image>(); image.DOFade(0, duration);対象コンポーネントはCanvasGroup,Graphic,Image,Text,Outline
意味はそのままアルファ値を操作します。
DOColor系と同じくtargetのcolorを変更する為、競合します。
その場合は「Fadeの処理順を後にする」「targetを別にする」などの工夫が必要になります。DONormalizedPos系
var scroll = GetComponent<ScrollRect>(); scroll.DONormalizedPos(vector2, duration, snapping); scroll.DOHorizontalNormalizedPos(1, duration, snapping); //横方向 scroll.DOVerticalNormalizedPos(1, duration, snapping); //縦方向対象コンポーネントはScrollRectのみです。
終点を0〜1で指定します。スクロール方向や基準点はScrollView側の設定を使用します。
snapping=trueの場合、整数未満の位置を無視して「カクっとスクロール」します。DOText
var target = GetComponent<Text>(); target.DOText( text, duration, richTextEnable, scrambleMode,scrambleChars);対象コンポーネントはTextのみです。
指定した文字列を順次表示するアニメメーションで、テキスト表示なんかに使えるかもしれません。
scrambleModeは非表示部分にランダムな文字を表示する下の設定になります。
Mode メモ None 非表示部分は何も表示しない Uppercase 大文字のみ Lowercase 小文字のみ Numerals 数字のみ All 大文字小文字数字全て Custom scrambleChars内の文字を使用 サンプルgifのCustomは「X」をscrambleCharsに設定しています。
Numeralsを使えば、リザルトでのスコア表示なんかにも使えるかと思います。ただ、非表示文字の切り替えは毎フレーム行なっているようです。
なのでtimeScaleを変更しても文字の切り替えは遅くなりません。
timeScaleを小さな値にしても「正しい文字列が表示される間隔」が長くなるだけです。DOValue
var target = GetComponent<Slider>(); target.DOValue(to, duration, snapping);対象コンポーネントはSliderのみです。
snappingはDONormalizedPos系と同じく「小数点以下を無視します」
サンプルgifの左側はsnapping=trueにしています。DOFillAmount
var target = GetComponent<Image>(); return target.DOFillAmount(to, duration);対象コンポーネントはImageのみです。
ImageTypeがFilledの場合、その設定を利用つつFillAmountの値を変更します。サンプルgifは
アニメーションしないImage(白)の前にアニメーションをするImageを3つ配置して
DelayをかけながらDOFillAmount()を行なっています。
「DOFillAmount」のTextは、左詰めにしたTextに対して同じくDOText()を使用してみました。まとめ
今回はUIに関するDOTweenの解説を行いました。
前回のTransformに作用するTweenと違い、ほとんどの場合は個別のUIに作用します。
あまり深く考えずに使える為、Transform系よりはつまずくことが少ないと思います。今回UI編ではあるのですがRectTransfrom系とLayoutElement系を解説しませんでした。
これらは他のTweenと比較しても特殊なのと、特に面白い使い方が思いつかなかったので解説しませんでした。
また機会があったら記事にしようかなと思います。
- 投稿日:2020-07-05T01:05:50+09:00
AdmobをUnityで導入した時の話
導入イメージ
- アプリ画面下にバナー表示
- 全画面動画広告を見て、リワードとしてバナー表示を1日無効化する
- つまりバナー表示と、リワード動画再生ができればOK
前置き
- Unity2018
- Google Mobile Ads Unity Plugin v5.2.0
- v3系導入時に記事を書き始めて、v5系でブラッシュアップしたので、所々古かったらすみません……
- 基本的に公式ドキュメントが素晴らしいので、それに沿って導入すればほぼ問題なし
- 初回導入時、色々エラーになって調べたりで、Firebaseをいれないといけないみたいな情報にも惑わされたが、Unityであれば、Google Mobile Ads Unity Pluginさえimportしておけば広告は表示可能
まずはアカウント発行
1.アクセス:https://admob.google.com/intl/ja/home/
2.右上のお申し込みボタン押す
3.申し込みたいアカウントをよしなに選択
4.申し込みページでよしなに記載&同意お支払い情報を登録する
1. お支払い情報を追加リンク
2. 登録情報を適宜入力して送信アプリ設定からGooglePlayとリンクする
広告ユニットの作成
バナータイプ
- メニューでアプリを選択:https://apps.admob.com/v2/apps/list
- 新しいアプリの設定:https://apps.admob.com/v2/apps/create
![]()
- アプリの情報を入力
![]()
- 確認&広告ユニットを作成
![]()
- 広告フォーマットの選択
![]()
広告ユニットの作成(各項目、
?の説明が分かりやすいので、不明ならそちらを参照)
- 広告の種類:どちらも選択しておいたほうが良さそう。
- 自動更新:Googleによる最適化に任せるのが良さそう。
- eCPM Floor:ヘルプの内容が分かりやすい。ざっくり、高めに設定すると高レートの広告を表示するけど、広告も無限にあるわけでないので表示できない状態が発生するケースが出る。なので、無難に無効にして、基本AdMobの動作に任せる。
iPhone用も同様の手順で作成する。
動画タイプ
- 広告ユニット「リワード」に対して、バナー同様に作成すればOK。
SDK導入
基本コレに沿って実施:Mobile Ads SDK(Unity)スタートガイド
1. プラグイン(.unitypackage)DL:https://github.com/googleads/googleads-mobile-unity/releases
2. プラグインimport(Assets > Import Package > Custom Package)
3. 全て選択されているのを確認してimport
※play-services-resolverも含まれているので、一緒にimport
4. 確認ダイアログ等が表示されるのでよしなに確認&進めてく
5. 確認(Assets > Play Services Resolver > Android Resolver > Resolve)
こうなってればOKそう
6. メニューの[Assets] > [Google Mobile Ads] > [Settings]より、アプリIDを入力
7. 以下のようなスクリプトを作成、初回起動時に生成されるGameObjectにアタッチGoogleMobileAdsDemoScript.csusing UnityEngine; using GoogleMobileAds.Api; public class GoogleMobileAdsDemoScript : MonoBehaviour { public void Start() { // Initialize the Google Mobile Ads SDK. MobileAds.Initialize(initStatus => { }); } }バナー表示
- 公式のドキュメントそのままでいけた:バナー広告
リワード動画
- こちらも公式のドキュメントそのままでいけた:リワード広告
- 動画広告をみた報酬としての処理は、
OnUserEarnedRewardで呼び出す形にすればOKiOSについて
- switch platform実施時に、自動で必要なもののinstallなど色々処理されたので、特に困ったことなかった。
トラブルシュート
import時エラー
Gradle failed to fetch dependencies.JDK入ってないだけだった。
インストール。何か表示されない
07-03 10:26:32.555 5086 5109 E Unity : AndroidJavaException: java.lang.ClassNotFoundException: com.google.android.gms.ads.MobileAds 07-03 10:26:32.555 5086 5109 E Unity : java.lang.ClassNotFoundException: com.google.android.gms.ads.MobileAds 07-03 17:48:45.900 8645 8663 E Unity : Caused by: java.lang.ClassNotFoundExc eption: Didn't find class "com.google.android.gms.ads.MobileAds" on path: DexPat hList[[zip file "/data/app/com.Yasuragitei.DayDayDay-2/base.apk"],nativeLibraryD irectories=[/data/app/com.Yasuragitei.DayDayDay-2/lib/arm64, /data/app/com.Yasur agitei.DayDayDay-2/base.apk!/lib/arm64-v8a, /system/lib64, /vendor/lib64, /syste m/vendor/lib64, /product/lib以下のケースで、解決できた。
- Resolveが上手くいっていない(Assets>Plugin>Andrdroid配下にxxx.aarがない)
- Force ResolveすればOK
Ad failed to load : 3
07-19 14:49:30.848 18028 18028 I Ads : Ad failed to load : 3こちら参考に、GooglePlayとAdMobの関係付けしてあげる。
Admob on Androidで Ad failed to load : 3と言われるエラー
ただ、関連付けしなくてもテスト時は問題ない(テスト広告が表示される)ので、あまり気にしなくてよい。
あと、広告がない時にも出力されていそうなので、その時は、OnAdFailedToLoadなどで、アプリ側で広告ロードに失敗しましたなどの処理が必要になってきそう。感想
- 初回は何故かすごく色々突っかかったが、一度環境が整うとGoogle Mobile Ads Unity Pluginのバージョンアップや、再importはスムーズに行った。きっとどこかに記載されている環境構築を最初にちゃんとやるべきだった……。
- テストが実機でしかできないのでつらい。
参考














































