HUAWEI FreeBuds 3 vs FreeBuds 3i
FreeBuds 3とFreeBuds 3i
FreeBuds 3とFreeBuds 3iはHUAWEIから販売されているワイヤレスイヤホンです。
FreeBuds 3の後にFreeBuds 3iが出たので3iの方がパワーアップしているような気がしますが、どちらかというとノイズキャンセリング以外の機能はパワーダウンしているような感じです。
値段もこの記事を書いている時点でAmazonで3が20000円程度、3iは13000円程度です。 このことからもFreeBuds 3の方が力をいれて作られたことが分かります。
私はずっとFreeBuds 3を使っていたのですが、 FreeBuds 3iを購入したので、それまで使っていたFreeBuds 3と比べてみました。
様々な機能やスペックについての比較は、色んな所でされているので今更感があるので個人的な感想ベースでの比較となります。
2つの製品については以下の公式ページをご覧ください。
着け心地
まずは耳へ付けたときの装着感の違いです。
これに関してはFreeBuds 3の完全に勝利でした。
この辺に関しては人による違いが物凄く大きくでる部分だと思うので参考までにしてください。
まず、この2つのモデルは開放型とカナル型という大きな違いがあるため、以下のように見た目も大きく異なります。
そのため、FreeBuds 3は耳に優しくはまる感じなのですが、FreeBuds 3iのほうはグリグリっと押し込む必要があります。
3iをつけた後に3を付けると、ホントに軽くとまっている感じで耳への負担も少なく優しい感じがします。
ただ、これは耳の形状にもよって変わると思うので全ての人で同じ感想は得られないかもしれないです。
色々な人のレビューを見ていると、3は「直ぐに耳から落ちてしまう」という人もいるみたいですが、私はランニングの時に着けても全然外れることなく使えているので、よほど耳にフィットしているようです。
ケースの持ち運び
自分が大分気になっている点としてケースの持ち運びがあります。
下の写真は左がFreeBuds 3で右がFreeBuds 3iです。
見ていただくと分かる通り、3iの方が厚みがあります。
私は普段ズボンの左ポケットに入れる事が多いのですが、その時に3iの厚みは大分気になっちゃいます。
横幅をもう少し広げてでも厚みは抑えてほしかったなあという感想です。
ノイズキャンセリング
この2つのモデルは両方ともアクティブノイズキャンセリングの機能をもっています。
このノイズキャンセリングの比較をすると、これはカナル型である3iの圧勝です。
電車に乗っている時のガタンガタンという音で比較すると、FreeBuds 3が体感20%くらい音が小さくなるのに比べてFreeBuds 3iは60~70%くらいは軽減されていると思います。
そもそも3のほうは開放型にノイズキャンセリングを載せているだけでも凄いので、これはしょうがないは思います。
3iの方は耳へ付ける角度でもだいぶノイズキャンセリングの感じが変わってくるので、どの角度が一番静かになるかは最初に検証したほうがより良く使えると思います。
あと細かな点として、3のほうは使うたびにノイズキャンセリングがOFFの状態に戻ってしまうのですが、3iの方は前回のON,OFF状態を覚えていてくれるのでそこも異なる点です。
ただこれは一概にどちらが良いとは言えないなあとも思えてきました。
というのも前回使った時がちょい前とかだと、前回ノイズキャンセリングをONにしたまま終えたかどうか覚えておらず、3iを装着してから今どっち(ノイズキャンセリングON or 外部音取り込み)だろうと思ったことが既に何度かあります。
3の方は毎回OFFですから、イヤホンのダブルタップをしてONにするというルーチンを毎回すればよいので悩むことはないです。
また3の方はアプリでノイズキャンセリングの調整を行う必要がありますが、3iは必要ありませんというかアプリメニューになかったです。
スマホとの接続
私はスマホは同じHUAWEIのP30 Proを使っています。
個人的にBluetoothを使ったデバイスはブチブチ切れるのが凄いストレスなので、可能な限り同じメーカーで揃えたいというのがあるので今は両方HUAWEIにしてます。
なのですが、この2つのモデルは接続安定性に大分差があるように感じました。
まずケースから出してからスマホにつながるまでの時間には差は感じません。 どちらも一瞬でつながります。
なのですが、音楽を流している途中にブチっと無音になることがFreeBuds3ではほとんど遭遇したことがないのですが、3iは1日の通勤の間に1回は遭遇するくらいの頻度で起こっています。
これはなかなか気になるので3iに対してはマイナスポイントです。
このへんは3の方のみBluetoothにKirin A1 チップセットを使っている事が影響しているんですかね。
音質
これについては専門家でないので詳しい事を述べる知識や経験がなく、あんまり言う事はないです。
2つとも個人的には悪くないなと思っています。
充電
FreeBuds 3はワイヤレス充電できるのですが3iはできません。
この辺は別にtype-cの充電器は持ち歩いているので困ることはありませんが、ワイヤレス充電は便利なので3iにもあったらよかったなあとは思います。
(一度P30 Proの機能であるワイヤレス充電出力を使ってFreeBuds 3を充電してみましたが便利でした!)
最終結果
私としてはこれから日常的に使うのは最初に買ったFreeBuds 3になりそうです。
トータル的に見て使い勝手が良く、あと耳に優しくフィットする点がとても気に入っているので。
では3iはどうするのかというと、今の職場は結構エアコンや色んな機械の"ゴー"っという騒音があってかなり集中力が奪われるので職場で集中したい時専用にノイズキャンセリングに助けてもらおうかと思ってます。
なので適材適所で2つとも有効利用していきたいです。
番外編
私がFreeBuds 3iを買った次の日に、"FreeBuds Pro"というモデルが発表されました。。。
これは3iよりもノイズキャンセリングがパワーアップされており、見た目も今までの2モデルよりもカッコいいです。
ただお値段もPro価格(Aliexpressで25000円くらい)なので買うか悩みます。。
WinUI 3 Preivew1のプロジェクトをPreview2に更新する
WinUI 3 Preview 2
気づかずに半月程たってしまいましたが、WinUI 3のPreview2がでてました。
なので、以前WinUI 3 Preview1を使って作成した以下のWinUI3 DesktopプロジェクトをPreview 2に更新してみました。
今回はその記録です。
基本的に公式の説明通り進めていっただけなので有用な情報は少ないです。
環境の更新
公式ページの記述に従い環境を確認していきます。
開発用コンピューターに Windows 10 バージョン 1803 (ビルド 17134) 以降がインストールされていること
Windows10 2004なのでOK.
Visual Studio 2019 バージョン 16.7 Preview 3 のインストール
インストール済みのVS2019 Previewのバージョンを確認してみると、16.7.0 Preview 3.1でした。
なのでこのままいくことに。
更新プログラムの確認をしたところ、16.8.0 Preview 1.0がありましたが、前回.NET 5のバージョンを最新にしたことでWinUI 3 Preview1の起動が失敗するようになったのでむやみに最新にするのはやめました。
.NET 5 Preview 5
WinUI3 Preview2では.NET 5のPreview5に対応したみたいです。 現状の最新を確認したところ、Preview7がありましたが確実に動作させるためにPreview5にしておきます。
ただし、今回は公式ページの説明に「Preview5よりも新しいのを入れないで」という但し書きが無かった(前回はあった)のでひょっとしたらPreview7でも動くかもしれません。
WinUI 3 Preview 2 VSIX パッケージのインストール
VSへの拡張機能をいれます。 ただダウンロードしてインストールしてみたところ、既に入っていますと表示されていたのでPreview1から変わってないと思います。
Projectの更新
既にWinUI Preview1で作ったプロジェクトは、そのままではPreview2では動作しないらしく、いくつか編集する必要があるようです。
詳細は以下のページに書かれています。
では上記ページに従って編集していきます。
nugetでMicrosoft.WinUIのPreview2をインストール
NuGetパッケージマネージャーにて検索したところ表示されませんでした。
ただその場合の対処も書いてあり、パッケージマネージャコンソールからinstall-package Microsoft.WinUI -Version 3.0.0-preview2.200713.0
をいれてインストールしました。
.csprojの編集
TargetFrameworkの記述をnetcoreapp5.0
からnet5.0
に変更します。
このファイルはこれだけ。
.wapprojの編集
パッケージプロジェクトの設定ファイル(.wapproj)をいくつか編集します。
変更1
<Import Project="$(WapProjPath)\Microsoft.DesktopBridge.props" />
の後のPropertyGroup
の中に<AppxTargetsLocation Condition="'$(AppxTargetsLocation)'==''">$(MSBuildThisFileDirectory)build\</AppxTargetsLocation>
を記載を挿入します。
変更2
<Import Project="build\Microsoft.WinUI.AppX.targets" />
を <Import Project="$(AppxTargetsLocation)Microsoft.WinUI.AppX.targets" />
に変更します。
パッケージプロジェクトのbuildフォルダ内のファイルを置換
まず、新規プロジェクトを作ります。 これは使い捨てなのでどこか一時的なフォルダへの作成でOKです。
テンプレートはBlank App, Packaged (WinUI in Desktop)
にします。
プロジェクトが作成されたら、
[Packageプロジェクトフォルダ]/build/
にある以下の2つのファイルをコピーします。
- LiftedWinRTClassRegistrations.xml
- Microsoft.WinUI.AppX.targets
そして、自分のパッケージプロジェクトの同じ階層のファイルを上書きします。
ビルドして実行
VSを再起動してビルド→実行してみるとちゃんと起動しました。
儀式完了。WinUI Preview1のプロジェクトをPreview2にアップデートできた。 pic.twitter.com/b509OoP6K8
— ayuma (@ayuma_x) 2020年8月5日
WinUI3 Preview2はPreview1からたくさんバグも修正しているらしく、色々とみていきたいと思います。
UE4 Windowsパッケージにおいてのアンフォーカス時の対応(Sound再生、GamePadの入力)
フォーカスがないときの挙動
私はお仕事でUE4をゲーム以外の用途で使っているのですが、Windowsで他のアプリケーションと一緒に使うことが多いです。
よくあるパターンがUE4で表示部分を作り、WPFでコントローラーのようなものを作り組み合わせて使うパターン。
このパターンではどうしてもフォーカスはWPFアプリケーション側にくるので、UE4のウィンドウにはフォーカスが無い状態になります。
そうすると以下のような困ったことが起こりました。
- 音が聞こえない
- GamePadの入力がとれない
これらについて中々対策が見つからなかったのですが、Engineソースを確認したところ2つとも対策が見つかりましたので覚書として書いておきます。
なお、この記事の対処をしたのは4.24の環境ですが、4.25でもその部分の実装は変わってなさそうなので同じ対応でよいのかと思っています(4.25では動作確認まではしてません)
アンフォーカス時の音の再生
WindowsPlatformApplicationMisc::PumpMessages
にて以下の記載を見つけました。
// if its our window, allow sound, otherwise apply multiplier FApp::SetVolumeMultiplier( HasFocus ? 1.0f : FApp::GetUnfocusedVolumeMultiplier() );
SetVolumeMultiplierは音のボリュームをセットする関数のようですが、ここでフォーカス有りの場合は1.0を、そうでない場合はGetUnfocusedVolumeMultiplierの値を使っています。
そしてGetUnfocusedVolumeMultiplierが返す値を調べると0になっておりました。 なので聞こえないんだなあ。
ただ、この値の初期値はConfigからとられていることが分かります。
static bool GUnfocusedVolumeMultiplierInitialised = false; float FApp::GetUnfocusedVolumeMultiplier() { if (!GUnfocusedVolumeMultiplierInitialised) { GUnfocusedVolumeMultiplierInitialised = true; GConfig->GetFloat(TEXT("Audio"), TEXT("UnfocusedVolumeMultiplier"), UnfocusedVolumeMultiplier, GEngineIni); } return UnfocusedVolumeMultiplier;
ということで、以下の設定をConfig/DefaultEngine.iniに書いてUnfocusedVolumeMultiplierの値も1.0になるようにして動作を確認しました。
[Audio] UnfocusedVolumeMultiplier=1.0
アンフォーカス時でも音が聞こえてきて成功です。
アンフォーカス時のGamePadの入力
UE4でGamePadの入力はWindowsではXInputというAPIを使っています。
なので、そこから検索するとXInputInterface
というクラスが見つかりました。
中身を確認するとXInputのAPIを呼んでいるのでここがGamePadの入力を受け取っている可能性が高いです。
次にXInputInterface
を使っている箇所を探し、FWindowsApplication
にて使われていることが分かりました。
するとFWindowsApplication::PollGameDeviceState
にてXInputの入力イベントを発火していそうです。
更にこの関数はFSlateApplication::PollGameDeviceState()
から呼ばれています。
FSlateApplication::PollGameDeviceState()
の中身を見るとifでガードされています。
if( ActiveModalWindows.Num() == 0 && !GIntraFrameDebuggingGameThread && (!bRequireFocusForGamepadInput || IsActive())) { // Don't poll when a modal window open or intra frame debugging is happening PlatformApplication->PollGameDeviceState( GetDeltaTime() ); }
ここには3つの条件がありますが、最後のIsActive()はフォーカスを持つことのような気がします。
しかにここにbRequireFocusForGamepadInput
というダイレクトで関わりが深そうな変数があります。
これの定義を調べるとコンソールコマンドで値が変えられそうでした。
static bool bRequireFocusForGamepadInput = false; FAutoConsoleVariableRef CVarRequireFocusForGamepadInput( TEXT("Slate.RequireFocusForGamepadInput"), bRequireFocusForGamepadInput, TEXT("") );
私の仕事の用途ではdevelopment buildしているのでCtrl+@で上記コマンドを入れてみたところアンフォーカスでもGamePadの入力がとれるようになりました。
ただし毎回打ち込むのもメンドクサイので(BluePrintから呼ぶ手もあるけどできればやりたくない)
ちょっと調べているとコンソールコマンドもiniファイルに定義できるみたいなので、その対応をすることにしました。
まとめ
UE4のアプリケーションはフォーカスを失うと色々できない事が起こるのですが、なんとか希望の動作をするように調整できました。
エンジンソースが見られるっていうのはこういった調査もやりやすいのでホント嬉しいです!
UProperty -> FPropertyへの変化(UE4 4.25)
Unreal Engine 4.25 Preview
先日UE4の4.25のPreviewがダウンロードできるようになりました。
(この記事を書いている段階ではPreview3になっています)
その内容は以下のページに記載されています。
たくさんある更新内容の中で、以下の項目が目に留まりました。 (FProperty Refactor...の部分です)
内容は「4.25からはUPropertyはFPropertyとなってUObjectではなくなりました」というものです。
今までUPorpertyはUObjectを継承していました。 UE4ではUObjectを継承しているクラスはUから始まる命名規則があります。そのためUPropertyというクラス名でした。
それがUObjectを継承しなくなった(FFieldというクラスを継承するようになっています)ため、FPropertyというクラス名に変わったということでした。
UPropertyとは
そもそも今回変更されたUProperty(FProperty)とは何なのかというと、UE4のプロパティシステムによって作られたC++クラスのメンバー変数または関数の引数の情報になります。
詳細は以下の公式ページに詳しく説明されています。
更にUPropertyはプロパティの型によって、UStringPropertyやUBoolPropertyなどに派生されています。
UE4のエンジン内部では色々な用途に使われていますが、我々UE4を使う側の立場では、例えば以下のようなことをしたい時に使うことになります。
- ブループリントで定義したオブジェクトのプロパティにC++側からアクセスしたい
- オブジェクトの独自のシリアライザを作りたい
- オブジェクトのプロパティをプロパティ名を指定して触りたい
このようにUPropertyを使ってUE4のプロパティシステムを使うと、より柔軟にオブジェクトを操作する仕組みが構築できます。
(参考)オブジェクトの全てのプロパティの値を取得する例
以下のコードはObjの全てのプロパティの値を取得しています。
(※ この例では全てのプロパティの型はuint8であるという前提です)
for (TFieldIterator<UProperty> PropIt(Obj->GetClass()); PropIt; ++PropIt) { UProperty* Property = *PropIt; // プロパティ名を取得 FString PropertyName = Property->GetName(); // プロパティの値を取得 uint8 CurrentPropAddr = *(PropIt->ContainerPtrToValuePtr<uint8>(Obj)); }
なぜFPropertyに変更されたのか
全部の情報を追い切れてないので個人的な想像を多分に含みますが、UObjectで無くすことによりGC(ガベージコレクション)の管理対象外にしたかったのが大きな理由なのかなと考えています。
UObjectとしてインスタンスをメモリに確保すると、GCが適切なタイミングでインスタンスを破棄するべきかどうかをチェックします。 チェックの結果破棄するべきとなったインスタンスは破棄されることになります。
プロジェクト全体でUObjectのインスタンス数が多いと、このGCが破棄対象をチェックする際の検索時間が多くなってしまいます。 (調べる対象の個数が絶対的に多くなってしまうため)
検索時間が多くなると、ひどくなるとGCのチェックタイミングでフレームレートがガクンと落ちたりするほど悪影響がでます(経験あり)
そのためケースバイケースではありますが、UObjectのインスタンス数はなるべく少ないほうがパフォーマンス上は良くなると考えられます。
従来(4.24まで)のC++コードを4.25でビルドすると
試しに先ほど書いたオブジェクトのプロパティを列挙するコードを4.25でビルドしてみました。
するとエラーにはなりませんでしたが、以下のWarningがでています。
warning C4996: UProperty has been renamed to FProperty Please update your code to the new API before upgrading to the next release, otherwise your project will no longer compile.
「UPropertyはFPropertyに変わったから、早く変えてね」っというWarningです。ですがWarningということはこのままでも一応動くということになります。
その理由は4.25でのUPropertyの定義を見ると分かります。
DefineUPropertyMacros.h
というヘッダに以下のように定義されていました。
U~Propertyは全てF~Propertyとして置換されるようになっています。
この置換する際にDEPRECATED_MACROも埋め込まれているため、先ほどのWarningがでているようです。
では、とりあえずはUPropertyを使っているコードでも当面は何もしなくて良いのかというとそうではないようです。
置換されているだけでC++コードに書かれているUPropertyは既にUObjectではないので、以下のようなコードはエラーとなります。
UProperty* Property = *PropIt; if (UBoolProperty* BoolProperty = Cast<UBoolProperty>(Property)) { bool bValue = BoolProperty->GetPropertyValue(Obj); }
上記は「あるプロパティの型がbool型だったらboolとして値を取得する」といった意味のコードです。
この中でCast<UBoolProperty>(Property)
を使ってUPropertyをUBoolPropertyにキャスト可能か調べているのですが、CastはUObjectに対してしか使用することはできません。
そのためこのコードをビルドするとエラーとなります。
(※ 上記の例ではCastField関数に置き換える事でFPropertyでも同じことができます)
同様の理由でIsValid
なんかもダメになりそうです。
その他UPropertyをUObjectとして引数に関数に渡している部分がある場合は、適切に変更する必要があります。
まとめ
- 4.25からはUPropertyはFPropertyになりました
- C++でU~Propertyを使っている箇所はF~Propertyに置換しましょう
- ただ置換するだけでなくUPropertyをUObjectとして使っている箇所がないかチェックしましょう
おまけ
以前のままのUProperty定義はUnrealTypePrivate.h内に残っているため、このヘッダをIncludeするともうしばらくUPropertyは延命できそうな気もしますが、あるべき流れからは外れる事になりそうなので使わないほうが良さそうかな。
ObjectDeliverer Ver 1.5.0リリース ObjectのJsonシリアライズ機能の強化
本日ObjectDelivererのVer 1.5.0をマーケットプレイスにリリースしました。
今回の実装内容はObjectのJsonシリアライズ機能の強化です。
これについては以前の記事に現状の課題として書いていました。
今回のリリースでは上記課題を改善する機能実装になります。
実装項目
以下の2項目を対応しました。
プロパティ名の変換
シリアライズ対象のクラスにIODConvertPropertyName
インターフェイスを実装することで、プロパティ名の変換ができるようになりました。
使用例)
// 変換前 { "Prop2": 1, "ObjProp": { "ValueB": 0, "ValueA": 0 }, "ObjProp2": { "ValueC": "abc" } } // 返還後 { "IntProperty": 1, "ObjectPropertyA": { "ValueB": 0, "ValueA": 0 }, "ObjectPropertyB": { "ValueC": "abc" } }
この機能を使うことで以下のようなことが可能になります
- FNameによる大文字小文字を区別できない問題の解消
- 対象クラスとJsonとで異なるプロパティ名にする
地味ですがじんわり役立つはず。
シリアライズフォーマットの選択
今回一番力を入れて実装した項目です。 Json内にクラス名を埋め込むことができるようになりました。
// クラス名を入れない従来の方式 { "Prop": 1 } // クラス名埋め込む方式 { "Type": "SampleClassName", "Body": { "Prop": 1 } }
これにより、以下のようなことが可能になります。
- 1つのDeliveryBoxで複数クラスのシリアライズ、デシリアライズ対応
- Object型のプロパティに派生クラスのインスタンスが入っている場合でも復元可能
またクラス名を埋め込む方式のみでは上記メリットがある一方で、文字数(データサイズ)が大きくなってしまうデメリットもあるため、Objectの型に応じて従来の方式と新方式を使い分けるオプションも用意しています。
例えば以下のようにあるプロパティのみクラス名を埋め込むといった使い方も可能です。
{ "Prop2": 1, "ObjProp": { "Type": "CustomObjB_C", "Body": { "ValueB": 2, "ValueA": 3 } }, "ObjProp2": { "ValueC": "abc" } }
まとめ
今回はObjectのJsonシリアライズ機能に焦点を絞って機能UPをしました。
まだ実装したいネタは他にもあるので、着々と機能UPするObjectDelivererをよろしくお願いします。
2019年の振り返り
今年は早かった。あっという間に終わった1年でしたが、去年もやったように1年を振り返ってみようと思います。
去年の振り返りはこちら
何をした?
月 | やったこと |
---|---|
1月 | ObjectDelivererを作った |
2月 | ObjectDelivererをマーケットプレイスに出品した |
3月 | VRのコンテンツを作って会社にプレゼンした |
4月 | VRのお仕事Get |
5月 | Pimax 8K試した |
.NET Coreを試した | |
6月 | EasyXMLParserをマーケットプレイスに出品した |
EasyJsonParserをマーケットプレイスに出品した | |
7月 | WindowCapture2Dをマーケットプレイスに出品した |
8月 | UE4 + ARKitでARコンテンツ作成 |
9月 | IL2C + UE4の実験 |
ARのお仕事Get | |
10月 | .NET Conf 2019 meetup in AICHIでLTした |
11月 | 疲れて抜け殻 |
12月 | C#とUE4のアドベントカレンダー記事を1つずつ投稿 |
VR開発
今年の大きな出来事として仕事でVR開発を始めたことがあります。
世間的にはVRはもうそこまで珍しいものではありませんが、弊社では初の試みなので周りからは色々(期待やら不安やら)言われてましたが、一定の成果は残せました。
これも今後どこまで広げれるかっていうのは課題なので、来年も引き続き頑張ることになります。
AR開発
今年はUE4とARKitでARの開発も始めました。こちらはUE4でARKitを使って開発する基本をなぞっただけの段階ですがやりたい事は全てできたので満足してます。
来年以降どうやって業務に取り入れるのか考えたいなあと思ってます。
あとモバイル端末だけでなくHoloLensとか、Magic Leapもさわりたいですね。
あと、個人開発でもなんかやりたいなあと。
UE4 マーケットプレイス
プライベートの活動としてはマーケットプレイスに出品したことが大きい出来事です。
年のはじめに自分でも出品する事はできるのかなあと思って調べはじめ、なんやかんやで4つのコードプラグインをリリースしました。
どれも魂込めて作ってますので、ぜひたくさんの方に使っていただけたら嬉しいなあとおもいます。
あとオープンソースにしているプロジェクトもあるので、GitHub上での他の人とのやりとりが楽しかった。 英語なのでGoogle翻訳様の力を借りないと、会話が成立しないのですが、おかけで翻訳しやすい日本語の生成力があがった気がします!
マーケットプレイスでの評価もとても励みになっているのでありがたいです。
反省点としては、調子に乗って4つもリリースしたことでメンテナンスが追いついてない事です。
どのプラグインにも実装したいけど、できていないネタがあるので、来年はそこをやっていきたいなあと思ってます。
登壇
今年は当初どこかで2回は登壇することを目標にしてたのですが、結果としては1回のみでした。
でもそういう機会がもらえただけでもありがたいですし、少しは成長できたと思うので今後も引き続き機会があればやりたいなあと思います。
そのためにも普段からネタをためておかないと。
まとめ
自分としては例年に比べて、とても濃い一年でした。
来年も今年以上に色々やりたいと思っているので頑張ります!
UE4+ARKitで作る遠隔タッチディスプレイ
先日頭の中にあったアイデアを形にしたくて、↓こんなものをつくりました。
ARKitを使ったリモートタッチパッドの実験。精度がまだ荒いけど基本的には上手くいってる。#ue4 #arkit pic.twitter.com/Kt5E5fPCQn
— ayuma (@ayuma_x) 2019年12月26日
iPadの上を指でなぞると、iPadの中のディスプレイに線が引かれているのですが、iPadを取り除くと実際のディスプレイにも同じ線が引かれています。
これは初見だと混乱するかもしれませんが、実はiPad側には何も表示は加えていません。iPadのディスプレイにはカメラの映像のみ表示しています。
何をしたかったのか?
少し前に仕事でiPad使ってARのコンテンツを作っていたのですが、その時に感じた「AR=[何か表示を重ねる]以外の使い方を考えてみたいな」と思ったのがきっかけです。
今回行った内容も多分誰かが似たようなことはやっているのだとは思いますが、「自分で考えてアイデアを形にすることに意味がある」ということでやってみました。
今回の実験においてiPadは入力機器として使います。 ARKitには画像検知や物体検知の機能があり、これをうまく使うことで遠隔タッチディスプレイを作ろうというアイデアです。
やるべきこと
このアイデアを形にするためにやるべきことを以下にまとめました。
今回iPadのアプリケーションはUE4(4.24)のHandheld AR ブループリント テンプレートをベースに作ります。
- iPadに映っているディスプレイの位置、角度を知る
- iPadに触れた指の位置をディスプレイの相対座標として知る
- iPadからPC側に座標を送る
- PC側で送られた座標にしたがって線を引く
iPadに映っているディスプレイの位置、角度を知る
まずはディスプレイの位置を知る必要があります。
位置を知る方法として「画像を利用したマーカー方式」と「物体検知」の2通りを考えましたが今回は物体検知を選びました。
UE4+ARKitでの物体検知の使い方は以前記事にしているので、興味ある方はみてください。
まずはディスプレイをスキャンして検知するためのデータづくりを行ったのですが、ディスプレイの形状に特徴点が少なく満足する結果が得られませんでした。
そこでディスプレイ横のごちゃごちゃした所の検知データを作成し、ディスプレイの位置、角度はそのポイントからの相対位置で判断するようにしました。
当然ですが、この方法ではディスプレイとの位置関係を変えてしまうと破綻します。
↓スキャンしているところ
スキャン後にディスプレイの位置、角度を知るのは上記した以前の記事の内容そのままです。
iPadに触れた指の位置をディスプレイの相対座標として知る
次にiPadに触れた指の位置を知るにはどうすれば良いか考えます。
今回使っているUE4のARテンプレートの中の実装には、タッチしたポイントにActorがあるかどうか調べるロジック(BP_ARPawnのWorldHitTest関数)があります。
これをちょっといじってディスプレイの位置を知ることを考えました。
まず現実世界のディスプレイと同じ大きさになるような板メッシュを用意してレベルに配置しておきます。このメッシュは見える必要はないので透明なマテリアルをつけときました。
次に毎Tick, ARKitが現実のディスプレイを検知した位置、角度にこのメッシュを移動させます。
次にWorldHitTest関数の中を見ると、LIneTraceForObjects
というノードが使用されているのが分かります。
LIneTraceForObjects
は指定した2点の間に3Dオブジェクトがあるかどうかの判定ができます。
このノードと直前に配置されているDeproject Screen to World
を組み合わせると、タッチしたときのiPad上のスクリーン座標(x, y)から触れている場所に配置してある3Dオブジェクトが特定できます。
この仕組みを使うと、先ほど配置したメッシュを指で触れているかどうかということが分かります。
ただし、今回知りたいのはディプレイのどこを触れているかなので、もう少し詳細な情報が必要です。
そこでLIneTraceForObjects
で返される値をFind Collision UV
に入れる部分を足してみました。
これで触れた3Dオブジェクトの表面のどの部分(UV座標)という情報までとれます。
※ Find Collision UV
を使うにはProject SettingsでSupport UV From Hit Results
のチェックをONにする必要があります。(多分ONにすると処理負荷は上がっちゃいます)
iPadからPC側に座標を送る
iPadからPC(Windows)へはソケット通信(UDP)で座標を送ることにします。
UDP送信にはObjectDelivererを使います。 ObjectDelivererについては過去記事みてください。
まずは送信するための入れ物を作ります。といっても簡単で以下のようなブループリントを定義します。TouchPositionというオブジェクトの名前にしました。
X座標とY座標を入れる変数のみ定義しただけです。
このオブジェクトをObjectDelivererを使って送るための初期化を行います。
↓こんな感じ。
そしてFind Collision UV
で板メッシュのUVを取得している箇所で、以下のようにしてUDPを使ってPCに座標を送ります。
これでiPadに映るディスプレイをタッチする度にPCにディスプレイのどこを触っているのかの座標を送り続けます。
PC側で送られた座標にしたがって線を引く
PC側が難しいことは何もしていなくて、ObjectDelivererを使ってUDPの受信を行い、送られてきたX, Y座標を取り出した後、UMGで線を引いているだけです。
この部分は今回重要な部分ではないので割愛します。
やってみた感想
考えた通りの動きを実現することができました。
iPadにはカメラ映像以外のものは何も表示していませんが、ARKitの機能を利用して面白いものができました。 こういった使い方はいろいろな可能性をひめているような気がします。
また新しいアイデアが湧いたら形にしてみようと思います。