Quantcast
Channel: かずきのBlog@hatena
Viewing all 1388 articles
Browse latest View live

electron 製アプリを DesktopBridge で Microsoft ストアに出せるようにしてみよう

$
0
0

詳細は、こちらのブログ記事にあります。

Convert your Electron app using the Desktop Bridge – App Consult Team

この手順に沿ってやってみます。

Node.js のインストール

これが無いと始まりませんね。

Node.js

electron のクイックスタートプロジェクトを取ってくる

雛形があるというのは素晴らしいですね。ということでクローンします。

git clone https://github.com/electron/electron-quick-start

初期設定

では以下のコマンドを打って各種 npm パッケージを取ってきます。

cd electron-quick-start
npm install

動作確認

以下のコマンドで動作確認が出来ます。

npm start

exe を作成して appx にパッケージング

electron-packager を使ってやるみたいですね。ということで -g オプションを付けてインストールします。

npm install -g electron-packager

electron-windows-store を使って appx にするので以下のものもインストールしておきます。

npm install -g electron-windows-store

electron-windows-store が PowerShell のスクリプトが実行可能に設定されてないといけないので以下のコマンドを管理者権限で実行した PowerShell で実行しておきます(一度だけやれば OK)

Set-ExecutionPolicy -ExecutionPolicy RemoteSigned

出来たら以下のコマンドを実行して exe を作ります。

electron-packager "F:\temp\electrondac\electron-quick-start" "ElectronSample" --platform=win32 --arch=ia32 --overwrite

これで electron-quick-start フォルダの下に ElectronSample-win32-ia32 フォルダが作られて中に exe が出来てます。起動することを確認しましょう。

そして以下のコマンドで appx にします。

electron-windows-store --input-directory "F:\temp\electrondac\electron-quick-start\ElectronSample-win32-ia32" --output-directory "F:\temp\electrondac\electron-quick-start\ElectronSampleStore" --flatten true --package-version 1.0.0.0 --package-name ElectronSample

Desktop App Converter 使うか?みたいなこと聞かれますが n を選択しても動きます。 Y を選択した場合は DesktopAppConverter.ps1 のパスを聞かれます。これは以下のようなコマンドでパスが取れます。

PS F:\temp\electrondac\electron-quick-start> Get-AppXPackage "*DesktopAppConverter*"


Name              : Microsoft.DesktopAppConverter
Publisher         : CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US
Architecture      : X64
ResourceId        :
Version           : 2.1.1.0
PackageFullName   : Microsoft.DesktopAppConverter_2.1.1.0_x64__8wekyb3d8bbwe
InstallLocation   : C:\Program Files\WindowsApps\Microsoft.DesktopAppConverter_2.1.1.0_x64__8wekyb3d8bbwe
IsFramework       : False
PackageFamilyName : Microsoft.DesktopAppConverter_8wekyb3d8bbwe
PublisherId       : 8wekyb3d8bbwe
IsResourcePackage : False
IsBundle          : False
IsDevelopmentMode : False
IsPartiallyStaged : False
SignatureKind     : Store
Status            : Ok

上記コマンドの出力の InstallLocation をエクスプローラーのアドレスに張り付けて直接開くと DesktopAppConverter.ps1 があることが確認できます。

その他に Windows 10 SDK のパスが聞かれます。これについてはデフォルトでインストールしていればそのまま Enter で問題ありません。 Visual Studio 2017 のインストール時に UWP 開発するように構成していれば入っていると思います。Visual Studio 2017 入れてない場合は個別に入れることもできます。

Windows 10 SDK – Windows アプリ開発

パッケージングの過程でパスワードを入れるように求められますが、これは appx を署名するための証明書のパスワードになります。空でも大丈夫です。

二度目からはここらへんの設定は聞かれません。Publisher の名前とかはストアに出すときには自分のストアのダッシュボードからとれる値にしないといけなかったりするので、そこらへん再設定したければ c:\users\ユーザー名\.electron-windows-storeというファイルに設定があるので消して再実行するか書き換えればいいと思います。

テスト環境にインストール

ということで appx が出来たら実際に動作するか確認ですね。 サイドローディングでない限りは、ストアに出すことになると思うのですが下記サイトの手順で Windows 10 S 環境で動くことを確認しておいたほうがいいです。2018/03/02 現在でWindows 10 S で動くことが必須条件なので。

Windows アプリの Windows 10 S 対応をテストする - UWP app developer | Microsoft Docs

課題

ネイティブの npm のモジュールを使ってると electron-packager で exe にした段階で動いてない…。

これどうやるのが正攻法なんだろう? electron-packager する前に下記サイトの electron-rebuild は試してみたんだけどなぁ…。

github.com


Windows で分割された zip ファイルを結合する方法

$
0
0

copyコマンドで出来るんですね。 以下のような感じです。

copy /b FileA.zip.000+FileB.zip.001+FileC.zip.003 Output.zip

/bオプションをつけて連結したいファイルを+で繋いで最後に出力ファイル名で OK。 便利。

Xamarin のドキュメントが docs.microsoft.com にマージされました

$
0
0

やったぜ!!

嬉しい副作用としては、機械翻訳ですが日本語で読めます。なんだかんだいって母国語のほうがいいのはいいですよね。 私も英語見るにも得意ではないので…なんとなく日本語で雰囲気つかんでから原文あたるほうが理解が早いです。

ReactiveProperty v4.2.1 をリリースしました

$
0
0

Xamarin.Android 関連で提供してた機能を ReactivePropertySlim/ReadOnlyReactiveSlim に対応するための Pull Request をもらってマージしてたのでリリースしました。

github.com

NuGet からインストールして使ってください。

www.nuget.org

Reactive Extensions とか知らない人向け ReactiveProperty のはじめの使い方

$
0
0

ReactiveProperty は MVVM + Rx でプログラム組むときにいい感じにしてくれるものですが MVVM だけでも大変なのに Rx なんて魔法みたいなものを覚えないといけないなんて…!!ということで学習コストが高いので導入をためらうことがあると思います。

当然、何かを使うということは導入のためのコストを払って開発を通じて回収していかないといけないので、そこのバランスが取れないものをあんまり入れると大変なことになりますよね。 ということで Rx 知らない人向けに Rx 成分無しで ReactiveProperty 使って感覚を掴むための使い方を紹介したいと思います。

導入

ReactiveProperty の NuGet パッケージを追加して終わりです。

UWP, Xamarin.Forms, WPF のような XAML を使うものでいい感じで動きますが、まぁ使えそうだと思ったら XAML 系のプラットフォームじゃなくてももちろん OK です!

ViewModel で使ってみよう

さて、MVVM パターンでは変更通知機能を持ったプロパティを定義して XAML でバインディングして同期をとったりします。 これがメンドクサイ。

例えばこんな感じになります。

using System.ComponentModel;

namespace RpHello.Xamarin
{
    publicclass MainPageViewModel : INotifyPropertyChanged
    {
        publicevent PropertyChangedEventHandler PropertyChanged;

        privatestring _input;

        publicstring Input
        {
            get => _input;
            set
            {
                _input = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Input)));
            }
        }
    }
}

もうちょっと簡単に書けるように工夫できますが、何度も書くのは単純にめんどくさいです。めんどくさいことは個人的に楽しくないです。

では、ReactiveProperty を使って書き換えてみましょう。 こんな感じになります。

using Reactive.Bindings;

namespace RpHello.Xamarin
{
    publicclass MainPageViewModel
    {
        public ReactiveProperty<string> Input { get; } = new ReactiveProperty<string>();
    }
}

アプローチとしては、INotifyPropertyChanged の実装を ViewModel クラスにするのではなく、プロパティ自身に実装させておくというものになります。 データバインディングのパスが以下のように .Valueを追加しないといけないという制約がつきますが簡単に変更通知のプロパティが定義できます。

<!-- ReactiveProperty を使わない場合の Binding --><Entry Text-"{Binding Input}" /><!-- ReactiveProperty を使う場合の Binding --><Entry Text-"{Binding Input.Value}" />
補足

WPF では DataContext に設定する型は INotifyPropertyChanged を実装してないとメモリリークの原因になるので使わなくても実装しましょう

とまぁ、これだけなので簡単に使えますね。 最初の最初はこれくらいの感覚で使ってみてもいいかもしれません。

入力値のチェックをしたい

入力値チェック機能が組み込まれているので簡単に入力値チェックをすることが出来ます。使い方は System.ComponentModel.DataAnnotations の属性をつけて初期化時にバリデーション機能を有効化するメソッドを呼び出します。

こんな感じで。

using Reactive.Bindings;
using System.ComponentModel.DataAnnotations;

namespace RpHello.Xamarin
{
    publicclass MainPageViewModel
    {
        // 検証ルールを System.ComponentModel.DataAnnotations で指定
        [Required(ErrorMessage = "入力は必須です")]
        public ReactiveProperty<string> Input { get; }

        public MainPageViewModel()
        {
            // フィールドの初期化ではフィールドの参照が出来ない(() => Inputがエラーになる)ので初期化をコンストラクタに移動
            Input = new ReactiveProperty<string>()
                // 検証機能を有効化するメソッド
                .SetValidateAttribute(() => Input);
        }
    }
}

ReactiveProperty には HasErrors プロパティがあって入力値にエラーがあるかどうかが簡単に判断できます。

publicvoid Foo()
{
    if (Input.HasErrors)
    {
        // エラー有り
    }
    else
    {
        // エラーなし
    }
}

コマンド

コマンドは ReactiveCommand ですが、何か既存で ICommand の実装クラスがあるなら Rx 成分が不要な場合は使わなくてもいいです。はい。

Rx 成分でよく使うもの

複数の入力項目があって値が変わったらすべての入力項目の値を舐めて何かしたいということが結構ある気がします。

例えば苗字と名前の入力項目があって、確認用にフルネームを入力が更新されるたびにくっつけて表示するようなケースですね。

そんな時によくつかうのが Rx のメソッドの CombineLatest です。これは ReactiveProperty の値が変わったら、それぞれのプロパティの最後の値を使って処理をすることが出来るようなものです。

using Reactive.Bindings;
using System;
using System.ComponentModel.DataAnnotations;
using System.Reactive.Linq;

namespace RpHello.Xamarin
{
    publicclass MainPageViewModel
    {
        public ReactiveProperty<string> FirstName { get; } = new ReactiveProperty<string>();
        public ReactiveProperty<string> LastName { get; } = new ReactiveProperty<string>();
        public ReactiveProperty<string> FullName { get; } = new ReactiveProperty<string>();
        public MainPageViewModel()
        {
            new[]
            {
                FirstName,
                LastName,
            }.CombineLatest(x => $"{x[0]} {x[1]}")
            .Subscribe(x => FullName.Value = x);
        }

    }
}

まず、変わったら何かしたいプロパティ(今回の場合は FirstName と LastName)を配列にまとめあげます。そして CombineLatest を呼び出すと引数のラムダ式に IList<T>型としてわたってきます。今回の例では x[0]に FirstName の最後の値が入ってて x[1]に LastName の最後の値が入っています。

あとは、それをいい感じに加工します(今回は単純な連結ですね)

最後に Subscribe メソッドで加工された値を FullName に代入しています。Subscribe メソッドは加工された値を受け取って何か処理をするための場所です。

これで完成ですが1つだけ。Subscribe で ReactiveProperty に代入するだけの場合は ToReactiveProperty メソッドや ToReadOnlyReactiveProperty メソッドに置き換えが可能です。ReadOnly がついている方は、代入が出来ない ReadOnlyReactiveProperty を生成します。今回の例のような場合は FullName を直接編集することはないので ReadOnly のほうが望ましい例になります。

ということで、こんな感じになります。

using Reactive.Bindings;
using System;
using System.ComponentModel.DataAnnotations;
using System.Reactive.Linq;

namespace RpHello.Xamarin
{
    publicclass MainPageViewModel
    {
        public ReactiveProperty<string> FirstName { get; } = new ReactiveProperty<string>();
        public ReactiveProperty<string> LastName { get; } = new ReactiveProperty<string>();
        public ReadOnlyReactiveProperty<string> FullName { get; }
        public MainPageViewModel()
        {
            FullName = new[]
            {
                FirstName,
                LastName,
            }.CombineLatest(x => $"{x[0]} {x[1]}")
            .ToReadOnlyReactiveProperty();
        }

    }
}

まとめ

こんな感じで単純な PropertyChanged の実装の簡略化のため + 入力値検証 + CombineLatest くらいを使うくらいの雰囲気で試してみてみるといいのかなぁと思ったので書いてみました。

本当は ReactiveCommand とか AsyncReactiveCommand とか ReactiveCollection とか ReadOnlyReactiveCollection とか etc... いっぱいあるので Rx の勉強がてら使ってみて頂けたらいいな!って思ってます。

HoloLens を大規模導入したり企業できっちり管理したいときに参考になりそうなドキュメント

$
0
0

自分用メモです。

docs.microsoft.com

ここら辺の機能を使いたかったらお高いほうのバージョンを買いましょうという話しなんですね。

ngrok で Azure Functions のローカルで実行しているエミュレーターにインターネットからアクセスできるようにしてみよう

$
0
0

いつも忘れるのでメモです。

ngrok 使うとローカルのサーバーを簡単にインターネットからアクセスするためのおぜん立てをしてくれる! 開発中のお供に便利です。例えば Google Assistant 対応アプリ作るときに Dialogflow 使ってると Webhook を指定できるのですが、こいつはインターネットに公開されてないといけない。

でもデバッグするにはローカルで実行されてるもののほうが都合がいいです。 別に Azure にデプロイしてもデバッガアタッチできるのですがレスポンス面ではローカルにあるほうがいいですしデプロイが割とすぐ終わるとしてもローカル実行に比べると開発のテンポが悪いですしね。

そんなわけで ngrok 使うと手元のものがインターネットに公開されるので便利。 理想的には Dialogflow で作ったものもローカル実行が出来ればいいのですが、出来ないことを嘆いても仕方がないですね。

やってみよう

Azure Functions で HttpTrigger の関数を作って実行するとこんな URL でアクセスできるようになります。

http://localhost:7071/api/Function1

なので以下のような ngrok のコマンドを実行すればいいのですが…

ngrok http 7071

だけで実行して

ngrok by @inconshreveable                                                                               (Ctrl+C to quit)

Session Status                online
Session Expires               7 hours, 59 minutes
Version                       2.2.8
Region                        United States (us)
Web Interface                 http://127.0.0.1:4040
Forwarding                    http://54eeb34f.ngrok.io -> localhost:7071
Forwarding                    https://54eeb34f.ngrok.io -> localhost:7071

Connections                   ttl     opn     rt1     rt5     p50     p90
                              1       0       0.01    0.00    0.03    0.03

うまくいったような雰囲気になるのですが postman あたりで上記 URL を叩いてみるとこんな結果になります。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd"><HTML><HEAD><TITLE>Bad Request</TITLE><METAHTTP-EQUIV="Content-Type"Content="text/html; charset=us-ascii"></HEAD><BODY><h2>Bad Request - Invalid Hostname</h2><hr><p>HTTP Error 400. The request hostname is invalid.</p></BODY></HTML>

これは、単純に Azure Functions のローカルエミュレータが localhost からのアクセスしか許可してくれないというのがあるからですね。

で、これが今回メモしたかった内容なのですが、そういうときは ngrok 経由の呼び出しもローカルからのアクセスですよ~っていうのをエミュレーターに送り付けてだましてやればいいのですが、その設定が -host-headerオプションになります。

こんな感じで指定します。

ngrok http 7071 -host-header="localhost:7071"

これで表示された URL に postman でアクセスすると無事 200 番 OK が返ってくるようになります。

ということで、毎回調べるのでメモでした。

WPF などの .NET Framework のアプリから UWP の API を呼ぶ

$
0
0

同じチームの Matteo さんが書いてくれてた記事に ConditionalAttribute が使ってあって、あぁこういう機能あったなぁと思ったのでメモがてら記事をなぞってやってみました。

Desktop Bridge – Enhancing a desktop application with the UWP APIs – App Consult Team

WPF や Windows Forms とかで書かれたアプリから UWP の API は呼べます。UwpDesktop という NuGet パッケージを追加するのがお手軽ではあります。 まぁでも UwpDesktop が何をやってくれるかというのを確認するためにも手動の方法を確認してみましょう。

因みに、今回の方法を使っても全 API が呼べるわけではなく以下にある API がサポートされている API です。

パッケージ デスクトップ アプリで利用可能な UWP API (デスクトップ ブリッジ) - UWP app developer | Microsoft Docs

全部がサポートされてないのは、例えばですが UWP のコントロールを WPF でそもそも出せないとか、そういう色んな制約のせいですね。

手動でやる方法

C:\Program Files (x86)\Windows Kits\10\UnionMetadata\Windows.winmdを参照に追加します。 ただ、デフォルトで dll などの拡張子しか表示しないようになっているので「すべてのファイル」を選択しないと winmd の拡張子のファイルは表示されないので気を付けてください。

f:id:okazuki:20180329091724p:plain

参照先の Blog 記事がトーストを表示してみようという内容だったのでそれにならってやってみましょう。名前空間を省略せずに書くとこのようになります。

publicvoid ShowNotification()
{
    var xml = @"<toast><visual><binding template='ToastGeneric'><text>Desktop Bridge</text><text>The file has been created</text></binding></visual></toast>";

    Windows.Data.Xml.Dom.XmlDocument doc = new Windows.Data.Xml.Dom.XmlDocument();
    doc.LoadXml(xml);

    Windows.UI.Notifications.ToastNotification toast = new Windows.UI.Notifications.ToastNotification(doc);
    Windows.UI.Notifications.ToastNotificationManager.CreateToastNotifier().Show(toast);
}

そして、このメソッドを画面に適当に置いたボタンのクリックイベントから呼び出すとエラーになります。(元記事もそうですね)

f:id:okazuki:20180329093141p:plain

UWP の機能の中でも、いくつかは appx という UWP の形式にパッケージングしないと呼べないものがあります。 ということで、以下のような記事の手順で appx にする必要があります。

Windows アプリケーション パッケージ プロジェクトを使って UWP として実行すると無事トーストが出ました。

f:id:okazuki:20180329094220p:plain

appx じゃないバージョンもあるのですが…

これがメモしたかった内容なのですが(機能自体は知ってたけど使ってなかった)appx にパッケージングする方法でないと動かない機能があると困りますよね。既存の exe 配る方法などで。

そんな時は「ビルド→構成マネージャー」でUWP用の構成を作ります。

f:id:okazuki:20180329094746p:plain

プロジェクトのプロパティで条件付きコンパイルシンボルに DesktopUWPのようなものを定義しておきます。

あとは #if ディレクティブでくくるでもいいですし、見通しがいいのは元記事でも行われている ConditionalAttribute を使って UWP に依存する機能はメソッドにまとめておいて、DesktopUWP が定義されてないときは無視するようにするといいです。

privatevoid Button_Click(object sender, RoutedEventArgs e)
{
    ShowNotification();
}

[Conditional("DesktopUWP")]
publicvoid ShowNotification()
{
    var xml = @"<toast><visual><binding template='ToastGeneric'><text>Desktop Bridge</text><text>The file has been created</text></binding></visual></toast>";

    Windows.Data.Xml.Dom.XmlDocument doc = new Windows.Data.Xml.Dom.XmlDocument();
    doc.LoadXml(xml);

    Windows.UI.Notifications.ToastNotification toast = new Windows.UI.Notifications.ToastNotification(doc);
    Windows.UI.Notifications.ToastNotificationManager.CreateToastNotifier().Show(toast);
}

こうすると通常の Debug / Release でビルドしたときはトーストの処理が呼ばれなくなります。 DesktopUWP - Debugや Release をもとに同じ手順で DesktopUWP - Reelaseとかを作っておくと appx にパッケージングするときに、その構成を選んで行うとトーストが出るようになります。

非同期メソッドを呼ぶには

UWP の API の多くは非同期な API です。そのため await をすることが多いです。 ただ、UWP の API の戻り値は Task ではありません。IAsyncOperator<T>などになります。 そのため、何も考えずに以下のようなコードを書くと…。

public async Task GenerateWavFileAsync()
{
    var speech = new SpeechSynthesizer();
    var result = await speech.SynthesizeTextToStreamAsync("こんにちは世界");

    var fileName = System.IO.Path.Combine(
        Environment.GetFolderPath(Environment.SpecialFolder.Desktop),
        "speech.wav");
    using (var fs = File.Create(fileName))
    {
        await result.AsStreamForRead().CopyToAsync(fs);
        await fs.FlushAsync();
    }
}

エラーになります。

f:id:okazuki:20180329100854p:plain

await するためにはいくつかの条件を満たす必要があるのですが、これを満たしてないということです。 それらは拡張メソッドとして定義されているのですが、その拡張メソッドが定義されているのは以下の dll になるので、それを参照に追加してやる必要があります。

  • C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETCore\v4.5\System.Runtime.WindowsRuntime.dll

これでエラーが消えます。この SpeechSynthesizer クラスは appx にしなくても呼べるので ConditionalAttribute はいりません。

まとめ

最近は Windows 10 じゃないと使えない API も増えてきました。 そして Pure UWP じゃなくても UWP の API を使わないといけないケースがあると思います。

そんな時はここの手順を試してみてください。Desktop Bridge を使えば、トーストとかにも対応できます。


DevCenter ダッシュボードに UWP アプリをテスト用に配布するための Private audience という機能が追加されました

$
0
0

blogs.windows.com

UWP のアプリを本番環境(要はストアから入れた状態)でテストするために限定的なユーザーに配る方法が追加されました。

これは再申請とかしなくても、配る対象のユーザーを変更すれば配布対象のユーザーが絞れそうですね。

やり方

まずユーザーグループを作ります。

f:id:okazuki:20180329110640p:plain

そしてアプリの申請時に「価格と使用可能状況」で「プライベート ベータ 対象ユーザー」を選択して配るユーザーのグループを指定します。 暫くはプライベート公開で、時間がきたら一般公開に切り替えるように設定もできるみたいですね。

f:id:okazuki:20180329111246p:plain

便利そう。

HoloLens や Windows MR で NuGet のライブラリ使いたい

$
0
0

便利なライブラリが沢山ある NuGet ですが Unity で開発する HoloLens や Windows MR では簡単には使え無さそう? UWP のプロジェクトをビルドで吐いたところに手動で NuGet 追加すれば使えるけど、リポジトリにはビルドで出したプロジェクトは入れないからクローンするたびとかに手動で追加しないといけないよね?(認識違ってたら教えてください)

めんどくさいですね。

Unity から出力されるプロジェクトの形

とりあえずシーンを追加して、適当なスクリプトを作成してシーンのカメラあたりにアタッチしました。 この状態で UWP のプロジェクトを出力すると以下のような形のものが出てきます。

f:id:okazuki:20180413093123p:plain

project.json を使った少し前までの形のプロジェクトみたいですね。この形のプロジェクトの場合は NuGet で参照を追加すると project.json に定義が追加されます。試しに Json.NET の 9.0.4 を追加してみました。(最新のだとエラーになったので)

Assembly-CSharp プロジェクトの project.json

{"dependencies": {"Microsoft.NETCore.UniversalWindowsPlatform": "5.0.0",
    "Newtonsoft.Json": "9.0.1"
  },
  "frameworks": {"uap10.0.10240": {}},
  "runtimes": {"win10-arm": {},
    "win10-arm-aot": {},
    "win10-x86": {},
    "win10-x86-aot": {},
    "win10-x64": {},
    "win10-x64-aot": {}}}

メインのプロジェクト(今回は NuGetApp という名前で作ったので NuGetApp プロジェクト)の project.json

{"dependencies": {"Microsoft.ApplicationInsights": "1.0.0",
    "Microsoft.ApplicationInsights.PersistenceChannel": "1.0.0",
    "Microsoft.ApplicationInsights.WindowsApps": "1.0.0",
    "Microsoft.NETCore.UniversalWindowsPlatform": "5.0.0",
    "Newtonsoft.Json": "9.0.1"
  },
  "frameworks": {"uap10.0.10240": {}},
  "runtimes": {"win10-arm": {},
    "win10-arm-aot": {},
    "win10-x86": {},
    "win10-x86-aot": {},
    "win10-x64": {},
    "win10-x64-aot": {}}}

これで Json.NET が使えます。こんな感じで。

using Newtonsoft.Json;
using UnityEngine;

publicclass Sample : MonoBehaviour
{

    // Use this for initializationvoid Start()
    {
        var jsonText = JsonConvert.SerializeObject(new Person
        {
            Name = "tanaka",
        });
    }

    // Update is called once per framevoid Update()
    {

    }
}

publicclass Person
{
    publicstring Name { get; set; }
}

問題点 1 ビルドエラーになる

この状態で、Unityから再ビルドするとエラーになります。何個かエラーが出ますが、恐らくこれが原因でしょう。

Assets/Scripts/Sample.cs(1,7): error CS0246: The type or namespace name `Newtonsoft' could not be found. Are you missing an assembly reference?

まぁ当然ですよね。Unity 側からしたらプラットフォーム固有のビルド用に出力した先で追加されたライブラリのことなんて知ったこっちゃないので。

#if ディレクティブで回避

エラー自体は #if で回避できます。UNITY_UWP で括れば OK です。

先日教えてもらったのですが、WINDOWS_UWPとか何個か定義される定数があるのですが、ものによって定義されるタイミングが違うらしいのですよね。WINDOWS_UWP だと Unity から UWP のプロジェクト出力するタイミングのビルドで評価されるらしいので UWP プロジェクトの出力段階でエラーになります。

UNITY_UWP だと大丈夫みたいです。 ということで以下のようなコードにしておけば OK です。

#if UNITY_UWPusing Newtonsoft.Json;
#endifusing UnityEngine;

publicclass Sample : MonoBehaviour
{

    // Use this for initializationvoid Start()
    {
#if UNITY_UWP
        var jsonText = JsonConvert.SerializeObject(new Person
        {
            Name = "tanaka",
        });
#endif
    }

    // Update is called once per framevoid Update()
    {

    }
}

publicclass Person
{
    publicstring Name { get; set; }
}

問題点 2 再ビルド時に手動で NuGet 追加

これでなんとか開発出来そうなのですが、出力されたプロジェクトを削除して Unity から再ビルドすると当然ながら project.json が新規に作られて NuGet の定義が足りなくなるので、手動で追加しないといけません。

1 つ 2 つならいいのですが数が増えてくるとやってられませんよね。

ということで自動で追加してもらいましょう。Unity にはビルド後に任意のスクリプトを動かす方法があります。

Unity - Scripting API: PostProcessBuildAttribute

いけそうですね。ということで Editor/AddNuGetPackagePostProcess.cs を作って以下のように書きます。

using UnityEditor;
using UnityEditor.Callbacks;
using UnityEngine;

publicclass AddNuGetPackagePostProcessor
{
    [PostProcessBuild]

    publicstaticvoid OnPostProcessBuild(BuildTarget target, string pathToBuildProject)
    {
        Debug.Log(pathToBuildProject);
    }
}

ビルドすると以下のようなログが出ます。

C:/Projects/NuGetApp/UWP
UnityEngine.Debug:Log(Object)
AddNuGetPackagePostProcessor:OnPostProcessBuild(BuildTarget, String) (at Assets/Editor/AddNuGetPackagePostProcessor.cs:11)
UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)

いけそうですね。あとはゴリゴリ書くだけ。

project.json では Dictionary<string, string>を扱いたかったのですが JsonUtility ではサポートされてないので泣く泣く MiniJSON を使うことにしました。

Unity3D: MiniJSON Decodes and encodes simple JSON strings. Not intended for use with massive JSON strings, probably < 32k preferred. Handy for parsing JSON from inside Unity3d. · GitHub

ということで Editor フォルダに MiniJSON.cs ファイルを作って上記コードをコピペします。 そして、AddNuGetPackagePostProcessor.cs を以下のようにします。単純に指定した定義を project.json に追加してるだけです。

using MiniJSON;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEngine;

publicclass AddNuGetPackagePostProcessor
{
    [PostProcessBuild]

    publicstaticvoid OnPostProcessBuild(BuildTarget target, string pathToBuildProject)
    {
        Debug.Log(pathToBuildProject);

        var targetProjectJsonFiles = new[]
        {
            Path.Combine(pathToBuildProject, "NuGetApp",  "project.json"),
            Path.Combine(pathToBuildProject, "GeneratedProjects", "UWP", "Assembly-CSharp", "project.json"),
        };
        var libraries = new Dictionary<string, string>
        {
            { "Newtonsoft.Json", "9.0.1" },
        };

        foreach (var projectJsonPath in targetProjectJsonFiles)
        {
            AddNuGetReference(projectJsonPath, libraries);
        }
    }

    privatestaticvoid AddNuGetReference(string projectJsonPath, Dictionary<string, string> libraries)
    {
        var projectJson = Json.Deserialize(File.ReadAllText(projectJsonPath)) as Dictionary<string, object>;
        var dependencies = (Dictionary<string, object>)projectJson["dependencies"];
        foreach (var library in libraries.Where(x => !IsAlreadyDefined(dependencies, x)))
        {
            dependencies[library.Key] = library.Value;
        }

        File.WriteAllText(projectJsonPath, Json.Serialize(projectJson));
    }

    privatestaticbool IsAlreadyDefined(Dictionary<string, object> dependencies, KeyValuePair<string, string> library)
    {
        if (!dependencies.ContainsKey(library.Key))
        {
            returnfalse;
        }

        return ((string)dependencies[library.Key]) == library.Value;
    }
}

UWP のプロジェクトを一旦消して Unity から再出力すると以下のように project.json のインデントが死んだ状態ですが Json.NET の定義が追加されてることがわかります。

AddNuGetApp プロジェクト

{"dependencies":{"Microsoft.ApplicationInsights":"1.0.0","Microsoft.ApplicationInsights.PersistenceChannel":"1.0.0","Microsoft.ApplicationInsights.WindowsApps":"1.0.0","Microsoft.NETCore.UniversalWindowsPlatform":"5.0.0","Newtonsoft.Json":"9.0.1"},"frameworks":{"uap10.0":{}},"runtimes":{"win10-arm":{},"win10-arm-aot":{},"win10-x86":{},"win10-x86-aot":{},"win10-x64":{},"win10-x64-aot":{}}}

Assebmly-CSharp プロジェクト

{"dependencies":{"Microsoft.NETCore.UniversalWindowsPlatform":"5.0.0","Newtonsoft.Json":"9.0.1"},"frameworks":{"uap10.0":{}},"runtimes":{"win10-arm":{},"win10-arm-aot":{},"win10-x86":{},"win10-x86-aot":{},"win10-x64":{},"win10-x64-aot":{}}}

確認

生成されたソリューションを Visual Studio で開いてビルドすると参照のところに、ちゃんと Json.NET が追加されていることが確認できます。

f:id:okazuki:20180413113612p:plain

f:id:okazuki:20180413113624p:plain

まとめ

ということでまとめです。

  • NuGet から入手したライブラリに関する処理は #if UNITY_UWP ~ #endif で括ろう
  • NuGet パッケージの手動追加がメンドクサイ場合は PostProcessBuild で project.json を書き換えよう

ちなみに、多分ですが近い将来きっと Unity が出力するプロジェクトは project.json の存在しない形になると思うので、そのタイミングでこの PostBuildProcess は使えなくなります。 その時は、csproj ファイルに NuGet のリファレンスが書き足される形になると思うので、同じ要領で PostProcessBuild で csproj を書き換えてしまいましょう。

Progressive Web app を Windows 10 のインストールパッケージに固めてしまおう(Microsoft ストアに出せるようになるよ)

$
0
0

さて、Progressive Web app(PWA)が Microsoft store で配られるのですが自分のサイトも!!と思ってる人は以下のような手順でいけると思います。

マニフェストを用意しよう

PWA Builder というサイトを使うと簡単に出来ます。

www.pwabuilder.com

まぁ、マニフェストファイル用意するだけなら別にいらないんですが。 このサイトに URL を入れて開始すると manifest.json を生成してくれます。まだマニフェスト無いひとはやってみよう!

f:id:okazuki:20180417155412p:plain

manifest.json という名前で保存して Web サイトの html の head タグに以下のような定義を追加します。

<linkrel="manifest"href="manifest.json">

manifest.json と修正した html をデプロイしましょう。

注意点

Azure Web app にデプロイすると json ファイルへアクセスしても 404 エラーになるので以下のような web.config もセットでデプロイしましょう。

<?xml version="1.0"?><configuration><system.webServer><staticContent><mimeMap fileExtension=".json"mimeType="application/json" /></staticContent></system.webServer></configuration>

ネイティブ API (Windows Runtime の API)を呼び出してみよう

あとは固めておしまいなんですが、面白くないので Windows Runtime の API を叩いてみましょう。 そのまま呼べばいいのですが、それだとブラウザで見たときとかにエラーになるので実行時に API の存在チェックなどをして UWP として動いてないときは、Windows Runtime の API は叩かないようにします。

例えば、以下のような感じ。

<!DOCTYPE html><html><head><linkrel="manifest"href="manifest.json"><title>winrt sample</title><scripttype="text/javascript">function myAlert(){if(isInUWP()){var dlg = new Windows.UI.Popups.MessageDialog('Hello world');                dlg.showAsync();}else{alert('Hello world');}};function isInUWP()
{returntypeof Windows !== 'undefined';}</script></head><body><h1>PWA sample</h1><buttononclick="myAlert()">Alert from Windows Runtime API.</button></body></html>

ブラウザだと alert 関数で、UWP として動いてるときは Windows Runtime API の MessageDialog クラスを使って書いてます。 デプロイしてみましょう。これで下準備完了です。

パッケージ化

PWA Builder のコマンドラインツールを入れます。 Node.js があれば npm で入れれます。

npm install -g pwabuilder

まずは、以下のコマンドを叩きます。これでマニフェストから Windows 10 のアプリパッケージをつくるための下準備をしてくれます。

pwabuilder WebサイトのURL -d .\output -p windows10

そうすると output フォルダに アプリ名\PWA\Store packages\windows10というフォルダが出来ます。 その中の manifest\appxmanifest.xmlがストアに提出するときに使うアプリのマニフェストファイルになります。これを適切な値にしましょう。

3 箇所 INSERT-YOUR-...という箇所があります。これは DevCenter ダッシュボードから取得した値にします。 アプリを登録したときにアプリ情報だったかな?で見れる値です。

因みに、ここの編集をしないで次の手順をすると、ここの値をちゃんと入れろ!みたいなログが出ます。

[error] pwabuilder  : Failed to package the Windows 10 Platform app.
                      The specified path does not contain a valid app manifest file.
                      The application manifest is incomplete. Register the app in the Windows Store to obtain the Package/Identity/Name,
                      Package/Identity/Publisher, and Package/Properties/PublisherDisplayName details.
                      Then, use this information to update the corresponding placeholders in the appxmanifest.xml file before
                      creating the App Store package.

とりあえず試すだけなら適当に変えれば OK です。ストアに出すときにはダッシュボードに書いてある値にしましょう。

例えば適当に変えたらこんな感じね。

<?xml version="1.0" encoding="utf-8"?><Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"xmlns:build="http://schemas.microsoft.com/developer/appx/2015/build"IgnorableNamespaces="uap mp build"><Identity Name="winrtsample"Publisher="CN=sample"Version="1.0.0.0"ProcessorArchitecture="neutral"/><mp:PhoneIdentity PhoneProductId="06bef117-32a6-3ec2-d8e4-2acdf8a2626a"PhonePublisherId="00000000-0000-0000-0000-000000000000"/><build:Metadata><build:Item Name="GenerationTool"Version="pwabuilder"/><build:Item Name="GenerationToolVersion"Version="2.0.3-rc.0"/><build:Item Name="PlatformId"Value="windows10"/><build:Item Name="PlatformPackage"Value="pwabuilder-windows10"/><build:Item Name="PlatformVersion"Version="2.0.3-rc.1"/><build:Item Name="GeneratedFrom"Value="CLI"/><build:Item Name="GenerationDate"Value="2018-04-17 07:30:34 Z"/><build:Item Name="GeneratedURL"Value="https://pwalab-okazuki.azurewebsites.net/manifest.json"/></build:Metadata><Properties><DisplayName>winrtsample</DisplayName><PublisherDisplayName>okazuki</PublisherDisplayName><!-- 省略 --></Package>

これで準備が出来ました。output\アプリ名\PWA\Store packages\windows10 にコマンドプロンプトなどで移動して以下のコマンドを打ちましょう。

pwabuilder package -p windows10

package フォルダが出来て、その中に windows.appx というファイルが生成されます。これがストアに提出するパッケージです。

サイドローディング(野良アプリインストール)して試してみよう

サイドローディングするためには証明書で署名する必要があります。Windows 10 SDK が入っていれば以下のコマンドでさくっと作れます。

makecert -r -pe -n "CN=sample" -eku 1.3.6.1.5.5.7.3.3 -pe -sv my.pvk my.cer
pvk2pfx -pvk my.pvk -spc my.cer -pfx my.pfx
signtool sign /fd SHA256 /a /f .\my.pfx .\windows.appx

パスワードを求められるので None とでも押しておきましょう。パスワードなしていく感じになります。 以下のようなログが出れば成功です。

Done Adding Additional Store
Successfully signed: .\windows.appx

注意

makecert コマンドの CN=xxxx のところは appxmanifest.xml に書いた値とそろえましょう。

証明書のインストール

では、この証明書を信頼済みの証明書としてインストールします。(企業でサイドローディングで配布するときはきちんとした証明書を使って署名するといい感じになると思います)

my.pfx をダブルクリックして保存場所をローカル コンピューターにします。いくつか次へ次へと進めていくと証明書ストアを選択する画面になります。そこで「信頼されたルート証明機関」を選択します。

インストールして実行

インストールが出来たら windows.appx をダブルクリックしてみましょう。以下のような画面が立ち上がりインストールできます。

f:id:okazuki:20180417164338p:plain

証明書関連で何か失敗してるとインストールボタンを押したところでエラーになります。もしくは Windows でサイドローディングを許可されてないか。

サイドローディングは、設定で更新とセキュリティの開発者向けのところで選択できます。

実行してボタンを押してみましょう。Webサイトはアラート関数、アプリとして実行すると Windows のダイアログが出ます。

f:id:okazuki:20180417164652p:plain

ちなみに appx にしてインストールしてますが Web 上の html/javascript を実行してるのでアプリの更新は Web サイトの更新で OK です。 お手軽。

ストアに出すには

Microsoft Store に出すには appxmanifest.xml をダッシュボードの値を編集したりアイコンをきちんとしたものにしたりして提出すれば審査を受けてストアに公開されると思います。

まとめ

ということで Web サイトを公開しててネイティブの API を使えばもうちょっといい体験をユーザーに提供できるのになぁと思っているけれど、だからといってネイティブアプリを 1 から作るほどのリソースも無い…というケースでは、上記のような方法で Web サイトに追加する形でストアからインストールしてくれた人にはネイティブの API を使った機能を提供しつつ Web サイトでは今まで通りの機能ということが出来そうですね。

Web サイトでは特定のリッチな機能のページにアクセスしたり、OS を検出するコードをを埋め込んで Windows 10 だったらストアへの誘導の導線を出すことで、ライトユーザーは普通に Web で、使い込んでくれてるユーザーはインストールしてくれるかも?という流れが出来そうですね。

頑張ればオフラインでも動くようにしたりとか出来るので強い。

LINQ を使う時に一般的に気を付けること via C#でLinqを使うよりPythonの方が2倍速かったのでベンチマークをしてみた

$
0
0

以下の記事を見ました。

qiita.com

そして、それを受けての記事があります。

qiita.com

上記記事に ToList を複数回呼び出してることに関する言及があります。

そう LINQ を使う時に一番効くのが無駄にループを回す処理をいつの間にか書いてしまっていないかというところです。

LINQ は基本的に遅延評価です。例えば…

var array = new[] { 1, 2, 3 };
var result = array.Where(x => x % 2 == 0);

上記コードだけだと、Where を呼び出した行では「あぁ、フィルタリングしたいのね。わかった」というのをお願いしてるだけなので実際に処理は行われません。 LINQ の多くのメソッドは、このようにお願いだけしておいて必要になったら実際に処理をするというように動きます。

じゃぁいつ実行されるの?ということですが、これは実際に処理を実行しないと出来ないものが呼ばれたときになります。わかりやすいのが foreach で回すときです。

var array = new[] { 1, 2, 3 };
var result = array.Where(x => x % 2 == 0);
foreach (var r in result) // 実際に列挙するときはさすがに処理しないと出来ないよね
{
    Console.WriteLine(r);
}

その他には配列やリストに変換するときです。

var array = new[] { 1, 2, 3 };
var result = array.Where(x => x % 2 == 0);
var list = result.ToList(); // リストにするには全要素舐めないと無理
var a = result.ToArray(); // 配列にするには全要素舐めないと無理

といった感じです。因みに上記処理では 2 回要素を走査してます。ToListで1回とToArrayで1回ですね。 このように複数回同じものにたいしてループすることが多い場合は予め配列にしておいたほうが早いケースもあります。

var array = new[] { 1, 2, 3 };
var result = array.Where(x => x % 2 == 0).ToArray(); // ここで一回foreach (var x in result)
{
    // 何か処理
}

foreach (var x in result)
{
    // 何か処理
}

foreach は配列は特別扱いしてくれるようなので早いんですよね。確か。 今回の Where が 1 つだけなら大したことないですが途中で何かしら重い処理を挟んでいる場合は一旦配列にして(ここで重い処理が走る)から後でループで処理(ここでは重い処理ではなく評価された結果だけ使う)といった形で使えます。

ということなので、最初の python より凄く遅いケースのプログラムを確認してみると…

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Newtonsoft.Json;

namespace app
{
    class Program
    {
        staticvoid Main(string[] args)
        {
            var stopwatch = new Stopwatch();
            stopwatch.Start();

            using (var reader = new StreamReader("App_Data/test.csv"))
            {
                reader.ReadLine();
                while (!reader.EndOfStream)
                {
                    string s = reader.ReadLine();
                    var columns  = s.Split(',');
                    testData.Add(new TestData
                    {
                        a = columns[0],
                        b = columns[1],
                        x = double.Parse(columns[2]),
                        y = double.Parse(columns[3])
                    });
                }
            }               

            var testData_0 = testData.Select(d => new {d.a, d.b, d.x, d.y, z = MultiplyToInt(d.x, d.y)}).ToList();
            var testData_1 = testData_0.GroupBy(d => d.a)
                .Select(g => new {a = g.Key, sum = g.Sum(d => d.z)}).ToList();
            using (StreamWriter file = File.CreateText("App_Data/result.json"))
            {
                JsonSerializer serializer = new JsonSerializer();
                serializer.Serialize(file, testData_1);
            }

            var ts = stopwatch.Elapsed;            
            Console.WriteLine($"処理時間: {ts.TotalSeconds}秒");
        }

        staticint MultiplyToInt(double x, double y)
        {
            if (x > 0)
                return (int)(x * y + 0.0000001);
            return (int)(x * y - 0.0000001);
        }
    }

    class TestData
    {
        publicstring a { get; set; }
        publicstring b { get; set; }
        publicdouble x { get; set; }
        publicdouble y { get; set; }
    }
}

まず、StreamReader を使ってデータを読み込むところで 100 万回のループ、そして途中に ToList が 2 個あるので 100 万回ループが追加で 2 つ。そして GroupBy も実際に全部走査しないと出来ない類の操作になるので、ここの評価でも 100 万回ループとなります。そしてシリアライズのタイミングでもループを回さないといけないで、ここでも 1 回ループが入ります。(厳密には最後の ToList とシリアライズ時のループは GroupBy した結果に対してなので 100 万件もデータはないですが)

こういう意図しないループが複数回走ると LINQ はというよりまぁ当然遅くなります。 LINQ 使ってみたんだけど凄く遅いんだよねっていうのは割とこのケースが多いので遅かったら、この観点でチェックしてみるといいなと思います。

最後に

この手の話は結構前から何度も出てるのですが、まぁ新たに書くことで新たに目に触れる機会のある人もいるかな?ということで書いてみました。 あと C で書かれた最適化されまくったライブラリが pip で手軽にインストールしてさくっと呼べるという形に整備されまくってる python はいいなと思いました。 最近は何言語使ってもそこそこ早いですよね。なので得意なものでさくっとやってしまうのが 1ms の速度を気にするよりはコーディングにかかる時間を考えるといいと思います。

まぁ、パフォーマンスが重要なものに関しては別ですが。

ReactiveProperty v4.2.2 をリリースしました

$
0
0

シリアライズの関係で ReactivePropertySlim クラスの Mode プロパティを public にしました。 更新は、いつも通り NuGet からどうぞ。

www.nuget.org

UWP の配置に関する blog があった!

$
0
0

今日まで知らなかったんですが、UWP の配置に特化したブログがあったんですね…。

blogs.msdn.microsoft.com

msix とかについても、何かあったらここになるのかな?それとも別の blog とかがあるんだろうか(探してない)

とりあえずもうすぐ出るであろう Redstone 4 ではサイドローディングシナリオでも自動更新機能とかも入ってきたりして、やっと UWP も企業内配布で使えそうな雰囲気を醸し出してきました。

気になる人は、上記 blog をチェックしておいてもいいかもですね。

SignalR を Windows Mixed Reality で使いたい

$
0
0

SignalR は簡単に言うと WebSocket とかをいい感じに隠してくれてサーバーからクライアント(Webやネイティブアプリなど)に対して処理を実行することが出来るライブラリです。

最近は ASP.NET Core でも使えるようになってるんですね。私が前にやってたときは ASP.NET でやってました。

こっちが ASP.NET Core で

docs.microsoft.com

こっちがクラシカルな ASP.NET のほうですね

docs.microsoft.com

便利なんです

Web 系の技術をベースに実装されたリモートプロシージャーコールのライブラリなので非常に便利なんですよね。 サーバーからクライアントに一斉に指示をばらまいたりとかなんとか。ということで Windows Mixed Reality でも使いたい!!ってことで試してみました。

よく見てないんですが ASP.NET Core SignalR って ASP.NET Core 2.1 Preview に依存してるんですかね?

docs.microsoft.com

.NET core 2.1.0 Preview 1 SDK以降
Visual Studio 2017 15.6 またはそれ以降のバージョン、 ASP.NET および web 開発ワークロード
npm

まだプレビューならとりあえず今回は古き良きほうで試してみたいと思います。

サーバーサイドの準備

ではサーバーを用意しましょう。 ASP.NET MVC の空のプロジェクトをさくっと作ります。

そして NuGet から SignalR のライブラリを追加しましょう。Microsoft.AspNet.SingalR ですね。

SingalR 2.x 系は Owin 使ってるんですかね?ということで NuGet でインストールしたら表示される readme に従って Startup クラスを作ります。

OWIN Startup クラスというテンプレートが Visual Studio にあるので、それを使ってさくっと作りましょう。 MapSignalR を呼びます(自分がしてたころは MapHub とかいうメソッドだった気がする)

using System;
using System.Threading.Tasks;
using Microsoft.Owin;
using Owin;

[assembly: OwinStartup(typeof(SignalRServer.Startup))]

namespace SignalRServer
{
    publicclass Startup
    {
        publicvoid Configuration(IAppBuilder app)
        {
            app.MapSignalR();
        }
    }
}

確か、このスタートアップクラスは Microsoft.Owin.Host.SystemWeb とかが入ってないと動かなかった気がするので何か動かないなぁって人はチェックしてみましょう。

では、Hubを作りましょう。今回はクライアントに対して Cube を出せ!ってサーバーから命令するようなのを作りたいなぁと思ったのでそういうハブを1つ作りました。

using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;

namespace SignalRServer.Hubs
{
    [HubName("createCubeHub")]
    publicclass CreateCubeHub : Hub
    {
        publicvoid CreateCube()
        {
            Clients.All.create("Cube");
        }
    }
}

今回は Clients.All を使ってつないでる人全員に命令を投げてます。特定のグループに対して送るとか制御もできるので実際には、グループを作って特定の人達に指示を投げるみたいなのになるんでしょうね。

あとは、この Hub をたたく Web 画面を作ります。 HomeController とかを作って View を適用に用意しましょう。 SignalR 関連の JavaScript を jquery のあとに読み込む必要があるので追加します。 とりあえず、普通に Visual Studio にお願いして各種 View を作っていたら _Layout.cshtml というのが出来てるはずなので、そこの jquery を読み込んでる src タグの下らへんに2つの script タグを追加します。

あと、スクリプトが全部読み込まれた後に自前のスクリプトを読み込むためのセクションも追加するために @RenderSectionも追加しておきます。

<scriptsrc="~/Scripts/jquery-1.10.2.min.js"></script><scriptsrc="~/Scripts/bootstrap.min.js"></script><!-- ここから追加するやつ --><scripttype="text/javascript"src="~/Scripts/jquery.signalR-2.2.3.min.js"></script><scriptsrc="~/signalr/hubs"></script>
    @RenderSection("script")
    <!-- ここまで追加するやつ -->

重要なのは jquery の後に signalr 系のものを読み込むことなので、_Layout.cshtml を使ってない場合には Index.cshtml とかに全部まとめて書いても OK です。

View の中で以下のような感じで SignalR の Hub の CreateCube メソッドを叩く処理を書きます。

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2><buttonid="createCubeButton">Create cube</button>

@section script {
    <scripttype="text/javascript">        $(function(){            $.connection.hub.start().done(function(){                $('#createCubeButton').click(invokeCreateCube);});});function invokeCreateCube(){            $.connection.createCubeHub.server.createCube();}</script>
}

これだけでサーバーの CreateCube メソッドを呼べるのはお手軽ですね。サーバーの CreateCube ではクライアントの create メソッドを叩いてるって感じのコード書いてるので、これで Web 画面からクライアントの create メソッドを叩く下準備が出来ました。

一応ここらへんで、CreateCubeHub クラスの CreateCube メソッドにブレークポイントをはって実行して画面のボタンを押すとブレークポイントで止まるかというのを確認しておくとあとがスムーズだと思います。

デプロイ!

では Windows MR デバイスからアクセスできるところ(ローカルのネットワーク内でもお手持ちのクラウドサーバー上でもなんでも)に、上記のアプリをデプロイそてきましょう。次はいよいよ Windows MR 側の話になります。

Windows MR 側

ついに Windows MR 側の世界に来ました。長かった。 Unity で適当にプロジェクトを作ります。

各種設定がめんどくさいので MRTK を入れて MRTK のメニューからプロジェクトとシーンの設定をしてしまいましょう。 注意点はインターネットにつなぐので Capability で Internet Client にチェックを入れておきます。

そして、シーンに空のゲームオブジェクトを1つ作って SignalRManager とかいう名前で作りましょう。 そして、SignalRManager とかいう名前で C# のスクリプトを作成して先ほど作成した SignalRManager というスクリプトにアタッチしておきます。

準備ができたので、UWPのプロジェクトを Unity からビルドして出力しましょう。 出力先でゴリゴリコードを書くので Unity C# Project にはチェックを入れておくのを忘れずに。

f:id:okazuki:20180426115524p:plain

出力したプロジェクトを Visual Studio で開いたら Microsoft.AspNet.SignalR.Client を NuGet から出力されたプロジェクトに対して追加します。

ここらへんは新規に Visual Studio のプロジェクト吐き出したらまたやらないといけないので、何らかの簡略化の仕組みは本番では取り入れておくといいと思います。以下のような感じで。

以下のは、私のやったゴリゴリ project.json をビルドプロセスのあとに書き換えるアプローチ。

blog.okazuki.jp

以下のは、インストールする NuGet ライブラリをインストールする PowerShell のスクリプトを用意しておいて、再作成後は一発それを叩けば OK な状態にしておくアプローチ

[http://yotiky.hatenablog.com/entry/2018/04/26/HoloLensNuGetUWP向けのLibrary_を利用したい:embed:cite]

とりあえず今回は手動で追加します。

追加したら SignalRManager.cs を以下のようにしましょう。単純につないで SignalR から何か呼ばれたらものを作るといった感じにしています。

using UnityEngine;
using System;
using System.Threading;

#if UNITY_UWPusing Microsoft.AspNet.SignalR.Client;
#endifpublicclass SignalRManager : MonoBehaviour
{
#if UNITY_UWPprivate HubConnection _connection;
    private SynchronizationContext _unityThreadContext;


    privatevoid Start()
    {
        // Unity のメインスレッドの SynchronizationContext をとっておく
        _unityThreadContext = SynchronizationContext.Current;
        InitializeConnection();
    }


    privatevoid InitializeConnection()
    {
        // SignalR を配備した先の URL (例は Azure 上の Web app に配備した場合)
        _connection = new HubConnection("http://<your site name>.azurewebsites.net/");
        var createCubeProxy = _connection.CreateHubProxy("createCubeHub");
        createCubeProxy.On<string>("create", Create);
        _connection.Start().ContinueWith(x =>
        {
            UnityEngine.Debug.Log(x.Exception?.Message ?? "Connected");
        });
    }

    privatevoid Create(string name)
    {
        // Unity のメインスレッド上で処理しないとまずいと思うので
        _unityThreadContext.Post(_ =>
        {
            PrimitiveType type;
            if (!Enum.TryParse<PrimitiveType>(name, out type))
            {
                UnityEngine.Debug.LogWarning($"{name} is not defined.");
                return;
            }
            var obj = GameObject.CreatePrimitive(type);
            obj.transform.position = Camera.main.transform.position + Camera.main.transform.forward * 3;
            UnityEngine.Debug.Log($"{name} created at {obj.transform.position}");
        }, null);
    }
#endif
}

これで完成です。実行すると以下のようになります。

https://github.com/runceel/WinMRSignalR/blob/master/movie.gif?raw=true

まとめ

すなおに NuGet 使いたい欲が高まりますね。


Microsoft ストアに iTunes が公開されてます!

$
0
0

前々から噂されてましたが、ついに来ましたね。

www.microsoft.com

Desktop Bridge 使ってると思うけど頑張ったんだろうなぁ。

モンスターハンターワールド 弓の使い方メモ

$
0
0

ハンターランクも 200 くらいになってきたので今メインで使ってる弓の使い方をメモっておこうと思います。

基本的な立ち回り

今作から弓を使い始めたので過去作がどうだったのかはよく知らないのですが、今作の弓は基本的にチャージステップと呼ばれる素早くシュッと移動しながら弓の溜め段階をあげれる動きをしては攻撃するというのが基本的な流れになります。

ため段階は最大で 3 です。ため段階解放のスキルをつけると 4 になります。これがあるのとないのとではかなり違います。でもレアなので無くても諦めずに歴戦を回すしかありません。

その時、基本的に R2 で溜め攻撃を当てていきます。 エイムを合わせて(知らない単語だった要は照準を合わせる)撃つ。シンプルですね。

そのときに、一番威力が出る距離をなるべく保ちつつ敵のまわりをチャージステップで、ちょろちょろしながら撃っていく形になります。

敵に少し隙がある場合は R2 の後やチャージステップの後に◯ボタンを押すことで剛射と呼ばれる射程は若干短いけど普通よりも多くの矢を横に少しひろがる感じに撃つ矢が撃てます。

ということで、基本的に以下のような動きですね。

R2 ->チャージステップ -> R2 ->チャージステップ -> R2 ->チャージステップ -> R2 -> ... (隙があったら剛射)

ずっと回避行動しながら溜め段階をあげて最大威力を維持しながら延々と弓を撃てたら最強なので、そんなことは出来なくなってます。 この R2 や◯やチャージステップをしてるとスタミナがモリモリ減っていきます。なので、回避行動が一回できるくらいの残量が残るくらいを目安に一連の動きを続けることがコツです。

調子にのってパシャパシャ撃ちこんでると「ここで華麗に敵の顔面に撃ちこんで華麗に横に避ける!!」と思ってたら、顔面に撃ちこんだところでスタミナが切れて動けない!?ってなります。その時はだいたい被弾してしまいますね。

ガンナーは防御力が低いのでくらうと大ダメージです。気をつけましょう。稀によくあります。

因みに、基本操作の方法はよくあるまとめ的なサイトに書いてあるので見てみるといいと思います。

弓立ち回り

スタミナが大事

ということで弓は動くたびにスタミナが減っていくのでスタミナが減りにくかったりスタミナが多いことが長く最大火力の攻撃を続けることができるということです。なので体術と呼ばれるスキルが必須と言われてます。 護石で 3 まであげれるので作れるタイミングでサクッと作ってしまいましょう。最終的には護石で 3 まで上げて珠でさらに 2 上げて 5 までやると結構長いあいだ動き続けることができるようになります。

人によって最適解は違う

スタミナが大事なのですがいろんな流派があるみたいです。ざっと思い付くだけでこんなかんじです。

  • 体術 5
  • 体術 5 + スタミナ急速回復 2
  • 体術 3 + スタミナ急速回復 2
  • 体術 3 + 強走剤
  • 体術 1 + 猫飯 + 強走剤
  • 体術 3 + キノコ大好き(鬼ニトロ茸が強走剤の効果)

最初は体術 5 を目指してみて徐々に自分で運用が回せるものにシフトしていくのがいいかなと思います。

属性が大事

弓は属性で殴るみたいなものなので、物理攻撃力よりも属性値が高い方が強くなります。 属性で殴るということは敵に応じて弱点属性になる武器を担いでいくのが一番いいということになるので最終的には全属性のその時点での最強と言われる武器を準備することになります。

MHW は雷属性が効きやすい敵が多いみたいなので、最初はトビカガチの素材で作れる弓があるといいみたいです。 私はクリア後に弓を始めたので最初のころはどうしたらいいのかっていうのはよくわかりませんが…。

自分がよくやる動き

素直に R2 で溜め始める

R2 を押すと弓を構えて離すと撃ちます。R2 が押しっぱなしだと溜めていきます。 なので、敵を攻撃するのにいい感じの位置に移動しながら R2 を押して移動しつつ溜まったら L2 を押して照準を出して撃ちます。撃ったらチャージステップで移動しながら撃ち続けます。

クイックショットから始める

今すぐここから攻撃を始めたい!!という位置にいる場合には◯で撃つことが出来るクイックショットを撃ってチャージステップにつなげます。 クイックショットは当たらなくても気にしません。射程がすごく短いので敵の方になんとなく向いて撃ってればいいです。

弓は連携が続いてれば溜め段階が上がっていくのでクイックショットを最初にさっと撃ちこんでから動き始めることでスタミナを消費することなく溜め段階 2 から動きを開始することができます。 なので以下のような流れで行く感じですね。

クイックショット ->チャージステップ -> R2 ->チャージステップ -> R2 ...

当然ですが R2 で撃つ時はちゃんと狙います。

曲射

剛射のあとに◯を押すことで曲射と呼ばれるぱっと見しょっぱい攻撃ができます。 これはバラバラと空からつぶてを振らせる攻撃で、1 粒あたり 1 とか 2 とかのダメージだったりします。なのでダメージソースにならないのですが頭に当てるとスタンさせやすい攻撃です。ちなみに曲射のあとに剛射につながるので何も考えずに◯を延々と連打してると、俗に言う剛射曲射ループというのができます。

これは敵が閃光やダウンなどで倒れてるところにクイックショットで撃ちながら近づいて曲射が頭のあたりに落ちる位置まできたら剛射曲射ループをすると、うまくいくと敵がダウンします。ダウンしたらまた頭に向けて剛射曲射ループといった感じで結構ダメージを与えることができます。

ただ、曲射は味方に当たると怯むので怯み耐性付けてない人の動きを止めてしまうという特徴があるので身内だけでやってて意思の疎通が取れるなら問題ないと思いますが野良でやると嫌われると思います。

気をつけましょう。

竜の一矢

読み方はりゅうのいちや。通称カズヤ。 ぐっと溜めてずばばばばーーーーーーーんっ!!!と撃つ必殺技です。気持ちいい。

貫通する矢なので縦に長い敵の頭から尻尾に通すことで最高のダメージを叩き出します。 つまり小さい敵にすると溜めで動けない状態を作りつつしょっぱいダメージを叩き出す技になってしまいますね。

しかも敵の動きが止まってるタイミングで撃たないと、こっちが溜めてる間に移動されたり攻撃されたりして散々な目にあいます。 あと、よく言われるのはチャージステップで弓を撃ち続けるほうがダメージは一般的に大きいと言われています。 なので人によっては「乙る可能性を上げる割には大したダメージを与えないしょっぱい技」という印象を持たれてるみたいなので、野良マルチでこればっかりしてると嫌われる可能性があります。

まぁでも倒れてるバゼルギウスなんかに頭から尻尾にかけて撃ち込むとバゼルギウスは全身が弱点みたいなものなのですごいダメージが出て気持ちいいです。 あとはディアブロスも突進を回避したあとに後ろから撃ち込むということを繰り返すことで倒すということもできるみたいです。

まぁ普通に弱点属性で弱点の翼に向けてバシバシ打ち込み続けるほうが早そうな気がします。

照準合わせ

これは練習するしかないです。 最初はチャージステップで横に移動したときに、とりあえず敵のどこかに当たるくらいになるように照準を移動させましょう。何度もしてるとチャージステップをすると照準がこれくらい移動するからあらかじめこれくらいを向いてチャージステップすると大体ちょうどいいところに来るなとか感覚が身につきます。

感覚なので感覚を掴むまで繰り返すしかありません。

あと、私は敵のロックオンはしてません。 ロックオンしてると敵が激しく動くと意図しない感じでカメラの向きが変わったりするのでカメラ速度を最大にした状態で頑張って自分で追いかけてます。

慣れればこれも困ることはないです。

キノコ大好き

ちなみに色々あるスタミナを減りにくくするスキルの組み合わせですが私は体術 3 + キノコ大好き 3 です。 敵の攻撃もまだくらってしまうのでマンドラゴラを 10 個持ち込んで秘薬がわりにガブガブ飲んでます。あとはマヒダケで防御を上げてニトロダケで攻撃力を上げて鬼ニトロダケで強走効果を得てます。

敵の攻撃なんてくらわないぜ!って人にはいらないものだと思います。 早くそうなりたい。

まとめ

チャージステップによる高い機動力と属性を合わせて弱点部位に攻撃を当て続けた時の火力の高さが弓の魅力です。 練習すればするだけ強くなる武器だと思うので興味をもったら弓担いで見るのもいいと思います!

ということでメモでした。

UWP Community Toolkit が Windows Community Toolkit に名前が変わったみたいです

$
0
0

de:code 2018 の準備のために Blog チェックしてたらこんな記事を見つけました。

blogs.windows.com

おぉ!名前変わったんですね!!

github.com

正直なところ Toolkit の名前のライブラリにはあまりいい思い出がないので大丈夫かなぁと思ってましたけど、結構頑張ってるみたいでウォッチしてもいいかも。

UWP でアプリを閉じるときに確認画面を出す方法

$
0
0

これずっと出来ないと思ってたのですが de:code 用に色々見てたら出来るっちゃぁ出来るようになってるのを見つけました。 下記の権限のサイトをみてみると confirmAppCloseっていうのがある!?

docs.microsoft.com

試してみよう

confirmAppCloseの機能を Package.appxmanifest に追加します。

<!--この名前空間を追加してxmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"IgnorableNamespaces に以下のように rescap も追加IgnorableNamespaces="uap mp rescap"--><Capabilities><Capability Name="internetClient" /><!-- これを追加 --><rescap:Capability Name="confirmAppClose" /></Capabilities>

そして、SystemNavigationManagerPreview クラスを使って頑張る感じですね。

using System;
using Windows.UI.Core.Preview;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace ConfirmCloseApp
{
    publicsealedpartialclass MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
        }

        privatevoid Page_Loaded(object sender, RoutedEventArgs e)
        {
            SystemNavigationManagerPreview.GetForCurrentView().CloseRequested += MainPage_CloseRequested;
        }

        private async void MainPage_CloseRequested(object sender, SystemNavigationCloseRequestedPreviewEventArgs e)
        {
            e.Handled = true;
            var d = new MessageDialog("閉じてもよろしいですか?");
            var okCommand = new UICommand("OK");
            d.Commands.Add(okCommand);
            var cancelCommand = new UICommand("Cancel");
            d.Commands.Add(cancelCommand);
            var r = await d.ShowAsync();
            if (r == okCommand)
            {
                Application.Current.Exit();
            }
        }
    }
}

こうすることで閉じる前に確認を挟むことが出来ます。ただ、このイベントは必ず発生するものではなくタブレットモードで上からしたにスワイプしたりしたときには発生しないとかいくつか発生しないケースがあるみたいですね

docs.microsoft.com

なので、この機能を使うにしてもサスペンドのタイミングでユーザーの作業中の未保存のデータがある場合には保存してあげて、そして再度アプリが立ち上げられたタイミングで復元してあげるのが良さそうです。

HoloLens の入力デバイスとしてのスマートフォン

$
0
0

HoloLens って AirTap とかだけだと操作めんどくさいですよね? ボタンを押したい or 文字を入れたいだけなのに視線を移動させてえいってやる感じが。

Project Rome

Windows 10 アニバーサリーアップデートのタイミングで公表された Project Rome というものを覚えているでしょうか?

Project Rome – Microsoft デベロッパー

引用:
Windows、Android、iOS、Microsoft Graph 向けの API として提供され、クライアントとクラウド アプリで Project Rome 機能を使った エクスペリエンスを構築できるプログラミング モデル 

何が出来るのかというと別デバイス上の別アプリなんだけど 1 つのアプリとして協調して動くような基盤を提供しますよってことなんだと理解してます。 出来ることとしては以下のようなものがあります。

  • 別デバイスからのアプリケーションの起動
  • 別デバイスのアプリ間で接続を確立して相互にデータのやり取りが可能
  • アプリのアクティビティの履歴の共有

これらのことが自前のサーバー無しで実現可能ということが結構大きいです。 データのやり取りが可能とはいってもジャイロセンサーの値を無慈悲に送り付けるくらいのリアルタイム性はちょっとしんどいですが、テレビリモコンとテレビくらいの関係にはちょうどいいです。

メインはスマートフォンでも作業しつつ続きがシームレスにパソコンでも出来て、さらにまたスマートフォンに戻っていくとかいうユースケースを想定されているように見えます。

Project Rome の SDK

因みに SDK なんかは 2018/05/20 現在では以下のステータスです。

  • Windows UWP (stable)
  • Android (preview)
  • iOS (preview)
  • Microsoft Graph (preview)

まだちょっと本番導入には早いかな?といった印象。

作ってみたもの

パソコン向けに作ったんですが UWP なので HoloLens の RS4 入れたやつで試してみました。

youtu.be

動いた。UWP 凄いな。

まとめ

コードは、以下の場所に放流してあります。

github.com

割と HoloLens と相性いいんじゃないんでしょうか?手元のコンソールとしてスマートフォンとか。

Viewing all 1388 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>