fbxファイルからAnimationClipを取得する

fbxファイルからAnimationClip取得する方法がググっても見付けきれなかったのですが
以下のように取得対象のfbxファイルのみをLoadした状態にして、FindObjectsOfTypeAllで取り出すという
力技で取得することができた。(もっとスマートな方法があるのかも...)

public List<AnimationClip> GetAnimationClipInFbx(string iFbxFileName)
{
	// 現在のLoad中のAnimationClipをUnloadし、何もLoadしていない状態にする
	Resources.FindObjectsOfTypeAll<AnimationClip>().ToList().ForEach(x => Resources.UnloadAsset(x));

	// AnimationClipを取り出したいFbxファイルをLoadする
	string aFbxFilePath = Application.dataPath + "/Resources/Models/" + iFbxFileName;
	aFbxFilePath = aFbxFilePath.Replace(".fbx", "").Replace("\\", "/");
	Resources.LoadAll(aFbxFilePath);

	// 現在のLoad中のみのAnimationClipを返す
	var aAnimationClips = Resources.FindObjectsOfTypeAll<AnimationClip>();

	List<AnimationClip> aAnimationClipList = new List<AnimationClip>();
	foreach (var aAnimationClip in aAnimationClips)
	{
		if (Regex.IsMatch(aAnimationClip.name, @"^Take\d*"))
		{
			aAnimationClipList.Add(aAnimationClip);
		}
	}
	return aAnimationClipList;
}

Editor拡張でキーバインドをカスタマイズ

Unityのデフォルトショートカットキーが扱いにくいので
自分用のEditor拡張Scriptを作成してみた

変更または新たに定義したいショートカットキー

  • ゲームオブジェクトの作成、削除、有効/無効
  • Prefab(プレハブ)のApply
  • コンソール出力のクリア
  • 再インポート
  • 各Windowのフォーカス変更

Menu Item

ショートカットキーはMenu Item関数を利用して定義できる
&#g で Alt + Shift + g みたいな感じで。
本家:http://docs-jp.unity3d.com/Documentation/ScriptReference/MenuItem.html

% : Command or Windows key
# : Shift key
& : Alt key

以下のスクリプトを /Assets/Editor に配置する

using UnityEditor;
using UnityEngine;
using System.Text.RegularExpressions;
using System.Reflection;

public class UnityKeyRemapEditor : EditorWindow
{
	// オブジェクトの共通Openコマンド
	[MenuItem("KeyRemap/Open &o")]
	static void KeyRemapOpen()
	{
		foreach (var aObj in Selection.objects)
		{
			var aObjPath = AssetDatabase.GetAssetPath(aObj);
			if (Regex.IsMatch(aObjPath, @"^.*\.unity"))  { EditorApplication.OpenScene(aObjPath); }
			if (Regex.IsMatch(aObjPath, @"^.*\.cs"))     { AssetDatabase.OpenAsset(aObj); }
			if (Regex.IsMatch(aObjPath, @"^.*\.prefab"))
			{
				PrefabUtility.InstantiatePrefab(aObj);
				CommonExecuteMenuItem("Window/Hierarchy");
			}
		}
	}

	// ゲームオブジェクト作成
	[MenuItem("KeyRemap/CreateGameObject &g")]
	static void KeyRemapCreateGameObject() { CommonExecuteMenuItem("GameObject/Create Empty"); }

	// ゲームオブジェクトの削除
	[MenuItem("KeyRemap/Delete &h")]
	static void KeyRemapDelete()
	{
		foreach (var aObj in Selection.objects)
		{
			GameObject aGameObject = aObj as GameObject;
			if (aGameObject) { GameObject.DestroyImmediate(aGameObject); }
		}
	}

	// ゲームオブジェクトの有効、無効
	[MenuItem("KeyRemap/ActiveToggle &t")]
	static void KeyRemapActiveToggle()
	{
		foreach (var aObj in Selection.objects)
		{
			GameObject aGameObject = aObj as GameObject;
			if (aGameObject) { aGameObject.SetActive(!aGameObject.activeSelf); }
		}
	}

	// PrefabのApply
	[MenuItem("KeyRemap/PrefabApply &a")]
	static void KeyRemapPrefabApply() { CommonExecuteMenuItem("GameObject/Apply Changes To Prefab"); }

	// コンソール出力のクリア
	[MenuItem("KeyRemap/ClearConsoleLogs &c")]
	private static void ClearConsoleLogs()
	{
		var type = Types.GetType("UnityEditorInternal.LogEntries", "UnityEditor");
		var info = type.GetMethod("Clear", BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
		info.Invoke(null, new object[0]);
	}

	// 再インポート
	[MenuItem("KeyRemap/Reimport &r")]
	static void KeyRemapReimport() { CommonExecuteMenuItem("Assets/Reimport"); }

	// フォーカス変更
	[MenuItem("KeyRemap/Scene #&s")]
	static void KeyRemapScene() { CommonExecuteMenuItem("Window/Scene"); }

	[MenuItem("KeyRemap/Scene #&g")]
	static void KeyRemapGame() { CommonExecuteMenuItem("Window/Game"); }

	[MenuItem("KeyRemap/Inspector #&i")]
	static void KeyRemapInspector() { CommonExecuteMenuItem("Window/Inspector"); }

	[MenuItem("KeyRemap/Hierarchy #&h")]
	static void KeyRemapHierarchy() { CommonExecuteMenuItem("Window/Hierarchy"); }

	[MenuItem("KeyRemap/Project #&p")]
	static void KeyRemapProject() { CommonExecuteMenuItem("Window/Project"); }

	[MenuItem("KeyRemap/Animation #&a")]
	static void KeyRemapAnimation() { CommonExecuteMenuItem("Window/Animation"); }

	[MenuItem("KeyRemap/Console #&c")]
	static void KeyRemapConsole() { CommonExecuteMenuItem("Window/Console"); }

	static void CommonExecuteMenuItem(string iStr)
	{
		EditorApplication.ExecuteMenuItem(iStr);
	}
}

UnityのGUIで日本語が文字化けする

MacでUnity開発してるとGUIで日本語が「????」に文字化けしました。
原因は、C#ファイルが「UTF-8 BOM無し」になってるためでしたので、「UTF-8 BOM有り」に変換すると解消されます。
ちなみにWindowsでは、文字化けせずにコンパイルエラーになります

抽選確率の計算

あまりC#に精通していない自分ですが
抽選確率の計算ロジックを書いたのでメモ。

public class Program
{
    public static void Main ()
    {
        Dictionary<string, int> aFruitsDict = new Dictionary<string, int>();
        aFruitsDict.Add("リンゴ", 10);   // 確率 0.3%
        aFruitsDict.Add("ミカン", 20);   // 確率 0.6%
        aFruitsDict.Add("バナナ", 50);   // 確率 1.4%
        aFruitsDict.Add("マンゴ", 500);  // 確率 14.0%
        aFruitsDict.Add("パイン", 1000); // 確率 27.9%
        aFruitsDict.Add("イチゴ", 2000); // 確率 55.9%

        Dictionary<string, int> aResultFruitsDict = new Dictionary<string, int>();
        aResultFruitsDict.Add("リンゴ", 0);
        aResultFruitsDict.Add("ミカン", 0);
        aResultFruitsDict.Add("バナナ", 0);
        aResultFruitsDict.Add("マンゴ", 0);
        aResultFruitsDict.Add("パイン", 0);
        aResultFruitsDict.Add("イチゴ", 0);

        for (int i = 0; i < 100000; ++i) {
            string aResultFruit = lottery(aFruitsDict);
            aResultFruitsDict[aResultFruit] += 1;
        }

        foreach (string aFruit in aResultFruitsDict.Keys) {
            float aLotteryValue = (float)aFruitsDict[aFruit] / aFruitsDict.Values.Sum() * 100;
            Console.WriteLine("{0}(想定確率 {1, 4}%): {2}", aFruit, aLotteryValue.ToString("F1"), aResultFruitsDict[aFruit]);
        }
    }

    public static string lottery(Dictionary<string, int> iFruitsDict)
    {
        // 抽選を確定させる基準値を生成
        Random aRandom = new System.Random();
        int aBasicValue = aRandom.Next(0, iFruitsDict.Values.Sum());

        // フルーツ名配列を作成する
        string[] aFruits = new string[iFruitsDict.Keys.Count];
        iFruitsDict.Keys.CopyTo(aFruits, 0);

        // フルーツ名配列をシャッフル
        string[] aShuffleFruits = aFruits.OrderBy(i => Guid.NewGuid()).ToArray();

        string aChoice = "";
        int aCurrentValue = 0;
        foreach (string aFruit in aShuffleFruits)
        {
            aCurrentValue += iFruitsDict[aFruit];
            aChoice = aFruit;
            if (aCurrentValue > aBasicValue)
            {
                break;
            }
        }
        return aChoice;
    }

__10万回実行した結果__

リンゴ(想定確率  0.3%): 274
ミカン(想定確率  0.6%): 614
バナナ(想定確率  1.4%): 1391
マンゴ(想定確率 14.0%): 13934
パイン(想定確率 27.9%): 27990
イチゴ(想定確率 55.9%): 55797

とりあえず、実行結果の回数が想定している確率にそこそこ合ってる感じ

コルーチンについて

シングルスレッド環境でマルチスレッドっぽく動作できる
コルーチンについての理解度が低かったので挙動を確認してみた

コルーチンの特徴

コルーチンについては、通常の関数のとは異なり、
処理を途中で中断して任意のタイミングで中断部分から処理を再開できることが特徴です。
yield return が呼び出されると、その時点で関数は処理を中断して呼び出し元へ制御を返します。

使いどころ

遅延評価をする場合に使用するので、ゲームでは状態シーケンス管理になどに使えそう。
バトルに入る前では、フィールドオブジェクト生成 → バトル初期値設定 → プレイヤー、敵オブジェクトを生成
→ バトルシーントランザクション → バトル開始 みたいな感じで

Unityで挙動を検証

UnityのStartCoroutineで実装した場合
    public void Start ()
    {
        StartCoroutine("StartCoroutine");
    }

    public void Update ()
    {
        Debug.Log ("Update");
    }

    public IEnumerator StartCoroutine()
    {
        Debug.Log("Start-1");
        Debug.Log("--------");
        yield return StartCoroutine("NestCoroutine");
        Debug.Log("--------");
        Debug.Log("Start-2");
        yield return null;
        Debug.Log("Start-3");
    }

    public IEnumerator NestCoroutine()
    {
        Debug.Log("Nest-1");
        yield return null;

        Debug.Log("Nest-2");
        yield return null;

        Debug.Log("Nest-3");
    }

__実行結果__

Start-1
--------
Nest-1
Update.
Update.
Nest-2
Update.
Nest-3
--------
Start-2
Update.
Start-3
Update.
Update.
:
:
c#のみで実装した場合
    public IEnumerator mStartCoroutine;

    public void Start ()
    {
        mStartCoroutine = StartCoroutine();
    }

    public void Update ()
    {
        if (mStartCoroutine != null) {
            bool aStatus = mStartCoroutine.MoveNext();
            if (aStatus) {
                Debug.Log ("StartCoroutine continue");
            } else {
                Debug.Log("StartCoroutine done.");
                mStartCoroutine = null;
            }
        }
        Debug.Log ("Update.");
    }

    public IEnumerator StartCoroutine()
    {
        Debug.Log("Start-1");
        yield return null;
        Debug.Log("Start-2");
        yield return null;
        Debug.Log("Start-3");
    }

__実行結果__

Start-1
StartCoroutine continue
Update.
Nest-2
StartCoroutine continue
Update.
Nest-3
StartCoroutine done.
Update.
Update.
:
:

Qiitaのtokenを取得するまでのメモ

Qiita API v2 documentation - Qiita:Developer

1. Qiitaのユーザー設定からパスワードを設定しておく
2. auth API(/api/v1/auth)をPOSTで叩くことで取得できるのでshellから、以下を実行。
% python
>>> import json
>>> import requests
>>> user = 'fujimisakari'
>>> password = 'xxxxxxxxxx'
>>> post_data = {'url_name': user, 'password': password}
>>> ret = requests.post(url='https://qiita.com/api/v1/auth', data=post_data)
>>> ret.content
'{"url_name":"fujimisakari","token":"xxxxxxxxxxxxxxxxxxxxxxxxxx"}'

※ requestsがない場合は、pip install requests する

Emacs(Mac)でC#の開発する その2

その1の[Emacsで開発できるようにする]で紹介してるの以下の情報が変ったので再度投稿。

  • imenu
  • シンタックスチェック(flymake)
  • コードスタイルチェック(flymake)

Emacsで開発できるようにする(続)

imenu

csharp-modeに機能が入ってるっぽいけど動かないので保留にしてたけど
以下を編集することでcsharp-mode.elのimenuが動くようになった

@@ -5561,8 +5561,8 @@ Key bindings:
         ;; `imenu-generic-expression'; imenu will do a "generic scan" for you.
         ;; csharp-mode uses the former method.
         ;;
-        (setq imenu-create-index-function 'csharp-imenu-create-index)
-        (imenu-add-menubar-index)))
+        (setq imenu-create-index-function 'csharp-imenu-create-index)))
+        ;; (imenu-add-menubar-index)))
シンタックスチェック(flymake)

その1で以下を紹介したけど、C#知ってる人見てもらったら
毎回コンパイルしてたら実用的ではないよってアドバイスをもらった

#! /bin/bash

/usr/bin/mcs $1
exe_file=`echo $1 | sed -e "s/\.cs/.exe/"`
ls ${exe_file} && rm ${exe_file}

true

っで、コンパイルせずシンタックスチェックできるようにした

#! /bin/bash

/usr/bin/mcs --parse $1

true

ただ、シンタックスチェックはできるけどエラーまで見てくれないみたい
たとえば、
System.Console.WriteLine("hoge"); → flymakeでは正常扱い
tem.Console.WriteLine("hoge"); → flymakeでは正常扱い
Sys tem.Console.WriteLine("hoge"); → flymakeでは異常扱い

コードスタイルチェック(flymake)

stylecop CLIっというのを見つけたけど、windows用で動かなかったけど動くかすことができた。
準備として、MonoDeloperインストールしておく必要がある

導入手順↓
1. 以下のサイトからバイナリをダウンロード
http://sourceforge.net/projects/stylecopcli/files/Binary/

2. MonoDeloperのアドオンマネージャーからsytlecopを追加
メニューのアドオンマネージャー→gallery →IDE extentions →sytlecop supportからインストール

3. flymakeに設定
これで StyleCopCLI.exe が動作できるようになるので
flymakeのチェックスクリプト、検出パターン、不要defadviceをコメントをする

・チェックスクリプト

#! /bin/bash

# /usr/bin/mcs --parse $1
/usr/bin/mono ~/.emacs.d/bin/StyleCopCLI.exe -cs $PWD/$1 -out /tmp/stylecop.log
cat /tmp/stylecop.log | sed -e "s/\"//g" | sed -e "s/>/ /g" | grep -v "SA1600\|SA1633"

true

・エラー文言の検出パターン追加

(add-to-list 'flymake-err-line-patterns
             '("LineNumber=\\([0-9]+\\) .* RuleId=\\(.+\\)</Violation" nil 1 1 2))

ということで、現在のcsharp-mode設定は以下になってます
https://github.com/fujimisakari/.emacs.d/blob/master/inits/41-csharp.el