20211026のC#に関する記事は3件です。

C# Mathf

0.0 はじめに 数関係の処理に便利なMathfをいくつかまとめてみました。 1.0 関数 1.1 小数点四捨五入等 関数 説明 使用例 Floor() 切り捨て Mathf.Floor(1.5f) // = 1 Ceil() 切り上げ Mathf.Ceil(1.5f) // = 2 Round() 四捨五入 Mathf.Round(1.5f) // = 2 1.2 比較や判定 関数 説明 使用例 Sign() 符号取得 (0の場合は1) Mathf.Sign(-1.5f) // = -1 (= マイナス) Min() 最小数を返す Mathf.Min(1.5f, 1.7f) // = 1.5 Max() 最大数を返す Mathf.Max(1.5f, 1.7f, 1.9f, 2.5f) // = 2.5 1.3 特殊計算 関数 説明 使用例 Pow() 指数 Mathf.Pow(2, 3) // 2の3乗=8 Sqrt() 平方根 Mathf.Sqrt(4) // √4 = 2 Abs() 絶対値 Mathf.Abs(-1.5f) // = 1.5 1.4 範囲指定 関数 説明 使用例 Clamp() 上下限を指定 Mathf.Clamp(-10, 0, 50); // 0 Clamp01() 値を0 と 1 の間に制限 Mathf.Clamp01(1.5f); // 1 Lerp() 線形補完 第三引数は0~1f Mathf.Lerp(0.5f, 4.5f, 0.75f); // 0.5と4.5の間75%=3.5 ?ポイント ベクトルVector3にも同様に線形補完用Lerp()関数があります。(Vector3.Lerp():ベクトルでの線形補完) 2.0 定数 定数 説明 PI π (3.1415926...) Deg2Rad 度からラジアンに変換する定数 Rad2Deg ラジアンから度に変換する定数
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[WPF] ListBoxで選択可能な数に上限を設定する

WPFのListBoxコントロールにはSelectionModeプロパティがあり、Multipleを指定すると複数項目の選択が可能です。 しかし、「最大で5件まで」のような制御をXAMLから設定することはできません。本稿では、ViewModel側で最大選択可能数の制御を行う方法について紹介したいと思います。 事前準備 ListBoxにバインドするデータに、bool型のIsSelectedプロパティを生やします。このプロパティは変更時にPropertyChangedが起きるようにしてください。 生やしたIsSelectedプロパティをListBoxにバインドし、SelectedItemsChangedイベントが起きるたびに選んだ項目のIsSelectedが変更されるようにします。具体的には、以下のようにXAMLを設定します。 View.xaml <ListBox ItemsSource="{Binding Items.Value}" SelectionMode="Multiple" <ItemsControl.ItemContainerStyle> <Style TargetType="{x:Type ListBoxItem}"> <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" /> </Style> </ItemsControl.ItemContainerStyle> </ListBox> また、ViewModelも先にバインドするデータソースを定義しておきます。モデル部分は本質ではないので省略してますが、IModelを実装するクラスがItemsプロパティで画面表示するデータの元を持っているとします。 ViewModel.cs // バインドするデータ -> Modelから取得する public class ItemData : BindableBase { public bool IsSelected { get => _isSelected; set => SetProperty(ref _isSelected; value); } private bool _isSelected; } public class ViewModel { public ReadOnlyReactiveCollection<ItemData> Items { get; } private readonly IModel _model; public ViewModel(IModel model) { _model = model; // もしModelがObservableCollection<T>で持つなら Items = _model.Items.ToReadOnlyReactiveCollection(/* 変換処理 */); // もしModelがIEnumerable<T>で持つなら Items = _model.ObserveProperty(m => m.Items). SelectMany(item => /* 変換処理 */). ToReadOnlyReactiveCollection(); } } 上記ではModelがIEnumerableでデータを持っている場合とObservableCollectionでデータを持っている場合の2通りでReadOnlyReactiveCollection<ItemData>の変換処理を書いています。ReactiveCollectionを使う理由はコレクションの中身のIsSelectedの変更状態を監視する必要があるためです。 上限の実装方法 「選択可能な数に上限を設定する」を実現するには、以下の方法が思いつきます。 現在の選択数が上限数に達したら、未選択項目は選択不可にする。選択済み項目は選択解除が可能で、解除されて上限数を下回ったら未選択項目は再び選択可能になる。 項目を選択したとき、すでに上限数と同じ数だけ選択されていたら、新しく選択した項目の選択を解除する。そうでなければ後続に選択項目を流す。 前者の方法では上限数と現在の選択数に応じてListBoxにおける各項目の選択可否を制御しなければなりません。後者の場合、とりあえず選択可能にしておいてあとから選択を解除し直せばよいことになります。ここでは後者のほうが簡単なので後者で実装します。 「項目を選択したとき」 準備段階でIsSelectedプロパティに項目の選択状態をバインドしているので、データ全体を持つReadOnlyReactiveCollectionから各要素のIsSelectedの変更を監視します。それにはRxのObserveElementPropertyを使います。 ViewModel.cs Items.ObserveElementProperty(item => item.IsSelected).Subscribe(x => { /* コレクションのどれかの要素のチェックが変更されたときの処理 */ }); Subscribeに流れてくるのは、この例ではPropertyPack<ItemData, bool>の変数となります。これは「ObserveElementPropertyで監視しているプロパティが属するインスタンス(ItemData)」と「ObserveElementPropertyで監視しているプロパティの変更後の値(bool)」を持つインスタンスです。 「すでに上限数と同じ数だけ選択されていたら、新しく選択した項目の選択を解除する」 現在の選択項目を取得するには、コレクション全体をなめてIsSelected == trueなもののみ抽出すればよいでしょう。その項目の数を上限値と比較して超えていたらIsSelectedをfalseに上書きします。 なお、上限値はint型のプロパティで持っておきましょう。未設定は無制限としておけばよいと思います。 ViewModel.cs public int MaxSelection { get; set; } = int.MaxValue; ~~ Items.ObserveElementProperty(item => item.IsSelected).Subscribe(x => { var selected = Items.Where(w => w.IsSelected); var count = selected.Count(); if (count > MaxSelection) { // IsSelectedが変更されたインスタンスはx.Instanceで取得できる // x.Value は変更後のIsSelectedの値なのでx.Value = false;としても無意味 x.Instance.IsSelected = false; return; } }); 後続に選択項目を流す 選択されている項目を使ってReactiveProperty<IEnumerable<T>>やReactiveCollection<T>を使いたい場合、Subscribe内でそれらのプロパティに値を設定してやる必要があります。しかしそのようなケースだと内容は選択状態に応じて自動で決まるべき(ReadOnlyであるべき)ケースが多いです。これら2つを実現するにはSystem.Reactive.SubjectsのSubject<T>を使ってやると簡単です。 ViewModel.cs // バインドするデータ -> Modelから取得する public class ItemData : BindableBase { public bool IsSelected { get => _isSelected; set => SetProperty(ref _isSelected; value); } private bool _isSelected; } public class ViewModel { // ListBoxに設定するItemSource public ReadOnlyReactiveCollection<ItemData> Items { get; } // 元データの取得処理のModel private readonly IModel _model; // 最大選択可能数 public int MaxSelection { get; set; } = int.MaxValue; // 選択項目の文字列表現を持つReactiveProperty public ReadOnlyReactivePropertySlim<IEnumerable<string>> SelectedItemTexts { get; } // 現在選択されている項目の文字列表現を流すSubject private Subject<IEnumerable<ItemData>> _selectedItemSubjects; public ViewModel(IModel model) { _model = model; // もしModelがObservableCollection<T>で持つなら Items = _model.Items.ToReadOnlyReactiveCollection(/* 変換処理 */); // もしModelがIEnumerable<T>で持つなら Items = _model.ObserveProperty(m => m.Items). SelectMany(item => /* 変換処理 */). ToReadOnlyReactiveCollection(); // 正しい選択項目はSubject経由で流れてくるのでそれをもとにReactivePropertyを作る _selectedItemSubjects = new Subject<ItemData>(); SelectedItemTexts = _selectedItemSubjects. SelectMany(s => s.ToString()). ToReadOnlyReactivePropertySlim(); Items.ObserveElementProperty(item => item.IsSelected).Subscribe(x => { var selected = Items.Where(w => w.IsSelected); var count = selected.Count(); if (count > MaxSelection) { // IsSelectedが変更されたインスタンスはx.Instanceで取得できる // x.Value は変更後のIsSelectedの値なのでx.Value = false;としても無意味 x.Instance.IsSelected = false; return; } // ここまで来たら現在の選択状態は正当なので後続に選択項目を流す _selectedItemSubjects.OnNext(selected); }); } } これで「MaxSelectionに指定された選択可能な上限まで選択可能」「上限を超えて選択しようとしたら選択が解除される」「選択状態が変わったとき、正当なときだけ値が流れてくる」を実現できました。 変更の通知について ObserveElementPropety(x => x.IsSelected)は変更状態の監視をするIObservableの生成です。上限超過だとSubscribe内でfalseに値を更新しなおすので、Subscribeにもう一度値が流れてきます。具体的には、上限数が2のときに3つめを選択すると 新しく選択されたためObserveElementProperty(~).Subscribe(~)が3項目選択状態で流れる。内部で選択を解除 選択解除によりIsSelectedが変更されたので、ObserveElementProperty(~).Subscribe(~)に2項目の選択状態で流れてくる。これは3項目を選択する前と同じ状態。 選択内容が変わっていない状態でOnNextするが、OnNextに流すIEnumerable<ItemData>を作り直しているため、要素が実質同じでも後続のReactivePropertyで値変更イベントが起きる。 という動きになります。最初の1・2については選択解除の都合上仕方がないのですが、3の「実質同じでも後続のReactivePropertyで値変更が起きる」は無駄です。Subjectにぶら下がる後続が多い場合などこれが気に入らないなら、現在の選択数を別にフィールドで保持(ローカル変数でもよい?)して同じ値ならOnNextしない、という実装が必要になります。 // 現在選択中の数 選択前と選択後で数値が同じなら選択状態は変わっていない private int _selectedCount; ~~ Items.ObserveElementProperty(x => x.IsSelected).Subscribe(x => { var selected = Items.Where(w => w.IsSelected); var count = selected.Count(); if (count > MaxSelection) { x.Instance.IsSelected = false; // 変更をもとに戻す } else if (_selectedCount != count) { _selectedCount = count; _selectedItemSubjects.OnNext(selected); } }) こうしておくと上限以上を選択しようとしたときにSubscribeは選択→選択解除で2回起きますが、後続には値が流れません。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unity設計入門:第3章「出力の手法1・監視型View」・理論編

Unity設計入門の目次はこちら はじめに 今回は、MVPの実現方法として、ViewがModelを監視して、毎フレーム自分を書き換えていくという監視型Viewの考え方について、Unityでの有効性を検討していきます。 監視型Viewとは ViewはModelを読み出す権限を持っています。そして毎フレームにModelの状況を監視します。 そして、Modelに変更が生まれた時点でViewを書き換えます。 監視型Viewの実装 Modelが内部状況をpublicかつ読み取り専用な変数として提供します。ViewはModelへの参照を持ちます。Viewは直前のModelの値を常にキャッシュしておき、Update()で変更を検知します。 監視型Viewが不適切だといえる理由 ModelにもViewにも属さないデータの行き先が用意されていない これは、第2章その1で挙げた問題点です。詳しくはそちらの記事をご覧ください。 監視Viewはデータ出力における演出関係の変数の行き場がないという問題を完全に無視しています。 それだけでも監視型Viewがゲームでは使いづらい概念であることが分かると思います。 ゲームではModelの変化以外の要因でも見た目が変わることがある 監視型Viewは根本的に「ViewはModelを映す存在である」という発想に立っています。 しかし、ゲームではこの発想が常に正しいとは限りません。例をいくつか挙げてみましょう。 キャラの着せ替え画面で閲覧しているときに、時間経過で勝手にキャラが回転する。Modelで保持しているキャラの服装は変化しない レースゲームにおいて、離脱した後に、元の場所に復帰するときにアニメーション演出が発生するが、Modelで保持しているゲーム上での車の位置は変わらない(マリ〇カートで崖から落下した後、カートがジュゲムに戻してもらえるなどを想像してください) などと言った場合、Modelとは無関係に見た目が変わります。 しかし、監視型Viewはこれらを感知する手段がありません。 なぜなら、プレイヤーの入力は全部Presenterに投げていて、ViewからPresenterに問い合わせをすることは禁止だからです。 監視型ViewはModelだけを見る存在です。 これを解決するためには「ModelにView変更用のフラグを用意する」などModelの純粋性を破壊するようなことをせざるを得なくなります。監視型ViewはModelが変更されないのにViewが変更されるような処理に弱いのです。 毎フレーム監視するコードはパフォーマンス・可読性ともに良くない 毎フレーム監視している場合 直前フレームで値が何だったかをViewに覚えさせる(Viewのコードが単純に複雑化する) 毎フレームModelの情報に従ってViewを更新をする(パフォーマンス的に劣る) のうちどちらかをしないといけません。 また、UIの仕様がややこしくなっていくにつれて、2フレーム前のキャッシュだったり、3秒前のキャッシュだったりを管理する必要が生まれてきます。(典型的にはキーの長押しなど)この手のデータは、「単純なView」という原則を著しく破壊します。なぜなら、フレームという低レベルの仕組みを強く意識したコードになるからです。 なお、どうしても監視型Viewを使いたい場合には、UIの規模が大規模にならないうちはパフォーマンスへの影響は微々たるものなので、どちらかと言うと毎フレーム機械的に再生成してViewを薄く保つことを勧めます。 Viewの検証にModelを使いたくなるので、独立したテストが難しい 監視型Viewをテストするには、毎フレーム値を提供しなければなりません。 こういうプログラムをModelとは別にテスト用で提供するのはそれなりに手間がかかってしまいます。 結果、ModelとViewの独立性が下がってしまうことを許容してでも、動作確認にModelを使ってしまい、保守性を下げてしまいます。 ModelがViewを意識してしまう Viewを小さくしようとすると、どうしてもModelはViewで加工の手間が小さくなるようにView寄りのデータを提供したくなります。結果的に、Modelとは関係ない「カードの表示アイコンの色(RGB)」とか「設定画面のチェックボックスの成否」とかをModelに定義していしまうことが起こりやすいです。 Modelの純粋性が下がることのデメリットはここまで強調してきた通り「Viewを意識した項目のテストはViewを起動しないとできない」という点で現れます。 ViewがModelを監視するロジックが複雑化する Modelの変数を監視する、とひとくちにいっても、かなり複雑化します。ゲームならなおさらです。 例えば、ボードゲームを作る状況を考えてみましょう。この手のゲームでは、Modelは「各マスに存在する駒」を二次元配列として提供していたと考えるのが自然だと思います。 ここで「駒が1マス進む」というイベントが発生したとします。すると、ViewはModelの変更を検知するわけですが、「二次元配列のどのマスのインスタンスがどのマスに移動したか」を調べるのは、結構ややこしいことになります。二重forや大量のキャッシュなど、容易に複雑化を生みます。そして、Viewにややこしいロジックが存在すること自体のデメリットもあるので、非常に悪い設計を生みやすいです。 どういう場合に監視型Viewが適しているのか 「絶対に演出などを目的とした変数・条件分岐を追加しないと決めているシーン」に利用する分には全く問題は発生しません。 メリットは以下の2つです。 Presenterはデータの流れが1方向になり、理解が容易になります。 Presenterのデータ加工コストをViewに持たせることができるのでPresenterが楽になるでしょう。(なおこの加工が複雑だった場合、Presenterで書いた方がマシですので、監視型Viewによる実装はやめましょう) 具体的に役に役に立つ場面としては 3D機能などを目当てに業務用ツールをUnityで作成する場合 プロトタイプとして演出以外を先に作ってしまって、そのあと改めて演出を含むシーンを新規作成する場合 では有用な実装方法になる可能性があります。 特に3D機能を持つ業務用アプリに関してはOpenGLやDirectXを触って作成するよりUnityの方が開発コストが低くなることも多いように思います。 おわりに Unityにおける監視型Viewは不要に巨大なViewを招きやすいようです。 やはりゲーム開発は本質的に複雑であることを痛感させられました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む