20201207のC#に関する記事は10件です。

カンニング万歳!JetBrains Rider逆引きチートシート

最初に

この記事は JetBrains2020.3 と、その拡張機能であるVimなどのよく使うショートカットキーに関する逆引きチートシートとなっております。vim拡張機能でのショートカットも記載しております。当方の環境としては、Windowsを使用しておりますので、Macの方には分かりにくい部分も多いかもしれません。ご了承ください。

逆引きチートシートの推奨する見方

このチートシートを効率良く確認する推奨環境の紹介です。まずディスプレイが二枚以上あると、常にチートシートを表示させながらエディタを操作することができるのでオススメです。また、スクロールを行わずに効率的に作業を進めながらチートシートを確認するために、チートシートのページを普段使いのブラウザ以外で文字を小さくして複数ウィンドウに表示させることをオススメします。「Spectacle」というアプリをPCに入れるとショートカットキーの押下でウィンドウを簡単に上寄せ半分表示、斜め右上1/4表示などの表示が可能なので、是非使用してみて下さい。

共通操作

行の編集

キー 動作 備考
[ctrl]+[X] 行の切り取り 未選択状態の行全体を切り取り
[ctrl]+[C] 行のコピー 未選択状態の行全体をコピー
[Ctrl]+[↩︎] 現在のカーソル行の下に空行を追加 カーソルは行頭に移動する
[ctrl]+[shift]+[↩︎] 現在のカーソル行の上に空行を追加 カーソルは行頭に移動する
[shift]+[alt]+[クリック] マルチカーソルをクリック場所に作成
[shift]+[alt]+[Down] マルチカーソル下方向に作成 複数行を選択中はその行数分
Tab 行をインデント 行のスペース部分にカーソルがある状態で有効
[shift]+[Tab] 行をアンインデント
[ctrl]+[W] 選択範囲を段階ごとに広げていく
[ctrl]+[shift]+[s] 全てを保存
[alt]+[↩︎] 現在の選択箇所でのクイックアクション一覧を表示

行の移動

キー 動作 備考
[alt]+[Down] メソッド単位で下に移動する
[alt]+[Up] メソッド単位で上に移動する

行の選択

キー 動作 備考
[alt]+ドラッグ 矩形選択

コメントの編集

キー 動作 備考
[ctrl]+[k]+[c] 行コメント記号をトグル 複数行を選択中はその行数分
[ctrl]+[shift]+[/] ブロックコメント記号をトグル 選択中は選択部分

ファイル/フォルダ自体の表示

キー 動作 備考
[ctrl]+[Tab] 開いているファイルの切り替え表示 開いた後は^を押下したままTabで切り替えられる
[ctrl]+[F4] 現在開いているファイルを閉じる

ファイルやコードのパスコピー

キー 動作 備考
[ctrl]+[shift]+[c] 現在アクティブなファイルのパスをコピー
[ctrl]+[alt]+[shift]+[c] 現在アクティブなファイルのパスをコピー

検索/置換(シンボルに関しては後述)

キー 動作 備考
[ctrl]+[F] 上部に検索ポップアップ表示 カーソル位置のワードがマッチされていればその単語を使用する
[ctrl]+[shift]+[F] 全ファイルにまたがってキーワード検索をする
[ctrl]+[H] 置換ポップアップ表示 同上
[F3] 次を検索 マッチワードがあればその単語で検索
[shift]+[F3] 前を検索 同上

ブックマーク操作操作

キー 動作 備考
[ctrl]+[k],[k] 現在の位置にブックマークを追加・削除
[ctrl]+[`] ブックマーク一覧を表示
[ctrl]+[k],[n] 次のブックマークへ移動
[ctrl]+[k],[p] 前のブックマークへ移動
[ctrl]+[k],[p] 前のブックマークへ移動
[ctrl]+[shift]+[1~9] 数字に対応したブックマークを追加・削除
[ctrl]+[1~9] 数字に対応したブックマークへ移動

IDE操作

キー 動作 備考
[shift]+[F4] 現在開いているコードファイルを新しいウィンドウで開く
[ctrl]+[shift]+[E] 最近変更した箇所群の表示
[alt]+[1] Explorer画面の表示
[alt]+[3] Find画面の表示
[alt]+[5] デバッグ画面の表示 デバッグ時に有効
[alt]+[7] NuGet画面の表示
[alt]+[8] UnitTests画面の表示
[alt]+[9] Git画面の表示
[alt]+[左右キー] 各ツールウィンドウ内でのタブ移動
[ctrl]+[Tab] 画面のSwitcherウィンドウ表示 タブに対応した文字を打ち込むと即座に切り替わる
[ctrl]+[G] 指定した行番号に移動
[ctrl]+[-] 前に表示していた箇所に戻る
[ctrl]+[shift]+[-] 次に表示していた箇所に戻る
[↩︎] エディタにフォーカスを戻す
[shift]+[esc] ツールウィンドウを閉じる
[ctrl]+[alt]+[s] Setting画面の表示

エラー表示時移動

キー 動作 備考
[ctrl]+[alt]+[2] ソリューション内でのエラー一覧表示
[alt]+[shift]+[Page Up] 次のエラーと警告に移動
[alt]+[shift]+[Page Down] 前のエラーと警告に移動
[shift]+[esc] ツールウィンドウを閉じる

Git操作

キー 動作 備考
[alt]+[9] Git画面の表示 以下はGitツールがアクティブ状態の時に有効
[ctrl]+[alt]+[k] 選択ファイルをコミットする
[ctrl]+[alt]+[z] 選択ファイルをロールバックする
[ctrl]+[shift]+[h] 選択ファイルをスタッシュする
[ctrl]+[alt]+[z] 選択ファイルをロールバックする
[ctrl]+[alt]+[u] 選択ファイルをスタッシュから取り出す
[↩︎] エディタにフォーカスを戻す 以下はエディタがアクティブ状態の時に有効
[ctrl]+[alt]+[w] pull用のウィンドウ表示 MergeするかRebaseするか選べる
[ctrl]+[alt]+[k] コミット用のウィンドウ表示
[ctrl]+[shift]+[k] push

Merge操作

キー 動作 備考
[shift]+[alt],[right] リモートの変更を優先する
[F7] 次の差分に移動する

コーディング時の操作

シンボル検索/表示

キー 動作 備考
[F12] シンボルの定義を表示 基本はこっちの方が使い勝手が良い
[shift]+[F12] 全ファイルにまたがって参照検索しファイルを開く
[ctrl]+[R],[R] シンボルのリネーム

デバッグ操作

キー 動作 備考
[F9] ブレークポイントの切り替え
[F5] デバッグスタート又はコンティニュー
[alt]+[F5] アタッチする
[shift]+[F5] アタッチを外す
[F10] ステップオーバー
[alt]+[shift]+[F8] フォースステップオーバー
[F11] ステップイン
[shift]+[F7] スマートステップイン
[shift]+[F11] ステップアウト

Vim操作

モード切り替え

キー 動作 備考
[i] 挿入モード カーソル位置から
[a] 挿入モード カーソルの後から
[:] コマンドラインモード
[v] ビジュアルモード
[esc] ノーマルモード

ファイル操作

キー 動作 備考
[:]+[e] ファイルをパス指定で開く
[:]+[q] 開いているファイルを閉じる

エディタ操作(ノーマルモード)

キー 動作 備考
[dd] カーソルがある行を切り取り [dd]の前に数値を入れて切り取る行数の指定が可能
[yy] カーソルがある行のコピー [yy]の前に数値を入れてコピーする行数の指定が可能
[P] カーソルがある行にペースト
[p] カーソルの下の行にペースト
[u] Undo
[^]+[r] Redo
[U] 行に対して行った変更の全てを取り消す
[v] 選択開始
[V] 行選択
[^]+[v] 矩形選択
[gv] 直前の選択範囲を再選択

検索/置換(ノーマルモード)

キー 動作 備考
[/]+[文字列] 前方検索
[?]+[文字列] 後方検索
[#] カーソル位置の単語を前方検索
[*] カーソル位置の単語を後方検索
[n] 次の候補
[shift]+[n] 前の候補
[gd] カーソル位置のローカル宣言を検索
[gD] カーソル位置のグローバル宣言を検索
[:]+[%s/from/to/g] ページ全体で置換 fromが検索語句,toが置換語句
[:]+[32,50s/from/to/g] 32〜50行目まで置換 gは繰り返し、cなら一回毎に確認

テキスト操作(ノーマルモード)

キー 動作 備考
[x] 1文字削除 Deleteキーと同じ
[X] 1文字削除 BSキーと同じ
[D] カーソル位置から行末まで削除
[s] 1文字削除して挿入モードへ切り替え
[S] 現在の行を削除して挿入モードへ切り替え
[r] カーソル位置の一文字だけ置換
[R] 置換モードに切り替え
[J] 現在の行と下の行をスペースありで連結
[gJ] 現在の行と下の行をスペースなしで連結
[~] 大文字/小文字に変換
[^]+[a] カーソル位置の数字をインクリメント
[^]+[x] カーソル位置の数字をデクリメント
[^]+[p] (挿入モード時)単語を後方向に検索し、補完する
[^]+[n] (挿入モード時)単語を前方向に検索し、補完する

インデント操作(ノーマルモード)

キー 動作 備考
[>] 現在の行をインデント
[<] 現在の行を逆インデント

画面分割(ノーマルモード)

キー 動作 備考
[:]+[sp]or[vsp] エディタウィンドウを縦に分割
[:]+[q] エディタウィンドウを閉じる
[:]+[qall] 全てのエディタウィンドウを閉じる

移動操作(ノーマルモード)

キー 動作 備考
[gg] 最初の行へ
[G] 最後の行へ
[数値]+[G] 指定した行へ
[0] 行の最初へ インデント無視
[^(Ctrlではない)] 現在の行の最初へ テキストの最初
[$] 行の末尾へ
[-] 前の行の最初へ
[+] 次の行の最初へ
[%] カーソル位置にある括弧に対応する括弧へ
[H] エディタの上端へ移動 Hの前に数値で上から数えた行へ
[M] エディタの中央行へ移動
[L] エディタの下端へ移動 Lの前に数値で上から数えた行へ
[ctrl]+[o] 前回のジャンプ位置へ戻る
[ctrl]+[i] 次回のジャンプ位置へ戻る
[z]+[↩︎] 現在の行をエディタの一番上に位置なるようにスクロールする テキストの最初
[w] 次の単語に移動
[b] 前の単語に移動
[e] 単語の末尾に移動
[W] 次の単語に移動 記号は無視
[B] 前の単語に移動 記号は無視
[E] 単語の末尾に移動 記号は無視
[f]+[一文字] カーソルを指定した文字まで移動 3fiで3つ目のiまで移動
[t]+[一文字] カーソルを指定した文字の左端まで移動 3tiで3つ目のiの左側まで移動

スクロール操作(ノーマルモード)

キー 動作 備考
[ctrl]+[b] 1画面上に移動
[ctrl]+[y] 1行上に移動
[ctrl]+[f] 1画面下に移動
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ScriptableObjectでシステム系クラスを実装する

はじめに

サムザップ #2 AdventCalendar 2020 の12/9の記事です。

株式会社サムザップ Unityエンジニアの尾崎です。

内容

UnityでScriptableObjectを使ってシステム系クラスを実装する手法を紹介します。

ScriptableObjectは一般的にはデータを効率よく格納するために使われます。
今回はシステム系クラスの実装に使ってみます。

ScriptableObjectの公式マニュアル

※ システムはいろんなクラスから呼び出される共通的なプログラムのことを表しています。サブシステムや基盤と呼ばれることもあります。
例えばゲームでは外部リソースやログ、サウンドなどを扱うクラスなどが該当します。

この手法のメリットとデメリット

メリット

  • Playモードでなくても動作する
  • MonoBehaviourと違いシーンに依存せずプロジェクトのどこからでも使える
  • アセットとして存在するので、他GameObjectのpublic変数(SerializeField)から参照できる
    • staticクラスやシングルトンいらず
  • インスペクタにツールを作ることができる
    • デバッグがしやすくなる
    • 開発効率化ツールを作りやすい
  • システムをインターフェース化(抽象クラス化)することで柔軟なシステム構成にできる
    • 実装を切り替えられる
    • クラス内の分岐を減らせる
  • Play中に変更した値がPlay終了後も残る

デメリット

  • UpdateなどMonoBehaviourのイベントを使った処理ができない
  • Play中に変更した値をPlay終了時に残さないためには工夫が必要

実装例 (シンプル版)

まずはシンプルにScriptableObjectを継承してシステム系クラスを実装するコード例を紹介します。
後半、ScriptableObjectとSerializeFieldを活用して複数の実装を簡単に切り替えるコード例を紹介します。

システム

[CreateAssetMenu]
public class SomeSystem : ScriptableObject
{
    /// <summary>
    /// 何らかのパラメーター
    /// </summary>
    [SerializeField]
    private string _someString;

    /// <summary>
    /// 何らかのメソッド
    /// </summary>
    public void SomeMethod()
    {
        Debug.Log("SomeSystem");
    }

    /// <summary>
    /// インスペクタのボタンからも実行できる何らかのメソッド
    /// </summary>
    private void Test()
    {
        Debug.Log("Test");
    }
}

ScriptableObjectのアセットを生成すると以下のようになります。
スクリーンショット 2020-12-04 20.35.22.png
Projectにアセットとして存在するのでプレイしなくてもインスペクタで操作できます。
パラメーター設定したり、Editor拡張でツールを作るのに便利です。

※ インスペクタにボタンを表示するEditor拡張については省略しています

システムを利用

public class SomeScene : MonoBehaviour
{
    [SerializeField]
    private SomeSystem _system;

    void Start()
    {
        _system.SomeMethod();
    }
}

スクリーンショット 2020-12-04 20.52.17.png
アセットなのでシステムを使いたいクラスのSerializeFieldで参照にセットできます。

代替手段との比較

MonoBehaviour

MonoBehaviourはシーンが変わると破棄されます。シーンをまたいでデータ保持したり処理するには不向きです。
ScriptableObjectはシーンに関わらず常に存在します。

staticクラス

UnityのEditorから見えません。
アセットとして存在するのでインスペクタに表示でき、ビジュアル的に設定やツール提供を行えます。
1つのクラス(実装)に依存することになります。

MonoBehviour + DontDestroyOnLoad (シングルトン)

プレイしてはじめてHierarchyに表示されインスペクタで操作できます。
そのため編集中にインスペクタで操作することはできません。
1つのクラス(実装)に依存することになります。

Prefab

PrefabはInstantiateして使う前提なのでインスタンス化するたびにデータのコピーが発生します。
またシステム系クラスとしてPrefabを利用するには、利用側からのアクセス方法に工夫が必要です。多くは上のシングルトンになるでしょう。

Instantiateしない場合は今回の手法と近しい性質を持っています。しかし、Unity標準ではPrefabはInstantiateしてHierarchyに配置して使うものなのでInstantiateしないというのは避けた方が良いでしょう。
Hierarchyに配置(画面に出す)のが前提なので、TransformやTag、Layerを標準要素として持っています。MonoBehaviourのライフサイクルイベントも扱います。

ScriptableObjectはHierarchyに配置しない前提なのでTransformなど余分な要素はありません。

Prefabに利点があるとすると、複数のコンポーネントを持てることです。

依存性注入との比較

DIコンテナの導入が必要で大掛かりになります。
ScriptableObjectはUntyの標準的な仕組みで簡単に使えます。

実装例 (切り替え版)

1つのシステムに対して複数の実装を行い、切り替えるコード例を紹介します。

システムのインターフェースを抽象クラスとして定義し、いくつかの具象クラスを作ります。
利用するクラスのSerializeFieldで使用する具象クラスのScriptableObjectアセットを選択します。

システム (抽象クラス)

public abstract class SomeSystem : ScriptableObject
{
    public abstract void SomeMethod();
}

各実装が持つべきメソッドやプロパティを定義した抽象クラスです。
interfaceにしたいところですが、インスペクタに表示するために抽象クラスを選択します。

システム (実装A)

[CreateAssetMenu]
public class SomeSystemA : SomeSystem
{
    public override void SomeMethod()
    {
        Debug.Log("SomeSystemA");
    }
}

SomeSystemの1つ目の実装
何かの機能の正式な実装を行います。

システム (実装B)

[CreateAssetMenu]
public class DebugSomeSystem : SomeSystem
{
    public override void SomeMethod()
    {
        Debug.Log("DebugSomeSystem");
    }
}

SomeSystemの2つ目の実装
SomeSystemAに対する別実装を行います。
例. デバッグコードに変更する、実行プラットフォームごとに使用ライブラリ変更する、チュートリアル用など

システムを利用

public class SomeScene : MonoBehaviour
{
    [SerializeField]
    private SomeSystem _system;

    void Start()
    {
        _system.SomeMethod();
    }
}

スクリーンショット 2020-12-07 午後5.42.05.png

SerializeFieldで参照するアセットを切り替えて、使用するシステム実装を選択します。
SomeSystem型で宣言しているのでSomeSystemのサブクラスのみが選択候補にリストアップされます。

大規模プロジェクトでは

上記で紹介したコード例は、小規模なプロジェクトで使いやすいものになっています。

しかし、大規模なプロジェクトになってくるとGameObjectの数が膨大になります。
そうすると各GameObject(コンポーネント)のSerializeField(public変数)に使用するシステムの参照をセットするというのは数が多すぎて現実的でなくなります。
大規模なプロジェクトでは使用する各システムの参照を保持するシステムを1つ作るのがおすすめです。

具体的にはこちらの記事で紹介しているサービスロケーターが使えます。
Unityでサービスロケーター(ServiceLocator)を活用する

サムザップの運用中タイトルではこれらを組み合わせて、デバッグしやすい大規模プログラムを構築しています。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

.NET 5での嬉しい変更点

こちらはDeNA 21 新卒 Advent Calendar 2020の10日目の記事です。

正式リリースされたばかりの.NET 5についての情報を自分の中でまとめたかったため、今回の記事を書きました。
主にAnnouncing .NET 5.0の中から個人的に嬉しい変更点を紹介・説明していきます。

.NET 5について

2020/11/20に.NET 5.0が正式リリースされました。

.NET系について簡単に整理すると、

  • 今後、.NET Frameworkにはバグ修正とセキュリティ修正が行われる
  • .NET Core 3.0の次期バージョンが.NET 5.0となる

となっています。

.NET Coreがバージョン4を飛ばした理由は、Introducing .NET 5に明記されています。

We’re skipping the version 4 because it would confuse users that are familiar with the .NET Framework, which has been using the 4.x series for a long time. Additionally, we wanted to clearly communicate that .NET 5 is the future for the .NET platform.

要約すると「長年使われている.NET Framework 4.x系と混同しちゃうかもしれないから」ということのようです。

.NET 5.0では、主にパフォーマンス向上が重点的に行われました。加えて、いくつか新機能も追加されたようです。
今回は、その中でも個人的に嬉しかった変更点についてピックアップしたいと思います。

HttpClientにJSON拡張メソッド追加

HttpClient extension methods

C#でhttp経由の通信を行う際に、よくHttpClientが使われます。
いままで、JSON形式のレスポンスを取得する際は、以下のように3ステップに分けて実装を行う必要がありました。

// .NET Core 3以前
var client = new HttpClient();
var response = await client.GetAsync("https://example.com/example.json"); // 1.レスポンスを受け取る
var jsonBody = await response.content.ReadAsStringAsync(); // 2.コンテンツを文字列として読み出し
var data = JsonSerializer.Deserialize<Message>(jsonBody); // 3.デシリアライズ

これだと、API通信をよく行うアプリでは、書き方が冗長で非常に面倒です。
.NET 5.0では、上記の実装がよりシンプルになりました。

// .NET 5.0
using System.Net.Http.Json;

var client = new HttpClient();
var data = await client.GetFromJsonAsync<Message>("https://example.com/example.json");

たったこれだけです。すごい!

加えて、忘れがちな以下の処理が実装されています。

  • ステータスコードの判別
    • Success(200~299)以外ではHttpRequestExceptionが発生
  • 文字コードの自動解析
    • デフォルトではUTF-8
    • Content-Typecharsetが含まれる場合は、その文字コードでデコード

ただし、レスポンスのContent-Typeapplication/jsonになっているかは判定していない模様です。text/plainで試したところ、エラー無しでJSONとしてパースされました。(3/31時点の記事では判定していたようですが、その後削除された?)

上記では、GETメソッドを例として書きましたが、POSTPUTでも同様の拡張メソッドが追加されました。(PATCHDELETEなどには追加されていない模様です)
もちろん、POSTPUTでは引数に何らかのオブジェクトを渡すとJSONシリアライズして送信してくれます。

HttpContent拡張メソッド

HttpContentクラスに拡張メソッドとしてReadFromJsonAsyncが追加されました。
こちらを使うことで、任意のHttpContentからJSONデータを扱えるようになります。

使用する場面としては、以下が挙げられます。

  • PATCHDELETEリクエストのレスポンスをJSONとして扱いたい
  • Success(200~299)以外のステータスコードでも、レスポンス内容をJSONとして扱いたい
var client = new HttpClient();
var response = await client.GetAsync("https://example.com/example.json");

if (response.IsSuccessStatusCode)
{
  // OK処理
  var data = await response.content.ReadFromJsonAsync<Message>();
}
else
{
  // NG処理
  var data = await response.content.ReadFromJsonAsync<ErrorInfo>();
}

JsonContent

HttpContentを継承したJsonContentクラスが、拡張メソッドの追加に伴って追加されました。

以下のようなコードを書くことで、Content-Type: application/jsonを設定済みのJsonContentを生成できます。(charsetに応じてエンコードしてくれます。デフォルトではUTF-8)

var content = JsonContent.Create<Message>(message); // Content-Type: application/json; charset=utf-8

.NET 5.0以前でも使いたい!

.NET 5.0に変更せずとも、.NET Core 2.0または.NET Framework 4.6.1以上であればSystem.Net.Http.Jsonパッケージを追加すれば、同様の機能が使用できます。

Windows ARM64対応

Windows Arm64

ARM64環境において、いままでの.NET Coreと.NET Frameworkはx86エミュレーション環境で動作していました。
.NET 5.0では、ARM64にネイティブ対応するため、より高速に動作するようになるようです。

ただし、Windows Forms及びWPFに関する機能は対応しておらず、.NET 5.0の今後のアップデートで提供するかもしれないみたいです。

M1 Mac(Apple Silicon)対応は?

Apple Siliconには、現状ネイティブ対応していません。ネイティブ対応は.NET 6で提供予定とのことです。(該当Issue)
加えて、Rosetta 2でのデバッグにも対応しておらず、コンパイルはできますがデバッグすると以下のエラーで落ちます。

Stack overflow.
   at System.Collections.HashHelpers.GetPrime(Int32)
   at System.Collections.Generic.Dictionary`2[[System.__Canon, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.__Canon, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].Initialize(Int32)
   at System.Collections.Generic.Dictionary`2[[System.__Canon, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.__Canon, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]..ctor(Int32, System.Collections.Generic.IEqualityComparer`1<System.__Canon>)
   at System.AppContext.Setup(Char**, Char**, Int32)

既にIssueは上がっており、.NET 5のうちにRosetta 2上で対応する予定のようです。
手元の環境(M1 Mac mini)でもデバッグできませんでした。(デバッグなしで実行すれば動作しました)

シングルファイルアプリケーション

Single file applications

シングルファイルアプリケーションとは、その名の通り単一の実行ファイルで動くアプリのことです。

いままでは、各種バイナリを一つのファイルに圧縮し、実行時にそれらを一時ディレクトリに展開することで、単一ファイルを実現していました。ですが、.NET 5.0から一時ディレクトリに展開せずとも動作するようになります。
ただし、.NET 5.0からは単一ファイルと言いつつ複数ファイルが出力されるパターンがあります。以下の表に構成とファイル数を記しておきます。

Windows x64 Linux x64 macOS x64
Self-Contained1 5 1 8
Framework-Dependent1 1 1 1

複数ファイルが出力される原因は、CLRなどのネイティブバイナリが別で出力されてしまうためです。これを解決するためには、ネイティブバイナリも含めて出力する必要があります。
これにはプロジェクトファイルのIncludeNativeLibrariesForSelfExtracttrueに設定することで実現できます。ただし、この方法を用いると.NET Core 3.1と同じように一時ディレクトリに展開してから実行されるようになります。(参考)

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0</TargetFramework>
    <IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
  </PropertyGroup>

</Project>

終わりに

.NET 5.0では、上述した機能の追加が行われました。加えて、パフォーマンスもかなり改善されました。RyuJITやGCレベルでのパフォーマンス改善のため、利用者側としては.NET 5.0に切り替えるだけでパフォーマンス向上が期待できます。
Apple Siliconにネイティブ対応するであろう.NET 6も楽しみです!

この記事を読んで「面白かった」「学びがあった」と思っていただけた方、よろしければ LGTM、Twitter や Facebook、はてなブックマークにてコメントをお願いします!

また DeNA 公式 Twitter アカウント @DeNAxTech では、 Blog 記事だけでなく色々な勉強会での登壇資料も発信しています。ぜひフォローして下さい!
Follow @DeNAxTech


  1. パブリッシュ方法。Self-Containedは.NETランタイムも含める。Framework-Dependentは.NETランタイムを除く。 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【翻訳】用語 | Discord.Net 公式ドキュメント

※この記事はDiscord Documentation 2.0.1 https://discord.foxbot.me/stable を勉強用に翻訳した記事です。リンク先は翻訳が完了次第、修正いたします。

ガイド / はじめに / 用語

用語

はじめに

オブジェクトのほとんどの用語は、0.9と1.0以降で同じです。
主な違いは、Serverが内部的にDiscordと一致するようにGuildと呼ばれるようになったことです。

実装固有のエンティティ

Discord.Netはコアライブラリと2つの異なる実装に分かれています。
Discord.Net.CoreDiscord.Net.Rest、そしてDiscord.Net.WebSocketsです。

bot開発者としては、Discord.Net.WebSocketsのみを使用することになりますが、それぞれの違いを知っておく必要があります。

Discord.Net.Coreは、DiscordのAPIをモデル化した一連のインターフェースを提供しています。
これらのインターフェイスは、Discord.Netのすべての実装で一貫しています。
また、実装に依存しないライブラリやアドオンを作成する場合には、
コアインターフェイスを利用して、すべてのプラットフォームで確実にアドオンが実行されるようにすることができます。

Discord.Net.Restは、DiscordのAPIのREST部分でのみ使用される具体的なクラスのセットを提供します。
この実装のエンティティには、Restという接頭辞が付けられています(例:RestChannel)。

Discord.Net.WebSocketは、主にDiscordのWebSocket APIやキャッシュに保持されるエンティティで使用される、具体的なクラスのセットを提供します。
botを開発する場合は、この実装を使用することになります。
すべてのエンティティには、Socketという接頭辞が付けられています(例:SocketChannel)。


このページの最終更新日 : 2018/10/1 5:44:33 AM+08:00 (UTC).

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【翻訳】botの作成を開始する | Discord.Net 公式ドキュメント

※この記事はDiscord Documentation 2.0.1 https://discord.foxbot.me/stable を勉強用に翻訳した記事です。リンク先は翻訳が完了次第、修正いたします。

ガイド / はじめに / 初めてのbot

初めてのbotをDiscord.Netで作成しましょう

Discord APIを使うため、まずは、ピンポンbot(簡単な受け答えをするbot)を作成しましょう。
このbotは「ping」というコマンドに反応します。
これについては後で詳しく説明して、より多様なコマンドを作成する予定ですが、
今のところはこれが良い出発点となるでしょう。

Discord Botの作成

botを作成する前に、まずDiscord Applications Portalからbot用のアカウントを作成する必要があります。
1. Discord Applications Portal にアクセスします。
2. 新しいアプリケーションを作成します。
3. アプリケーションに名前を付けます(これがbotの初期ユーザー名になります)。
4. 左側のSettingsで、Botをクリックします。
画像
5. Add Botをクリックします。
画像
6. ポップアップを確認します。
7. (任意) このボットを公開する場合はPublic Botにチェックを入れます。
画像

サーバーへのボットの追加

botは招待リンクを使うことができません。OAuth2フローで明示的に招待する必要があります。
1. Discord Applications Portalでボットのアプリケーションを開きます。
2. 左側のSettingsで、OAuth2をクリックします。
画像
3. OAuth 2 URL Generatorまでスクロールし、Scopesの中のbotにチェックを入れます。
画像
4. 生成されたURLをブラウザで開きます。
5. サーバーを選択します。
6. Authorizeをクリックします。


管理者権限を持つサーバーのみがこのリストに表示されます。

画像

Discordへの接続

プロジェクトをまだ作成しておらず、Discord.Netをインストールしていない場合は、ここで行います。

詳細については、インストール を参照してください。

非同期(Async)

Discord.Netは.NETのタスク ベースの非同期パターン(TAP)を広範囲に使用します。
ほとんどすべての操作が非同期です。
可能な限り、これらの操作は適切に確立された非同期コンテキストで待機することを強くお勧めします。

非同期コンテキストを確立するために、コンソールアプリケーションに非同期なmainメソッドを作成し、
新しい非同期なmainを呼び出すように静的なmainメソッドを書き直します。

public class Program
{
    public static void Main(string[] args)
        => new Program().MainAsync().GetAwaiter().GetResult();

    public async Task MainAsync()
    {
    }
}

こうすることで、プログラムが起動したあと、すぐに非同期コンテキストにジャンプします。
これにより、正しく非同期実装ができたかどうか心配することなく、後でDiscordへの接続処理を作成することができます。

警告
アプリケーションが非同期コンテキスト内で例外をスローすると、最初の非同期ではないメソッドまで遡ってスローされます。
最初の非同期でないメソッドはプログラムのMainメソッドなので、処理されていない例外はすべてそこにスローされ、アプリケーションがクラッシュします。
Discord.Netを使用すると、イベントハンドラの例外によってプログラムがクラッシュするのを防ぐことができますが、非同期のmainの例外によってアプリケーションがクラッシュすることがあります

ロギングメソッドの作成

Discordクライアントを作成して設定する前に、Discord.Netのログイベントを処理するメソッドを追加します。

できるだけ多くのログプロバイダをサポートするために、独自のLogMessageパラメータを持つLogイベントを使用して情報をログに記録します。このイベントの APIドキュメント を参照してください。

独自のロギングフレームワークを使用している場合は、ここでそれを呼び出します。
簡単にするために、ここではコンソールにのみロギングします。

この概念については、Logging Events/Dataを参照してください。

private Task Log(LogMessage msg)
{
    Console.WriteLine(msg.ToString());
    return Task.CompletedTask;
}

Discord Clientの作成

最後に、Discordへの新しい接続を作成します。

botを作成しているので、ソケットエンティティとともに DiscordSocketClient を使用します。
違いがわからない場合は、 用語 を参照してください。

新しい接続を確立するために、新しい非同期mainに DiscordSocketClient のインスタンスを作成します。
ほとんどの場合、これで問題なく動作します。
必要に応じて、オプションの DiscordSocketConfig を渡すことができます。

接続する前に、クライアントのLogイベントを作成したログハンドラに関連付ける必要があります。
Discord.NetのイベントはC#の他のイベントと同様に動作します。

次に、 LoginAsync メソッドを使用して、アプリケーションがDiscordにログインするための「トークン」を設定する必要があります。


developer portalからコピーしているものに注意してください。
トークンは、アプリケーションの「client secret」とは異なります。

画像

重要
botのトークンを使用すると、botに完全にアクセスできるようになるので、このトークンを他の人と共有しないでください!
botのソースコードを配布する予定がある場合には、このトークンを外部ソースに保管する必要があるかもしれません。

ここで、接続/再接続のロジックを開始する、クライアントの StartAsync メソッドを呼び出します。
このメソッドは、接続ロジックが開始されるとすぐにreturnすることに注意してください。

クライアントの状態に依存するメソッドはすべて、イベントハンドラに入ります。
つまり、クライアントの準備が完全に整うまでは、クライアントと直接対話するべきではありません

最後に、アプリケーションの実行時にasync mainメソッドがreturnしないようにします。
無限遅延や、コンソールからの読み込みのようなブロック方法によって待つことができます。

以下の行を追加できるようになりました。

private DiscordSocketClient _client;

public async Task MainAsync()
{
    _client = new DiscordSocketClient();

    _client.Log += Log;

    // トークンは非公開にするか、外部ソースから読み込むことを忘れないでください。
    // この例では、環境変数からトークンを読み取ります。
    // 環境変数の設定方法がわからない場合は、インターネット上で、
    // または構成からの読み取りなどの他の方法を使用して、詳細情報を参照できます。
    await _client.LoginAsync(TokenType.Bot, 
        Environment.GetEnvironmentVariable("DiscordToken"));
    await _client.StartAsync();

    // プログラムが終了するまで、このタスクをブロックします。
    await Task.Delay(-1);
}

この状態で、プログラムを起動し、Discordでbotがオンラインになるのを確認してください。

TIP
A supplied token was invalid.という警告が出た場合。または、ログインに問題があった場合は、
正しい認証情報を入力したかどうかを再確認し、それがトークンとは異なるクライアントシークレットでないことを確認します。

TIP
botの起動時にPlatformNotSupportedExceptionが出る場合は、ターゲットプラットフォームが.NETのデフォルトのWebSocketクライアントをサポートしていません。
この修正方法については、 インストール を参照してください。

「ping」の処理

警告
これはコマンドを作成する適切な方法ではないことに注意してください。
代わりに、 コマンドガイド セクションで説明されているように、ライブラリによって提供されるCommandServiceを使用します。

Discordへの接続方法を学習したので、ユーザが送信するメッセージの処理を開始できます。
まず始めに、私たちのbotは、内容が!pingと一致するメッセージを受け取り、「Pong!」という応答を返します。

新しいメッセージを受信したいので、関連付けるイベントは MessageReceived です。

プログラムで、MessageReceivedイベントのシグネチャに一致するメソッドを追加します。
このメソッドは、Task型を返し、 SocketMessage という1つのパラメータを受け取るメソッド (Func) である必要があります。
また、このメソッドではデータをDiscordに送信するため、非同期(async)としてフラグを立てます。

このメソッドでは、ifブロックを追加して、メッセージの内容が!pingと一致するかどうかを判断します。

この条件内で、メッセージを送信します。Pong!は、メッセージの送信元のチャンネルに返信されます。
チャンネルを見つけるには、messageパラメータのChannelプロパティを探します。

次に、このチャンネルにメッセージを送信します。
Channelオブジェクトは ISocketMessageChannel 型なので、 SendMessageAsync インスタンスのメソッドを呼び出すことができます。
メッセージの内容として、文字列「Pong!」を返信します。

これで、次の行が追加されました。

public async Task MainAsync()
{
    // ...
    _client.MessageReceived += MessageReceived;
    // ...
}

private async Task MessageReceived(SocketMessage message)
{
    if (message.Content == "!ping")
    {
        await message.Channel.SendMessageAsync("Pong!");
    }
}

これで最初のbotは完成です。
必要に応じて、これに続けて追加することもできますが、複数のコマンドを実行するbotには、以下に示すようなコマンド・フレームワークを使用することを強くお勧めします。

NOTE
参照用に、完成したプログラムを表示できます。

コマンドを使用したボットの構築

コマンドサービスの概要 では、コマンドサービス (高度なコマンド使用ができるサービス) を使用できるようにプログラムを設定する方法について説明します。

参照用に、この構造の 注釈付きの例 を表示します。

重要なのは、ボットの推奨デザインパターンは分離するべきだということです...

  1. プログラム(初期化とコマンドハンドラ)
  2. モジュール(ハンドルコマンド)
  3. サービス(永続的なストレージ、純粋な関数、データ操作)

このページの最終更新日 : 2018/10/1 5:44:33 AM+08:00 (UTC).

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【翻訳】ナイトリービルドのインストール | Discord.Net 公式ドキュメント

※この記事はDiscord Documentation 2.0.1 https://discord.foxbot.me/stable を勉強用に翻訳した記事です。リンク先は翻訳が完了次第、修正いたします。

ガイド / はじめに / インストール / ナイトリービルドのインストール

Discord.Net ナイトリービルドのインストール

Discord.Netが新しい機能を追加したセットを安定したバージョンにプッシュする前に、
ナイトリービルドを使用してコミュニティで機能を長期間テストします。
各ナイトリービルドは新しいコミットが行われるたびにAppVeyorによってコンパイルされ、MyGetフィードにプッシュされます。

重要
ナイトリーは一般的に安定しており、NuGet上の安定版ビルドよりも多くの機能がありバグの修正もされていますが、
開発中に大規模な変更やバグがあったりすることがあります。
これらのバグは発見されるとすぐに修正されますが、それでも注意してください。

MyGetによるインストール (推奨)

MyGetは通常、機能が完成してNuGetにプッシュされる前に、最新のプレリリースパッケージを公開するために多くの開発チームによって使用されます。

以下はDiscord.Netのフィードリンクです。

  • https://www.myget.org/F/discord-net/api/v3/index.json

使用するIDEによって、パッケージソースにフィードを追加する方法はさまざまです。

Visual Studio

1. ツール > NuGet パッケージ マネージャー > パッケージ マネージャー設定 の順に選択します。
画像
2. パッケージ ソース を選択します。
画像
3. + のアイコンをクリックします。
4. 以下のように希望の名前とソースを入力し、更新 をクリックします。
画像


「プレリリースを含める」 チェックボックスにチェックを入れることを忘れずに。
画像

ローカル NuGet.Config

botのデプロイやVisual Studio外での開発を予定している場合は、プロジェクト用のローカルNuGet設定ファイルを作成する必要があります。

これを行うには、プロジェクトが置かれているアプリケーションのルートにNuGet.Configという名前のファイルを作成します。

次のスニペットをこの設定ファイルに貼り付け、必要に応じてフィードを追加します。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <packageSources>
        <add key="discord.net ci feed" value="https://www.myget.org/F/discord-net/api/v3/index.json" />
    </packageSources>
</configuration>

その後、プロジェクトファイルを直接修正してバージョンを指定するか、パッケージマネージャ コンソールに次のコードを入力して(Install-Package Discord.Net -IncludePrerelease)パッケージをインストールします。

AppVeyor Artifactsからのインストール

はじめに述べたように、私たちはAppVeyorを使って自動テストを実行し、新しいビルドを公開します。
公開プロセスでは、AppVeyorのArtifact collectionにNuGetパッケージもアップロードします。

最新のビルドステータスはAppVeyorプロジェクト内にあります。

  1. プロジェクトには、前述の成果物を含む最新のビルドが含まれています。 画像
  2. artifacts コレクションには、*.nupkg形式でパックされた最新のパッケージがあり、ダウンロードして使うことができます。 画像

このページの最終更新日 : 2018/10/1 5:44:33 AM+08:00 (UTC).

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【翻訳】インストール | Discord.Net 公式ドキュメント

※この記事はDiscord Documentation 2.0.1 https://discord.foxbot.me/stable を勉強用に翻訳した記事です。リンク先は翻訳が完了次第、修正いたします。

ガイド / はじめに / インストール

Discord.Netのインストール

Discord.NetはNuGetパッケージマネージャを通じて配布されています。
このライブラリをインストールする方法として最も推奨されています。
あるいは、必要に応じてこのライブラリを自分でコンパイルすることもできます。

サポートされるプラットフォーム

Discord.Netのターゲットプラットフォームは .NET Standard both 1.3および2.0です。
つまり、.NET Coreの最新バージョンを使用してアプリケーションを作成することをお勧めします。
Windows固有のAPIやその他の制限がある場合は、.NET Framework 4.6.1以降をご利用ください。

警告
Monoでのこのライブラリの使用は、現在サポートされていません。
ライブラリのWebSockets実装に問題があることがわかっており、起動時にアプリケーションがクラッシュする可能性があります。

NuGetを使用したインストール

Discord.Netのリリースビルドは、NuGetの公式フィードに公開されます。

アドオンだけでなく、Discord.NetのデプロイもMyGetフィードに公開されます。
詳細は、ナイトリービルドのインストールを参照してください。

Visual Studioを使用する場合
1. bot用の新しいソリューションを作成します。
2. ソリューションエクスプローラで、botのプロジェクトの下にある「参照」を見つけます。
3. 「参照」を右クリックし、「NuGet パッケージの管理」を選択します。
画像
4. 「参照」タブでDiscord.Netを検索します。
5. Discord.Netのパッケージをインストールします。
画像

JetBrains Riderを使用する場合
1. bot用の新しいソリューションを作成します。
2. NuGetウィンドウを開きます(Tools > NuGet > Manage NuGet packages for Solution)。
画像
3. 「packages」タブでDiscord.Netを検索します。
画像
4. プロジェクトにパッケージを追加してインストールします。
画像

Visual Studio Codeを使用する場合

1. bot用の新しいプロジェクトを作成します。
2. .csprojにDiscord.Netを追加します。
<Project Sdk="Microsoft.NET.Sdk">

  <!--
  The following may differ depending on the latest version of 
  .NET Core or Discord.Net.
  -->

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Discord.Net" Version="2.0.0" />
  </ItemGroup>

</Project>


dotnet CLIを使用する場合
1. コマンドラインを開き、.csprojがある場所に移動します。
2. dotnet add package Discord.Netと入力します。

ソースからのコンパイル

Discord.Netをコンパイルするには、次のものが必要です。

Visual Studioを使用する場合

Visual Studioのインストール時には、.NET CoreとDockerのワークロードが必要です。

コマンドラインを使用する場合

追加情報

WebSocket がサポートされていないプラットフォームへのインストール

WebSocket がネイティブでサポートされていない古いオペレーティング・システム(例: Windows 7)上で、
Discord.Net ベースのボットを実行すると、接続時に PlatformNotSupportedException が発生することがあります。

これを解決するには、次のうちどちらかを実行します。
.NET Core 2.1以降をインストールするか、カスタムパッケージをインストールします。

.NET Core 2.1以降をインストールする
1. 最新の.NET Core SDKをダウンロードします。
2. .NET Coreを使用する既存のプロジェクトを作成または移動します。
3. <TargetFramework>タグを少なくともnetcoreapp 2.1に変更するか、ビルド時に--framework netcoreapp 2.1switch を追加します。

カスタムパッケージをインストールする

1. 次のパッケージをインストールまたはコンパイルします。
Discord.Net.Providers.WS4Net
Discord.Net.Providers.UDPClient (任意) これは、botが音声チャットを利用する場合にのみ必要です。
2. 既定のプロバイダではなく、これらのカスタムプロバイダを使用するように DiscordSocketClient を構成します。
・ これを行うには、クライアントに渡す DiscordSocketConfigWebSocketProviderプロパティおよび任意のUdpSocketProviderプロパティを設定します。
using Discord.Providers.WS4Net;
using Discord.Providers.UDPClient;
using Discord.WebSocket;
// ...
var client = new DiscordSocketClient(new DiscordSocketConfig 
{
    WebSocketProvider = WS4NetProvider.Instance,
    UdpSocketProvider = UDPClientProvider.Instance,
});


このページの最終更新日 : 2018/10/1 5:44:33 AM+08:00 (UTC).

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【翻訳】概要 | Discord.Net 公式ドキュメント

※この記事はDiscord Documentation 2.0.1 https://discord.foxbot.me/stable を勉強用に翻訳した記事です。リンク先は翻訳が完了次第、修正いたします。

ガイド / 概要

概要

Discord.Netを始めたいですか?

歓迎します!
ただし、このライブラリに入る前に、使用する言語についてある程度理解しておく必要があります。
このライブラリは、タスク ベースの非同期パターン(TAP)ポリモーフィズムインターフェイス
そしてより高度な話題を幅広く扱っています。
次に進む前に、これらのトピックをある程度理解しておいてください。
以上の説明を踏まえて、ご不明な点がございましたら、以下のリンクのDiscordにお気軽にアクセスしてください。

以下はサンプルとなります。
1. 公式サンプル
2. 公式テンプレート


盲目的にコピー&ペーストしないでください。
これらのサンプルは、テンプレートまたはガイドとして用意されています。

.NET/C#を使用するのは初めてですか?

このガイドに記載されているすべての例およびスニペット、すべてのAPIドキュメントは、C#で記述されています。

C#を初めて使う方には、このラッパーを使用するのは難しいと思われるかもしれませんが、心配しないでください。
オンライン上には、.NETの素晴らしい世界を始めるのに役立つ多くの資料があります。
ここでは、入門用の資料をいくつか紹介します。

まだご不明な点がある場合

Discord APIサーバの #dotnet_discord-net にアクセスしてください。
私たちに、あなたが何をしたのか、問題を詳しく説明してください。
そしてよろしければ、Hastebinにソースコードをアップロードしてください。


このページの最終更新日 : 2018/10/1 5:44:33 AM+08:00 (UTC).

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Odin Validatorのススメ

Unityでのゲーム開発では、多くのGameObjectやPrefabそしてAssetが必要で、それらは他のGameObjectやAssetへの参照を持っています。その参照関係を正しく管理・運用するのは大規模なゲーム・長期間のプロジェクトではとても難しいです。「間違って使っているAssetを削除してしまい、それを参照している部分が原因で不具合が発生した」という経験はありませんか。このような不具合を、どうやったら防げるでしょうか?

自分のおすすめは、Odin Validatorを用いたプロジェクト全体の自動検証です。Odi Validatorを用いれば「ここは必ず何かしらのAssetを参照しないといけないと設定する。もし参照していない場合は、エラーを表示する」という設定が実現できます。「間違って使っているAssetを削除してしまい、それを参照している部分が原因で不具合が発生した」という不具合を検知し、事前に防ぐことができます。

この投稿では、Odin Validatorを用いた、プロジェクトの自動検証を紹介します。

参照しているやつがなくなってしまった!

Unityでのゲーム開発では、多くのGameObjectやPrefabそしてAssetが必要で、それらは他のGameObjectやAssetへの参照を持っています。その参照関係を正しく管理・運用するのはとても難しいです。特に、大規模なゲームで長期間のプロジェクトとなれば、なおさらでしょう。

たとえば、次のようなScriptableObjectのクラスがあるとします。

using UnityEngine;

[CreateAssetMenu]
public class Enemy : ScriptableObject
{
    [SerializeField] private string enemyName;
    public string EnemyName => enemyName;

    [SerializeField] private Sprite sprite;
    public Sprite Sprite => sprite;

    [SerializeField] private GameObject model;
    public GameObject Model => model;

    // 略
}

次のようにEnemy000というScriptableObjectを作成し、Inspectorから参照を設定したとしましょう。

odin_scriptable_object_enemy_normal.png

さて、プロジェクトがすすみ、Assetも多くなってきて管理が難しくなってきたとします。本当は必要だったAsset(EnemySprite0)を誰かがうっかりプロジェクトから消してしまいました。その場合、次のようにSpriteの参照が「None」という状態になってしまいます。

odin_scriptable_object_enemy_none.png

もしこの状態で、ビルド・実行したらおそらく表示すべきSpriteが表示されない不具合が発生してしまいます。

このような「間違って使っているAssetを削除してしまい、それを参照している部分が原因で不具合が発生した」という不具合を経験した方も多いのではないでしょうか?ここで紹介した例は「Spriteが表示されない」でしたが、ゲームが進行不可能になったり、課金関連の不具合が発生したりと、もっと大きは不具合が発生してしまうことも考えられます。

このような不具合に、対応するために自分は、Odin Validatorを用いたプロジェクト全体の自動検証を導入することをおすすめします。

Odin Validatorとは?

Odin Validatorの前に、まずOdinを紹介します。

OdinOdin - Inspector and Serializer)はUnity向けのツール・ユーティリティカテゴリのライブラリです。非常に強力で豊富な機能を持っている人気のライブラリです。主な機能は次のとおりです。Inspectorの強化をはじめ、エディター拡張のユーティリティの提供します。Odin - Inspector and Serializerは、Asset Store・もしくはOdinの公式ページから購入が可能です。

odin_asset_store_screenshot.png

自分の、Odinのおすすめ機能は「Required属性」です。Required属性がついたSerialize対象のフィールドは、「必ず参照や値を設定する必要がある」という条件が追加されます。もし参照や値を設定していない場合、次のようにInspectorに設定されていないことをわかりやすく表示してくれます。

odin_scriptable_object_enemy_required_error.png

Odinを使って制約や条件を追加してみよう

Odin - Inspector and Serializerでは、多くの属性とそれに対応するValidatorを提供しています。これらの属性が付与されたフィールドは、ValidatorによりInspectorからの入力に条件や制約を加えることができます。

Odin Project Validatorでは、これらの属性を用いて付与された条件や制約を検証できます。この節では、Odin - Inspector and Serializerで提供されている属性とValidatorの使い方の一部を紹介します。

Required

Required属性をつけることで、参照の設定・値の入力を必須にできます。

次のようなScriptableObjectのクラスがあります。

using UnityEngine;
using Sirenix.OdinInspector;

[CreateAssetMenu]
public class ExampleRequire : ScriptableObject
{
    // 本当はプロパティを準備したほうがいいが、サンプルのためpublicフィールド
    [Required] public string stringValue;
    [Required] public GameObject gameObjectReference;
    [Required] public Transform transformReference;

    [Required] public int intValue;
    [Required] public double doubleValue;
}

このScriptableObjectのInspectorは次のようになります。stringValue、gameObjectReference、transformReferenceの上部に「stringValue is Required」などのエラーメッセージが表示されていることに注目してください。Required属性を付与することで、そのフィールドの参照・値を設定していない箇所をエラー表示できます。

odin_example_require_error.png

stringValueなどに参照・値を設定すると次のようにエラー表示が解消されます。

odin_example_require_valid.png

intやdoubleなどのフィールドには、Requiredをつけてもエラーメッセージが表示されないことに注意してください。

ValidateInput

ValidateInput属性をつけることで、指定したstaticメソッドを用いて検証できます。たとえば、次のコードは「ListなtargetAssetsが要素を1つ以上持っているか」を検証します。

using System.Collections.Generic;
using Sirenix.OdinInspector;
using UnityEngine;

[CreateAssetMenu]
public class ExampleValidateInput : ScriptableObject
{
    [Required] [ValidateInput(nameof(ValidateNotEmpty))]
    public List<GameObject> targetAssets;

    private static bool ValidateNotEmpty(
        List<GameObject> targetAssets,
        ref string message
    )
    {
        if (targetAssets.Count == 0)
        {
            message = "List is empty.";
            return false;
        }

        return true;
    }
}

このScriptableObjectのInspectorは次のようになります。targetAssetsは要素を1つも持っていないので、上部に「List is empty.」というエラーメッセージが表示されています。

odin_example_validate_input_error.png

targetAssetsが要素を1つ以上もつようになると、エラーメッセージが消えます。

odin_example_validate_input_valid.png

このようにValidateInputを使うと、自分で指定したstaticメソッドにより、独自の検証を行うことができます。

AssetsOnly と SceneObjectsOnly と ChildGameObjectsOnly

AssetsOnly、SceneObjectsOnly、ChildGameObjectsOnly属性をつけることで、参照の設定を次のように条件づけできます。

  • AssetsOnly・・・プロジェクトのAsset限定
  • SceneObjectsOnly・・・シーン上のGameObjectやComponent限定
  • ChildGameObjectsOnly・・・シーン上の自分の子GameObject限定

AssetsOnlyを指定しているフィールドに、シーン上のGameObjectやComponentは参照設定できません。SceneObjectsOnlyを指定しているフィールドに、プロジェクト上のAssetは参照設定できません。ChildGameObjectsOnlyはIncludeSelfという引数があり、自身も許可するかどうかを指定できます。

次のコードのtargetは、プロジェクト中のPrefabもシーン上のGameObjectも、どちらも参照設定できてしまいます。本来はどちらかしか設定できるべきではありません。

using UnityEngine;

public class Example : MonoBehaviour
{

    // プロジェクト中のPrefabもシーン上のGameObjectも
    // どちらも参照設定できてしまう
    [SerializeField] GameObject target;

    // プロジェクト中のAsset(Prefab)を指定すべき場合は、AssetsOnlyを
    // [SerializeField, AssetsOnly] GameObject target;

    // シーン上のGameObjectを指定すべき場合はSceneObjectsOnlyを
    // [SerializeField, AssetsOnly] GameObject target;
}

プロジェクト中のAsset(Prefab)を指定すべき場合はAssetsOnlyを、シーン上のGameObjectを指定すべき場合はSceneObjectsOnlyをつけることをおすすめします。

Odin Validatorを使ってみよう

RequiredやValidateInput、AssetsOnlyなどOdin - Inspector and Serializerの属性を使うことで、入力に条件や制約を加えそれを満たさなかった場合、Inspectorにエラー表示できます。しかしそのエラー表示に気がつかなかったとしたら、結局不具合が発生してしまいます。しかし、せっかくのわかりやすいエラー表示も、みのがしてしまったら意味がありませんね。そこでOdin Validatorの出番です。

Odin Validatorは、Odin - Inspector and SerializerのAddonです。Odin Validatorを用いると、Required属性などの条件を満たしていないAsestやPrefab、GameObjectが存在しないか検証し、その結果を次のようにわかりやすく確認できます。Odin Validatorは、公式サイトから購入することができます。

odin_project_validator_error_example.png

検証する範囲も、「今開いてるシーン」や「プロジェクト全体」など指定できます。また、その検証のタイミングをゲームプレイ実行時、ビルド実行時など指定できます。Odin Validatorを用いることで、「間違って使っているAssetを削除してしまい、それを参照している部分がきっかけでシーンが壊れてしまった」という不具合を回避できます。

Odin ValidatorはOdin公式ページから購入が可能です。この投稿の執筆時点では、2019年5月28日より前にOdinを購入したユーザーはOdi Validatorを無料でダウンロードできるようです。価格・購入については、最新の公式情報を参照してください。

変更を加えるたびに、いちいちプロジェクトにあるSceneやPrefab、ScriptableObjectすべてにエラー表示がないかを確認することは、現実的ではありません。Requiredなどを使っていたとしても、「使っていないと思ったAssetを削除したら、最近まったく更新していないシーンで使っていて、それが原因で不具合が発生してしまった」ということは起きてしまいます。

先に説明した通り、このような不具合は、Odin Validator使うことで回避できます。
@{Odin Project Validator}を使えば、RequiredやValidateInput、AssetsOnlyなどの属性がついている部分を自動で検証し、満たしていない箇所をわかりやすく表示してくれます。
いちいちプロジェクト全体を目視で確認する必要はありません。

OdinOdin Validatorをプロジェクトに導入し、メニューの「Tools > Odin Project Validator」を選択すると次のようなウィンドウが開きます。

odin_project_validator_top.png

一番上の「Scan Entire Project」を押すと次のような状態に遷移します。

odin_project_validator_entire_project.png

このウィンドウの右上にある「Run Scan Entire Project」というボタンを押下します。もしプロジェクトにRequiredやValidateInput、AssetsOnlyを満たさないものがあった場合、次のようにそれを一覧表示できます。

odin_project_validator_result_error.png

検証した内容を一覧できます。また、条件を満たしていなかったAssetやGameObjectをこのウィンドウから直接編集できます。

もしプロジェクトにRequiredやValidateInput、AssetsOnlyを満たさないものがなければ、次のようになります。

odin_project_validator_result_valid.png

「Scan Entire Project」はプロジェクト全体を検証します。Odin Project Validatorでは、これ以外にも次のような設定も提供しています。

名称 対象
Scan Entire Project プロジェクトにあるすべてのScene・Assetを検証する
Scan All Assets プロジェクトにあるすべてのAssetを検証する
Scan All Scenes プロジェクトにあるすべてのSceneを検証する
Scan Open Scenes 現在開いているSceneを検証する
Scan Scenes From Build Options ビルド対象に設定されているSceneを検証する

自分で設定を作ることも、既存の設定を変更することもできます。必要に応じて自分のプロジェクト運用にあった設定を試してみてください。

Automate Validation

Odin Validatorでプロジェクトの検証を行えば、参照や値の設定の間違いに気が付くことができます。しかし、ビルドする前にOdin Validatorでの検証を忘れてしまったら、結局不具合が発生してしまう可能性があります。Odin Validatorによる検証を忘れないために、Automate Validationを設定することをお勧めします。

メニューの「Tools > Odin Project Validator」からウィンドウを開き、右上部にある「Automate Validator」ボタンを押すと次のようなAutomate Validationウィンドウが開きます。

odin_automate_validation.png

ウィンドウ内のチェックボックスにチェックをつけて、Automate Validationを有効にするとそれぞれのタイミングで、指定した検証を実行できます。

  • On Play ・・・ デバック実行
  • On Build ・・・ ビルド実行
  • On Project Startup ・・・ プロジェクトの起動

odin_automate_validation_settings.png

また、ビルド実行時に検証を行うように設定し、もし設定した条件を満たさない箇所をみつけたら、ビルドを失敗させる、ということも可能です。こうすることで、ゲーム出荷・リリースする前に必ず条件を満たすことを保証することができます。

まとめ

この章では、Odin Validatorを紹介しました。

間違って使っているAssetを削除してしまい、それを参照している部分が原因で不具合が発生した」という不具合の経験がある人は多いのではないでしょうか。
こういう不具合を防ぐために、ぜひOdin Validatorを導入してみてください!

補足

各種バージョン

  • Unity 2019.4.8f1
  • Odin Inspector 3.0.2
  • Odin Validator 3.0.2
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

.NETCoreで画像をピクセル単位で扱う(SixLabors.ImageSharp)

.NETCoreで画像編集するときの定番ImageSharp、たまにしか使わないせいか、使い方を忘れていることがしばしばあるので備忘録として。
公式リポジトリ:GitHub: SixLabors/ImageSharp
動作確認:Version 1.0.2

1. ImageSharpの導入

nugetを使って導入します。WindowsでVisual Studio使っているならGUIで特に困らず導入できますが、
Linuxで.NETCoreで開発するときに困るのでコマンドラインも書いておきます。

dotnet add package SixLabors.ImageSharp

2. サンプルコード

2.1 画像の読み込み

C#
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;

string path = "filepath.jpg"
Image<Rgba32> img = Image.Load<Rgba32>(path);

2.2 画像のサイズを変更

C#
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Transforms;

img.Mutate(x =>
  x.Resize(targetSize.Width, targetSize.Height, new BicubicResampler(), false)
);

2.3 (複数処理)画像のサイズを変更してから画像を水平反転する

C#
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Transforms;

img.Mutate(x =>
  x.Resize(targetSize.Width, targetSize.Height, new BicubicResampler(), false).Flip(FlipMode.Horizontal)
);

2.4 ピクセル単位でアクセス

C#
using SixLabors.ImageSharp.PixelFormats;

Rgba32 px = img[x, y]
byte r = img[x, y].R
byte g = img[x, y].G
byte b = img[x, y].B
byte a = img[x, y].A

get / set 両方可能

3. 最後に

昔はこのライブラリを使ってピクセル単位のデータアクセスとか苦労しましたが、今はかなり楽になりました。
ライセンスもApache2.0ですし、色々な意味で使いやすいライブラリだと思います。
長らくalpha版として公開されていたこのライブラリ、リリースのたびに破壊的変更が入っている印象があります。
今後も破壊的変更がないという保証は無いので、使うときはバージョンに注意した方が良いかも知れないですね。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む