Slide 1

Slide 1 text

株式会社WFS クライアントエンジニア 市川 ひまわり ヘブンバーンズレッド × バトル × アドベンチャー 〜WFSのゲーム制作・演出の最大化手法〜 株式会社WFS クライアントエンジニア 伊藤 栄治

Slide 2

Slide 2 text

市川ひまわり / Himawari Ichikawa 株式会社WFS Unityクライアントエンジニア 2020年新卒入社 主にアドベンチャーパートの開発を担当 自己紹介 ©WFS

Slide 3

Slide 3 text

伊藤 栄治 / Eiji Ito 株式会社WFS Unityクライアントエンジニア 2016年新卒入社 主にバトルパートの開発を担当 ©WFS 自己紹介

Slide 4

Slide 4 text

● 第一部 ○ Luaで最上の演出を! 「ヘブンバーンズレッド」におけるアドベンチャー制作エンジンの裏側 ● 第二部 ○ Unity Timelineで作る! 「ヘブンバーンズレッド」バトルスキルシステム作成事例 構成 ©WFS

Slide 5

Slide 5 text

Luaで最上の演出を! 「ヘブンバーンズレッド」における アドベンチャー制作エンジンの裏側 市川ひまわり ©WFS

Slide 6

Slide 6 text

ヘブンバーンズレッドについて ©WFS

Slide 7

Slide 7 text

大枠の役務分担(シナリオ/ボイス周り)
 ©WFS ヘブンバーンズレッドとは

Slide 8

Slide 8 text

©WFS ADV フィールド バトル ヘブンバーンズレッドの構成要素

Slide 9

Slide 9 text

©WFS ADV フィールド バトル ヘブンバーンズレッドの構成要素

Slide 10

Slide 10 text

ヘブンバーンズレッドにおけるアドベンチャー ©WFS

Slide 11

Slide 11 text

ヘブンバーンズレッドにおけるアドベンチャー ©WFS 大量のセリフとそれに伴う演出

Slide 12

Slide 12 text

ヘブンバーンズレッドにおけるアドベンチャー ©WFS 大量のセリフとそれに伴う演出 量産体制を整える必要がある

Slide 13

Slide 13 text

ヘブンバーンズレッドにおけるアドベンチャー ©WFS Luaを使って作っている ● Lua:スクリプト言語の一種 ● WFSでは複数タイトルで使用されている

Slide 14

Slide 14 text

ヘブンバーンズレッドにおけるアドベンチャー ©WFS 演出を組むのは「スクリプター」 ● スクリプティングのレベルは様々 ● スクリプト初心者の人でも書ける仕組みにする必要がある

Slide 15

Slide 15 text

アドベンチャー実装の流れ ©WFS

Slide 16

Slide 16 text

アドベンチャー実装の流れ ©WFS シナリオ納品

Slide 17

Slide 17 text

アドベンチャー実装の流れ ©WFS  スクリプト  作業 シナリオ納品

Slide 18

Slide 18 text

アドベンチャー実装の流れ ©WFS テストと 修正  スクリプト  作業 シナリオ納品

Slide 19

Slide 19 text

アドベンチャー実装の流れ ©WFS テストと 修正  スクリプト  作業 シナリオ納品 シナリオ変換内製ツール 『Tulip』

Slide 20

Slide 20 text

アドベンチャー実装の流れ ©WFS テストと 修正  スクリプト  作業 シナリオ納品 Layout命令 シナリオ変換内製ツール 『Tulip』

Slide 21

Slide 21 text

アドベンチャー実装の流れ ©WFS テストと 修正  スクリプト  作業 シナリオ納品 Unity x Luaの ホットリロード Layout命令 シナリオ変換内製ツール 『Tulip』

Slide 22

Slide 22 text

アドベンチャー実装の流れ ©WFS テストと 修正  スクリプト  作業 シナリオ納品 Unity x Luaの ホットリロード Layout命令 シナリオ変換内製ツール 『Tulip』

Slide 23

Slide 23 text

納品されたテキストファイルをluaにする 納品時の問題 ©WFS テキスト

Slide 24

Slide 24 text

納品されたテキストファイルをluaにする 納品時の問題 ©WFS テキスト キャラクター指定 コメント 吹き出しの種類

Slide 25

Slide 25 text

納品されたテキストファイルをluaにする ● 1行ずつ手動でLua構文に直す必要がある 納品時の問題 ©WFS テキスト キャラクター指定 コメント 吹き出しの種類

Slide 26

Slide 26 text

納品されたテキストファイルをluaにする ● 1行ずつ手動でLua構文に直す必要がある ● 手作業だと正確性が低い 納品時の問題 ©WFS テキスト キャラクター指定 コメント 吹き出しの種類

Slide 27

Slide 27 text

納品されたテキストファイルをluaにする 納品時の問題 ©WFS テキスト ⇒変換ツールを作って自動化!

Slide 28

Slide 28 text

シナリオ変換内製ツール『Tulip』とは ©WFS ● 納品されたシナリオ(.txt)をLuaに変換するためのパーサー

Slide 29

Slide 29 text

シナリオ変換内製ツール『Tulip』とは ©WFS ● 納品されたシナリオ(.txt)をLuaに変換するためのパーサー ● 使用言語はPythonとC# ● Unityのツールとして作成

Slide 30

Slide 30 text

シナリオ変換内製ツール『Tulip』とは ©WFS ● 納品されたシナリオ(.txt)をLuaに変換するためのパーサー ● 使用言語はPythonとC# ● Unityのツールとして作成 ● シナリオの量産をしやすくする

Slide 31

Slide 31 text

簡単なサンプル ©WFS 【茅森】「サンプル」 【和泉】「サンプル2」 納品txtファイル

Slide 32

Slide 32 text

簡単なサンプル ©WFS 【茅森】「サンプル」 【和泉】「サンプル2」 納品txtファイル Tulip

Slide 33

Slide 33 text

簡単なサンプル ©WFS Adv.talk(“茅森”, [[サンプル]]) Adv.talk(“和泉”, [[サンプル2]]) 出力luaファイル 【茅森】「サンプル」 【和泉】「サンプル2」 納品txtファイル Tulip

Slide 34

Slide 34 text

実装 ©WFS ● テキストファイルを上から順に読んでいく ● 特定の文字列を探して変換する ● キャラ一覧は事前に読み込んでいる

Slide 35

Slide 35 text

シナリオ変換内製ツール『Tulip』とは ©WFS 選択肢 3x3選択肢

Slide 36

Slide 36 text

3 x 3 選択肢 ©WFS

Slide 37

Slide 37 text

3 x 3 選択肢 ©WFS @選択1=SELBTN( 選択肢1-1, 選択肢1-2, 選択肢1-3 ) @選択2=SELBTN( 選択肢2-1, 選択肢2-2, 選択肢2-3 ) @選択合算=(@選択1*10) + @選択2 switch(@選択合算){ … } 納品txtファイル

Slide 38

Slide 38 text

3 x 3 選択肢 ©WFS @選択1=SELBTN( 選択肢1-1, 選択肢1-2, 選択肢1-3 ) @選択2=SELBTN( 選択肢2-1, 選択肢2-2, 選択肢2-3 ) @選択合算=(@選択1*10) + @選択2 switch(@選択合算){ … } 納品txtファイル local s1, s2 = Adv.doubleQuestion([[]], [[選択 肢1-1|選択肢1-2|選択肢1-3]], [[選択肢2-1|選択肢2-2|選択肢2- 3]]) local s = s1 * 10 + s2 if s == 0 then … 出力luaファイル Tulip

Slide 39

Slide 39 text

3 x 3 選択肢 ©WFS @選択1=SELBTN( 選択肢1-1, 選択肢1-2, 選択肢1-3 ) @選択2=SELBTN( 選択肢2-1, 選択肢2-2, 選択肢2-3 ) @選択合算=(@選択1*10) + @選択2 switch(@選択合算){ … } 納品txtファイル local s1, s2 = Adv.doubleQuestion([[]], [[選択 肢1-1|選択肢1-2|選択肢1-3]], [[選択肢2-1|選択肢2-2|選択肢2- 3]]) local s = s1 * 10 + s2 if s == 0 then … 出力luaファイル Tulip

Slide 40

Slide 40 text

3 x 3 選択肢 ©WFS @選択1=SELBTN( 選択肢1-1, 選択肢1-2, 選択肢1-3 ) @選択2=SELBTN( 選択肢2-1, 選択肢2-2, 選択肢2-3 ) @選択合算=(@選択1*10) + @選択2 switch(@選択合算){ … } 納品txtファイル local s1, s2 = Adv.doubleQuestion([[]], [[選択 肢1-1|選択肢1-2|選択肢1-3]], [[選択肢2-1|選択肢2-2|選択肢2- 3]]) local s = s1 * 10 + s2 if s == 0 then … 出力luaファイル Tulip s1 s2

Slide 41

Slide 41 text

3 x 3 選択肢 ©WFS @選択1=SELBTN( 選択肢1-1, 選択肢1-2, 選択肢1-3 ) @選択2=SELBTN( 選択肢2-1, 選択肢2-2, 選択肢2-3 ) @選択合算=(@選択1*10) + @選択2 switch(@選択合算){ … } 納品txtファイル local s1, s2 = Adv.doubleQuestion([[]], [[選択 肢1-1|選択肢1-2|選択肢1-3]], [[選択肢2-1|選択肢2-2|選択肢2- 3]]) local s = s1 * 10 + s2 if s == 0 then … 出力luaファイル Tulip

Slide 42

Slide 42 text

3 x 3 選択肢 ©WFS @選択1=SELBTN( 選択肢1-1, 選択肢1-2, 選択肢1-3 ) @選択2=SELBTN( 選択肢2-1, 選択肢2-2, 選択肢2-3 ) @選択合算=(@選択1*10) + @選択2 switch(@選択合算){ … } 納品txtファイル local s1, s2 = Adv.doubleQuestion([[]], [[選択 肢1-1|選択肢1-2|選択肢1-3]], [[選択肢2-1|選択肢2-2|選択肢2- 3]]) local s = s1 * 10 + s2 if s == 0 then … 出力luaファイル Tulip z

Slide 43

Slide 43 text

3 x 3 選択肢 @選択1=SELBTN( 選択肢1-1, 選択肢1-2, 選択肢1-3 ) @選択2=SELBTN( 選択肢2-1, 選択肢2-2, 選択肢2-3 ) @選択合算=(@選択1*10) + @選択2 switch(@選択合算){ … } 納品txtファイル local s1, s2 = Adv.doubleQuestion([[]], [[選択 肢1-1|選択肢1-2|選択肢1-3]], [[選択肢2-1|選択肢2-2|選択肢2- 3]]) local s = s1 * 10 + s2 if s == 0 then … 出力luaファイル Tulip

Slide 44

Slide 44 text

3 x 3 選択肢 ©WFS @選択1=SELBTN( 選択肢1-1, 選択肢1-2, 選択肢1-3 ) @選択2=SELBTN( 選択肢2-1, 選択肢2-2, 選択肢2-3 ) @選択合算=(@選択1*10) + @選択2 switch(@選択合算){ … } 納品txtファイル local s1, s2 = Adv.doubleQuestion([[]], [[選択 肢1-1|選択肢1-2|選択肢1-3]], [[選択肢2-1|選択肢2-2|選択肢2- 3]]) local s = s1 * 10 + s2 if s == 0 then … 出力luaファイル Tulip

Slide 45

Slide 45 text

3 x 3 選択肢 @選択1=SELBTN( 選択肢1-1, 選択肢1-2, 選択肢1-3 ) @選択2=SELBTN( 選択肢2-1, 選択肢2-2, 選択肢2-3 ) @選択合算=(@選択1*10) + @選択2 switch(@選択合算){ … } 納品txtファイル local s1, s2 = Adv.doubleQuestion([[]], [[選択 肢1-1|選択肢1-2|選択肢1-3]], [[選択肢2-1|選択肢2-2|選択肢2- 3]]) local s = s1 * 10 + s2 if s == 0 then … 出力luaファイル Tulip

Slide 46

Slide 46 text

3 x 3 選択肢の実装 ©WFS ● txtファイルを上から順に読み 特定文字列を探して変換 ● switch文をif文に書き換える @選択1=SELBTN( 選択肢1-1, 選択肢1-2, 選択肢1-3 ) @選択2=SELBTN( 選択肢2-1, 選択肢2-2, 選択肢2-3 ) @選択合算=(@選択1*10) + @選択2 switch(@選択合算){ … } 納品txtファイル

Slide 47

Slide 47 text

アドベンチャー実装の流れ ©WFS テストと 修正  スクリプト  作業 シナリオ納品 Unity x Luaの ホットリロード Layout命令 シナリオ変換内製ツール 『Tulip』

Slide 48

Slide 48 text

アドベンチャーの抱える問題 ©WFS ● 基本構造はアドベンチャー内で共通 ⇒ 同じ命令をたくさん書く必要がある

Slide 49

Slide 49 text

アドベンチャーの抱える問題 ©WFS ● 基本構造はアドベンチャー内で共通 ⇒ 同じ命令をたくさん書く必要がある テンプレート化して 自動で作れるようにしたい

Slide 50

Slide 50 text

シンプルな演出例 ©WFS 茅森月歌 和泉ユキ

Slide 51

Slide 51 text

シンプルな演出例 ©WFS

Slide 52

Slide 52 text

3つに分けて考える ©WFS レイアウト1 レイアウト2 レイアウト3 キャラクター1人表示 キャラクター2人表示 キャラクター1人表示

Slide 53

Slide 53 text

シンプルな演出例:レイアウト1 ©WFS

Slide 54

Slide 54 text

シンプルな演出例:レイアウト1 ©WFS 茅森月歌が フェードイン

Slide 55

Slide 55 text

シンプルな演出例:レイアウト2 ©WFS

Slide 56

Slide 56 text

シンプルな演出例:レイアウト2 ©WFS 茅森月歌が 左へスライド

Slide 57

Slide 57 text

シンプルな演出例:レイアウト2 ©WFS 和泉ユキが フェードイン + 左へスライド

Slide 58

Slide 58 text

シンプルな演出例:レイアウト2 ©WFS 背景が 少しスライド

Slide 59

Slide 59 text

シンプルな演出例:レイアウト3 ©WFS

Slide 60

Slide 60 text

シンプルな演出例:レイアウト3 ©WFS 茅森月歌が 右へスライド

Slide 61

Slide 61 text

シンプルな演出例:レイアウト3 ©WFS 和泉ユキが フェードアウト + 右へスライド

Slide 62

Slide 62 text

シンプルな演出例:レイアウト3 ©WFS 背景が 少しスライド

Slide 63

Slide 63 text

シンプルな演出例から分かること ©WFS 演出にはパターンがある ● 表示切り替えはフェード ● 立ち位置の変更はスライド

Slide 64

Slide 64 text

シンプルな演出例から分かること ©WFS 演出にはパターンがある ● 表示切り替えはフェード ● 立ち位置の変更はスライド キャラクターの表示の演出を 自動化することができそう

Slide 65

Slide 65 text

Layout命令(内製) ©WFS ● 演出を直感的に作ることができる仕組み ● 立ち位置の変化から途中経過を補完

Slide 66

Slide 66 text

Layout命令(内製) ©WFS ● 背景をもとにステージを作る Layout.createStage(背景インスタンス)

Slide 67

Slide 67 text

Layout命令(内製) ©WFS ● 背景をもとにステージを作る ● ADV立ち絵を表示する Layout.createStage(背景インスタンス) Layout.show(キャラクターインスタンス, ...)

Slide 68

Slide 68 text

Layout命令で考えてみる ©WFS Layout.show("茅森", "和泉") Layout.show("茅森") Layout.show("茅森") レイアウト1 レイアウト2 レイアウト3

Slide 69

Slide 69 text

Layout命令で考えてみる ©WFS Layout.show("茅森", "和泉") Layout.show("茅森") Layout.show("茅森") レイアウト1 レイアウト2 レイアウト3 同じ命令でも 呼び出しタイミングによって 表示演出が変わる!

Slide 70

Slide 70 text

Layout命令で考えてみる ©WFS -- bgからステージを作る Layout.createStage(bg) -- 茅森表示 Layout.show(RKayamori) Adv.talk(RKayamori, [[1人レイアウト]]) -- 茅森、和泉表示 Layout.show(RKayamori, YIzumi) Adv.talk(YIzumi, [[2人レイアウト]]) -- 和泉表示 Layout.show(RKayamori) Adv.talk(RKayamori, [[1人レイアウト]])

Slide 71

Slide 71 text

-- bgからステージを作る Layout.createStage(bg) -- 茅森表示 Layout.show(RKayamori) Adv.talk(RKayamori, [[1人レイアウト]]) -- 茅森、和泉表示 Layout.show(RKayamori, YIzumi) Adv.talk(YIzumi, [[2人レイアウト]]) -- 和泉表示 Layout.show(RKayamori) Adv.talk(RKayamori, [[1人レイアウト]]) Layout命令で考えてみる ©WFS

Slide 72

Slide 72 text

-- bgからステージを作る Layout.createStage(bg) -- 茅森表示 Layout.show(RKayamori) Adv.talk(RKayamori, [[1人レイアウト]]) -- 茅森、和泉表示 Layout.show(RKayamori, YIzumi) Adv.talk(YIzumi, [[2人レイアウト]]) -- 和泉表示 Layout.show(RKayamori) Adv.talk(RKayamori, [[1人レイアウト]]) Layout命令で考えてみる ©WFS

Slide 73

Slide 73 text

-- bgからステージを作る Layout.createStage(bg) -- 茅森表示 Layout.show(RKayamori) Adv.talk(RKayamori, [[1人レイアウト]]) -- 茅森、和泉表示 Layout.show(RKayamori, YIzumi) Adv.talk(YIzumi, [[2人レイアウト]]) -- 和泉表示 Layout.show(RKayamori) Adv.talk(RKayamori, [[1人レイアウト]]) Layout命令で考えてみる ©WFS

Slide 74

Slide 74 text

-- bgからステージを作る Layout.createStage(bg) -- 茅森表示 Layout.show(RKayamori) Adv.talk(RKayamori, [[1人レイアウト]]) -- 茅森、和泉表示 Layout.show(RKayamori, YIzumi) Adv.talk(YIzumi, [[2人レイアウト]]) -- 和泉表示 Layout.show(RKayamori) Adv.talk(RKayamori, [[1人レイアウト]]) Layout命令で考えてみる ©WFS

Slide 75

Slide 75 text

Layout命令で考えてみる ©WFS -- bgからステージを作る Layout.createStage(bg) -- 茅森表示 Layout.show(RKayamori) Adv.talk(RKayamori, [[1人レイアウト]]) -- 茅森、和泉表示 Layout.show(RKayamori, YIzumi) Adv.talk(YIzumi, [[2人レイアウト]]) -- 和泉表示 Layout.show(RKayamori) Adv.talk(RKayamori, [[1人レイアウト]])

Slide 76

Slide 76 text

シンプルな演出例 ©WFS -- bgからステージを作る Layout.createStage(bg) -- 茅森表示 Layout.show(RKayamori) Adv.talk(RKayamori, [[1人レイアウト]]) -- 茅森、和泉表示 Layout.show(RKayamori, YIzumi) Adv.talk(YIzumi, [[2人レイアウト]]) -- 和泉表示 Layout.show(RKayamori) Adv.talk(RKayamori, [[1人レイアウト]]) 茅森フェードイン 茅森スライド 和泉フェードイン + スライド 背景 スライド 茅森スライド 和泉フェードアウト + スライド 背景 スライド

Slide 77

Slide 77 text

他にもさまざまなLua命令がある ©WFS ● ADV ● Field ● Battle ● etc…

Slide 78

Slide 78 text

アドベンチャー実装の流れ ©WFS テストと 修正  スクリプト  作業 シナリオ納品 Unity x Luaの ホットリロード Layout命令 シナリオ変換内製ツール 『Tulip』

Slide 79

Slide 79 text

開発サイクル ©WFS 確認 修正 ファイルを Reimport Editor 再生 Editor 止める

Slide 80

Slide 80 text

開発サイクル ©WFS 確認 修正 ファイルを Reimport Editor 再生 Editor 止める

Slide 81

Slide 81 text

開発サイクル ©WFS 確認 修正 ファイルを Reimport Editor 再生 Editor 止める

Slide 82

Slide 82 text

開発サイクル ©WFS 確認 修正 ファイルを Reimport Editor 再生 Editor 止める

Slide 83

Slide 83 text

開発サイクル ©WFS 確認 修正 ファイルを Reimport Editor 再生 Editor 止める

Slide 84

Slide 84 text

開発サイクル ©WFS 確認 修正 ファイルを Reimport Editor 再生 Editor 止める

Slide 85

Slide 85 text

開発サイクル ©WFS 確認 修正 ファイルを Reimport Editor 再生 Editor 止める

Slide 86

Slide 86 text

開発サイクル ©WFS 確認 修正 ファイルを Reimport Editor 再生 Editor 止める

Slide 87

Slide 87 text

Unity x Lua ©WFS ホットリロードが可能 ● エディタを再生したまま ● Luaスクリプトのリロード

Slide 88

Slide 88 text

Unity x Lua ©WFS ホットリロードが可能 ● エディタを再生したまま ● Luaスクリプトのリロード 開発スピードが早い!

Slide 89

Slide 89 text

©WFS ホットリロード

Slide 90

Slide 90 text

まとめ ©WFS

Slide 91

Slide 91 text

©WFS ● ヘブンバーンズレッドのリッチなアドベンチャーは 様々な工夫を凝らしてできている まとめ

Slide 92

Slide 92 text

©WFS ● ヘブンバーンズレッドのリッチなアドベンチャーは 様々な工夫を凝らしてできている ● UnityとLuaの相性が結構いい ○ エディタを再生したままホットリロードが可能 ○ インポート不要 まとめ

Slide 93

Slide 93 text

©WFS ● ヘブンバーンズレッドのリッチなアドベンチャーは 様々な工夫を凝らしてできている ● UnityとLuaの相性が結構いい ○ エディタを再生したままホットリロードが可能 ○ インポート不要 ● アドベンチャーの量産がしやすい まとめ

Slide 94

Slide 94 text

Unity Timelineで作る! 「ヘブンバーンズレッド」 バトルスキルシステム作成事例 伊藤栄治 ©WFS

Slide 95

Slide 95 text

目次 ● Unity Timelineとは (ざっくり) ● ヘブンバーンズレッドでの利用事例 ○ Timelineで作るスキル演出 ○ Timeline Markerで繋がるシステムと演出 ● おまけ: Luaで繋がるバトルとカットシーン ● まとめ ©WFS

Slide 96

Slide 96 text

©WFS Unity Timelineとは (ざっくり)

Slide 97

Slide 97 text

● カットシーンなどの映画的なコンテンツを作りやすい機能 ○ 複数のオブジェクトを同時に管理しやすい Unity Timelineとは (ざっくり) ©WFS

Slide 98

Slide 98 text

©WFS ヘブンバーンズレッドでの利用事例

Slide 99

Slide 99 text

● スキル演出 ヘブンバーンズレッドでの利用事例 ©WFS

Slide 100

Slide 100 text

● スキル演出 ● 敵の攻撃 ©WFS ヘブンバーンズレッドでの利用事例

Slide 101

Slide 101 text

● スキル演出 ● 敵の攻撃 ● カットシーン ©WFS ヘブンバーンズレッドでの利用事例

Slide 102

Slide 102 text

● スキル演出 ● 敵の攻撃 ● カットシーン ● 敵/味方の登場演出 ● など ©WFS ヘブンバーンズレッドでの利用事例

Slide 103

Slide 103 text

©WFS スキルを構成する要素

Slide 104

Slide 104 text

スキルを構成する要素 ©WFS

Slide 105

Slide 105 text

©WFS キャラクターが 飛びかかるモーション スキルを構成する要素

Slide 106

Slide 106 text

©WFS ワープエフェクト再生 キャラクターが 飛びかかるモーション スキルを構成する要素

Slide 107

Slide 107 text

©WFS ワープエフェクト再生 ボイス再生 キャラクターが 飛びかかるモーション スキルを構成する要素

Slide 108

Slide 108 text

©WFS カメラをぐるっと回して敵の方に向ける スキルを構成する要素

Slide 109

Slide 109 text

©WFS カメラをぐるっと回して敵の方に向ける この辺りからゆっくり背景を暗くする スキルを構成する要素

Slide 110

Slide 110 text

©WFS 別のカメラに切り替えて正面から映す スキルを構成する要素

Slide 111

Slide 111 text

©WFS 斬りかかる 効果音を再生 スキルを構成する要素

Slide 112

Slide 112 text

©WFS 斬撃のエフェクトを再生 斬りかかる 効果音を再生 スキルを構成する要素

Slide 113

Slide 113 text

©WFS 斬撃のエフェクトを再生 ゲージを減らす ダメージを表示 斬りかかる 効果音を再生 スキルを構成する要素

Slide 114

Slide 114 text

©WFS 2撃目が命中するタイミングに合わせて.. スキルを構成する要素

Slide 115

Slide 115 text

©WFS BREAK! ラベルを表示 スキルを構成する要素

Slide 116

Slide 116 text

©WFS BREAK! ラベルを表示 最終ヒット時に与ダメージの 合計値や平均値を表示 スキルを構成する要素

Slide 117

Slide 117 text

©WFS BREAK! ラベルを表示 BREAK時や撃破時は ヒットストップを強めにかける 最終ヒット時に与ダメージの 合計値や平均値を表示 スキルを構成する要素

Slide 118

Slide 118 text

©WFS Timelineで制御すべきスキルの構成要素

Slide 119

Slide 119 text

● キャラクターの動き ● カメラの動き Timelineで制御すべきスキルの構成要素 ©WFS

Slide 120

Slide 120 text

● キャラクターの動き ● カメラの動き ● エフェクト再生 ○ 再生するタイミングや位置を演出に合わせて調整するため ● 効果音/ボイス再生 ○ 再生するタイミングを演出に合わせて調整するため Timelineで制御すべきスキルの構成要素 ©WFS

Slide 121

Slide 121 text

● キャラクターの動き ● カメラの動き ● エフェクト再生 ○ 再生するタイミングや位置を演出に合わせて調整するため ● 効果音/ボイス再生 ○ 再生するタイミングを演出に合わせて調整するため ● ヒットタイミング ○ ダメージ量表示や体力ゲージの変動を演出に組み込むため、 攻撃が命中したタイミングにダメージを反映する必要がある Timelineで制御すべきスキルの構成要素 ©WFS

Slide 122

Slide 122 text

©WFS スキルTimelineの実例紹介

Slide 123

Slide 123 text

©WFS スキルTimelineの実例紹介

Slide 124

Slide 124 text

©WFS スキルTimelineの実例紹介 キャラクター関連

Slide 125

Slide 125 text

©WFS スキルTimelineの実例紹介 キャラクター関連 キャラクターの transformやanimatorを 動的にBind(リンク)する

Slide 126

Slide 126 text

©WFS スキルTimelineの実例紹介 カメラ関連

Slide 127

Slide 127 text

©WFS スキルTimelineの実例紹介 カメラ関連 アクティブトラックを使って 3つのカメラを切り替えている

Slide 128

Slide 128 text

©WFS スキルTimelineの実例紹介 カメラ関連 アクティブトラックを使って 3つのカメラを切り替えている

Slide 129

Slide 129 text

©WFS スキルTimelineの実例紹介 カメラ関連 アクティブトラックを使って 3つのカメラを切り替えている ココ

Slide 130

Slide 130 text

©WFS スキルTimelineの実例紹介 エフェクト関連

Slide 131

Slide 131 text

©WFS スキルTimelineの実例紹介 エフェクト関連 各種エフェクトの 再生タイミングを制御

Slide 132

Slide 132 text

©WFS スキルTimelineの実例紹介 飛びかかるモーション 1つめのカメラ ワープエフェクト

Slide 133

Slide 133 text

©WFS スキルTimelineの実例紹介 着地モーション 2つめのカメラ

Slide 134

Slide 134 text

©WFS スキルTimelineの実例紹介 攻撃モーション 3つめのカメラ 斬撃エフェクト

Slide 135

Slide 135 text

©WFS スキルTimelineの実例紹介

Slide 136

Slide 136 text

©WFS スキルTimelineの実例紹介 ヒット関連 サウンド関連

Slide 137

Slide 137 text

©WFS Timeline Markerで繋がる システムと演出

Slide 138

Slide 138 text

● Marker: Timelineに配置するとその時点で関数呼び出しが行えるもの ○ 扱い方はAnimation Eventと近い Timeline Markerで繋がるシステムと演出 ©WFS これ

Slide 139

Slide 139 text

● Marker: Timelineに配置するとその時点で関数呼び出しが行えるもの ○ 扱い方はAnimation Eventと近い ● Markerは自作することができる ○ 引数の設定も自由に行える Timeline Markerで繋がるシステムと演出 ©WFS これ

Slide 140

Slide 140 text

● Marker: Timelineに配置するとその時点で関数呼び出しが行えるもの ○ 扱い方はAnimation Eventと近い ● Markerは自作することができる ○ 引数の設定も自由に行える Timeline Markerで繋がるシステムと演出 ©WFS 2つめのピンは ボイスマーカー

Slide 141

Slide 141 text

● Marker: Timelineに配置するとその時点で関数呼び出しが行えるもの ○ 扱い方はAnimation Eventと近い ● Markerは自作することができる ○ 引数の設定も自由に行える Timeline Markerで繋がるシステムと演出 ©WFS 2つめのピンは ボイスマーカー ボイス用の引数

Slide 142

Slide 142 text

● Marker: Timelineに配置するとその時点で関数呼び出しが行えるもの ○ 扱い方はAnimation Eventと近い ● Markerは自作することができる ○ 引数の設定も自由に行える Timeline Markerで繋がるシステムと演出 ©WFS 3つめのピンは サウンドマーカー 設定項目が異なる

Slide 143

Slide 143 text

● ヒットマーカー ○ 攻撃のヒットタイミングを設定するマーカー Timeline Markerで繋がるシステムと演出 ヒットストップ情報 これ

Slide 144

Slide 144 text

[Serializable, DisplayName("ヒットマーカー")]
 [CustomStyle("HitMarker")]
 public class HitMarker : Marker, INotification, INotificationOptionProvider 
 {
 public PropertyName id => new PropertyName("Hit");
 
 public bool isHitStop = false;
 public float hitStopSpeed = BattleConstant.DEFAULT_HIT_STOP_SPEED ;
 public float hitStopTime = BattleConstant.DEFAULT_HIT_STOP_TIME ;
 
 public int indexOnTrack { get; set; } // トラック内の同種マーカーのうち何番目か 
 public int countOnTrack { get; set; } // トラック内にいくつ同種のマーカーがあるか 
 public bool isFinal => indexOnTrack == countOnTrack - 1;
 }
 HitMarker.cs Timeline Markerで繋がるシステムと演出

Slide 145

Slide 145 text

[Serializable, DisplayName("ヒットマーカー")]
 [CustomStyle("HitMarker")]
 public class HitMarker : Marker, INotification, INotificationOptionProvider 
 {
 public PropertyName id => new PropertyName("Hit");
 
 public bool isHitStop = false;
 public float hitStopSpeed = BattleConstant.DEFAULT_HIT_STOP_SPEED ;
 public float hitStopTime = BattleConstant.DEFAULT_HIT_STOP_TIME ;
 
 public int indexOnTrack { get; set; } // トラック内の同種マーカーのうち何番目か 
 public int countOnTrack { get; set; } // トラック内にいくつ同種のマーカーがあるか 
 public bool isFinal => indexOnTrack == countOnTrack - 1;
 }
 HitMarker.cs Timeline Markerで繋がるシステムと演出 引数で入力する部分

Slide 146

Slide 146 text

[Serializable, DisplayName("ヒットマーカー")]
 [CustomStyle("HitMarker")]
 public class HitMarker : Marker, INotification, INotificationOptionProvider 
 {
 public PropertyName id => new PropertyName("Hit");
 
 public bool isHitStop = false;
 public float hitStopSpeed = BattleConstant.DEFAULT_HIT_STOP_SPEED ;
 public float hitStopTime = BattleConstant.DEFAULT_HIT_STOP_TIME ;
 
 public int indexOnTrack { get; set; } // トラック内の同種マーカーのうち何番目か 
 public int countOnTrack { get; set; } // トラック内にいくつ同種のマーカーがあるか 
 public bool isFinal => indexOnTrack == countOnTrack - 1;
 }
 HitMarker.cs Timeline Markerで繋がるシステムと演出 マーカーに 持たせたい変数を定義

Slide 147

Slide 147 text

public class AttackPlayableTrack : TrackAsset
 {
 public void Awake()
 {
 // トラック内のマーカーに事前情報を渡す
 var hitMarkers = GetMarkers().OfType().OrderBy(m => m.time).ToList();
 for (var i = 0; i < hitMarkers.Count; i++)
 {
 var marker = hitMarkers[i];
 marker.indexOnTrack = i;
 marker.countOnTrack = hitMarkers.Count;
 }
 }
 }
 
 AttackPlayableTrack.cs Timeline Markerで繋がるシステムと演出

Slide 148

Slide 148 text

public class AttackPlayableTrack : TrackAsset
 {
 public void Awake()
 {
 // トラック内のマーカーに事前情報を渡す
 var hitMarkers = GetMarkers().OfType().OrderBy(m => m.time).ToList();
 for (var i = 0; i < hitMarkers.Count; i++)
 {
 var marker = hitMarkers[i];
 marker.indexOnTrack = i;
 marker.countOnTrack = hitMarkers.Count;
 }
 }
 }
 
 AttackPlayableTrack.cs Timeline Markerで繋がるシステムと演出 ヒットマーカーを 集めてきて

Slide 149

Slide 149 text

public class AttackPlayableTrack : TrackAsset
 {
 public void Awake()
 {
 // トラック内のマーカーに事前情報を渡す
 var hitMarkers = GetMarkers().OfType().OrderBy(m => m.time).ToList();
 for (var i = 0; i < hitMarkers.Count; i++)
 {
 var marker = hitMarkers[i];
 marker.indexOnTrack = i;
 marker.countOnTrack = hitMarkers.Count;
 }
 }
 }
 
 AttackPlayableTrack.cs Timeline Markerで繋がるシステムと演出 indexとcountを格納

Slide 150

Slide 150 text

HitReceiver.cs Timeline Markerで繋がるシステムと演出 public class HitReceiver : MonoBehaviour, INotificationReceiver
 {
 public void OnNotify(Playable origin, INotification notification, object context)
 {
 switch (notification)
 {
 case HitMarker marker:
 Hit(null, marker.indexOnTrack, marker.isFinal, marker.isHitStop, marker.hitStopSpeed, marker.hitStopTime);
 return;
 }
 }
 private static void Hit(int hitIndex, bool isFinal, bool isHitStop, float hitStopSpeed, float hitStopTime)
 {
 BattleModule.Instance.actionEntryManager.FireMainHit(hitIndex, isFinal, isHitStop, hitStopSpeed, hitStopTime); 
 }
 }


Slide 151

Slide 151 text

HitReceiver.cs Timeline Markerで繋がるシステムと演出 public class HitReceiver : MonoBehaviour, INotificationReceiver
 {
 public void OnNotify(Playable origin, INotification notification, object context)
 {
 switch (notification)
 {
 case HitMarker marker:
 Hit(null, marker.indexOnTrack, marker.isFinal, marker.isHitStop, marker.hitStopSpeed, marker.hitStopTime);
 return;
 }
 }
 private static void Hit(int hitIndex, bool isFinal, bool isHitStop, float hitStopSpeed, float hitStopTime)
 {
 BattleModule.Instance.actionEntryManager.FireMainHit(hitIndex, isFinal, isHitStop, hitStopSpeed, hitStopTime); 
 }
 }


Slide 152

Slide 152 text

HitReceiver.cs Timeline Markerで繋がるシステムと演出 public class HitReceiver : MonoBehaviour, INotificationReceiver
 {
 public void OnNotify(Playable origin, INotification notification, object context)
 {
 switch (notification)
 {
 case HitMarker marker:
 Hit(null, marker.indexOnTrack, marker.isFinal, marker.isHitStop, marker.hitStopSpeed, marker.hitStopTime);
 return;
 }
 }
 private static void Hit(int hitIndex, bool isFinal, bool isHitStop, float hitStopSpeed, float hitStopTime)
 {
 BattleModule.Instance.actionEntryManager.FireMainHit(hitIndex, isFinal, isHitStop, hitStopSpeed, hitStopTime); 
 }
 }


Slide 153

Slide 153 text

Timeline Markerで繋がるシステムと演出 ©WFS

Slide 154

Slide 154 text

©WFS おまけ Luaで繋がるバトルとカットシーン

Slide 155

Slide 155 text

Luaで繋がるバトルとカットシーン ©WFS

Slide 156

Slide 156 text

● 今回の例で必要な要件 Luaで繋がるバトルとカットシーン ©WFS

Slide 157

Slide 157 text

● 今回の例で必要な要件 ○ 敵がBREAKしたらバトルの進行を一時停止させる Luaで繋がるバトルとカットシーン ©WFS

Slide 158

Slide 158 text

● 今回の例で必要な要件 ○ 敵がBREAKしたらバトルの進行を一時停止させる ○ バトルの状態を残したまま会話パートを再生する Luaで繋がるバトルとカットシーン ©WFS

Slide 159

Slide 159 text

● 今回の例で必要な要件 ○ 敵がBREAKしたらバトルの進行を一時停止させる ○ バトルの状態を残したまま会話パートを再生する ○ カットシーンを再生する Luaで繋がるバトルとカットシーン ©WFS

Slide 160

Slide 160 text

● 今回の例で必要な要件 ○ 敵がBREAKしたらバトルの進行を一時停止させる ○ バトルの状態を残したまま会話パートを再生する ○ カットシーンを再生する ■ と同時にBGMをフェードアウトさせる Luaで繋がるバトルとカットシーン ©WFS

Slide 161

Slide 161 text

Luaで繋がるバトルとカットシーン ©WFS ● 今回の例で必要な要件 ○ 敵がBREAKしたらバトルの進行を一時停止させる ○ バトルの状態を残したまま会話パートを再生する ○ カットシーンを再生する ■ と同時にBGMをフェードアウトさせる ○ カットシーンの特定タイミングで別の戦闘BGMを再生する

Slide 162

Slide 162 text

● Luaスクリプトは並列で実行してバトルの状態を監視 Luaで繋がるバトルとカットシーン ©WFS

Slide 163

Slide 163 text

● Luaスクリプトは並列で実行してバトルの状態を監視 ● バトルからイベント通知が来たらバトルを一時停止して演出を行う Luaで繋がるバトルとカットシーン ©WFS

Slide 164

Slide 164 text

● Luaスクリプトは並列で実行してバトルの状態を監視 ● バトルからイベント通知が来たらバトルを一時停止して演出を行う ● 演出が終わったらバトルを再び再生する Luaで繋がるバトルとカットシーン ©WFS

Slide 165

Slide 165 text

● Luaスクリプトは並列で実行してバトルの状態を監視 ● バトルからイベント通知が来たらバトルを一時停止して演出を行う ● 演出が終わったらバトルを再び再生する Luaで繋がるバトルとカットシーン ©WFS バトル
 処理
 Lua Battle 敵がブレイク
 するまで待機


Slide 166

Slide 166 text

● Luaスクリプトは並列で実行してバトルの状態を監視 ● バトルからイベント通知が来たらバトルを一時停止して演出を行う ● 演出が終わったらバトルを再び再生する Luaで繋がるバトルとカットシーン ©WFS 敵のブレイクを 通知
 バトルを
 一時停止
 Luaイベント
 終了まで待機
 Lua Battle 敵がブレイク
 するまで待機
 バトル
 処理


Slide 167

Slide 167 text

● Luaスクリプトは並列で実行してバトルの状態を監視 ● バトルからイベント通知が来たらバトルを一時停止して演出を行う ● 演出が終わったらバトルを再び再生する Luaで繋がるバトルとカットシーン ©WFS Luaイベント終了まで待機
 ADV
 再生
 カットシーン再 生
 BGM フェード アウト
 Lua Battle

Slide 168

Slide 168 text

● Luaスクリプトは並列で実行してバトルの状態を監視 ● バトルからイベント通知が来たらバトルを一時停止して演出を行う ● 演出が終わったらバトルを再び再生する Luaで繋がるバトルとカットシーン ©WFS Luaイベント終了まで待機
 ADV
 再生
 カットシーン再 生
 BGM フェード アウト
 マーカー通知ま で待機
 Lua Battle

Slide 169

Slide 169 text

● Luaスクリプトは並列で実行してバトルの状態を監視 ● バトルからイベント通知が来たらバトルを一時停止して演出を行う ● 演出が終わったらバトルを再び再生する Luaで繋がるバトルとカットシーン Luaイベント終了まで待機
 ADV
 再生
 カットシーン再 生
 BGM フェード アウト
 マーカー通知ま で待機
 BGM再生用 マーカー通知
 BGM
 再生
 Lua Battle ©WFS Timeline

Slide 170

Slide 170 text

● Luaスクリプトは並列で実行してバトルの状態を監視 ● バトルからイベント通知が来たらバトルを一時停止して演出を行う ● 演出が終わったらバトルを再び再生する Luaで繋がるバトルとカットシーン ©WFS Luaイベント
 終了まで待機
 カットシーン再生終 了まで待機
 Lua Battle 再生終了
 通知
 Timeline

Slide 171

Slide 171 text

● Luaスクリプトは並列で実行してバトルの状態を監視 ● バトルからイベント通知が来たらバトルを一時停止して演出を行う ● 演出が終わったらバトルを再び再生する Luaで繋がるバトルとカットシーン ©WFS Luaイベント
 終了まで待機
 カットシーン再生終 了まで待機
 第二形態バトル開 始を通知
 Luaイベント
 終了まで待機
 Luaイベント終了を 通知
 Lua Battle 再生終了
 通知
 Timeline

Slide 172

Slide 172 text

● Luaスクリプトは並列で実行してバトルの状態を監視 ● バトルからイベント通知が来たらバトルを一時停止して演出を行う ● 演出が終わったらバトルを再び再生する Luaで繋がるバトルとカットシーン Luaイベント
 終了まで待機
 カットシーン再生終 了まで待機
 再生終了
 通知
 第二形態バトル開 始を通知
 Luaイベント
 終了まで待機
 Luaイベント終了を 通知
 バトル 再開
 Lua Battle Timeline ©WFS

Slide 173

Slide 173 text

Luaで繋がるバトルとカットシーン ©WFS

Slide 174

Slide 174 text

©WFS まとめ

Slide 175

Slide 175 text

● ヘブンバーンズレッドではTimelineが様々なシチュエーションで活躍 ○ スキル、カットシーン、登場演出など まとめ ©WFS

Slide 176

Slide 176 text

まとめ ©WFS ● ヘブンバーンズレッドではTimelineが様々なシチュエーションで活躍 ○ スキル、カットシーン、登場演出など ● Timelineで演出のほぼ全てを制御している ○ モーション、カメラ、エフェクト、サウンド、ラベル表示など

Slide 177

Slide 177 text

まとめ ©WFS ● ヘブンバーンズレッドではTimelineが様々なシチュエーションで活躍 ○ スキル、カットシーン、登場演出など ● Timelineで演出のほぼ全てを制御している ○ モーション、カメラ、エフェクト、サウンド、ラベル表示など ● Markerを活用して演出からシステム処理のタイミングを制御 ○ 演出側で指定したタイミングでダメージ処理やSE再生などを行う

Slide 178

Slide 178 text

● ヘブンバーンズレッドではTimelineが様々なシチュエーションで活躍 ○ スキル、カットシーン、登場演出など ● Timelineで演出のほぼ全てを制御している ○ モーション、カメラ、エフェクト、サウンド、ラベル表示など ● Markerを活用して演出からシステム処理のタイミングを制御 ○ 演出側で指定したタイミングでダメージ処理やSE再生などを行う ● Lua処理を走らせることでバトル中に自在に演出を再生 ○ 会話パートやカットシーンを指定タイミングで差し込む まとめ ©WFS

Slide 179

Slide 179 text

179