- 投稿日:2021-02-13T19:46:40+09:00
Unityの野球ゲームで簡単に変化球を実装する
Unityの野球ゲームで、投手に変化球を投げさせたい場合の簡易的な方法を書きました。
あくまで簡易的なので若干リアルさには欠けるかもしれません。
手順
PlaneとSphereを用意します。
Sphereとカメラの距離はある程度空けておきます。
ボールをど真ん中に投げてみる
まずはボールを真ん中に投げてみます。
SphereにRigidBodyコンポーネントを追加し、スクリプトは以下の通りで試してみます。
powerは1000ぐらい。Ball.cspublic class BallTest : MonoBehaviour { public Rigidbody _rb; public int power; Vector3 direction = Vector3.back;//カメラの方向に投げてみる void Start() { _rb.AddForce(direction * power); } }
powerは丁度いいですが、球が転がってしまったので力を与える方向(direction)を修正します。Vector3 direction = new Vector3(0,0.2f,-1.0f);球に変化を与える
飛ばした球がColliderに当たった際に変化方向に力を加えるという方法でいきます。
以下のように空のオブジェクトを作成しBoxColliderを追加。ボールの軌道の被るように配置します。
isTrrigerはONにします。
最初はスライダーをかけてみます。
Sphereオブジェクトのtagは"Ball"に変更しておきます。ChangeCollider.csint changePower = 100; //変化させる力 Vector3 sliderDirection = new Vector3(1.0f, -1.0f, 0); //変化の方向 private void OnTriggerEnter(Collider other) { if (other.gameObject.tag == "Ball") { Rigidbody ball_rb = other.gameObject.GetComponent<Rigidbody>(); ball_rb.AddForce(sliderDirection * changePower);//ボールに力を加える } }Colliderの位置を前後させることで曲がり始めのタイミングをずらせます。
Z軸に力を加えるとブレーキをかけることもできます。様々な変化をさせてみた
せっかくなので投手を準備して色々な変化球を試してみました。
directionをいじれば様々な変化球を実現できます。
投手のアセットはこちらを使用しました。
- 投稿日:2021-02-13T17:33:47+09:00
ASP.NET Core MVC WWWROOT以外の静的ファイル
ASP.NET Core 2.1~5.0までは同じみたい。
実際にプログラムで確認したのはASP.NET Core 3.1です。課題
システムの構成はこうなります
- 動的なシステム部分(ASP.NET Core MVCで構築)
- 静的な既定ドキュメント「展示、説明」など(ログインかもしくは申込などリンクで動的な部分と繋がる)
「wwwroot」以外の場所から静的な部分を提供する、理由は展開して確認
上記二つの部分、それぞれ独自のcss,javascript,imageが存在します。
基本的にウエブシステムを構築する際は「wwwroot」に静的内容とcss,javascript,imageなどを配置することになります。でも今回の場合は全部一ヶ所に配置すると、色々と混乱し易くなります。
そもそも上の二つの部分はそれぞれ単独に動けるから、二つのシステムと考えても全然問題ありませんし、一ヶ所に配置するのもどうかと思う。
開発の観点から見ると、静的の部分は基本的にデザイナーかデザイン会社にお願いして、動的な部分と並行して開発することになりますので。
以上の原因で「wwwroot」以外の場所から静的な部分を提供するほうが良い。
実装
Startup.cspublic void Configure(IApplicationBuilder app, IWebHostEnvironment env) { //前略... //wwwrootに対して静的コンテンツサービスを登録 app.UseStaticFiles(); //wwwroot以外に静的コンテンツサービスを登録 app.UseFileServer(new FileServerOptions { //登録するフォルダーを指定、絶対パスを設定する必要がある FileProvider = new PhysicalFileProvider("C:\\StaticContent"), }); //後略... }そうすることで「https://<ホスト名>」をアクセスした場合、「C:\StaticContent\」の中で以下のファイルを検索し
- default.htm
- default.html
- index.htm
- index.html
最初に見つけたファイルをクライアントへ返す。
「wwwroot」のなかに「index.html」を配置しても、検索されることはない。
静的な既定ドキュメントを提供するには「UseDefaultFiles」でサービスを登録する必要があります。
静的なファイルに関するサービスは以下四つがある
- UseStaticFiles
- UseDefaultFiles
- UseDirectoryBrowser
- UseFileServer
この四つのサービスに関して、詳しく見ていきましょう。
UseStaticFiles
静的ファイルを提供するサービスです。
Startup.cspublic void Configure(IApplicationBuilder app, IWebHostEnvironment env) { //前略... //パラメータなしの場合は「wwwroot」フォルダー及びサブフォルダの全ファイルを静的コンテンツとして提供する //デフォルトでは認証と認可ミドルウェアの前に登録されているので、「wwwroot」の静的コンテンツをアクセスする際は認証と認可は不要 app.UseStaticFiles(); //後略... app.UseAuthentication(); app.UseAuthorization(); //後略... }UseStaticFilesがパラメータ無しの場合は「wwwroot」を指しているので、最初から入っている静的ファイルをアクセスできる理由はここにあります。
Startup.cspublic void Configure(IApplicationBuilder app, IWebHostEnvironment env) { //前略... //パラメータなしの場合は「wwwroot」フォルダー及びサブフォルダの全ファイルを静的コンテンツとして提供する app.UseStaticFiles(); //「wwwroot」以外に「StaticContent」フォルダーも静的ファイルを提供する //app.UseStaticFiles();の呼び出しがないと「wwwroot」へのアクセスができなくなる app.UseStaticFiles(new StaticFileOptions { FileProvider = new PhysicalFileProvider("C:\\StaticContent") }); //後略... app.UseAuthentication(); app.UseAuthorization(); //後略... }UseDefaultFiles
既定のドキュメントの提供
Startup.cspublic void Configure(IApplicationBuilder app, IWebHostEnvironment env) { //前略... app.UseDefaultFiles(); app.UseStaticFiles(); //「wwwroot」以外に「StaticContent」フォルダーも静的ファイルを提供する //app.UseStaticFiles();の呼び出しがないと「wwwroot」へのアクセスができなくなる app.UseStaticFiles(new StaticFileOptions { FileProvider = new PhysicalFileProvider("C:\\StaticContent") }); //後略... app.UseAuthentication(); app.UseAuthorization(); //後略... }UseDefaultFilesは「wwwroot」にある以下のファイルを既定ドキュメントとして提供
- default.htm
- default.html
- index.htm
- index.html
上記四つ以外のファイルを既定ドキュメントにするには以下のようにパラメータを設定する必要がある
Startup.cspublic void Configure(IApplicationBuilder app, IWebHostEnvironment env) { //前略... //既定ドキュメントの設定 DefaultFilesOptions defaultFilesOptions = new DefaultFilesOptions(); //デフォルトのファイル名設定をクリア defaultFilesOptions.DefaultFileNames.Clear(); //既定ドキュメント名を指定 defaultFilesOptions.DefaultFileNames.Add("myIndex.html"); app.UseDefaultFiles(defaultFilesOptions); app.UseStaticFiles(); //「wwwroot」以外に「StaticContent」フォルダーも静的ファイルを提供する //app.UseStaticFiles();の呼び出しがないと「wwwroot」へのアクセスができなくなる app.UseStaticFiles(new StaticFileOptions { FileProvider = new PhysicalFileProvider("C:\\StaticContent") }); //後略... app.UseAuthentication(); app.UseAuthorization(); //後略... }UseDefaultFilesが影響できるのは「wwwroot」フォルダーのみ、独自に新規で追加した静的コンテンツフォルダーには影響しない
UseDirectoryBrowser
サーバー上に指定したディレクトリ内のディレクトリを一覧表示にする、だがセキュリティー上の問題があるため、利用を避けるべき。デフォルトは無効になっている。
Startup.cspublic void ConfigureServices(IServiceCollection services) { //前略... //EnableDirectoryBrowsing = trueの時、下記を呼び出す必要がある services.AddDirectoryBrowser(); //後略... } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { //前略... app.UseStaticFiles(); //「wwwroot」以外に「StaticContent」フォルダーも静的ファイルを提供する //app.UseStaticFiles();の呼び出しがないと「wwwroot」へのアクセスができなくなる app.UseStaticFiles(new StaticFileOptions { FileProvider = new PhysicalFileProvider("C:\\StaticContent") }); //ディレクトリ参照を起用する app.UseDirectoryBrowser(new DirectoryBrowserOptions { //ディレクトリ参照の対象フォルダー FileProvider = new PhysicalFileProvider("C:\\StaticContent"), //ディレクトリ参照するためのURL相対パス //https://<hostname>/DirectoryBrowser RequestPath = "/DirectoryBrowser" }); //略... app.UseAuthentication(); app.UseAuthorization(); //後略... }「https://<ホスト名>/DirectoryBrowser」でアクセスすると、「C:\StaticContent」内のディレクトリを一覧で表示される
UseFileServer
UseStaticFiles、UseDefaultFiles、UseDirectoryBrowser の機能を兼ね備えているのがUseFileServerです
Startup.cspublic void ConfigureServices(IServiceCollection services) { //前略... //EnableDirectoryBrowsing = trueの時、下記を呼び出す必要がある services.AddDirectoryBrowser(); //後略... } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { //前略... app.UseStaticFiles(); //「wwwroot」以外に「StaticContent」フォルダーも静的ファイルを提供する app.UseFileServer(new FileServerOptions { //静的ファイルを提供する対象フォルダー FileProvider = new PhysicalFileProvider("C:\\StaticContent"), //デフォルトがtrue:デフォルトファイルを起用 EnableDefaultFiles = true, //デフォルトがfalse:ディレクトリブラウザ禁止 EnableDirectoryBrowsing = true //静的コンテンツをアクセスするための相対パス RequestPath = "/StaticFiles", }); //略... app.UseAuthentication(); app.UseAuthorization(); //後略... }まとめ
UseStaticFilesとUseDefaultFilesの既定では下記のFileProviderを提供している
FileProvider = new PhysicalFileProvider(Path.Combine(env.ContentRootPath, "wwwroot"));だからパラメータ無しの場合の操作対象が「wwwroot」となっている。
でも既定の「wwwroot」を変更することは可能
変更方法はProgram.cspublic static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { //ここで既定の「wwwroot」を指定したフォルダーに変更できる webBuilder.UseStartup<Startup>().UseWebRoot("C:\\NewWebRoot"); });
- 投稿日:2021-02-13T17:30:34+09:00
ビット演算を理解する
なぜビット演算が必要なのか?
大きく2つのメリットがあります。
・多くのbool変数を1つの変数にまとめる事が出来る
・1つにまとまってるので操作が色々便利
例えば戦闘中のゲームのキャラクターの状態を示す以下のフラグ(bool型変数)があったとします。
bool isSleep; //眠らされているか? bool isPoison; //毒に侵されているか? bool isNoMagic; //魔法を封じ込められているか? bool isConfusion; //混乱して敵味方の区別がつかないか? bool isAttackDown; //攻撃力がダウンしているか? bool isDefDown; //防御力がダウンしているか? bool isAttackUp; //攻撃力がアップしているか? bool isDefUp; //防御力がアップしているか?ビット演算を使えば、全て1つの変数だけで制御できます。
ドラクエだとキャラクターの状態変化は数十もあるので1つ1つbool変数にしていたら面倒です。そして回復魔法でマイナスの状態だけ(攻撃力・防御力アップ以外)を回復させるなど一括操作も簡単にできます。
このページの目的は、最後に載せているゲームのキャラクターの状態を管理するビット演算のコードを理解できるようにする事です。
だいたいこんな感じ
0001
の眠りと、0010
の毒を足すと、0011
の毒に侵されて眠ってる状態が表現できます。
ただビット演算では、+
や-
ではなく、|
や&
や~
などを使います。2進数の書き方
ビット演算するには2進数の書き方を覚えた方が便利です。ついでに16進数の書き方も示します。
var dec = 123; //10進数 var bin = 0B111; //2進数(0Bか、0bを頭につける)4 + 2 + 1 = 7 var hex = 0X1F; //16進数(0Xか、0xを頭につける)16 + 15 = 31 //C#7以降では見やすいように"_"で、区切る事が出来ます var bin2 = 0B_0011_1111_1001; //2進数 var hex2 = 0X_FF_2F_FF; //16進数10進数を2進数で表示
10進数で返されたビット演算の結果は、2進数で確認した方がフラグの状態を理解しやすいです。
Convert.ToStringメソッドの第2引数に2を指定すると、第1引数を2進数に変換されます。
ちなみに16だと16進数に変換されます。
PadLeftメソッドは第1引数の桁になるまで、第2引数で文字列の左側を埋めます。using System; public class Prog { public static void Main() { int[] array = { 1, 2, 3, 4, 5, 0B111, 0XFF }; foreach( int i in array) { Console.WriteLine( Convert.ToString(i, 2).PadLeft(8, '0') + " " + i); } } } //00000001 1 //00000010 2 //00000011 3 //00000100 4 //00000101 5 //00000111 7 //11111111 255ビット演算子
2つの数値を
ビット演算子
を使って計算します。1+1=2の+
みたいものです。
特に良く使う|
と&
はどっちがどっちか分からなくなるので、自分は次のイメージで覚えています。
|
は、片方だけでも1があれば、回転して残像で両方1になる。
&
は、両方の穴に1が収まって、はじめて1になる。using System; class Prog { static void Main(string[] args) { int a = 0B_0101; int b = 0B_0110; Print( a&b ); //両方1なら1 論理積 Print( a|b ); //どちらか1なら1 論理和 Print( a^b ); //片方だけ1なら1 排他的論理和 Print( ~a ); //0なら1に、1なら0に 反転 Console.WriteLine(""); Print( a<<1 ); //左にシフト Print( a>>1 ); //右にシフト } static void Print(int i) { Console.WriteLine( Convert.ToString(i, 2).PadLeft(4, '0')); } } //0100 //0111 //0011 //11111111111111111111111111111010 //1010 //0010良く使う演算子のパターン
ビット演算はかなり特殊なので、最初は分からなくても実際に使う段階で理解出来ます。最初はこういうものかと深く考えずに暗記するのが早いです。
左からn番目にフラグを立てる
i | (1 << n)
int b = 1 << 2; //0100 10進数だと4を表します int i = 0B_0001; i |= (1 << 2); //0101 10進数だと5を表します同じ値をXOR 演算子
^
を使ってゼロクリアする。int i = 0B_0101; i ^= i; //0000左からn番目のフラグを消す
i &= ~(1 << n)
int i = 0B_1111; i &= ~(1 << 2); //1011左からn番目のフラグが立っているか確認する
i & (1 << n)
int i = 0B_0101; Convert.ToBoolean( i & (1 << 2)); //Trueint i = 0B_0101; bool b = (i & (1 << 2)) != 1; //True複数のフラグをまとめて、マスクビットを作る
int a = (1 << 1); //0010 int b = (1 << 2); //0100 var mask = a |= b; //0110 int c = 0B_1111; //1111 c &= ~mask; //1001enumを使ってビット演算を見やすく
ビット演算は便利ですが、フラグを数値だけで管理すると分からなくなりそうです。(0B_0100は「毒」で、0B_1000は「混乱」など自分で決めても間違えそうです‥)
enumの前に、FlagsAttribute属性を指定することで、列挙型の名前でビット演算のフラグを管理出来ます。(Flagsを外すと数値に対してビット演算しません[Flags]
をコメントにすると違いが分かります)using System; class Prog{ [Flags] //enumをビットフラグにするFlagsAttribute属性 enum Col:short { Red = 1 << 0, //0001 Green = 1 << 1, //0010 Blue = 1 << 2, //0100 }; static void Main() { for(int i = 0; i <= 8; i++) Console.WriteLine("{0} {1} {2}", i, Convert.ToString(i, 2).PadLeft(4, '0'), (Col)i); } } //0 0000 0 //1 0001 Red //2 0010 Green //3 0011 Red, Green //4 0100 Blue //5 0101 Red, Blue //6 0110 Green, Blue //7 0111 Red, Green, Blue //8 1000 8ちなみに赤と緑をかけ合わせると黄色になり、赤と青をかけ合わせると紫色になり、赤、緑、青、全てをかけ合わせると無色になります。三原色をビット演算で組み合わせる事で虹色を表現する事が出来るわけです。
1つ具体例として、タスクのオブションで使うフラグの例を見てみましょう。
TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness
は、列挙型のフラグです。var task = new Task( () => Method_A(), TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness); task3.Start();列挙型の説明を見ると、フィールド毎に番号がふってありますが、全て2の倍数になっています。
この整数値をビット演算で組み合わせて、1つの引数で複数のフラグを立てる事を可能にしています。番号だけだと分かりにくいので列挙型にして名前を付けている訳です。ゲームのキャラクターの戦闘状態を制御
ビット演算の締めくくりに、ゲームの戦闘中のキャラクターの状態を次々と変化させていきましょう。
using System; [Flags] public enum Status { //10進数で書く場合は、2の倍数にする Normal = 0, //マイナスの状態異常 Sleep = 1, //眠っている 0001 Poison = 2, //毒に侵されている 0010 //プラスの状態変化 AttackUp = 4, //魔法で攻撃力アップ 0100 DefUp = 8, //魔法で防御力アップ 1000 } public class Prog { public static void Main() { var chara = Status.Normal; //キャラクター chara |= Status.AttackUp; //味方の魔法で、攻撃力アップ! Print( chara); //AttackUp chara |= Status.Poison; //敵の毒に侵される! Print( chara); //Poison, AttackUp chara |= Status.Sleep; //敵に眠らされる! Print( chara); //Sleep, Poison, AttackUp //回復魔法(眠りと毒だけを解除、攻撃力・防御力アップはそのまま) Status heal = Status.Sleep | Status.Poison; chara &= ~heal; //味方の回復魔法で復活! Print( chara); //AttackUp //攻撃力・防御力どちらか、もしくは両方がアップしているか確認 Status up = Status.AttackUp | Status.DefUp; bool b = (chara & up) != Status.Normal; //True chara ^= chara; //全ての効果を無効にする魔法を唱える! Print( chara); //Normal } //状態を確認するメソッド static void Print( Status s){ Console.WriteLine( "{0} {1}", Convert.ToString( (int)s, 2).PadLeft(4,'0'), s); } }
- 投稿日:2021-02-13T15:30:52+09:00
値型シーケンスをCast<T>, OfType<T>でキャスト出来ない
結論
LINQ自体の実装に問題がある。
Select
クエリを使う。
自分用にメモ。いきさつ
int[]
をdouble[]
にキャストしたい時にCast<double>()
を使ったらエラーになってしまった。
要素のアップキャストなのになぜか失敗する。int[] hoge = { 1, 2, 3 }; // InvalidCastExceptionが発生する var fuga = hoge.Cast<double>(); // 結果は空(すなわちキャスト出来ていない) var piyo = hoge.OfType<double>();なんで
内部実装ではシーケンス要素を一旦
object
型にキャストしてからT
型にキャストしている模様。参考:
C# IEnumerable.Castメソッドでint→longはできない
OfTypeメソッドとCastメソッド
Enumerable.Castメソッドの罠// 要素に対する等価式(当然例外になる) var element = (double)((object)1);どうする
上記リンク通りこれでOK。
var fuga = hoge.Select(a => (double)a);終わりに
アカウントがあるのに何も書かないのはアレなので。