エフアンダーバー

個人でのゲーム開発

Unityと改行コード

Visual Studioを利用してUnityのスクリプトを書いているとよくこんな警告が出ます。

There are inconsistent line endings in the ‘Assets/XXX.cs’ script. Some are Mac OS X (UNIX) and some are Windows. This might lead to incorrect line numbers in stacktraces and compiler errors. Many text editors can fix this using Convert Line Endings menu commands.

和訳するとこんな感じ。

‘Assets/XXX.cs’ スクリプトの改行コードに一貫性がありません。Mac OS X (UNIX)のものとWindowsのものがあります。 これはスタックトレースの行番号の誤りやコンパイラエラーを引き起こすことがあります。多くのテキストエディタでは「改行コードの変換」メニューコマンドによりこの問題を修正できます。

今回はこの警告の意味するところと解決方法について。

改行コードとは?

実は現在よく使われている、コンピュータで改行を表す方法は二つあります。 ひとつはLFという特殊な文字のみの一文字で表す方法、もうひとつはこれにさらにCRという特殊な文字を加えたCR+LFの二文字で表す方法です。 この改行を表す文字の組み合わせのことを改行コードといいます。

なぜ二通りあるかというと、タイプライター時代の名残なのだそうです。 CRはキャリッジリターン(Carriage Return)の略で印字位置を現在の行の行頭に戻すという命令で、 LFはラインフィード(Line Feed)の略で印字位置を一段下にずらす命令です。 もともとタイプライターではこれらの二文字を組み合わせて改行を表現していました。

コンピュータでもこれらを受け継いで二つの文字があります。 しかし、タイプライターのときのように片方の文字のみ使うということをしなくなったため、 どちらか一方のみで改行を表すシステムやまったく別の文字を改行文字として定義するシステムが現れました。

いろいろな変遷を経て、現在主流のOSでは、LinuxとMacがLFを、WindowsがCR+LFを使っています。 Macで作成されたテキストファイルをWindowsのメモ帳で開くと一切改行されていないように見えるのはこの辺りが原因だったりします。

C#では、\rがCRを、\nがLFを表します。 現在のシステムの改行コードを使いたい場合にはSystem.Environment.NewLineプロパティを使います。

C#に限らず改行コードの扱いというのは結構面倒なため、よくバグの原因になります。 Unityの警告はまさにそのためで、ソースコードを処理するプログラムがバグるかもしれないから直しとけということです。

警告の原因

警告の内容を読む限り、対象のソースコードには改行コードとしてLFを使っている部分とCR+LFを使っている部分があるということのようです。 しかし、なぜ二つの改行コードが交ざってしまったのでしょうか。

Unityでメニューからスクリプトファイルを作成すると、作成されたファイルの初期コードには、例えWindows環境であってもLFが使われます。 一方で、Visual Studioは元々Windowsのみを対象とするエディタ(IDE)なので、ことあるごとにCR+LFを改行コードとして挿入します。 そのため、Unityで作成したファイルをVisual Studioで編集していると改行コードが交ざってしまうのです。

解決方法

ファイルを閉じて再度開く

Visual Studio上で問題が発生しているファイルを一度閉じて、再度開くと「行の終わりの不整合」 *1 というダイアログがでてきます(設定によってはでてこないかも)。 ここで、改行コードの種類を指定すると、すべての改行が指定した改行コードに置き換わります。

UnityがLFを挿入するのはファイル作成時だけなので、 特にWindows環境以外で使う予定がないのであれば、 ここでCR+LFを選択してしまえば同じファイルで警告がでることはなくなると思います。

改行コードをLFにしたい場合にはこの方法は推奨しません。 たとえここで一度変換したとしても、またVisual StudioがCR+LFを挿入してしまうためです。

保存オプションの詳細設定 (~VS2015)

Visual Studio 2015までは “ファイル” > “保存オプションの詳細設定” から改行コードの変換ができます。

しかし、あくまでも変換なので前述の方法と同じ問題があります。

EditorConfig (VS2017~)【推奨】

Visual Studio 2017からはEditorConfigという仕様に対応しました。

EditorConfigは各テキストエディタでのコードの書き方を統一するための仕組みで、 改行コードのほかに、文字コードやインデントスタイル(タブ・スペース)の指定ができます。

使い方は簡単で、プロジェクトフォルダに.editorconfig.を忘れないように注意)という名前のファイルを作成し、次のような記述をするだけです。

root = true

[*.cs]
indent_style = tab
indent_size = 4
end_of_line = lf
charset = utf-8

それぞれの意味はこんな感じ。

記述 意味
root = true これより上のフォルダにある.editorconfigを参照しない
[*.cs] すべてのC#ソースファイル(*.cs)に以下を適用
indent_style = tab インデントにタブを使用(スペースの場合はspaceを指定)
indent_size = 4 インデントの幅は4文字分
end_of_line = lf 改行コードにLFを使用(CR+LFの場合はcrlfを指定)
charset = utf-8 文字コードにUTF-8を使用

.editorconfigファイルはエクスプローラ上で作ろうとすると、 最初の.を拡張子の前の.と勘違いして名前を付けろと怒られるので、Visual Studio上で作るのがいいかもしれません。 プロジェクトを右クリックして、「追加」>「新しい項目の追加」>「テキストファイル」と選択して、.editorconfigと名前を付ければOKです。

既存のファイルに設定を適用するには、 メニューの「編集」>「詳細」>「ドキュメントのフォーマット」(ショートカットキー:Ctrl-K Ctrl-D)でコードをフォーマットします。 .editorconfigファイルの作成・変更時に開いていたファイルの場合には一度開きなおす必要があります。

ただ現状この機能は追加されたばかりのせいかバグがあるようで、 フォーマットしても一部の改行コードが修正されないことも多いです (どうもフォーマットが必要かどうかの判断に改行コードの不一致が含まれていないようで、 うまくいかない部分に無意味な空白などを入れてからフォーマットすると正しく変換されます)。 まあ、おそらくそのうち修正されると思うので、しばらくは他の方法と併用してどうにかするのがいいと思います。

おわりに

もともとEditorConfigに関する記事を書くつもりだったのですが、 せっかくなので改行コードうんぬんから全部書いてみました。 本題がわかりづらくなっていなければよいのだけれど。

余談ですが、リパッケージ版ユニティちゃんがUnity 5.6でエラーをはいていたので修正しました。 どうもPlayable APIが変更された結果、Animatorの継承元が変わり、GetTimeメソッドが消えたようで。 そもそもExperimentalなクラスを継承していたこと自体が異常だったのかもしれませんが、 Deprecatedでもなくいきなり消されると代替機能がどこにあるのかわからなくて結構困りました。



執筆時のUnityのバージョン:5.6.0f3

*1:英語だとInconsistent Line Endings、日本語訳はWeb上で調べたのでもしかすると違うかもしれません

ツクールMVでローグライク-ライクなゲームをツクった話

ふと「ツクールMV買ったのにゲームひとつもツクってないな」と気づき、衝動的にゲーム制作。 一週間くらいでできるかな、とか考えていたのですが、どうやらRPGの設定項目の多さを甘く見過ぎていたようです・・・。 結局、倍の二週間をかけ、とりあえず動いて、とりあえず遊べるくらいのものをRPGアツマールにアップしました。

今回はそんな感じでつくったものの紹介と製作途中のあれこれについてです。

制作物

ミニローグライク(RPGアツマールに飛びます)

ランダムダンジョンかつ死んだら終わりというローグのシステムを採用したゲームです。

f:id:fspace:20170404204124p:plain:w300

「不思議のダンジョン」シリーズのようなローグのシステムをまるまる受け継いだものを『Roguelike(ローグライク)』と呼ぶのに対し、 システムの一部だけを採用したゲームを『Roguelike-like』や『Rogue-Lite』、『PDL』と呼ぶそうです。 自分がやったことあるのだとSpelunkyとかですね。 今回つくったのは単なる妥協版ローグライクなのですが、一応ローグ的な性質を持っているのでローグライクライクゲームということに。

ゲームの目的は最上階にいるボスを倒すことです。 途中の階層では、障害物・敵・アイテム・罠がランダムに配置されます。 敵を倒してレベルをあげたり、アイテムを拾ったりして、 できるだけ有利な状況で最上階にたどり着くことで、ボスを撃破できる確率が上がります。

ローグライクゲームでは普通、敵がマップ上で様々な行動をとりますが、 このあたりのシステムをつくっていると時間がかかるので、このゲームでは単に徘徊するだけです。 敵と接触するとツクール標準のバトルが始まります。 戦闘中は一対一ですが、戦闘後にマップでアイテムやスキルを使うとターンが消費されるので、囲まれると連戦になります (とはいえ初期配置が悪くない限りはほとんどそうなりませんが)。

またローグライクゲームのダンジョンというと、部屋と道に分かれていてどこに何があるかわからないというのが普通ですが、 実装が面倒だったので、見通しのいい一画面にボンバーマンよろしく破壊可能な障害物をちりばめただけのダンジョンになっています。 一応、破壊に1ターンかかるので、うまく逃げられないような状況は作り出せているかな、と。

未識別なアイテムや攻撃系のアイテムは実装するつもりだったのですが、これ以上時間を割きたくなかったので断念しました。 バグっぽい挙動も修正が面倒そうなものは仕様ということにして目を瞑ってます。 実のところ、ろくにテストプレイもしていないのでゲームバランスはてきとーです。

ここまで読んでなお興味を持ってくれる人がいたならば、RPGアツマールにて遊んでみてください。

発想

このゲームシステムに至るまでの思考の垂れ流し。

もともと開発開始の時点で期間を一週間程度と決めていたので、まず短期間で面白いものをつくるためにどうするか考えました。 ツクールの本分ではないRPG以外のゲームをつくるのは時間がかかりますし、 かといってまともにRPGのマップやストーリーを作るような時間はありません。 そこで、ランダム生成ダンジョンをひたすら探索するゲームにすることに決めました。

以前のプラグイン開発(過去記事 #1 #2 参照) でまともにローグライクのシステムを組み込むのが面倒なことはわかっていました。 そこで、まず真っ先にマップ上での特殊行動(行動順の制御)と敵の追加発生(マップデータの動的変更)を切ることに決めました。 しかし、この二つがなくなるとマップの探索はただただ歩き回るだけで極端につまらなくなります。

どんなダンジョンにすれば探索が面白くなるのか考えた結果、ふとボンバーマンを思い出して、障害物の下にアイテムを隠すことにしました。 障害物を置くだけのマップ生成は非常に簡単なので一石二鳥です。 マップの大きさをどうするかは悩んだのですが、無駄に広くして壮大な感じを出すよりはわかりやすい一画面にしました。 クソゲーはクソゲーとしての身分をわきまえていた方が遊ぶ側としても気楽に手を出せると思ったので。

敵との戦闘は結局普通のツクールの戦闘を採用しました。 下手に複雑なシステムを組むよりわかりやすいですし、なにより実装の手間が省けるので。 ツクールには敵の群れを設定する機能がありますが、マップ上の敵キャラ表示からイメージがずれないように敵は一体ずつにしました。 同時に複数の敵と接触したら同時に戦闘、というような仕様も考えましたが実装が面倒なのでボツに。

そんなこんなで基本的なゲームシステムは決まりました。

絵や音を作ったり探したりしている時間はないだろうということで、これらは最初からデフォルト素材を使うことに決めていました。 とりあえず素材を一通り眺めたところ、パッケージキャラクターのデザインがいい感じだったのでこれらを使うことに。 主人公っぽいやつ一人だけだと少し面白みに欠けるので、アイテムを使ってキャラを切り替えることにしました。 「今のキャラの方がいいから切り替えなくてもいいや」となるとつまらないので、 切り替えアイテム(鏡)には、先制かつ状態異常含め全回復という特殊効果をつけました。

あとは各パッケージキャラクターの特徴をイラストから推測して能力に落とし込み、 それぞれが生きそうな状況を考えつつ、状態異常やスキル、敵キャラの能力なんかを設定してゲームが完成しました。

小技・改造

制作中の小技とか改造とかの話です。

ツクールでのゲーム制作の参考になれば。

TypeScript

ツクールMVは基本的にES5なのでJavaScriptでコードを書くのは結構苦痛だったりします。 そのため、今回のコードはすべてTypeScriptで書きました。

ツクールMVとTypeScriptについては過去記事を参照してください。

www.f-sp.com

ただし、過去記事ではモジュールを用いていたのに対し、今回はすべて名前空間で済ませてしまっています。 他のプラグインとの併用を考えないくてよいのであれば、名前空間で十分なので。

ダメージ計算式

ツクールMVでは各スキルごとにダメージの計算式を記述できます。 a.atk * 4 - b.def * 2のような感じで書くのですが、すべてのスキルに同じような計算式を書くのは結構面倒だったりします。 特に途中で計算式を変更しようものなら、全スキルの記述をいちいち直さなければなりません。

しかしこの計算式、実は内部的にはevalしているだけなのでプラグインで定義した関数呼び出しができます。 そこで、$dmgのようなグローバル関数を定義して$dmg(a,b)とだけ書いておくと、 プラグイン側の定義変更で全スキルの計算式が一括設定できます。 地味に便利。

取得経験値

各敵キャラには撃破時の取得経験値を設定できます。 最初、取得経験値は手動でそれっぽい値を入れていたのですが、 非常に面倒だったので最終的にプラグイン側でおおまかな値を計算してそれに係数をかける方式にしました。

const AUTOEXP_TAG = 'autoexp';

const _Game_Enemy_exp = Game_Enemy.prototype.exp;
Game_Enemy.prototype.exp = function (this: Game_Enemy): number {
    const meta = this.enemy().meta;
    if (AUTOEXP_TAG in meta) {
        const value = meta[AUTOEXP_TAG];
        const number = (value !== true ? parseFloat(value) : NaN);
        const rate = (isFinite(number) ? number : 1);

        return Math.ceil((this.mhp * 3 + this.atk + this.def + this.mat + this.mdf + this.agi) * rate);
    } else {
        return _Game_Enemy_exp.apply(this, arguments);
    }
}

あとは敵キャラのメモ欄に<autoexp:2>のようなタグをつけるだけです。

スキルのレーティング

各敵キャラの使用スキルにはレーティング(使用度合)が設定できます。 しかし、マニュアルをみるとわかるように結構わかりづらいアルゴリズムで割合が決まります。

そのままだと調整がしづらかったため 2^{x-1}で重みを設定する簡単な方式に変えました。 ただし、最大値が設定された場合には条件を満たす限り必ずそのスキルを使用するようにしています。

コードはこんな感じ。

Game_Enemy.prototype.selectAction = function (this: Game_Enemy, actionList: Action[], ratingZero: number): Action | null {
    if (actionList.length !== 0) {
        const MAX_RATING = 9;
        const max = actionList.filter(action => action.rating === MAX_RATING);
        if (max.length === 0) {
            const weights = actionList.map(action => 1 << (action.rating - 1));
            const total = weights.reduce((sum, weight) => sum + weight, 0);
            const value = Math.randomInt(total);
            for (let i = 0, sum = 0; i < weights.length; i++) {
                if (value < (sum += weights[i])) return actionList[i];
            }
        } else {
            return max[0];
        }
    }

    return null;
}

バトルイベントのスパン

ボス戦にて被ダメによるステート解除を検出する必要があったのですが、どう実装すべきか結構悩みました。 そもそもツクールにはステート変更に関するコールバックはありませんし、 戦闘中はコモンイベントの並列処理も働かないようで監視できません。

そこで、バトルイベントのスパン「モーメント」で監視することを考えたのですが、 この「モーメント」というのは通常イベントの「自動実行」と同じで、 条件を満たさなくなるまで他の処理より優先的に実行され続けます。 つまり、内部で条件を無効にするような処理を書かない限り、無限ループとなって戦闘が進みません。 一方で、条件を無効にする処理を書いてしまうと、再度有効にしない限り処理が実行されないので監視ができません。

仕方がないので、プラグイン側で「モーメント」の仕様を「並列実行」に近いものに書き換えて使用することにしました。 バトルイベントはボス戦でしか使わないので大丈夫だろうという妥協策です。

そもそも条件式としてスクリプトを書ければそれで済んだのだけど、 使ってみると案外ツクールのエディタからスクリプト差し込める場所って少ないんですよね・・・。

苦労話

開発期間が延びた原因とかハマったところの話。

名前・説明文

正直、今回のゲーム開発で一番苦労したのが名前や説明文といった文章問題だったり。

今まであまり王道RPGをやってこなかったことや、RPG開発経験がほぼなかったため、とにかくかっこいい言葉が出てこない・・・。 あんまりベタな英語なんかを使うとかっこ悪いし、かといってよく意味を知りもしない言葉を使うと誤用が怖いので、 なんとか日本語でそれっぽい感じにできないかといろいろ考えました。

しかしまあ、こうして記事を書きながら改めて見直してみると結構恥ずかしいこと書いてますね。 こういうのって案外、羞恥心との戦いなのかもしれません。

Edgeとアツマール

実はゲーム自体は投稿日より一日早く完成していたのですが、RPGアツマールにアップする過程でいろいろあって投稿が遅れてます。

そんな問題のひとつめがEdgeとアツマールの問題。

私は基本的にOSのデフォルトブラウザを使う人間なので、ブラウザはEdgeを使ってます。 しかし、Edgeからアツマールにゲーム情報を入力して投稿ボタンを押すとなぜか何度やってもエラー。 試しにFirefoxでやってみたらすんなり投稿できました。

Edgeが悪いのか、アツマールが悪いのか、はたまた私のブラウザ設定が悪いのかはわかりませんが、 とりあえずEdgeとアツマールの相性はよくない模様。 アツマールでゲームをプレイするときもEdgeだとキー入力がハンドルされなかったり、画面がぶれたりするんですよね・・・。

『未使用』ファイルを含まない

アツマールにどうにか投稿できたということでとりあえずテストプレイしてみたところ、敵を攻撃した瞬間に画面がフリーズ。 ブラウザのデバッグ機能を使ってみたところ、実際には動いているけれど、どこかで無限ループに近い状態に陥り進行不能となっているようでした。

ステップ実行で処理を追っていった結果、ロードに失敗した画像のロード待ちを延々としている状態のようでした。 ローカルでは動いていたことを考えると、おそらくデプロイ時の「未使用ファイルを含まない」というオプションで使用しているファイルが削除されたんだろうな、と。 そこで、出力されたデータのアニメーション画像を確認してみると、通常攻撃時の画像が含まれていませんでした。

データベースを確認して通常攻撃のアニメーション設定を見てみると「通常攻撃」になっているのを発見。 これは通常攻撃に使用するアニメーションと同じものを使用、という意味だと思われるのですが、 通常攻撃のスキル自体に設定した場合にどうなるのかわからなかったためコアスクリプトを確認。 すると、どうも武器を装備している場合には武器のアニメーションが使われ、そうでない場合には1番のアニメーションが自動的に選択されるようでした。

1番のアニメーションはデータベース上のスキルや武器などから明示的に参照されていないため、 未使用ファイルとみなされデプロイ時に削除、結果としてロードエラーで止まっていたようです。 通常攻撃スキルのアニメーションに明示的に1番のアニメーションを設定したところ、削除されなくなり正しく動作しました。

ありがちなバグだとは思いますが、正直勘弁してほしい。

セーブデータとアツマール

アツマールでは一人あたり90ブロックのセーブデータ領域が与えられます。 アツマールのAPIの説明を読むと、1ブロックあたり1KBと書いてあるのですが、 一人分の領域が90KBしかないなんてことはないだろうと勝手に書き間違いだと思い込んでいました。

が、実際にテストプレイしてみると、セーブデータひとつあたり4,5ブロック持っていかれます。 この時点で、このゲームは階層ごとに別ファイルにオートセーブする仕様だったため、 これはまずいということで急遽セーブ方法とデータ容量を見直すことにしました。

セーブ方法はこれまでの方式をオプションとして残しつつ、 基本的にひとつのファイルを上書き保存していく形に変えました。 データ容量は自動生成しているマップと使っていない仲間の情報を捨てることで軽量化しました。 それでも1ブロックにはなりませんでしたが、 これ以上軽量化しようと思うとかなり細かく制御する必要があり、 バグを生みそうなので諦めました。

おそらくビジネス上の戦略としてこの仕様なのだと思うのですが、 プログラマが1KB単位でセーブデータ容量を調整するのは非常に労力の無駄なので、 1ブロックの上限10KBで30ブロックくらいにしてほしいところ。

やりのこしたことメモ

現在特にアップデートの予定はありませんが、いつか気が向いたときにでもやるべきことのメモ。

  • 接触時の戦闘開始を移動と床イベントの発生後まで遅延orキャンセル
  • 足元コマンドの実装
  • 未識別状態の実装
  • 攻撃系アイテムの追加
  • マイナス効果アイテムの追加
  • マップ上で機能するアイテムの拡充
  • アイテム所持可能数の制限
  • 逃走時の敵体力の引継ぎ
  • 各種メニュー画面の簡素化
  • 戦闘画面の簡素化
  • 罠回避手段の考案
  • お金の取得とその利用法の考案
  • バランス調整

逆にやる必要がないこと、または迷っていること。

  • 攻撃による罠の発見
    • 現状、ターンを消費するリスクが少なく、単にテンポを悪くする
    • プレイヤーの感覚とずれるためなんとなく不完全感がある
  • 空腹度の実装
    • 歩数を意識させると障害物の破壊の作業感が増す
    • 階層数が少ないため、現状ひとつの階層の持つ役割が大きく、急かすと詰む確率が格段に上がる
    • 敵をアイテムから遠ざけるためにでたらめに歩き回ることを抑止できる
    • プレイヤーの生命値として体力以外の要素を組み込むこと自体は面白い
  • 階層ごとの行動回数制限
    • 現状、特に階層に留まるメリットがなく制限の必要がない

おわりに

ゲームの紹介だけだとあまり書くことないな、と思い、 とりあえずやったことや考えたことを片っ端から書いてみました。 わりと勢いだけで書いたので、間違いとか不快な部分とかあったらごめんなさい。

前回、Unityについていろいろ書くとか言いつつ、ツクールの記事で申し訳ない。
次こそUnity関係で記事書こうと思います。



執筆時のRPGツクールMVのバージョン:1.3.5

ユニティちゃんをRe-パッケージング!!

味気ないサンプルに華を添えてくれる存在として当ブログでもお世話になっているユニティちゃん。 非常にありがたい存在ではあるのですが、いざパッケージをインポートしてみるといろいろと問題が多いことに気が付きます。 そこで、Unity初心者でも簡単に扱えるようにユニティちゃんを再パッケージしてみました。

ユニティちゃん

f:id:fspace:20170318174913p:plain

ユニティちゃんとは?

ユニティちゃんはUnity Technologies Japanが提供するキャラクターで、 『ユニティちゃんライセンス』という非常に緩いライセンスのもと、 3Dモデルやドット絵、ボイスなどのアセットが配布されています。

今回、再パッケージしたのは最もシンプルでよく使われている次の二つのパッケージです。

また、次のパッケージをインポートする必要がなくなります。

公式サイト:UNITY-CHAN! OFFICIAL WEBSITE

ユニティちゃんの問題点

現状、ユニティちゃんのパッケージには次のような問題があります。

  • 複数に分割されている
  • 大量に発生する警告メッセージ
  • 長いインポート時間
  • 難解かつコメントの少ないスクリプト
  • 複雑なフォルダ構造

長いインポートを待ちつつ、パッケージを順番に読み込んだ後に、大量の警告メッセージが残されているという状況には、 さすがにため息が出るので、この辺りを解消することが今回の目的です。

成果物

ダウンロード

Unity Package

GitHubより最新バージョンのパッケージをダウンロードしてください。

パッケージ名 内容 説明
UnityChan 基本アセット 3Dモデルやシェーダ、スクリプトなどのボイス以外のあらゆるアセット
UnityChan_Voices ボイス ボイスのみの追加アセット

プロジェクト

GitHubにて公開中。

github.com

ライセンス

オリジナルのパッケージに含まれるアセットやその改変物はユニティちゃんライセンスを継承する必要があるので、 ユニティちゃんライセンスが適用されます。 気を付けるべきはロゴ・ライセンス表記と営利目的の成人向けコンテンツくらいだと思います。

私が追加したものに関してはCC0にしておきます。 これは可能な限り著作権を放棄するというものです。 MITライセンスとかにすると著作権表記が必要になってしまって面倒なので。

正直どこまでがユニティちゃんライセンスの適用範囲で、どこまでが自分の著作物と言っていいのかよくわからないのですが、 私は使用に関して一切制限するつもりはないので、ユニティちゃんライセンスにだけ気を付けてお使いください。

使い方

基本アセットのUnity Packageをダウンロードし、 メニューの"Assets" > “Import Package” > “Custom Package…"からパッケージを選択してインポートします。 ボイスを利用したい場合にはボイスのUnity Packageをダウンロードして、同様の手順でインポートします。

ユニティちゃんを自分の作成したシーン上で動かしたい場合には、 “UnityChan"プレハブまたは"UnityChanDynamic"プレハブをシーンのヒエラルキービューにドラッグアンドドロップします。 "UnityChanDynamic"プレハブでは処理負荷が少し高くなりますが、髪の毛やスカート等が物理演算によって揺れます。

ユニティちゃんのモーションやポーズ、スクリプトの動作などを確認したい場合には、 “Demo"フォルダ内の各シーンを読み込むとサンプルを確認できます。

セットアップされていない生のデータを触りたい方は"Data"フォルダ内に各種データが配置してあります。 スクリプトの動作に関するドキュメントは用意していないため、詳細を確認したい場合には、 このフォルダ内のスクリプトファイルを開いて、コードやコメントを参照してください。

また使用に際して、"License"フォルダ内の各種規約をご一読ください。 “ReadMe"ファイルは一応元パッケージのものを含めていますが、内容が古くなっている部分があります。

DynamicsXXX

揺れもの関連のDynamicsXXX系のスクリプトについて、仕様がわかりづらいので簡単に説明しておきます。

元パッケージに含まれていたSpringXXX系のスクリプトは、スクリプト内の計算で簡易的に物理っぽい挙動を再現するものでした。 一方、本パッケージに含まれるDynamicsXXX系のスクリプトはUnity標準の物理エンジンを利用して物理演算をしています。 しかし、そのためにDynamicsXXX系のスクリプトは少し特殊な仕様となっています。

レイヤー設定

Unityには特定の相手と「だけ」衝突させるような機能は存在しません。 好ましくない衝突を回避するためには、レイヤーを用いて衝突相手をフィルタリングする必要があります。 このためDynamicsManagerには揺れものと専用コライダーのレイヤーを一括で設定する機能がついています。

しかし、このような設定をしなければ動かせないという仕様ではサンプルを用意できません。 また、レイヤーおよびその衝突関係の設定はプロジェクトごとの設定項目であるため、 これらの項目を勝手に書き換えるような仕様にするのも好ましくありません。

そこで、DynamicsManagerには実行時に使われていないレイヤーを自動的に検索してそれを利用する機能をつけています。 このとき、「使われていない」ということの判断には名前が設定されているかどうかを利用しており、 全てのレイヤーに名前が設定されているとエラーとなります。

ただし、この機能はサンプルを動かすための対処的なものです。 もしゲームに組み込むことを考えているのであれば、レイヤーと衝突関係を正しく設定した後にこの機能を切ることをおすすめします。

物理オブジェクトの実体

UnityにはKinematicでない剛体(Rigidbody)で階層をつくってはいけないという制限があります。 一方で、モデルのボーンは階層を成しています。 このため、各ボーンにそのまま剛体をアタッチしてしまうと制限に違反しておかしな挙動となってしまいます。

そこで、DynamicsXXX系のスクリプトでは物理演算用のゲームオブジェクトをシーンのルートに生成し、 それに剛体をアタッチすることで物理演算を行っています。 モデルのボーンにはそれらの演算結果を転写しているだけなので、 ボーンのTransformになんらかの変更を与えてもそれらは全て無視されます。

物理演算用のゲームオブジェクトは通常見えないように設定されていますが、 DynamicsManagerの設定を変更すると、エディタ上で確認できます。

リパッケージングについて

方針

再パッケージの方針というか、目指したところ。

  • パッケージは基本アセットとボイスの二つで構成
    • インポートが遅くなる最も大きな要因がボイスの容量であるため
  • 初心者でも簡単に扱えるように
    • ドラッグアンドドロップのみで使用可能に
    • どれが本体でどれが部品なのかを明確に
  • 初心者の手本となるようなソースコード
    • なるべくきれいに整理
    • コメントを多めにつける
    • 極力基本構文のみ
    • 汎用性やパフォーマンスよりも可読性優先

スクリプト

スクリプトは元々のパッケージに含まれているものと同等の機能を持つ新しいスクリプトへとすべて書き直しました (一応元コードを参考にして書きましたがほとんど原形はないです)。

スクリプトを書き直すにあたって、 初心者でも頑張れば読めるくらいのコードにしようと考えたのですが、 Unityの入門書にどの程度C#の構文について書いてあるのかわからなかったため、ほとんど基本構文のみで書いています。

プロパティ、インターフェース、Unity定義クラス以外の継承、デリゲート、LINQあたりは意図的に避けています。 そのためC#に慣れ親しんだ人にはちょっと不格好なコードに見えるかもしれません。

一方で、条件演算子(~?~:~)、列挙型(enum)、構造体(struct)あたりはコードを読みやすくするために、 属性(~Attribute)、ifディレクティブ(#if/#endif)あたりはエディタの利便性のために許容しています。 読んでいて意味がわからなかった場合には各自調べてください。

・・・とまあ、こだわっている風ではありますが、 実際には一部のスクリプトはそこそこ難しく、おそらく初心者には読めないだろうなと思います。 制約を貫いているのは単に意地になっているだけですね。 とりあえず、C#っぽくないコードになってるのはこういう理由だよ、ということだけ。

対応表

各スクリプトの対応と相違点。

元スクリプト 新スクリプト 相違点
AutoBlink AutoBlink
BlinkablePart
対象の指定をスクリプトをアタッチする方式に。
CameraControl ViewingModeCamera
FaceUpdate FacialAnimator レイヤー指定が必要に。
IdleChanger MotionSwitcher 操作方法を上下から左右に。
IKCtrlRightHand IKTarget 右手以外にも切り替え可能に。
RandomWind RandomWind
SpringBone DynamicsRoot
DynamicsBone
スクリプトのアタッチ対象が末端に向けてひとつスライド、根本に専用スクリプトが必要に。
SpringCollider DynamicsCollider 形状が球からカプセルに。
SpringManager DynamicsManager スクリプト内での計算から標準の物理エンジンを使用する方式に。
ThirdPersonCamera ThirdPersonCamera 位置の指定がTransformの参照から相対座標値に。
UnityChanControl-
ScriptWithRigidBody
UnityChanController
UnityChanStateController
StateMachineBehaviourを利用する方式に。その他全体的に変更。

変更点一覧

とりあえずメモしておいたものだけ。 何か思い出したら追記します。

  • 各モデルのノードを適切な名前に変更
  • 各モデルのアニメーションスタックを適切な名前に変更
  • 各モデルのブレンドシェイプを適切な名前に変更
  • “unitychan_all"アバターマスクを作成し、各アニメーションのマスク設定に適用
  • 各アニメーションモデルのRigに"unitychan.fbx"のアバターを適用
  • “BoxUnityChan.fbx"のRigに"unitychan.fbx"のアバターを適用
  • “Character1_Spine"に設定されていたアニメーションを削除
  • “JUMP00"アニメーションのカーブ"JumpHeight"と"GravityControl"を削除
  • “UnityChanActionCheck/ARPose"アニメーターコントローラーのパラメータ"Back"と"Next"をBoolからTriggerへ変更
  • “UnityChanLocomotions"アニメーターコントローラーの構造を変更
  • “UnityChanLocomotions"アニメーターコントローラーのジャンプモーションを"JUMP00"から"JUMP00B"へ変更
  • “unitychan_dynamic"の"J_L_SusoFront_01"のTransformがリセットされている問題を修正
  • ステージのマテリアルをシンプルなものに変更
  • 古いシェーダーが適用されていた"BoxUnityChan.fbx"のマテリアルを再インポート
  • 重複していたマテリアルを削除
  • “CreateLocatorHere"エディタ拡張を削除
  • 各プレハブから"LookPos"を削除
  • 不要になったプレハブを削除
  • スプラッシュスクリーン用のアセット群を削除
  • 内容が正しくなくなった一部のドキュメントを削除
  • ライセンス関係のファイルを最新のものに更新
  • すべてのテキストファイルを改行コードLFのBOM付UTF-8に統一
  • 全スクリプトを書き直し
  • 一部アセットの名前を変更

ユニティちゃんライセンス

ユニティちゃんライセンス

この作品はユニティちゃんライセンス条項の元に提供されています

おわりに

元々少し整理するだけだったはずなのですが、いつの間にやら大改修に・・・。 おかげでブログの更新も滞って、ネタがたまってきたのでこれから少しずつ放出していくつもりです。

ブログ更新の通知用にTwitter始めようと思います。 SNS使いこなせない人間なので、以前アカウントだけ作って挫折したのですが、今なら最悪ブログの更新だけでもつぶやけるので。 アカウントについてはプロフィールの部分に書いておきます。

パッケージに関して質問やバグ報告等あればいつでもどうぞ。



執筆時のUnityのバージョン:5.5.2f1