Skip to content

Commit

Permalink
[WIP] ✨ add book "基礎から始める Unity Timeline"
Browse files Browse the repository at this point in the history
  • Loading branch information
murnana committed Sep 21, 2024
1 parent 13c79ca commit b9576aa
Show file tree
Hide file tree
Showing 16 changed files with 350 additions and 0 deletions.
1 change: 1 addition & 0 deletions books/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/.obsidian/
Empty file removed books/.gitkeep
Empty file.
12 changes: 12 additions & 0 deletions books/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
title: "基礎から始める Unity Timeline"
summary: |-
Unity Timelineを扱うことになったエンジニアへ。
私の知見が、役立ちますように。
topics: ["unity"]
published: false
price: 0 # 有料の場合200〜5000
# 本に含めるチャプターを順番に並べましょう
chapters:
- introduction
- what-playable-api
- references
6 changes: 6 additions & 0 deletions books/how-to-create-playable-behavior.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
title: PlayableBehabuourの作り方
---
## PlayableBehabiour

PlayableBehabiour
17 changes: 17 additions & 0 deletions books/how-to-create-playable.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
title: Playableの実装
---
Playable APIの概要を押さえたところで、実際にPlayable APIを

## 何もしないPlayableを組み立てる


## Playable APIで物体を移動させる
最初は単調に、ある地点まで移動するAPIを作ってみます。

### PlayableBehabiour を継承する

最初にやることは、PlayableBehabiourを継承したクラスを作ることです。

PlayableBehabiourには独自のライフサイクルがあります。Playable本体の再生、停止はもちろん、PlayableGraph本体での再生、停止など、パターンはいくつかありますが、最低限覚える関数は以下です。
-
120 changes: 120 additions & 0 deletions books/how-to-use-playable-api-level-1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
---
title: シンプルな PlayableGraph
---

シンプルな実装の流れを理解するために、「何もしない」PlayableGraphを作ってみましょう。

「何もしない」と書きましたが、実際にはPlayableGraph内にある、Playableの時間を経過させます。この動きはアニメーションを再生するのと同じです。

## PlayableGraphの生成

C#の実装では、最初に`UnityEngine.Playables.PlayableGraph` インスタンスを生成します。生成には静的関数 `UnityEngine.Playables.PlayableGraph.Create()` を呼び出します。

```csharp
var graph = PlayableGraph.Create();
```

次に、Playableを生成します。Playableの型は `UnityEngine.Playables.Playable` にします。別の型を使う時もありますが、今回は「何もしない」のでこれにします。

```csharp
var playable = Playable.Create(graph: graph, input: 0);
```

そして、PlayableOutputを作ります。
PlayableOutputを作る型にも色々ありますが、今回は `UnityEngine.Playables.ScriptPlayableOutput` を使います。

```csharp
var output = ScriptPlayableOutput.Create(graph: graph, name: null);
```

最後に、`playable``output` を接続します。

```csharp
output.SetSourcePlayable(value: playable, port: 0);
```

これで準備完了です。

## PlayableGraphの破棄
PlayableGraphはC#のGC[^1]と違い、自動的にメモリの解放をしません。メモリリークによるクラッシュを避けるためにも、実際に動かす前にメモリの解放を意識しましょう。

生成したPlayable、PlayableOutputは、大元であるPlayableGraphを破棄すれば同時に解放されます。

```csharp
if(graph.IsValid()){ // 変数 graph がメモリに存在しているのか
graph.Destroy(); // graph を破棄
}
```

あえて一つずつ破棄する場合は、以下のように書きます。

```csharp
// PlayableOutputの破棄
if(output.IsOutputValid()){
graph.DestroyOutput(output: output);
}

// Playable の破棄
if(playable.CanDestroy()){ // 破棄可能か
playable.Destroy();
}
```


## PlayableGraphの再生・停止

メモリ解放を仕込み終えたら、動かすための仕込みをします。

<!— TODO デフォルトのPlayableGraphって再生状態だったっけ? —>

PlayableGraph全体の再生・(一時)停止であれば、 `UnityEngine.Playables.PlayableGraph` の関数を使います。

```csharp
// 再生
graph.Play();

// 停止 (一時停止)
graph.Stop();
```

注意すべきなのは、この `Stop()` 関数は、Playable全体の動作的に「一時停止」と同じです。再び `Play()` をすれば、停止した時間から再開します。詳細は別の章に書きます。

PlayableGraphの再生・停止の状態も取得できます。

```csharp
// 再生中か
bool isPlaying = graph.IsPlaying() ;
```

PlayableGraph全体だけではなく、Playableそのものにも再生・一時停止があります。

```csharp
// 再生
playable.Play();

// 一時停止
playable.Pause();
```

Playableも再生・一時停止状態が取れます。

ただしPlayable自身の状態なので、親が停止しても子は再生状態だったり、逆の場合もあります。またPlayableGraphの状態とも関係がありません。これについては後の章で紹介します。

```csharp
UnityEngine.Playables.PlayState playState = playable.GetPlayState();

switch(playState){
case PlayState.Play: break; // 再生中
case PlayState.Pause: break; // 一時停止中
}
```

## Playable1個だけのPlayableGraph図

PlayableGraphは木構造を持つため、これを図で表すことができます。

この図は公式パッケージ「PlayableGraph Visualizer」 を使用すると、実際に動かしながら学ぶことができます。

残念ながらこのツール自体はデバックに向いていません。複雑なグラフになると全体図は描画されるものの、その中の一部を選択して中身を見ることが困難になります。

[^1]: Garbage Collection (ガベージコレクション) の略称。コンピューター上でメモリ解放を自動的に行う為の戦略の一つ。
10 changes: 10 additions & 0 deletions books/how-to-use-playable-api-level-1/one-line-graph.canvas
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"nodes":[
{"id":"d0647595cc3c8006","type":"group","x":20,"y":200,"width":360,"height":120,"label":"graph"},
{"id":"b3eafb0769673893","type":"text","text":"output","x":43,"y":234,"width":117,"height":53},
{"id":"bc39800d54d7c7fa","type":"text","text":"playable","x":223,"y":234,"width":127,"height":53}
],
"edges":[
{"id":"82441c3bfe655a5d","fromNode":"bc39800d54d7c7fa","fromSide":"left","toNode":"b3eafb0769673893","toSide":"right"}
]
}
74 changes: 74 additions & 0 deletions books/how-to-use-playable-api-level-2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
---
title: 複数の子を持つPlayable
---

Playableは、Playable同士で親子関係を持つことができます。この機能はアニメーションを切り替える際のモーションブレンドや、3Dモデルで表情と全身のモーション処理を分けるといった事をする時に役立ちます。

## 親一つ、子が2つのPlayableGraph

Playable同士のブレンド処理などについてはひとまず置いておき、ここではただPlayableの親子関係を作る実装をします。

親のPlayableを `parentPlayable` 、子のPlayableを `playableA``playableB` の2つとします。

### inputに子の数を指定して、Playableの生成

最初に `UnityEngine.Playables.PlayableGraph` インスタンスを生成します。

```csharp
var graph = PlayableGraph.Create();
```

そして親となるPlayableを作ります。引数 `input` には後で生成、接続するこの数を渡します。今回は2つ作って繋げるため、 `2` を渡します。

```csharp
var parentPlayable = Playable.Create(graph: graph, input: 2);
```

では、子のPlayableを2つ作ります。こちらは更に子に繋げるものがないため、 `input` に渡す引数は `0` です。

```csharp
var playableA = Playable.Create(graph: graph, input: 0);
var playableB = Playable.Create(graph: graph, input: 0);
```

### Playableを親子関係にする

親子関係を形成するには、 `UnityEngine.Playables.PlayableGraph``Connect` を使用します。

最初に `playableA` を繋げます。引数 `destinationInputPort` には、`parentPlayable` のinputポート番号を入れます。 `playableA` は1番目なので `0` を渡します。

```csharp
// playableAを子として、parentPlayableに繋げます
graph.Connect(
source: playableA,
sourceOutputPort: 0,
destination: parentPlayable,
destinationInputPort: 0
);
```

もう片方のPlayable、 `playableB`も繋げます。引数 `destinationInputPort` には、 `parentPlayable` にとって2番目の子なので、番号は `1` になります。

```csharp
// playableBを子として、parentPlayableに繋げます
graph.Connect(
source: playableB,
sourceOutputPort: 0,
destination: parentPlayable,
destinationInputPort: 1
);
```

最後に、親のPlayableを出力に繋げます。

```csharp
// PlayableOutputの生成
var output = ScriptPlayableOutput.Create(graph: graph, name: null);

// 生成したPlayableOutputに、親のPlayableを繋げる
output.SetSourcePlayable(value: parentPlayable, port: 0);
```

これでPlayableGraphが完成しました。


14 changes: 14 additions & 0 deletions books/how-to-use-playable-api-level-2/parent-child-playable.canvas
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"nodes":[
{"id":"cd5c67af7b085ba8","type":"group","x":-260,"y":-160,"width":660,"height":200,"label":"graph"},
{"id":"a43cdb978216d094","type":"text","text":"output","x":-237,"y":-87,"width":113,"height":50},
{"id":"cac61d64227a995d","type":"text","text":"parentPlayable","x":-40,"y":-87,"width":169,"height":50},
{"id":"f91476a8f3685ab8","type":"text","text":"playableA","x":236,"y":-37,"width":141,"height":50},
{"id":"d06b1cbab1eff935","type":"text","text":"playableB","x":236,"y":-137,"width":141,"height":50}
],
"edges":[
{"id":"b2e1975fcee31a43","fromNode":"f91476a8f3685ab8","fromSide":"left","toNode":"cac61d64227a995d","toSide":"right"},
{"id":"63ca2de7e0af7081","fromNode":"d06b1cbab1eff935","fromSide":"left","toNode":"cac61d64227a995d","toSide":"right"},
{"id":"7f4b4c26efbf1cb4","fromNode":"cac61d64227a995d","fromSide":"left","toNode":"a43cdb978216d094","toSide":"right"}
]
}
4 changes: 4 additions & 0 deletions books/how-to-use-playable-api-level-x.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## Mix or Passthrough

Timelineでは、ルート(根)のPlayableが1つだけです。その1つが複数のPlayableOutputを持ちます。これはPlayable自身の設定とPlayableOutputへの繋ぎ方によって実現されています。

17 changes: 17 additions & 0 deletions books/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
title: "はじめに"
---

Unity Timelineは、Unity 2017.1のバージョンでベータ版が発表されて以降、アニメーションのためのシステムとして今日まで使われ続けています。

私も、とあるゲームの開発・運営で使用しているエンジニアの1人です。

Zennで入門書を執筆しよう、と思ったのはいくつか理由があります。

- 基礎から綺麗にまとまった情報が欲しい
- 個人的に、実務で使用してから1年以上経過したので情報・知見を整理したい
- 個人的に、~~基礎からの説明を面倒なので、~~「これを見ておけ」と投げられるものが欲しい

そのため、本書では「Unityでのゲーム制作はしたことあるが、Timelineをよく知らない初学者」向けを目指して執筆しています。これを読んで、「役立った」といったポジティブなものや、「この情報が足りない」などといった改善点などありましたら、GitHubやSNSなどでお声がけ頂きますと幸いです。

最後に、Timelineを使ってとあるゲーム内で実装し始めた際、役立ったのはUnity公式のブログはもちろん、先に試した人々が書いたブログなどの技術記事です。本当に感謝しております。
19 changes: 19 additions & 0 deletions books/references.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
title: 参考
---
- Unityブログより
- [Unleashing Animation and Audio | Unity Blog](https://blog.unity.com/technology/unleashing-animation-and-audio-2)
- [Timeline | Unity Blog](https://blog.unity.com/topic/timeline)
- 日本語 [タイムライン | Unity Blog](https://blog.unity.com/ja/topic/timeline)
- [Unity 2017.1 feature spotlight: Playable API | Unity Blog](https://blog.unity.com/technology/unity-2017-1-feature-spotlight-playable-api)
- [Help us test the new Timeline cut-scene and sequencing tool by joining the 2017.1 beta | Unity Blog](https://blog.unity.com/community/help-us-test-the-new-timeline-cut-scene-and-sequencing-tool-by-joining-the-2017-1-beta)
- 日本語 [新機能のタイムラインをUnity 2017.1ベータ版でお試しください! | Unity Blog](https://blog.unity.com/ja/community/help-us-test-the-new-timeline-cut-scene-and-sequencing-tool-by-joining-the-2017-1-beta)
- - [Extending Timeline: A Practical Guide | Unity Blog](https://blog.unity.com/engine-platform/extending-timeline-practical-guide)
- 日本語 [Timeline の拡張 ― 実践ガイド | Unity Blog](https://blog.unity.com/ja/engine-platform/extending-timeline-practical-guide)
- [How to create custom Timeline Markers | Unity Blog](https://blog.unity.com/engine-platform/how-to-create-custom-timeline-markers)
- 日本語 [カスタム Timeline Marker の作成方法 | Unity Blog](https://blog.unity.com/ja/engine-platform/how-to-create-custom-timeline-markers)
- [How to use Timeline Signals | Unity Blog](https://blog.unity.com/engine-platform/how-to-use-timeline-signals)
- 日本語 [Timeline Signal の使用方法 | Unity Blog](https://blog.unity.com/ja/engine-platform/how-to-use-timeline-signals)
- Unity マニュアルより
- [Unity - Manual: Playables API](https://docs.unity3d.com/Manual/Playables.html)
- https://docs.unity3d.com/Packages/[email protected]/manual/index.html
26 changes: 26 additions & 0 deletions books/what-playable-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
title: "Playable API"
---

カスタマイズするにも、何らかの問題を修正するにも、まずTimelineがどのように動作するのかを知っていただく必要があります。

その為に、最初にPlayable APIについて説明します。
## Playable API とは
Playable APIは、例えばアニメーションと効果音をプログラミングでコントロールしたいときに役立つAPIです。実は Unity 5.4から[^1]ひっそりと登場しています。

特徴的なのは木構造で、例えば親が速度を2倍とすれば、その子につながったものも2倍速になる、といったことができます。子だけが2倍速で、親は1倍速のままにする、ということもできます。

また、制御できるのはAnimatorに限りません。 `ScriptPlayableBehaviour` などを使えば、Playable APIで動作するものを実装できます。


### Playable API を使う利点
従来からある Animator は、独自にステートマシンを持っていました。この仕組みはMecanimと呼ばれます。

この仕組みは便利ですが、用途によっては冗長なものでした(あるいは、足りないと感じる場合もありました)。例えば、UIのボタンを押下した際の、ボタンのアニメーションにも、このシステムに沿って作成する必要がありました。

またMecanimの設定ファイルである、AnimationControllerはよく肥大化していました。これは、予めAnimationController上で設定しておいたアニメーションのみ再生が可能、というのがあるためです。追加で特定のアニメーションデータを再生したい場合、プログラムで動的に特定のアニメーションデータを読み込み、再生するのではなく、予めAnimationControllerに使用する全てのアニメーションデータを追加しておく必要がありました。

Playable APIはアニメーションの全てをプログラムで制御する必要がありますが、その分自由度が高いです。動的にアニメーションデータを追加する仕組みもできますし、アニメーションの再生時間を見ながら、特定の条件で音を鳴らす仕組みなども作れます。


[^1]: [Unleashing Animation and Audio | Unity Blog](https://blog.unity.com/technology/unleashing-animation-and-audio-2) より
7 changes: 7 additions & 0 deletions books/what-playable-graph.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
title: PlayableGraphとは
---
### PlayableGraph とは
Playable達をまとめる存在です。正解には、Playableを節とした、木構造そのものです。

実際のAPIにも `PlayableGraph` が登場し、Playable生成時には「どのグラフの節なのか」が必要になります。
9 changes: 9 additions & 0 deletions books/what-playable-output.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
title: Playable Outputとは
---
### Playable Output とは
PlayableだけでPlayableGraphを生成しても、そのままでは動作しません。Playable達が動くには、出力先が必ず必要です。

その出力先が PlayableOutput になります。

Playable Outputは、1つのPlayableGraph に対して複数個持つことができます。一方で、Playable Outputと接続するもの…よくあるのは、コンポーネント…とは、1対1の関係です。
14 changes: 14 additions & 0 deletions books/what-playable.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
title: Playable とは
---
### Playable とは
Playable は、APIが駆動する単位のようなものです。正確には、木構造を形成する「節」の部分です。この節は末端になることもあれば、根(ルート)になることもあります。

例えば、1つのAnimationClipからは1つのPlayableが生成されます。Timelineであれば、1つのTimelineClipから1つのPlayableが生成されます。

このPlayableは親子関係を持つことができます。

例えば2つのAnimationClipをブレンドしたい場合、AnimationClipから1つずつと、親となるPlayableを用意します。その後、親のPlayableに子となるAnimationClipを繋げます。

Timelineの場合、TimelineClipがTrackを親として繋がり、さらにTrackがTimeline本体を親として繋がります。
※ TimelineとTrack、TimelineClipについての具体的な話は別のページになります。ここでは「Playableは親子関係を持つ」ことを覚えてください。

0 comments on commit b9576aa

Please sign in to comment.