- 投稿日:2020-04-26T19:47:51+09:00
C# Enumの文字列変換は重い
EnumをToStringすると激重なので、事前にswitch文やarrayで文字列を定義しておくと、パフォーマンスを上げることができる
コード
[RankColumn] public class EnumTest { [Params(Weapon.WeaponType.A)] public Weapon.WeaponType weaponType; [Benchmark] public string Case1() { return weaponType.GetNameSwitch(); } [Benchmark] public string Case2() { return weaponType.GetNameArray(); } [Benchmark] public string Case3() { return weaponType.GetNameToString(); } } public static class Weapon { public enum WeaponType { A, B, C, } private static string[] weaponTypeArray = { "A", "B", "C", }; public static string GetNameSwitch(this WeaponType type) { switch (type) { case WeaponType.A: return "A"; case WeaponType.B: return "B"; case WeaponType.C: return "C"; } throw new NotImplementedException(); } public static string GetNameArray(this WeaponType type) { return weaponTypeArray[(int)type]; } public static string GetNameToString(this WeaponType type) { return type.ToString(); } }結果
//BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18362 //Intel Core i5-9600K CPU 3.70GHz(Coffee Lake), 1 CPU, 6 logical and 6 physical cores // [Host] : .NET Framework 4.8 (4.8.4121.0), X64 RyuJIT // DefaultJob : .NET Framework 4.8 (4.8.4121.0), X64 RyuJIT //| Method | weaponType | Mean | Error | StdDev | Rank | //|------- |----------- |------------:|----------:|----------:|-----:| //| Case1 | A | 1.2287 ns | 0.0263 ns | 0.0246 ns | 2 | //| Case2 | A | 0.0523 ns | 0.0123 ns | 0.0115 ns | 1 | //| Case3 | A | 282.2946 ns | 1.4594 ns | 1.3652 ns | 3 |
- 投稿日:2020-04-26T19:45:18+09:00
C# Stringパフォーマンス計測
よく使うintから文字列への変換Performance測定
計測コード
[MemoryDiagnoser, RankColumn] public class ScoreBoard { [Params(999999)] public int score; [Benchmark] public string Case1() { return "Score : " + score.ToString(); } [Benchmark] public string Case2() { return "Score : " + score; } [Benchmark] public string Case3() { string scoreText = score.ToString(); return string.Format("Score : {0}", scoreText); } [Benchmark] public string Case4() { return string.Format("Score : {0}", score); } [Benchmark] public string Case5() { return $"Score : {score}"; } [Benchmark] public string Case6() { string scoreText = score.ToString(); return $"Score : {scoreText}"; } [Benchmark] public string Case7() { string scoreText = score.ToString(); return ZString.Concat("Score : " + scoreText); } [Benchmark] public string Case8() { return ZString.Concat("Score : ", score); } [Benchmark] public string Case9() { string scoreText = score.ToString(); return ZString.Format("Score : {0}", scoreText); } [Benchmark] public string Case10() { return ZString.Format("Score : {0}", score); } }結果
//BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18362 //Intel Core i5-9600K CPU 3.70GHz(Coffee Lake), 1 CPU, 6 logical and 6 physical cores // [Host] : .NET Framework 4.8 (4.8.4121.0), X64 RyuJIT // DefaultJob : .NET Framework 4.8 (4.8.4121.0), X64 RyuJIT //| Method | score | Mean | Error | StdDev | Rank | Gen 0 | Gen 1 | Gen 2 | Allocated | //|------- |------- |----------:|---------:|---------:|-----:|-------:|------:|------:|----------:| //| Case1 | 999999 | 59.98 ns | 0.670 ns | 0.627 ns | 2 | 0.0204 | - | - | 96 B | //| Case2 | 999999 | 60.23 ns | 0.331 ns | 0.310 ns | 2 | 0.0204 | - | - | 96 B | //| Case3 | 999999 | 132.33 ns | 1.099 ns | 0.975 ns | 5 | 0.0203 | - | - | 96 B | //| Case4 | 999999 | 139.31 ns | 1.503 ns | 1.406 ns | 6 | 0.0253 | - | - | 120 B | //| Case5 | 999999 | 139.09 ns | 0.712 ns | 0.666 ns | 6 | 0.0253 | - | - | 120 B | //| Case6 | 999999 | 61.18 ns | 0.730 ns | 0.683 ns | 2 | 0.0204 | - | - | 96 B | //| Case7 | 999999 | 117.34 ns | 0.965 ns | 0.903 ns | 4 | 0.0323 | - | - | 152 B | //| Case8 | 999999 | 51.19 ns | 0.522 ns | 0.488 ns | 1 | 0.0119 | - | - | 56 B | //| Case9 | 999999 | 196.12 ns | 1.129 ns | 1.056 ns | 7 | 0.0203 | - | - | 96 B | //| Case10 | 999999 | 108.29 ns | 0.723 ns | 0.676 ns | 3 | 0.0118 | - | - | 56 B |
ZStringが使えるならこれを使ったほうが一番パフォーマンスがいい。
無理なら
ToString() と +演算子の組み合わせ
ToString() と $ - 文字列補間がパフォーマンスが良い文字列の変換数が増えるとパフォーマンスは変わってくるが...
- 投稿日:2020-04-26T17:39:11+09:00
Unityを使ってクリエィティブなゲームを作れるようになるまで。(2日目)
題名を(〇日目)のようにしました。
色んなゲームを作る予定ですので、私の成長過程を楽しんでもらえたら幸いです!今日は何を作るの?
ネットサーフィンをしていると、なんとN予備校の受講が無料らしいとの事!!
引用:N予備校
https://www.nnn.ed.nico/中を漁ってみると、なんとunityのコースが!
早速受講してゲームを作ろう!作成したゲームはこれ
受講したコース通り、キャラクターの後ろに付いている赤い球を箱にぶつけるゲームを作りました。
学んだこと
- Terrainオブジェクトを使用して、簡単に地形が作れる。
- Spring jointでキャラクターと玉の位置関係、バネの力で制御することができる。
- scriptの中のpublicで定義された変数・クラスは、GUIから代入することができる。
- ↑の仕組みであるため、script内にconstructorは持たない。
- 投稿日:2020-04-26T17:37:57+09:00
【Unity】UIの透過をスクリプトからまとめて調整するCanvasGroup
環境
Unity 2019.3.7f1
はじめに
ボタンの透過率を変更しても
ボタンに付けているテキストの透過率は一緒に変化しません。まとめて透過率を変更したいときはCanvasGroupが便利です。
手順
1.透過値を変更したいUIを適当オブジェクトにまとめる
2.まとめたオブジェクトにCanvasGroupコンポーネントを追加
3.スクリプトからCanvasGroupコンポーネントを取得しアルファ値を変更する
CanvasGroup型の変数.alpha=0f~1f;具体例
3つのボタンの透過値を実行後50%に変更していきます。
1.ボタンを3つ作成
2.CanvasにCanvasGroupコンポーネントを追加
3.スクリプト作成
スクリプトを新規作成します。スクリプト名はCanvasTestとしました。CanvasTestusing System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI;//UIを使うとき追加 public class CanvasTest : MonoBehaviour { [SerializeField] private CanvasGroup a;//CanvasGroup型の変数aを宣言 あとでCanvasGroupをアタッチする void Start() { a.alpha = 0.5f;//変数aのalpha値を変更 } }5.スクリプトにCanvasGroupをアタッチ
Canvasを空のオブジェクトのスクリプトの変数a欄にアタッチ
おわりに
個別の透過率変更とはサヨナラです♪
- 投稿日:2020-04-26T16:29:27+09:00
【Unity】否定演算子の使い方
環境
Unity 2019.3.7f1
はじめに
否定演算子の使い方がわからなくなるのでメモ
使い方
使い方は2種類
・if文で使用
if(変数 != 数値)
{
処理内容
}・trueとfalseを逆にする
変数 = !変数;具体例
・if文で使用
using System.Collections; using System.Collections.Generic; using UnityEngine; public class test : MonoBehaviour { int a; void Start() { a = 2; //aが1ではないなら処理 if(a != 1) { Debug.Log("aは1じゃないよ");//コンソールに表示 } } }
・trueとfalseを逆にするusing System.Collections; using System.Collections.Generic; using UnityEngine; public class test : MonoBehaviour { bool a; void Start() { a = true; a = !a; Debug.Log("a=" + a); a = !a; Debug.Log("a=" + a); a = !a; Debug.Log("a=" + a); a = !a; Debug.Log("a=" + a); } }おわりに
なにかと使える否定演算子
- 投稿日:2020-04-26T15:44:33+09:00
待望の UWP 向け(厳密には Uno Platform 向け)Prism を使ってみよう
個人的に長らく望んでいた UWP 版 Prism ですが、このたび Prism の Uno Platform 対応という形の Pull request がマージされました。そのおかげで UWP でも Prism が使えるようになりました。なんといっても Uno Platform は UWP のコードを Android/iOS/WebAssembly などで動かすプロジェクトだからね!
該当のPR: Add support for Uno Platform and WinUI #2054
こちらですが、Prism 8.0 での公開が予定されています。なので現段階では以下の MyGet からプレビュー版を入手して使う必要があります。
https://myget.org/gallery/prism
ただ、これを書いている時点の MyGet の Uno 用の Prism は参照に追加しただけでビルドエラーになってしまいます。そのビルドエラーになる修正自体はソースに反映されていて、Prism の Azure DevOps のビルドパイプラインでビルドされているので、そのビルド成果物の NuGet をダウンロードして、そこからパッケージを追加するようにしました。
私が試したのは #1717 のビルドパイプラインの成果物から NuGet のパッケージをダウンロードしてます。
では、早速使ってみましょう。UWP で DI コンテナーを Unity にしたい場合は Prism.Unity.Uno になります。
とりあえず MainPage.xaml はいらないので消します。そして Views フォルダーを作って、そこに Shell という名前でページを作りましょう。
そして、App.xaml を PrismApplication タグに置き換えます。
App.xaml<prism:PrismApplication x:Class="PrismHelloWorld.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:prism="using:Prism.Unity"> </prism:PrismApplication>App.xaml.cs も PrismApplication に親クラスを置き換えて Prism ではおなじみのメソッドをオーバーライドします。とりあえず、先ほど作った Shell をメインの画面となるように CreateShell メソッドの戻り値として返すようにしました。
App.xaml.csusing Prism.Ioc; using Prism.Unity; using PrismHelloWorld.Views; using Windows.UI.Xaml; namespace PrismHelloWorld { sealed partial class App : PrismApplication { public App() { this.InitializeComponent(); } protected override UIElement CreateShell() => Container.Resolve<Shell>(); protected override void RegisterTypes(IContainerRegistry containerRegistry) { } } }現状のソリューションエクスプローラーはこんな感じです。
では、実行してみましょう。
ちゃんとウィンドウが出ました!!
リージョンとモジュールの追加
では、Prism の画面遷移の機能であるリージョンを追加します。
Shell.xaml のコードを以下のようにしてリージョンを追加します。Shell.xaml<Page x:Class="PrismHelloWorld.Views.Shell" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:PrismHelloWorld.Views" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:regions="using:Prism.Regions" mc:Ignorable="d" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition /> </Grid.RowDefinitions> <TextBlock Text="The hello world app" Style="{StaticResource HeaderTextBlockStyle}" /> <ContentControl Grid.Row="1" regions:RegionManager.RegionName="ContentRegion" /> </Grid> </Page>動かすとこんな感じになります。まだリージョンに何もページを追加してないので、TextBlock が表示されているだけです。
ソリューションにクラスライブラリープロジェクトを追加して、Prism.Unity.Uno パッケージを追加します。Class1.cs は消して
MainModule
という名前でクラスを作ります。
そして IModule インターフェースを実装します。MainModule.csusing Prism.Ioc; using Prism.Modularity; namespace PrismHelloWorld.Main { public class MainModule : IModule { public void OnInitialized(IContainerProvider containerProvider) { } public void RegisterTypes(IContainerRegistry containerRegistry) { } } }ここで登録する画面を作りましょう。Views フォルダーを作って MainView という名前のユーザーコントロールと、ViewModels フォルダーを作って MainViewModel という名前のクラスを作成します。
MainViewModel.cs は適当に以下のように作りました。
MainViewModel.csusing Prism.Commands; using Prism.Mvvm; using Prism.Services.Dialogs; namespace PrismHelloWorld.Main.ViewModels { public class MainViewModel : BindableBase { public string Message => "Hello from MainViewModel"; } }MainView.xaml.cs でコンパイル時データバインディング使うために DataContext を MainViewModel にキャストして返す ViewModel プロパティを定義します。
MainViewModel.csusing PrismHelloWorld.Main.ViewModels; using Windows.UI.Xaml.Controls; namespace PrismHelloWorld.Main.Views { public sealed partial class MainView : UserControl { private MainViewModel ViewModel => (MainViewModel)DataContext; public MainView() { this.InitializeComponent(); } } }MainView.xaml のほうは、AutoWireViewModel の設定をして、適当に ViewModel の Message とバインドするための TextBlock を置きました。
MainView.xaml<UserControl x:Class="PrismHelloWorld.Main.Views.MainView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mvvm="using:Prism.Mvvm" mvvm:ViewModelLocator.AutoWireViewModel="True" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400"> <StackPanel> <TextBlock Text="{x:Bind ViewModel.Message}" /> </StackPanel> </UserControl>画面が出来たので MainModule.cs でコンテナーに登録したりリージョンに表示するように設定してみたいと思います。
なんか、ここら辺書いてると WPF の Prism で色々やってたころを思い出して懐かしくなってきますね。MainModule.csusing Prism.Ioc; using Prism.Modularity; using Prism.Regions; using PrismHelloWorld.Main.Views; namespace PrismHelloWorld.Main { public class MainModule : IModule { public void OnInitialized(IContainerProvider containerProvider) { var regionManager = containerProvider.Resolve<IRegionManager>(); regionManager.RegisterViewWithRegion("ContentRegion", typeof(MainView)); } public void RegisterTypes(IContainerRegistry containerRegistry) { } } }モジュールが出来たのでアプリ側から読み込むようにしましょう。モジュールのプロジェクトへの参照をメインのアプリ側に追加して App.xaml.cs でモジュールを ModuleCatalog に追加します。
App.xaml.csusing Prism.Ioc; using Prism.Modularity; using Prism.Unity; using PrismHelloWorld.Main; using PrismHelloWorld.Views; using Windows.UI.Xaml; namespace PrismHelloWorld { sealed partial class App : PrismApplication { public App() { this.InitializeComponent(); } protected override UIElement CreateShell() => Container.Resolve<Shell>(); protected override void RegisterTypes(IContainerRegistry containerRegistry) { } protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog) { moduleCatalog.AddModule<MainModule>(); } } }実行してみると…
動いた!!
まとめ
とりあえずプロジェクトテンプレートで吐き出されるレベルのコードを作ってみただけですが、個人的に慣れ親しんだ WPF 版の Prism と同じ書き味で UWP のアプリ開発も出来るようになるのは、控えめに言って最高なので最高です。(語彙力の低下)
Prism 8.0 のリリースが楽しみですね。
今まで、UWP アプリ開発を楽にするためのフレームワークが個人的な決め手になるものが無かったのですが Prism が使えるなら割と積極的に使ってもいいかも。
- 投稿日:2020-04-26T15:03:53+09:00
【WPF】ItemsControlの基本的な使い方
はじめに
例えば
class Store { public string Name { get; set; } public string Prefecture { get; set; } public int FavoriteCount { get; set; } }このようなクラスの List を画面上に表示し、一覧画面を作成したいというようなケースは多々あるかと思います。
言語によってはView上でfor
を使用しListをループすることで実現可能だったりしますが
残念ながらWPFではそのような方法では実現できません。そこで登場するのがItemControlクラスです。
ItemsControlクラスはコレクションを並べて表示することができ、上記のようなことを実現するにはもってこいのクラスです。
また派生クラスにはListBoxクラスやComboBoxクラスなどがあり、UIのカスタマイズなどを行うことができるようになっています。
非常に柔軟性が高く、お世話になる機会も多いだろうと思い、ItemsControlクラス(とその派生クラス)の基本的な使い方を整理していきます。ソースコードについて
今回はMVVMでいうところのViewModelは以下を使用します。
MainViewModel.csnamespace ItemsControl.ViewModels { class MainViewModel { public MainViewModel() { this.Mall = Enumerable.Range(1 20).Select(x => new Store() { Name = "お店" + x, Prefecture = "東京都", FavoriteCount = x * 10, }).ToList(); } public List<Store> Mall { get; set; } } }またModelとして前述のStoreクラスを使用しています。
ItemsControlでカスタマイズできること
ItemsControlでは4つのプロパティが用意されており、それらを設定することでカスタマイズしています。
プロパティ名 概要 設定で使用するコントロール Template ItemsControlそのものを指定します。 ControlTemplate ItemsPanel 要素の並べ方について指定します。 ItemsPanaleTemplate ItemTemplate 要素を表示する際のテンプレートを指定します。 DataTemplate ItemContainierStyle 要素に適応するStyleを指定します。 Style ではそれぞれ見ていきます。
Template
ControlTemplate
を指定することで、コントロール全体の設定を行うことが多いです。MailView.xaml<ListBox ItemsSource="{Binding Mall}"> <ListBox.Template> <ControlTemplate TargetType="{x:Type ListBox}"> <Border BorderThickness="5" BorderBrush="Red" Background="LightGray"> <ItemsPresenter Margin="5" /> </Border> </ControlTemplate> </ListBox.Template> </ListBox>見るとわかる通り、表示されているのはListに格納されているオブジェクトの名前になっています。
これはそのオブジェクト(今回の場合はStore
クラス)にtoString()
が実装されていないためです。
なのでtoString()
を実装してあげればこのままでもデータを表示することは可能です。MainViewModel.csnamespace ItemsControl.ViewModels { class MainViewModel { public MainViewModel() { this.Mall = Enumerable.Range(1 20).Select(x => new Store() { Name = "お店" + x, Prefecture = "東京都", FavoriteCount = x * 10, }).ToList(); } public List<Store> Mall { get; set; } public override string ToString() { return this.Name; } } }ItemsPanel
ItemsPanelTemplate
でコレクションをどう並べるかを指定します。
指定できるのはPanelクラスの派生クラスである以下の3つです。
なお、デフォルトでStackPanel
が指定されているので、何も指定されていない場合は要素が縦に並びます。
クラス名 配置 StacPanel 縦に並ぶ WrapPanel 横に並ぶ Grid 指定はできるが子要素を並べる機能がないためすべて重なる MailView.xaml<ListBox ItemsSource="{Binding Mall}"> <ListBox.ItemsPanel> <ItemsPanelTemplate> <WrapPanel Orientation="Horizontal" /> </ItemsPanelTemplate> </ListBox.ItemsPanel> </ListBox>ちなみにGridで表示すると以下のようにすべての要素が重なります。
これ、どういうときに使うんだろうか...
ItemTemplate
DataTemplate
でコレクションの項目をどのように表示するかを指定します。
要は、各要素単位での表示設定です。MailView.xaml<ListBox ItemsSource="{Binding Mall}"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel> <TextBlock> <TextBlock.Text> <MultiBinding StringFormat="【{0}】{1}"> <Binding Path="Prefecture" /> <Binding Path="Name" /> </MultiBinding> </TextBlock.Text> </TextBlock> <TextBlock Text="{Binding FavoriteCount, StringFormat=お気に入り:{0}}" /> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox>ItemContainerStyle
Style
を指定します。
ItemTemplate
と同じく要素ごとの表示方法を指定するプロパティです。
要は、WebページでいうところのCSSです。MailView.xaml<ListBox ItemsSource="{Binding Mall}"> <ListBox.ItemContainerStyle> <Style TargetType="ListBoxItem"> <Setter Property="OverridesDefaultStyle" Value="True" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ContentControl}"> <Border Background="{TemplateBinding Background}"> <ContentPresenter /> </Border> </ControlTemplate> </Setter.Value> </Setter> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background" Value="LightBlue"/> </Trigger> <Trigger Property="IsSelected" Value="True"> <Setter Property="Background" Value="LightGreen" /> </Trigger> </Style.Triggers> </Style> </ListBox.ItemContainerStyle> </ListBox>
Trigger
では、特定の動作に連動させてStyleを変更することができます。
上記コードだと
- マウスオーバーで要素の背景をLightBlueに
- 選択した要素の背景をLightGreenに
という2点を対応しています。
最終的にできたもの
ここまで整理してきた4つのプロパティをすべて合わせたものがこちらです。
※見栄えを整えるためにItemContainerStyle
に一部Styleを追加していますMailView.xaml<ListBox ItemsSource="{Binding Mall}"> <ListBox.Template> <ControlTemplate TargetType="{x:Type ListBox}"> <Border BorderThickness="5" BorderBrush="Red" Background="LightGray"> <ItemsPresenter Margin="5" /> </Border> </ControlTemplate> </ListBox.Template> <ListBox.ItemsPanel> <ItemsPanelTemplate> <WrapPanel Orientation="Horizontal" /> </ItemsPanelTemplate> </ListBox.ItemsPanel> <ListBox.ItemTemplate> <DataTemplate> <StackPanel> <TextBlock> <TextBlock.Text> <MultiBinding StringFormat="【{0}】{1}"> <Binding Path="Prefecture" /> <Binding Path="Name" /> </MultiBinding> </TextBlock.Text> </TextBlock> <TextBlock Text="{Binding FavoriteCount, StringFormat=お気に入り:{0}}" /> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> <ListBox.ItemContainerStyle> <Style TargetType="ListBoxItem"> <Setter Property="OverridesDefaultStyle" Value="True" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ContentControl}"> <Border Background="{TemplateBinding Background}"> <ContentPresenter /> </Border> </ControlTemplate> </Setter.Value> </Setter> <Setter Property="Margin" Value="10" /> <!-- 追加したStykle その1 --> <Setter Property="Width" Value="100" /> <!-- 追加したStykle その2 --> <Setter Property="Height" Value="50" /> <!-- 追加したStykle その3 --> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background" Value="LightBlue"/> </Trigger> <Trigger Property="IsSelected" Value="True"> <Setter Property="Background" Value="LightGreen" /> </Trigger> </Style.Triggers> </Style> </ListBox.ItemContainerStyle> </ListBox>まとめ
ItemsControlでコントロールの外見を変更する方法を整理しました。
変更のためには4つのパラメーターを変更していきます。
一覧系の画面や選択肢を列挙など、使用用途は幅広くお世話になることも多いかと思います。なお、今回はListBoxを使用しましたが、基本的な考え方はComboBoxも同じなので流用できるはずです。
参考
- 投稿日:2020-04-26T12:40:16+09:00
【Unity】シーンの事前ロード
環境
Unity 2019.3.7f1
はじめに
移動先のシーンが重くて遷移まで間が開いてしまう問題を解決するべく
シーンの事前ロードについて調べました。方法
事前ロードし、ボタンを押したらシーン移動するコード
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement;//追加 public class SceneTest : MonoBehaviour { AsyncOperation a;//AsyncOperation型の変数aを宣言 void Start() { //SceneManager.LoadSceneAsync("GameScene")の返り値(型はAsyncOperation)を変数aに代入 a = SceneManager.LoadSceneAsync("GameScene"); //AsyncOperationの中の変数allowSceneActivationをfalseにする //これがtrueになるとシーン移動する a.allowSceneActivation = false; } //ボタンに割り当て public void Change_Scene_button() { //trueにしてシーン移動 a.allowSceneActivation = true; } }解説
SceneManager.LoadSceneAsync("シーン名");のみを使ってシーン移動する場合、
下図のことが処理されています。
allowSceneActivationの情報を下図のように一旦falseにしておくことで
シーン読み込み後のシーン移動をストップします。
おわりに
ちょっとややこしいですが、定型文として覚えておく程度で良いと思います。
動けばOKの精神♪
- 投稿日:2020-04-26T11:59:37+09:00
プログラムの書き出しの話
最近、プログラムをやっていて思うのであれば、
自分が中学校の時に、初めて触ったプログラムはN80-BASICというやつなのであるが
いまでも覚えているが、下記の書き出しが頭の中に完全に入っていた。WIDTH 80,25 CONSOLE 0,25,0,1 LOCATE 0,0 PRINT "HELLO WORLD"もちろん、あの頃はこのコードを打ったところで、
インタプリタが無機質なカーソルを示すだけで、
何のヘルプもでないのであるが。。。いわばプログラムの書き出しというやつは
今でも同じで、本当に無機質な奴だ。さて、今日のお題ではあるが、
新しいプログラム言語、開発環境で始まるのは、
やはりプログラム君こんにちわのような、
挨拶が重要なのである。別にプログラムの世界だけでなく、実際の世界でも
挨拶一つが通じれば、相手は何らかの話をしてくれるだろう。もしも、何も分からずに、ただただ相手に近づいたとしても、
それこそ、チンプンカンプンとばかりに、困った顔をされるか、
酷い場合には、あっちへ行けと追い返されるだけである。話が少々、脱線するが・・・
俺が始めて大学の卒業旅行に行ったとき、アメリカの空港のトランジットで
チケットを自分のキャスターケースに預けてしまって、
そして空港カウンターの黒人の女性に、何とか危機を説明したのであるが、
「ノーチケット!ノーライド!」
といって、本当に追い返されたことを、いまでも覚えている。その後は、空港のヘルプデスクから日本語が使えると思しき
老人がやってきて、俺を荷物引き取り所まで連れて行ってくれた。俺は自分の鞄の特徴が、銀色に近いものだったので
「ゴールド」といって、何とか鞄を引き戻して、
チケットを手にすることができた。あの時の恐怖と、一緒に行った友達が、あざ笑っているのが
今でも覚えている、初めての海外旅行のエピソードである。
(懐かしい動画が、同窓会の時に話題になり、いまでもYOUTUBEには上げている)
https://www.youtube.com/watch?v=joamkkQdKQwさて、そんな脱線話はさておいて、本題であるが、
つまりプログラム言語と言う奴は、一番最初に何を書くのか
という決まりごとは、いつになっても変わらず、
それを知っておかないと、まったく手も足も出せないのである。そのうち、チャットボット側から、
「 お客さん何を書きましょうか? 」
などと親切にコードの出だしを、教えてくれないものだろうか?せめてAIが出てきた暁には、本気でこのエンジニア不足と
コンピュータとのコミュニケーションのギャップを
埋めてくれることを期待するのであるが。そんな訳で、今日のお題としては、最近習い始めている
プログラム言語の出だしについて、初学者でも分かるように
自分なりにまとめてみようと思う。python ---> import
pythonの場合は、import 文だ。
これは間違っていないだろう。C# ---> using
C#の場合は、using節 だ。
別に演歌の出だしではない。C ---> #include < >
ちょっと昔覚えたから、付け足しておく
c++ も基本同じかもしれないけどnode.js ---> require()
あえて、HTMLに突っ込まれるJavaScriptには言及しない
VB.NET ---> Imports
残念ながら、VBAにはこのようなお決まりは存在しないので
VB.NETの場合で書いておく。
JAVA ---> import
個人的にJAVAは使ったことが無いので、知識として書き出しておく
その他
Typescript
RUBY
PHP
GOいろいろあるんだろうけど。
いまの所は知らないでいる。さてさて、こういう挨拶文というのは、
自分が相手に向かって、こんにちわ
という場合も、当然の如く必要であるが、
例えば、ちょっと他の人の会話を垣間見ようと
GITHUBなどを開いた場合でも同じである。まず、この出だしの挨拶文から、
何もかもが始まるのであって、
ここから紐解いて行かないと
プログラムは完全に理解できないのである。この挨拶文というのは、非常に重要であって、
例えるならば、相手の国のマナーや
しきたり、教養などの全般知識が詰め込まれている。特に、最近は名前空間とか言ったりして、
(名前空間なんて名前つけなくて良くて)
そもそも、これは単なるツリー構造のディレクトリの
名前であって、逆にそういう風に言ってほしいぐらいである。他にも、仮想空間とかいって、PYTHONではENVなどで
作られるのであるが、これも行ってしまえば、
単なる一時的に作られるフォルダの名前に過ぎないのである。
(本当に難しいことを、難しく言うのが、このITエンジニアの
嫌らしい決まりになっていて、だからこそ、回りくどくて
初学者を受け付けない、なんというか嫌らしい文化だ)そして、最近のオブジェクト思考なんて言うのも、
昔は一人の職人が、自営で商売を始めたものを、
段々、多くの人間を雇って、組織的に働くようになって
部署をどんどん作っているようなものだ。だいたいプログラムなんて言うのは、実際の世界のルールや
常識、決まりを、パソコンのストレージエリアに突っ込んでいるという
たんにそれだけの話なのに、どうも初学者を受け付けない
独特の雰囲気がある。そんな訳で、自分はこれからの世の中が、IT社会になればなるほど、
これらの先端エンジニア、お宅エンジニアが我が物顔で
はびこる世の中に、風穴を開けてみたいのである。
- 投稿日:2020-04-26T11:30:47+09:00
ConsoleAppFramework + Serilog の実装例
.NET core コンソールアプリを運用していますが
いつのまにか機能を詰め込みすぎて、全体が把握できなくなり
機能追加が困難な時期がやってきました。そこで、ConsoleAppFramework を導入してコードを整理しようと思います。
しかし、ログなどは Microsoft.Extensions の仕組みを使うようです。
残念ながら Microsoft.Extensions 関連は素人なので、
使い慣れたSerilogとの組み合わせ方法を探すのに苦労しました。
ようやく形になってきたので記録します。(ConsoleAppFrameworkの機能の一部しか活用できていませんが、
最初の一歩として参考になれば幸いです)ConsoleAppFrameworkとは
ConsoleAppFramework - .NET Coreコンソールアプリ作成のためのマイクロフレームワーク(旧MicroBatchFramework)
慣れてしまうと普通のコンソールアプリには戻れませんね。
実装例
まずは結論からということでProgram.csとConsoleAppBaseの実装です。
Program.cs (全文)using System; using System.IO; using System.Threading.Tasks; using ConsoleAppFramework; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Serilog; namespace ConsoleAppFrameworkSampleApp { public class Program { static async Task Main(string[] args) { Log.Logger = CreateLogger(); try { await CreateHostBuilder(args).RunConsoleAppFrameworkAsync(args); } catch (Exception ex) { Log.Fatal(ex, "Host terminated unexpectedly"); } finally { Log.CloseAndFlush(); } } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .UseSerilog() .ConfigureServices((hostContext, services) => { services.Configure<Settings>( hostContext.Configuration.GetSection("Settings")); }); public static ILogger CreateLogger() => new LoggerConfiguration() .ReadFrom.Configuration(CreateBuilder().Build()) .Enrich.FromLogContext() .WriteTo.Console() .CreateLogger(); public static IConfigurationBuilder CreateBuilder() => new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true) .AddEnvironmentVariables(); } }各機能の実装は ConsoleAppBase を継承して実装しますpublic class Base1 : ConsoleAppBase { readonly ILogger logger; readonly Settings settings; public Base1(ILogger<Base1> logger, IOptions<Settings> settingsOp) { this.logger = logger; this.settings = settingsOp.Value; } public void Hello( [Option("n", "name of send user.")]string name, [Option("r", "repeat count.")]int repeat = 3) { for (int i = 0; i < repeat; i++) { this.logger.LogInformation("Hello My ConsoleApp Base1 from {name} {path}", name, this.settings.Path); } } }exeを実行するときの引数で、実行するプログラムを指定します。ConsoleAppFrameworkSampleApp.exe Base1.Hello -t TEST
以上で全部です。
https://github.com/panda728/ConsoleAppFrameworkSample/blob/master/README.md
以下で、各部を説明します。
Install-Packageについて
ログはSerilogを使いますので、とりあえず以下のものをインストール
Install-Package ConsoleAppFramework Install-Package Serilog.Extensions.Logging Install-Package Serilog.Extensions.Hosting Install-Package Serilog.Settings.Configuration Install-Package Serilog.Sinks.Console Install-Package Serilog.Sinks.RollingFile Install-Package Serilog.Enrichers.Environment Install-Package Serilog.Enrichers.Process Install-Package Serilog.Enrichers.ThreadMain部分
mainでは、ロガーを作成してConsoleAppFrameworkを起動します。
起動後は、ConsoleAppFrameworkが、引数に応じて適切なConsoleAppBaseクラスを実行してくれます。
なおロガーの細かい設定は後述
static async Task Main(string[] args) { Log.Logger = CreateLogger(); try { await CreateHostBuilder(args).RunConsoleAppFrameworkAsync(args); } catch (Exception ex) { Log.Fatal(ex, "Host terminated unexpectedly"); } finally { Log.CloseAndFlush(); } }ロガーの設定について
ロガーの設定は appsettings.json にて行うスタイルにします。
appsettings.jsonから設定を読み込む処理は Serilog.Extensions.Hosting のSampleの書き方を参考にしました。
public static ILogger CreateLogger() => new LoggerConfiguration() .ReadFrom.Configuration(CreateBuilder().Build()) .Enrich.FromLogContext() .WriteTo.Console() .CreateLogger(); public static IConfigurationBuilder CreateBuilder() => new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true) .AddEnvironmentVariables();余談ですが appsettings.jsonについて。
テスト環境で「appsettings.Develogment.json」
本番環境で「appsettings.Production.json」
を優先して読み込むとのことMicrosoft.Extensions関連の設定について
Host.CreateDefaultBuilderで設定している内容は
・Serilogの登録
・appsettings.jsonから設定情報を読み込む
(設定情報は以下のようなSettingクラスを用意するスタイルです)ここでservicesに設定したものは、後で作成するConsoleAppBaseのコンストラクタで受け取ることができます。
機能が増えて共通的に使いたいものができたら、ここでいろいろ増やせばよさそうです。public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .UseSerilog() .ConfigureServices((hostContext, services) => { services.Configure<Settings>( hostContext.Configuration.GetSection("Settings")); });(設定用クラスのサンプル)public class Settings { public string Path { get; set; } public string ConnectionString { get; set; } }ConsoleAppBaseの実装例
上記のコードで準備が整いましたので
実際の処理部分を記述します。ConsoleAppFrameworkのサンプルにあるHelloメソッド相当の機能に、ロガーと設定情報クラスを追加すると、以下の形になります。
public class Base1 : ConsoleAppBase { readonly ILogger logger; readonly Settings settings; public Base1(ILogger<Base1> logger, IOptions<Settings> settingsOp) { this.logger = logger; this.settings = settingsOp.Value; } public void Hello( [Option("n", "name of send user.")]string name, [Option("r", "repeat count.")]int repeat = 3) { for (int i = 0; i < repeat; i++) { this.logger.LogInformation("Hello My ConsoleApp Base1 from {name} {path}", name, this.settings.Path); } } }コンストラクタでロガーと設定情報クラスを受け取ることができますが
この辺りはMicrosoft.Extensionsのお仕事DIがよくわからなかったのですが
「コンストラクタで書いたものが勝手にセットされてくる」
と思ったら、便利さがわかったつもりになりましたwappsetting.jsonの例
設定クラスとSerilogの設定は以下の要領で
{ "Settings": { "Path": "c:\\temp", "ConnectionString": "(connection string)" }, "Serilog": { "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.RollingFile", ], "MinimumLevel": "Verbose", "WriteTo": [ { "Name": "RollingFile", "Args": { "pathFormat": "logs\\log-{Date}.txt", "retainedFileCountLimit": "30" } }, ], "Enrich": [ "WithMachineName", "WithProcessId", "WithThreadId" ], "Destructure": [ { "Name": "ToMaximumDepth", "Args": { "maximumDestructuringDepth": 4 } }, { "Name": "ToMaximumStringLength", "Args": { "maximumStringLength": 100 } }, { "Name": "ToMaximumCollectionCount", "Args": { "maximumCollectionCount": 10 } } ] } }最後に
以前は引数で処理を分岐する部分を長々と記述していましたが
今後はConsoleAppFrameworkが引き受けてくれますのでProgram.csはシンプルに保てます。そして、これまで苦労していた、
・巨大クラスで見通しが悪く、影響範囲を考慮しながら慎重に機能追加
・いつの間にか似たような処理を複数作っていた
といった時代とはおさらばできます。新機能を追加したい場合は、新規にConsoleAppBaseを追加すれば、
従来の機能への影響を気にせず追加でき、共通機能はDIから受け取って使いまわせます。クラスが増えても、コンストラクタを見れば
共通利用で使っているクラスが把握できるので安心です。同じ処理のコードや、マスタデータが、複数クラスで必要になったら、DIに切り出す手が使えます。
なるべく見通しよいコード量でConsoleAppBaseを維持したいですね。
余談
appsettings.jsonの実行時パス問題が発生した場合は以下参照
How can I get my .NET Core 3 single file app to find the appsettings.json file?
https://stackoverflow.com/questions/58307558/how-can-i-get-my-net-core-3-single-file-app-to-find-the-appsettings-json-fileconfig.SetBasePath(GetBasePath()); config.AddJsonFile("appsettings.json", false);private string GetBasePath() { using var processModule = Process.GetCurrentProcess().MainModule; return Path.GetDirectoryName(processModule?.FileName); }