サークル獏の佐藤敏 Unityとか備忘録

サークル獏の佐藤敏がUnityとかで知ったTipsを書いておく備忘録です。

PhotoShopで作業中に連番を付けてセーブするスクリプト

そのまんま、「PhotoShopで作業中に連番を付けてセーブするJavaScriptのコード」です。
ファイル名に制限があったりpsd・jpg・pngしか対応していなかったり拡張子で中身判定する大雑把な奴だったりしますが、とりあえず置いておきます。

バグもあるかもしれません。。。
Windowsでしか動作確認していません。

save.jsとか適当な名前で保存してPhotoShopスクリプトフォルダに放り込んでください。
(例えば C:\Program Files\Adobe\Adobe Photoshop 2020\Presets\Scriptsとかですかね)
 
導入方法自体は色々なところで解説されているのでそちらに譲ります。
自分はF11キーを押すとこのスクリプトが起動するようにしています。

ソースコード

function saveFunc()
{
	try {
		//ファイル名を取得
		docName = activeDocument.fullName.fsName.toString();
	} catch(msg) {
		alert(msg + " 一度どこかにセーブしてから実行して下さい");
		return;
	}

	//ファイル名の長さ
	l = docName.length;

	if(l <= 8) {
		alert("ファイル名が短すぎます");
		return;
	}

	//拡張子を取り出す
	ext = docName.substr(l-3);
	if(ext.toLowerCase() == "psd")
		saveMode = "psd";
	else if(ext.toLowerCase() == "jpg")
		saveMode = "jpg";
	else if(ext.toLowerCase() == "png")
		saveMode = "png";
	else //jpgでもpngでもなければとりあえずpsdに
		saveMode = "psd";

	//後ろから5~8番目の文字を取得(例えばpics_006.psdなら_006の部分になる)
	c = docName.charCodeAt(l-8); //8番目
	c2 = docName.charCodeAt(l-7); //7番目
	c3 = docName.charCodeAt(l-6); //6番目
	c4 = docName.charCodeAt(l-5); //5番目

	//_034 とか _421 とか _200 とかの並びになってるか?(正規表現とか使うほうがかっこいいけど、
	//そこまでやるほどのスクリプトじゃないのでif連打)
	if(c == 95 && (c2 >= 48 && c2 < 48+10) && (c3 >= 48 && c3 < 48+10) && (c4 >= 48 && c4 < 48+10)) {
		//数字部分の文字列を数字に変換して
		n = eval(docName.substr(l-7,3));

		do {
			//1を足す
			n += 1;
	
			//桁数に応じて新しいファイル名を作り
			if(n > 999) {
				alert("1000以上には対応していません");
				return;
			} else if(n < 10) {
				fileName = docName.substr(0,l-7) + "00" + n + "." + saveMode;
			} else if(n < 100) {
				fileName = docName.substr(0,l-7) + "0" + n + "." + saveMode;
			} else {
				fileName = docName.substr(0,l-7) + n + "." + saveMode;
			}
			saveFileObj = new File(fileName);
		} while(saveFileObj.exists);
		//結果的に既存のファイル名とかぶってしまったらさらにnを1増やしてやり直す

	} else {
		//末尾が_002.psdとかになってないなら000からスタート
		fileName = docName.substr(0,l-4) + "_000" + "." + saveMode;
		saveFileObj = new File(fileName);
	}

	//セーブするときの設定
	psdSaveOpt = new PhotoshopSaveOptions;
	psdSaveOpt.alphaChannels = true;
	psdSaveOpt.annotations = true;
	psdSaveOpt.embedColorProfile = true;
	psdSaveOpt.layers = true;
	psdSaveOpt.spotColors = true;

	jpgSaveOpt = new JPEGSaveOptions();
	jpgSaveOpt.quality = 8;// 画質 最高画質で12?
	jpgSaveOpt.embedColorProfile = false;
	jpgSaveOpt.formatOptions = FormatOptions.OPTIMIZEDBASELINE;

	pngSaveOpt = new PNGSaveOptions();
	pngSaveOpt.interlaced = false;

	//保存する
	try {
		if(saveMode == "psd")
			activeDocument.saveAs(saveFileObj,psdSaveOpt,false,Extension.LOWERCASE );
		else if(saveMode == "jpg")
			activeDocument.saveAs(saveFileObj,jpgSaveOpt,false,Extension.LOWERCASE );
		else if(saveMode == "png")
			activeDocument.saveAs(saveFileObj,pngSaveOpt,false,Extension.LOWERCASE );
	} catch(msg) {
		alert(msg + " 保存できませんでした");
	}
}



//本体実行
saveFunc();

導入方法を解説されているサイトの例

miyabix.com

UMotion ProでさっくりとHumanoidの既存アニメ修正

痛恨のミス

www.asset-sale.net
このセールのときに「Very Animationがあるしなあ」+そのときあまりに金がなかったので見送っていたのですが、結局サイバーセールで27$+税でUMotion Proを買うハメになったのは私です。
 

UMotion Proの導入

アセットストアからUMotion Pro購入、すべてインポート。
メニューの「Window」→「UMotion Editor」からPoseEditorとClipEditorを表示させましょう。
Pose Editorは画面右、ClipEditorは画面下のほうがおすすめだとか。
ウチの環境ではVery Animationが併存しているんで、画面には両方映っていたりします。

f:id:VinSatoo:20191214140613p:plain
ここから「ClipEditor」と「PoseEditor」選択
f:id:VinSatoo:20191214140719p:plain
ClipEditor
f:id:VinSatoo:20191214140858p:plain
Pose Editor
  

ファイル作成

ClipEditorの「File」→「New Project」→「Humanoid」
f:id:VinSatoo:20191214141019p:plain
f:id:VinSatoo:20191214141138p:plain
適当な場所に保存。
直接animをいじるVery Animationとかと違い、UMotion Proでは別個に編集するためのファイルができるわけですね。
今回はsit_03というanimをいじるので、sit_03.assetという名前にしてみました。
(animが入っているフォルダとは別のほうがいいと思います。混乱するので)
 

Humanoidキャラを割り当てる

anim編集のときに使いたいHumanoidキャラをPose Editorのここにドラッグ・アンド・ドロップします。

f:id:VinSatoo:20191214141609p:plain
このスクショは合成です

そうするとボーンを登録していいか的なメッセージがでるんで、「Create configuration」を押しましょう。
f:id:VinSatoo:20191214142016p:plain

Blend Shapeについて聞かれるほうはお好みで。
今回は足をいじるだけなので「Don't Add」にしました。
f:id:VinSatoo:20191214142032p:plain
 

既存animをインポート

Clip Editorの「File」→「Import Clips」
そしてインポートしたいanimをドラッグ・アンド・ドロップ。
f:id:VinSatoo:20191214142203p:plain

f:id:VinSatoo:20191214142400p:plain
このスクショは合成です

んで、importを押します。
ちょっと時間がかかって、
f:id:VinSatoo:20191214143005p:plain

無事インポートされました。
 

設定変更

「Pose Editor」の「Auto Key」を「Generate」にします。
これをやらないと、編集作業してエクスポートするときに「unapplied modification」がどうこう文句を言われてにっちもさっちもいきませんでした……
(Offで手動で変更を反映させていく方法もあると思うのですが、どうもうまくいきませんでした)
f:id:VinSatoo:20191214143841p:plain
 

設定変更(状況によって切り替えるもの)

「Pose Editor」の「Mirror Editing」を「On」に。
左右対称でないモーションのときは「Off」ですね。
f:id:VinSatoo:20191214144608p:plain

「Pose Editor」の「Pivot」は回転に関する設定。
「Local」にするか「Global」にするかは状況次第です。
真横に回転したい、正面に回転したい……といったときはGlobal。
関節の流れにそって回転したい……といったときはLocalですね。
今回はGlobalから始めてみます。
f:id:VinSatoo:20191214144823p:plain

また、ボーン表示があまりにデカくてキャラがどうなっているか見づらいケースがあります。
そのときはPose EditorのDisplayから「Bone Style」を「Stick」に。
ただ状況によっては「Solid」のままのほうが操作しやすいでしょう。
ここは編集中にうまいこと切り替えを。
f:id:VinSatoo:20191214143310p:plain
 

レイヤー作成

ではUMotion Proのキモ、レイヤーを作ります。
Clip Editorの右下に何かが重なったようなボタンがあるので押すとレイヤーの設定が登場。
その左上にあるボタンを押してレイヤーを追加します。
f:id:VinSatoo:20191214143640p:plain

名前とかはそのままでいいでしょう。
f:id:VinSatoo:20191214143455p:plain

Layer 1ができました。
モトのanimのキーがゴチャゴチャと並んだ「Base Layer」と違い、何もなくてスッキリしていますね。
このLayer 1に、修正を加えていきます。
f:id:VinSatoo:20191214144202p:plain
 

編集開始

こちらsit_03という名前のanim。
とあるアセットに入っていたのですが、このキャラに当てると足とかがおかしいです。
多様なキャラに対応するモーションのHumanoidでは避けて通れないことですが……
なので修正を加えていきます。
f:id:VinSatoo:20191214144335p:plain

足の上部のボーンをクリックして選択。
普通にUnityでオブジェクトを回転させているときと同じように、この丸いツールをグリグリして回転させていきます。
Mirror EditingがONになっているので、右足をいじるだけで左足も動きます。
f:id:VinSatoo:20191214145335p:plain

ボーンの表示設定を変更するとこんな感じ。
結果が見やすいのはStickですね。
f:id:VinSatoo:20191214145323p:plain

同様に、足首がヘンだったのを修正しました。
f:id:VinSatoo:20191214145743p:plain
 

確認してみる

ここで動きを確認してみます。
Clip Editorの右下の再生ボタンを押すと、キャラが動きます。
f:id:VinSatoo:20191214145907p:plain

第一フレームの足の回転を修正しただけなのに、すべてのフレームで足の回転が修正されています。
f:id:VinSatoo:20191214154702g:plain

これがレイヤー機能の強みですね。

  • 「Base Layerにモトのモーション」+「Layer 1の第一フレームに、ボーンの回転をズラすポーズ」がある。
  • → 第一フレームだけしかないモーションは、すべてのフレームにおいて同じポーズになる。
  • 合成結果は「すべてのフレームで修正されたモーション」になる。

 
なお、確認のときは「Pose Editor」「Display」の「Bones」が「Off」のほうが見やすいかもしれませんね。
f:id:VinSatoo:20191214150053p:plain
 

エクスポート

では、修正したanimを書き出してみましょう。
ClipEditorから「File」「Export」「Current Clip」を押すと……
f:id:VinSatoo:20191214150742p:plain

出力先のフォルダが指定されてないゾ、と怒られます。
f:id:VinSatoo:20191214150338p:plain

なので出てきたSetting画面から出力先フォルダを指定してあげましょう。
モトのanimフォルダと同じにすると上書きされてしまうので、「やっぱりモトのが良かった」とか「もう一度モトのから作り直したい」とかいうときに困ります。
別のフォルダにしたほうが賢いでしょうね。
f:id:VinSatoo:20191214150648p:plain

あらためてClipEditorから「File」「Export」「Current Clip」。
f:id:VinSatoo:20191214150742p:plain

出力されるとこんなメッセージが出ました。
f:id:VinSatoo:20191214151004p:plain
 

後始末と確認

Pose Editorから「Clear」を押して、UMotion Proの制御下からキャラを解放します。
f:id:VinSatoo:20191214151122p:plain

Animatorタブに出来上がった新しい「sit_03」を放り込んで……(編集前の、モトのファイルを放り込まないように注意)
f:id:VinSatoo:20191214151850p:plain

Animation Controllerを持ったキャラを選択、Animationタブから「sit_03」を選択して再生ボタンを押せば確認ができますね。
きちんとおかしなとこが直っているようです。
f:id:VinSatoo:20191214151955p:plain
 

その後

IKを使った編集はどうだとか第1フレームとそれ以外で修正量を変えたい場合は、とか……
発展的内容もありますが、ひとまずはこうしてボーンの回転を微妙に修正できるだけで「買ったのに死にモーション」だったものが有益なモーションになります。
それだけでも効果は大きいのではないかと。

参考にさせて頂いたサイト

うちよりこちらを見たほうがわかりやすいかもしれません。
kyusque.hatenablog.com
www.asset-sale.net

Unity(のHumanoid)でクソみたいなAnimationControllerをできるだけ使わずにやる

Unityで3DキャラをアニメさせるとなるとまずはAnimationControllerのノードをいじってヒーコラってことになるんでしょうが、端的に言ってクソです。
UIデザイナーさんがUIをアニメーションさせるくらいならいいとして、3Dアニメーションでノードの線をアレコレ伸ばしてまともに使う気になれません。

そんなわけでPlayable APIを使っていきましょう。
ぜんぶ自力でもいけるんでしょうけど、今や便利なライブラリがあるのでそれを使わせていただきます。
gologius.hatenadiary.com
 
 

準備1(主にダウンロード編)

まず新規で3Dのプロジェクトを作り、

assetstore.unity.com
我らがTaichi Character Pack君に登場してもらいます。
 
 
キャラとしてもデキが良くてHumanoid形式のanimもたくさん入っているいいアセットです。けっこう重いですが、Unity アセットストアからダウンロードしてインポート。
(VRoidとかを使うとしても基本は変わりません。VRM importerとかを入れる必要は出てきますが)


つづいてMotion Playerをダウンロードします。こちらにアクセスして「Clone or Download」→「Download Zip」でzipをダウンロード。
github.com

f:id:VinSatoo:20191204003155p:plain
解凍して、Unityのプロジェクトフォルダに放り込みましょう。

f:id:VinSatoo:20191203200726p:plain
こんなんなりました。MotionPlayerについては別のフォルダを作ったほうが良かったかもしれないですな。
 
 

準備2(主にキャラ配置編)

f:id:VinSatoo:20191203201830p:plain
「TaichiCharacterPack」→「Resources」→「Taichi」→「Models Mecanim」フォルダからとりあえず「m01_jersey_00_l」を選び、

f:id:VinSatoo:20191203201135p:plain
Scene画面にドラッグ&ドロップして、

f:id:VinSatoo:20191203201242p:plain
ひとまずPositionとかは全部0にします。

f:id:VinSatoo:20191203201947p:plain
後ろを向かれているとさみしいのでMain Cameraの位置と回転と「Field Of View」についてはこんな数字にしましょうか。

ちなみに「Field Of View」は結構キャラの印象が変わるので注意しましょう。キャラがでかすぎる・小さすぎるというときも、カメラ自体を動かすか、FoVを変えるかでだいぶ見た目は変わります。
udasankoubou.blogspot.com
 
 

準備3(いやなAnimation Controller編)

Animation Contollerはイヤですが、多分これを設定しないと色々不便です。
形式だけ、設定しましょう。

f:id:VinSatoo:20191203202922p:plain
「MyAssets」→「controller」というフォルダを作りました。そこの中で右クリックして、「Create」→「Animation Controller」を選びましょう。
とりあえず「taichi_controller」という名前にしました。

f:id:VinSatoo:20191203203456p:plain
んで、Hierarchyから「m01_jersey_00_l」を選んで、Taichiにさっき作ったカラッポのAnimation Controllerを設定してあげます。
 

ほんで普通?のやり方に寄り道

f:id:VinSatoo:20191203203655p:plain
「Animator」タブを選んで、

f:id:VinSatoo:20191203203817p:plain
Taichiくんのアセットから「m01@back_20」の左の矢印をクリック、出てきた「back_20」を……

f:id:VinSatoo:20191203203943p:plain
Animatorタブにドラッグ&ドロップするとこうなりますんで、

f:id:VinSatoo:20191203204047p:plain
Unityのプレイボタンを押すと無限にバックするTaichi君が誕生。その上でAnimation Controllerを色々細かくいじっていくのが普通???のやり方ですね。
 
 

だがそれはクソだ。でもまだ準備は続く……

f:id:VinSatoo:20191203204336p:plain
先ほどと同じように、今度は「m01@idle_00」の左の矢印をクリック、「idle_00」をクリックして……

f:id:VinSatoo:20191203204459p:plain
Ctrlキーを押しながらDキーを押します。「複製」という意味です。
するとanimファイルだけ抜き出せます。

f:id:VinSatoo:20191203205232p:plain
同じことを繰り返して、他に「idle_20」「jump_10」「kick_20」というanimファイルを抜き出しました。

f:id:VinSatoo:20191203205520p:plain
んで「Resources」「anim」という名前のフォルダを作って、4つのanimファイルを移動させます。
※重要! ここで「Resources」はスペルミスすると動かないので慎重に。
 
 

ひとまずMotionPlayerをつけてあげる

f:id:VinSatoo:20191203210418p:plain
Taichi君(m01_jersey_00_l)に、Motion Playerをつけてあげます。
Add Componentを押して、Motionとか打ってあげれば「Motion Player」が出てくるはず。
出てこない場合は、「準備1」のMotion Playerを入れるどっかでミスっていると思います……

f:id:VinSatoo:20191203210548p:plain
こんな感じになったかと思います。
 
 

maskの作成

f:id:VinSatoo:20191203211411p:plain
つづけて、「MyAssets」フォルダに「mask」フォルダを作って「Create Avater Mask」とします。mymaskなど適当な名前をつけて保存。

f:id:VinSatoo:20191203211959p:plain
この「mymask」をプロジェクトからクリックして「Humanoid」を開くと最初は全部が緑になっていますが、腕・手先だけ緑で残し、他はクリックして赤にしてしまいましょう。

f:id:VinSatoo:20191203211705p:plain
で、Taichi君のMotionPlayerの「upper Mask」にさっき作ったmymaskを指定しておきましょう。
 
f:id:VinSatoo:20191203215401p:plain
mymaskが設定できました。
 
  

プログラマーっぽくなってくるよ

f:id:VinSatoo:20191203205718p:plain
「Game Object」→「Create Empty」でカラのオブジェクトを作り、名前を「GameObject」から「GameManager」に。

f:id:VinSatoo:20191203205957p:plain
「Create And Add」を押すと、ビミョーにタイムラグがあって、

f:id:VinSatoo:20191203210154p:plain
こんな風にGameManagerコンポーネントが付けられます。まだカラッポなので、歯車アイコンをクリック、Edit Scriptで中身を書いてあげましょう。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameManager : MonoBehaviour
{
	// Start is called before the first frame update
	MotionPlayer motionPlayer;
	GameObject taichi;

	float tick; //時間記録

	AnimationClip[] clipAr; //読み込んだanimの配列
	
    void Start()
    {
		taichi = GameObject.Find("m01_jersey_00_l"); //Taichiを探す
		if (taichi == null) {
			Debug.Log("Taichi が見つかりません。");
		} else {
			motionPlayer = taichi.GetComponent<MotionPlayer>(); //MotionPlayer保存

			clipAr = Resources.LoadAll<AnimationClip>("anim"); //アニメ読み込み
		}
	}

    // Update is called once per frame
    void Update()
    {
		if (taichi != null) {
			tick += Time.deltaTime;
			if (tick >= 1.0f) { //1秒ごとに
				tick -= 1.0f;
				var newAnim = clipAr[UnityEngine.Random.Range(0, clipAr.Length)]; //ランダムにアニメ選ぶ
				motionPlayer.play(newAnim, 0, true);
				Debug.Log(newAnim); //何を表示しているかデバッグ画面に表示
			}
		}
    }
}

こんな感じでいいと思います。
 
 

実行してみると?

Taichi君が1秒ごとに4つのモーションをランダムに動かします。
ここまでの作業で分かったとおり、Animation Controllerに手動で登録することはしていません。
形式的に登録した「back_20」を削除してもちゃんと動きます(実行直後だけおかしくなりますが)。

もちろんif (tick >= 1.0f)  を if( Input.GetKeyDown(KeyCode.Space)) とかに書き換えれば1秒ごとではなくスペースキーを押すたびにモーションが切り替わります。Animation Controllerの面倒なノード設定からはこれでオサラバ!? です。
 
 

さらにMotionPlayerの魅力はここから

GameManagerスクリプト

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameManager : MonoBehaviour
{
	// Start is called before the first frame update
	MotionPlayer motionPlayer;
	GameObject taichi;

	float tick; //時間記録

	AnimationClip[] clipAr; //読み込んだanimの配列
	
    void Start()
    {
		taichi = GameObject.Find("m01_jersey_00_l"); //Taichiを探す
		if (taichi == null) {
			Debug.Log("Taichi が見つかりません。");
		} else {
			motionPlayer = taichi.GetComponent<MotionPlayer>(); //MotionPlayer保存

			clipAr = Resources.LoadAll<AnimationClip>("anim"); //アニメ読み込み
			motionPlayer.play(clipAr[0], 0, true); //修正
		}
	}

    // Update is called once per frame
    void Update()
    {
		if (taichi != null) {
			tick += Time.deltaTime;
			if (tick >= 1.0f) { //1秒ごとに
				tick -= 1.0f;
				var newAnim = clipAr[UnityEngine.Random.Range(0, clipAr.Length)]; //ランダムにアニメ選ぶ
				motionPlayer.play(newAnim, 1, true); //修正 上半身の動き
				Debug.Log(newAnim); //何を表示しているかデバッグ画面に表示
			}
		}
    }
}

こんな風にしてみましょう。

f:id:VinSatoo:20191203214438p:plain
Taichi君が上半身だけの謎ダンスを始めたはずです。
これがMotionPlayerの魅力で、上半身と下半身で別の動きをさせることができます(avater maskの設定によってはさらに色々な動きもできますが、腕だけmaskするのが一番効果的だと思います)。
 
特に「上半身の動きが派手すぎる」「下半身の動きが派手すぎる」市販のモーションをうまく活用するのに使えるでしょう。

<<

UnityでSerializedObject target has been destroyed.が出る場合の対策?(Unity 2019.1、まだ未確定)

Unityで

SerializedObject target has been destroyed.
UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)

というエラーが出ることがある。

f:id:VinSatoo:20191103175633p:plain
アワレにもエラーが出て髪の毛がぶっとんだ状態で実行ストップ

検索すると「再起動すりゃ治る」とか身も蓋もない解決策ばかりが引っかかり、実際再起動したりOpen Sceneをしなおしたりすると大体直る。

teratail.com


しかし割と頻繁に起こり、原因が不明で非常に困っていた。

Sprite atlasに関するバグ報告の発見

で、エラーメッセージでなく「UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)」で検索しているとき、別のUnity不具合報告を見つけていた。

issuetracker.unity3d.com
Unity2019.1βとか2αでSprite atlasを扱い、インスペクタでatlasを表示したままPlayボタンを押すとエラーが出るという不具合報告である。(2019.2の正式版で治った?)

でもウチはSprite atlasをいじっていないときだしエラーメッセージも違うから関係ないよなあ、と思っていたのが、ふと思い立ってMaterialをインスペクタに表示した状態でゲームをPlayしてみると……

f:id:VinSatoo:20191103180403p:plain
こんな風に表示しながら

かなり高い確率でそのMaterialのキャラが画面に登場したとき「Serialized Object has been destroyed.」が出ることが判明。逆にMaterialをインスペクタに表示していない時はここ数日エラーが出ていない。

というわけで

  • 「Serialized Object has been destroyed.」が出るときは、Materialやらを選択せずにゲームをPlay
  • 2019.2の最新版にすればこのバグは取れているかもしれない? ←2019/11/6 追記 2019.2.11fにしてもダメでした……

DynamicBoneを(複数の)GameObjectに追加する拡張

若干仕様を追い切れているか自信がないのですが、とりあえず載せておきます。
DynamicBoneを(複数の)GameObjectに追加します。

ソースコード

using UnityEngine;
using UnityEditor;
using System.Collections.Generic;

public class DynamicBoneUtil {
	[MenuItem("GameObject/AddDynamicBone", false, 10)]
	public static void AddDynamicBone(MenuCommand command) {

		var list = Selection.gameObjects;

		foreach (var obj in list) {
			if (obj != command.context)
				continue;

			var col = obj.AddComponent<DynamicBone>();
			col.m_Root = obj.transform;

			var parent = obj.transform.parent;
			if (parent != null) {
				var headCol = parent.GetComponent<DynamicBoneCollider>();
				if (headCol != null) {
					if (col.m_Colliders == null)
						col.m_Colliders = new List<DynamicBoneColliderBase>();
					col.m_Colliders.Add(headCol);
				}
			}
		}
	}
}

これをDynamicBoneUtil.csとして、Editorフォルダ作ってその中に保存すると

f:id:VinSatoo:20190708201612p:plain
動作画面
こんな風に右クリックメニューにAddDynamicBoneが追加されます。
 

動作

・DynamicBoneを追加
・もしそのオブジェクトの親がDynamicBoneColliderを持っていたら、DynamicBoneのCollidersにソレを追加
(headにColliderをつけて、その子の髪にDynamicBoneをつけるパターンをイメージ)

col.m_Stiffness = 0.5f; とかを追加すれば DynamicBoneの初期パラメータをスキにいじることもできますね。
 

詰まったところ&注意点

フツーにやると「3つのGameObjectを選択していた場合、3つのオブジェクト全てに対して3つDynamicBoneがつく」みたいな動作に。command.contextをチェックすることで回避できるみたいです。
 
ただこれが、右クリックでなくメニューバーから呼び出した場合nullになっていることがあるなんて話もあるのでそこはご注意を。普通は右クリックから呼び出すと思いますが……
How to execute MenuItem for multiple objects once. - Unity Answers

Unity2018.3系からBlendShapeが100越えとかマイナスとかとれるようになっていたらしい……

いまのUnityはBlendShapeが100越えとかマイナスとかとれる!
(※スライダーから変更するのではダメで、直接値を入力するか、スクリプトから変更する)
めちゃくちゃ前から欲しかった機能なのに知らなかった。。。

使えると何がうれしいのか

f:id:VinSatoo:20190622172254p:plain
こんな風に200にしたり
f:id:VinSatoo:20190622172355p:plain
Close系のBlendShapeをマイナスにして目を見開いたり
 
2018.3系からはNested Prefabとか色々変わっているところがあるとはいえ、人によってはこの機能だけで2018.3系に変える意味があるでしょう。
 

設定変更が必要な場合も

なお、2018.2以前で作ったプロジェクトを持ってきてコンバートした場合はここの設定を変えないといけない模様。

f:id:VinSatoo:20190622172029p:plain
2018.2系から持ってきたプロジェクトの場合? こんな風にメッセージ

f:id:VinSatoo:20190622172115p:plain
Edit→ProjectSettings→PlayerのOtherSettingsからここのチェックを外す(2018.3でNewProjectしたら最初から外れてる?)
 

やれやれ

これでもうBlender上でくぱぁモーフの値をマイナスにして閉じモーフを別途作ったりする必要がなくなりますね。。。
ただいつものことですが、2018.2系から2018.3系にプロジェクトを持ってくる場合はバックアップをとってからにしましょう。
壊れたら泣くに泣けない。

複数のfbxファイルからアニメーション(anim)を抽出&余計なAnimationEventを削除

tsubakit1.hateblo.jp
こちらのテラシュールブログさんの記事にfbxからanimを抽出する方法は掲載されているんですが、複数のfbxからアニメーション(anim)を抽出するパターンを。
あと、最初からAnimationEventがついていると再生時にエラーが出て鬱陶しいのでそれを削除するコードも入っています。

f:id:VinSatoo:20190502043534p:plain
AnimationEventが必要ない場合、放置するとこんなエラーが出るので。

やり方

Unity プロジェクトの「Editor」フォルダに追加
→Project画面のfbxファイルを(複数)選択し右クリック
→「AnimExtractor」を実行すると同じフォルダにanimが抽出されます。

大量のファイルを処理すると結構重いんで、最初は10個以下くらいから様子見してください。20~30個とかanimが含まれるfbxなら個別にやったほうがいいかもしれません。

コード

using UnityEngine;
using UnityEditor;
using System.IO;

public class AnimExtractor
{

	static string tempExportedClip = "Assets/tempClip.anim";
	static AnimationEvent[] emptyAnimationEventArray = new AnimationEvent[0];

	[MenuItem("Assets/AnimExtractor")]
	static void AssetCopy() {


		Object[] selectedAsset = Selection.GetFiltered(typeof(Object), SelectionMode.Assets);
		foreach (var go in selectedAsset) {
			ExtractFunc(go);
		}

		AssetDatabase.Refresh();
	}

	private static void ExtractFunc(Object obj) {
		string path = AssetDatabase.GetAssetPath(obj);
		string currentFolder = Path.GetDirectoryName(path);

		var animations = AssetDatabase.LoadAllAssetsAtPath(path);
		var originalClips = System.Array.FindAll<Object>(animations, item =>
			  item is AnimationClip
		);

		foreach(var clip in originalClips) {
			copyClip(clip, currentFolder);
		}
	}

	private static void copyClip(Object clip, string folder) {
		if (clip.name.StartsWith("__preview__"))
			return;

		var instance = Object.Instantiate(clip);
		AnimationClip copiedAnim = instance as AnimationClip;
		AnimationUtility.SetAnimationEvents(copiedAnim, emptyAnimationEventArray);  //AnimationEventを削除しなくていいならこの行を削除

		AssetDatabase.CreateAsset(copiedAnim, tempExportedClip);
		string exportPath = folder + "/" + clip.name + ".anim";

		if(File.Exists(exportPath))
			exportPath = AssetDatabase.GenerateUniqueAssetPath(exportPath);

		File.Copy(tempExportedClip, exportPath, true);
		File.Delete(tempExportedClip);
	}
}