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

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

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

VRoidキャラをBlenderに持ち込んでデベソ(意味深)にする

VRoidキャラをデベソ(意味深)にする

VRoidキャラをBlenderに持ち込んでデベソ(意味深)のBlendShapeを作ります。
一度理解すればオヘソ以外のどこでも出っ張らせることができるようになります。
Unityに持ち込んだ後はエディタ上からいじるのはもちろん、スクリプトからいじることも可能です。
f:id:VinSatoo:20200825124846p:plain

  • VRoidキャラ自体の作り方、VRoid StudioからのVRMの書き出し方は知っているものとします。

Blender2.80にデータを持ち込む

  • BlenderVRM Importerを導入します。
  • そしてVRoid Studioから書き出したVRMを読み込みます。

以下のようなブログが分かりやすいのではないかと思います。
https://hoshimi12.com/?p=20779

Blender 2.80(Ver2.81以降ではうまく行かない) Vroidモデルを読み込む方法【忘備録メモ】古いバージョンのBlenderを新しいバージョンと共存させる方法stockmoneyphoto.wordpress.com

余計なものを隠して視点調整

  • ※このBlenderには日本語表示を入れています。
  • 操作しづらいので、skeltonとcolliderを隠します
  • そのあとでBody.bakedをクリックして選択します。

f:id:VinSatoo:20200825121633p:plain

こんな感じで、体の周りにオレンジ色の線が出るはずです。
f:id:VinSatoo:20200825121838p:plain:w360
あと、見やすいように視点を動かしたほうがいいと思います。

  • マウスのホイール回転で拡大縮小。
  • ホイールを押しながら動かすと視点が回転。
  • SHIFTとホイールを同時に押しながら動かすと視点が上下左右に動きます。

BlendShape(シェイプキー)を作る

初期設定では右のほうに並んでいるたくさんのアイコンから、緑色の▽を選択します。
f:id:VinSatoo:20200825122537p:plain
シェイプキーを作るため、[+]ボタンを2回押します。
「Basis」と「Key 1」ができ、「Key 1」が選択状態になっています。
f:id:VinSatoo:20200825122651p:plain
初期設定では画面左上にある「オブジェクトモード」というところを押し、「編集モード」に切り替えます。
f:id:VinSatoo:20200825122746p:plain

編集モードの準備

最初は頂点がたくさん選択されてしまっているので、何もないところをクリックして選択を解除します。
f:id:VinSatoo:20200825122953p:plain:w360
大量のオレンジ色の線や点が消えます。
f:id:VinSatoo:20200825124055p:plain
右の方にたくさん並んでいるアイコンから、一番下のピンクの丸のようなアイコンをクリックします。
f:id:VinSatoo:20200825123214p:plain:w360
ナンチャラTops~を選んで、「選択」を押します。
(※なお制服でなくワンピースとかだと違う表示が出ていると思いますが、上半身の服と思われるものを選択してください。)
f:id:VinSatoo:20200825123405p:plain:w360
制服の上半身だけが選択されました。
f:id:VinSatoo:20200825123542p:plain
キーボードの「H」を押します。
制服の上半身が隠されます。
f:id:VinSatoo:20200825123721p:plain

デベソにする最終準備

画面の左上、「編集モード」の右にあるコレをクリックします。
f:id:VinSatoo:20200825124257p:plain
注意深くオヘソをクリックします。
点が1つだけ黄色になります。
失敗したら、何もないとこをクリックしてやりなおしましょう。
f:id:VinSatoo:20200825124338p:plain
画面上あたりにあるこれをクリックして設定します。
f:id:VinSatoo:20200825124658p:plain

デベソにする

[Alt]を押しながら「S」を押します。

  • そのままだと周囲のポリゴンどころか体全体が動いてしまいます。
  • なのでマウスのホイールをどんどん上に回して下さい。最初は何も起こりませんが、そのうち白い丸が画面に出てきます。
  • ちょうどいい小ささになるまで上にホイールを回しましょう。
  • そしてマウスを動かすとオヘソが飛び出したり凹んだりします。

ちょっと調整が難しいので、失敗したらまた繰り返して調整しましょう。
f:id:VinSatoo:20200825124846p:plain

FBXとして書き出す

Blenderのメニューの「ファイル」から「エクスポート」→「FBX]を選びます。
f:id:VinSatoo:20200825124902p:plain
設定は大体これで上手くいくはずです。
f:id:VinSatoo:20200825125013p:plain:w360

  • 「アーマチュアをクリック。
  • つづけて[SHIFT]を押しながら「メッシュ」をクリック。この2つだけが選択状態になります。
  • いちおう「スケールを適用」は「すべてFBX」に。不要かもしれませんが……
  • あとは、パソコンの好きな場所に書き出してください。

Unityに読み込む

Unityで適当なプロジェクトを作り、Assetsフォルダに先ほどのFBXをコピーします。
それをドラッグ&ドロップでシーンに配置し、Body.bakedを選択してみるとたしかにBlendShapeができています。
(※初期状態ではBlendShapesが折りたたまれていて見づらいので注意)
f:id:VinSatoo:20200825125335p:plain:h360
スライダーを100にすると、たしかにデベソになっていました。
f:id:VinSatoo:20200825125417p:plain
これでVRMキャラの色々な部位を出っ張らせることができますね!

スクリプトからいじりたいときは?

SkinnedMeshRenderer.SetBlendShapeWeightを使います。
UnityでBlenderのシェイプキーを使う – Unityの使い方|初心者からわかりやすく

Unityに読み込むときの多少の注意

Humanoid用アニメーションを使いたい場合は、Unity上でFBXファイルをクリックし、インスペクタの「Rig」タブから「Humanoid」を選んでから配置する必要があります。
f:id:VinSatoo:20200825125447p:plain
Blend Shape NormalsはNoneのほうがいいようです。
f:id:VinSatoo:20200825125621p:plain:w360

【Unity初心者ちょっと脱出くらいの人に】雑だけどまあ動くSimpleSoundManager クラス

「ゲームで音を鳴らしたい!」でもUnityの画面から音声1つずつポチポチ登録するのはしんどい! って人に。

staticで呼び出していたり雑であまり上品な書き方じゃないですが、とりあえず動くとは思います。
使った報告とか著作権表記とかはなくて大丈夫です。改変も自由にしてください。

導入方法

  • 以下のコードをコピペしてSimpleSoundManager.csとして保存。
  • Unity上でGameObject→Create Emptyで空のオブジェクトを作る。名前は適当でいいです。
  • それにSimpleSoundManagerをAdd Componentします。
using System;
using System.Collections.Generic;
using UnityEngine;

//音の種類。必要に応じてテキトーに増やしたり減らしたり
public enum AudioSourceType {
  BGM,
  UI,
  SHOT,
  DAMAGE,
  VOICE,
  MAX
}

public class SimpleSoundManager : MonoBehaviour {
  static Dictionary<string, AudioClip> dic = new Dictionary<string, AudioClip> ();
  static AudioSource[] audioSourceAr = new AudioSource[(int) AudioSourceType.MAX];

  void Start () {
    LoadFromFolder ("sounds"); //音声データはResources/soundsフォルダに入れておく

    for (var i = 0; i < (int) AudioSourceType.MAX; i++) {
      audioSourceAr[i] = gameObject.AddComponent<AudioSource> ();
    }
  }

  private void LoadFromFolder (string folderName) {
    var clipAr = Resources.LoadAll<AudioClip> (folderName);
    foreach (var item in clipAr) {
      dic[item.name] = item;
    }
  }

  public static void PlaySound (AudioSourceType type, string key) {
    AudioClip result;
    if (dic.TryGetValue (key, out result)) {
      var source = audioSourceAr[(int) type];
      source.clip = result;
      source.loop = (type == AudioSourceType.BGM); //BGMだけはループする
      source.Play (); //同じ音が複数鳴るようにしたいならPlayOneShot
    } else {
      Debug.Log ("音声ファイル" + key + "が見つかりません。");
    }
  }

  public static void StopSound (AudioSourceType type) {
    var source = audioSourceAr[(int) type];
    source.Stop ();
  }


  public static void StopAllSounds () {
    for (var i = 0; i < (int) AudioSourceType.MAX; i++) {
      audioSourceAr[i].Stop ();
    }
  }

    //val=1fで最大音量 0.5fで半分とか
  public static void SetVolume (AudioSourceType type, float val) {
    var source = audioSourceAr[(int) type];
    source.volume = val;
  }

}

実際の音の鳴らし方

まずResourcesフォルダの中にsoundsフォルダを作り、そこに音声データを入れておきます。
使うときは、音を鳴らしたいときに

SimpleSoundManager.PlaySound (AudioSourceType.BGM, "snd勝利曲");
SimpleSoundManager.PlaySound (AudioSourceType.UI, "snd敵復活");

とかしてください。

SimpleSoundManager.SetVolume (AudioSourceType.BGM,0.5f); //BGMの音量を半分に
SimpleSoundManager.StopAllSounds (); //全部の音を止める

とかもできます。

よりレベルアップするためには?

Resourcesフォルダに大きなデータをぶちこんでおくと起動時に時間がかかります。
アセットバンドル」を使いこなせるようになると、データの読み込みがもっとスムーズになるでしょう。
あと、staticで関数を呼び出すのも雑すぎるので「シングルトン」とかを勉強するといいかもしれません。
まあ1人で開発するぶんにはstaticでいい気もしますが……

セール中のUnityアセット、以前買って良かったモノ

またUnityアセットがセールなので

Unity Asset Store - The Best Assets for Game Making
Unityアセットのセールがまたやってきた。
なので以前買って良かったモノなど。
とはいえド定番ばかりである。いいモノだからこそド定番なのだが。
 

モーション編集

UMotion Pro - Animation Editor | Animation | Unity Asset Store
Very Animation | Animation | Unity Asset Store
モーション編集では双璧のこの2アセット、結局「どちらも要る」気がする。
普通にいじるのはVery Animationのほうがやりやすい。
 
UMotion ProでさっくりとHumanoidの既存アニメ修正 - サークル獏の佐藤敏 Unityとか備忘録
既存のモーションを「レイヤー機能」を使っていじるのはUMotion Proでないとできない。
 

モーション系

RPG Character Mecanim Animation Pack | 3D Animations | Unity Asset Store
「ちょっと多過ぎ! そこまで沢山なくても大丈夫!」感もあるけどバトル要素があるゲームを作りたいならこれを持っていて損はなさそう。
 
Game Asset Studio - Asset Store
なおセールにはならないけど、アニメっぽいゲームを作るならGame Asset Studioさんのモーションには目を通しておこう。
Taichi Character Packに至っては無料である。
 

背景系

assetstore.unity.com
学校モノをやるときはやっぱ良い教室背景があると雰囲気が全く違う。
日本人作者さんなのも安心。
サンプルシーンが割とリッチなんで、やりたい絵作りによってはセッセとシェーダーを差しかえたりする必要があるかも。
(アニメっぽいマットな雰囲気とか)
Japanese School Corridor | 3D Environments | Unity Asset Store
セールになってないけど↑廊下アセットもお供にどうぞ。
 
assetstore.unity.com
「なるほどUnityでもこういう色使いができるんだなあ」と最初に気づかされるアセットというか。
どうしてもUnityを使い出したときは「リアルだけど全体に絵が暗いんだよなあ……どうにかならないのかな」ってなりがちなので。
岩だけ、木だけとか単品で使ってもそれなりに映えてくれる。
 

エフェクト系

Unique Projectiles Volume 1 | VFX Particles | Unity Asset Store
とりあえずこういうエフェクトを1つ持っておくと「弾のパーティクルってこうやって設定するんだなあ」という勉強になる。
そこから改造してみよう。
 

シェーダー系

assetstore.unity.com
Unityちゃんシェーダーよりくっきりした影を出すのに向いている気がする。
 

定番系

Dynamic Bone | Animation | Unity Asset Store
ド定番の揺れモノアセット。
とりあえず入れて髪やスカートや胸の根元のボーンに入れただけで「おーそれっぽい」となってくれる。
ただなんかパラメータが直感的でない部分もあったり、コライダーつけても思い通りにならなかったり不満もあったのでMagicaClothに期待中
Magica Cloth | Physics | Unity Asset Store
(※MagicaClothはセールではない)
 
ただ今後はMagicaClothが定番になっていくかもしれないけど、まだ導入に難しさとかちょっとあるんで2020年中くらいはDynamic Boneのほうが無難なケースも多い?
 
Final IK | Animation | Unity Asset Store
VTuber用で有名なこれもド定番。
でも「あるアニメを再生しながら手だけは壁に固定」とかしたいときにも必要になる。
→実はエチチなゲームを作るときにとても重要だったりする。。。手と足を固定して腰部分を動かすだけで……
今後はUnityに新しく加わったAnimation Rigを使えばFinal IKと同じことを無料でできる部分も多いらしい。
が、Preview段階の上に設定も結構大変みたいなので当面はやはりFinal IKがあったほうがよさげである。

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にしてもダメでした……