最初のオーバーレイを作る
このチュートリアルで作るもの
Section titled “このチュートリアルで作るもの”SteamVR のダッシュボードに、数字とボタンが出るオーバーレイを作ります。ボタンを押すと、数字が1つ増えます。
完成した画面は、青い四角の中に大きな数字と赤いボタンが縦に並んだものです。
作業は3段です。
- 色の四角をダッシュボードに出す
- 四角を数字に置き換え、数字を書き換える
- ボタンを足して、押すと数字を増やす
各段の終わりに、HMD の中で確かめる点を1つ置いています。
始める前に用意するもの
Section titled “始める前に用意するもの”用意するものは次の3つです。
- .NET 10 の SDK(
dotnet --versionが10.で始まる) - SteamVR(起動できる状態で、HMD をつないである)
- 空のコンソールアプリ
準備の手順は、ガイドの開発環境を用意するにあります。動作環境の一覧は、リファレンスの動作環境です。足りないものがあれば、リンク先で用意してから戻ってください。
空のコンソールアプリは、次の2つのコマンドで作ります。1行ずつ実行します。
dotnet new console -n FirstOverlaydotnet add FirstOverlay package FloatSodaFirstOverlay フォルダーの中に Program.cs ができます。以降は、このファイル(FirstOverlay/Program.cs)の中身を段ごとに置き換えます。
1. オーバーレイを SteamVR に出す
Section titled “1. オーバーレイを SteamVR に出す”最初の段では、青い四角を1つ出します。Program.cs を次の内容に置き換えてください。
using FloatSoda;using FloatSoda.Geometrics;using FloatSoda.Widgets;using FloatSoda.Widgets.Layout;using FloatSoda.Widgets.Paint;using Microsoft.Extensions.DependencyInjection;using Microsoft.Extensions.Hosting;using SkiaSharp;
var builder = Host.CreateApplicationBuilder(args);builder.Services.AddFloatSoda();
using var host = builder.Build();var app = host.Services.GetRequiredService<FloatSodaApp>();
Widget root = new Align{ Alignment = Alignment.Center, Child = new SizedBox { Width = 300, Height = 300, Child = new ColoredBox { Color = SKColors.CornflowerBlue } }};
app.CreateWindow(new DashboardWindow{ Title = "FirstOverlay", Child = root});
await host.RunAsync();画面は Align、SizedBox、ColoredBox の3つを入れ子にして書きます。Align が中央に寄せ、SizedBox が 300 × 300 の大きさを決め、ColoredBox が色を塗ります。DashboardWindow は、この画面を SteamVR のダッシュボードに出す指定です。Title が、ダッシュボードのタブ名になります。
SteamVR を起動してから、FirstOverlay フォルダーを作った場所で、次のコマンドを実行します。
dotnet run --project FirstOverlayそのまま HMD を被り、SteamVR のダッシュボードを開きます。
できた? ダッシュボードの「FirstOverlay」のタブに、青い四角が出た。
できなかったら
起動で例外が出たときは、メッセージを控えて、ガイドの起動で例外が出る: SteamVR または HMD につながらないを開きます。例外が出ないのにタブがないときは、同じページの起動で警告が出るが、表示はされるも確かめてください。
AddFloatSoda() や Host.CreateApplicationBuilder の意味は、ガイドの起動と Generic Hostで説明しています。ここでは、この数行を毎回の書き出しとして、そのまま使ってください。
2. 数字を表示して、書き換える
Section titled “2. 数字を表示して、書き換える”2段目では、四角を数字の表示に置き換え、1秒ごとに数字を増やします。画面が持つ値を書き換えて画面を描き直すには、StatefulWidget<T> と State<T> を使います。値を書き換える操作が SetState() です。
Program.cs を次の内容に置き換えてください。
using FloatSoda;using FloatSoda.Animation;using FloatSoda.Elements;using FloatSoda.Geometrics;using FloatSoda.Painting;using FloatSoda.Widgets;using FloatSoda.Widgets.Layout;using FloatSoda.Widgets.Paint;using Microsoft.Extensions.DependencyInjection;using Microsoft.Extensions.Hosting;using SkiaSharp;
var builder = Host.CreateApplicationBuilder(args);builder.Services.AddFloatSoda();
using var host = builder.Build();var app = host.Services.GetRequiredService<FloatSodaApp>();
app.CreateWindow(new DashboardWindow{ Title = "FirstOverlay", Child = new CounterPanel()});
await host.RunAsync();
record CounterPanel : StatefulWidget<CounterPanel>{ public override State<CounterPanel> CreateState() => new CounterPanelState();}
class CounterPanelState : TickerProviderState<CounterPanel>{ private int _count; private WidgetTicker? _ticker;
public override void InitState() { _ticker = CreateTicker(elapsed => { var seconds = (int)elapsed.TotalSeconds; if (seconds != _count) { SetState(() => _count = seconds); } }); _ticker.Start(); }
public override void Dispose() { _ticker?.Dispose(); base.Dispose(); }
public override Widget Build(IBuildContext context) { return new Align { Alignment = Alignment.Center, Child = new SizedBox { Width = 300, Height = 300, Child = new ColoredBox { Color = SKColors.CornflowerBlue, Child = new Center { Child = new Text(_count.ToString()) { Style = new TextStyle { Color = SKColors.White, FontSize = 120, FontWeight = 700 } } } } } }; }}段1のコードから、3か所が変わっています。
- 画面を
CounterPanelというウィジェットに分けた。値を持つウィジェットは、StatefulWidget<T>を継承した型と、値を入れるState<T>の2つで書く Build()が、_countの今の値で画面を返す。SetState()を呼ぶと、_countが書き換わり、Build()がもう一度呼ばれて画面が描き直される- 数字を増やすきっかけに、
WidgetTickerを使った。WidgetTickerは、フレームごとに開始からの経過時間を渡す部品で、フレームを処理するメインのスレッドから呼ばれる
きっかけを System.Threading.Timer にしないのは、別のスレッドから SetState() を呼ぶと、画面の更新が失われることがあるためです(追跡: #150)。WidgetTicker はフレームを処理するスレッドで呼ばれるので、このコードのままで SetState() を呼べます。
もう一度、SteamVR を起動してから dotnet run --project FirstOverlay を実行し、ダッシュボードを開きます。
できた? 青い四角の中央に数字が出て、1秒ごとに1つずつ増えていく。
できなかったら
数字が 0 のまま変わらないときは、ガイドの数字や文字が変わらないを開きます。数字そのものが出ないときは、文字や画像が出ないです。
SetState() が画面を描き直す仕組みは、ガイドの状態と作り直しで説明しています。
3. ボタンを押して、数字を増やす
Section titled “3. ボタンを押して、数字を増やす”3段目では、数字の下にボタンを足し、押すと数字が1つ増えるようにします。GestureDetector で、押された操作を受け取る領域を作り、見た目は他のウィジェットで組み立てます。
Program.cs を次の内容に置き換えてください。1秒ごとに増やす WidgetTicker は外し、TickerProviderState を State に戻しています。
using FloatSoda;using FloatSoda.Elements;using FloatSoda.Geometrics;using FloatSoda.Painting;using FloatSoda.Widgets;using FloatSoda.Widgets.Gesture;using FloatSoda.Widgets.Layout;using FloatSoda.Widgets.Paint;using Microsoft.Extensions.DependencyInjection;using Microsoft.Extensions.Hosting;using SkiaSharp;
var builder = Host.CreateApplicationBuilder(args);builder.Services.AddFloatSoda();
using var host = builder.Build();var app = host.Services.GetRequiredService<FloatSodaApp>();
app.CreateWindow(new DashboardWindow{ Title = "FirstOverlay", Child = new CounterPanel()});
await host.RunAsync();
record CounterPanel : StatefulWidget<CounterPanel>{ public override State<CounterPanel> CreateState() => new CounterPanelState();}
class CounterPanelState : State<CounterPanel>{ private int _count;
public override Widget Build(IBuildContext context) { return new Align { Alignment = Alignment.Center, Child = new SizedBox { Width = 300, Height = 300, Child = new ColoredBox { Color = SKColors.CornflowerBlue, Child = new Flex { Direction = Axis.Vertical, MainAxisAlignment = MainAxisAlignment.Center, CrossAxisAlignment = CrossAxisAlignment.Center, Children = { new Text(_count.ToString()) { Style = new TextStyle { Color = SKColors.White, FontSize = 120, FontWeight = 700 } }, new GestureDetector { OnTap = () => SetState(() => _count++), Child = new SizedBox { Width = 200, Height = 80, Child = new ColoredBox { Color = SKColors.Tomato, Child = new Center { Child = new Text("+1") { Style = new TextStyle { Color = SKColors.White, FontSize = 50, FontWeight = 700 } } } } } } } } } } }; }}画面の組み立てとタップの処理の変更点は、次のとおりです。
FlexにDirection = Axis.Verticalを指定して、数字とボタンを縦に並べた- 赤い四角を
GestureDetectorで包み、タップされたときにOnTapの処理でSetState()を呼んで数字を増やす
前の実行をターミナルの Ctrl+C で終了してから、SteamVR を起動した状態で dotnet run --project FirstOverlay を実行します。HMD を被り、ダッシュボードを開いて、コントローラーのレーザーで赤い四角を押します。
できた? 赤い四角を押すたびに、数字が1つ増える。
できなかったら
押しても反応しないときは、ガイドのボタンを押しても反応しないを開きます。DashboardWindow ではなく WorldSpaceWindow や DeviceTrackedWindow で試した人は、リファレンスのできないこと・まだ使えないものを確かめてください。SteamVR 上のオーバーレイでポインターの座標が届く範囲も、そのページにあります(追跡: #182)。
ボタンの組み立て方は、ガイドのボタンやトグルを組み立てるで詳しく説明しています。
次に読むページ
Section titled “次に読むページ”動くオーバーレイができました。次に進むページは、目的で選べます。
- 通しで1つのパネルを作りたい: ガイドの表情切り替えパネルを作る
- 作りたいものに近い作例から始めたい: リファレンスの作例のカタログ