$30 off During Our Annual Pro Sale. View Details »

ヘブンバーンズレッド × バトル × アドベンチャー 〜WFSのゲーム制作・演出の最大化手法〜

ヘブンバーンズレッド × バトル × アドベンチャー 〜WFSのゲーム制作・演出の最大化手法〜

GREE Tech Conference 2022で発表された資料です。
https://techcon.gree.jp/2022/session/TrackB-2

gree_tech
PRO

October 25, 2022
Tweet

More Decks by gree_tech

Other Decks in Technology

Transcript

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  7. 大枠の役務分担(シナリオ/ボイス周り)

    ©WFS
    ヘブンバーンズレッドとは

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  36. 3 x 3 選択肢
    ©WFS

    View Slide

  37. 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ファイル

    View Slide

  38. 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

    View Slide

  39. 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

    View Slide

  40. 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

    View Slide

  41. 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

    View Slide

  42. 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

    View Slide

  43. 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

    View Slide

  44. 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

    View Slide

  45. 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

    View Slide

  46. 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ファイル

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  51. シンプルな演出例
    ©WFS

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  70. 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人レイアウト]])

    View Slide

  71. -- 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

    View Slide

  72. -- 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

    View Slide

  73. -- 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

    View Slide

  74. -- 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

    View Slide

  75. 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人レイアウト]])

    View Slide

  76. シンプルな演出例
    ©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人レイアウト]])
    茅森フェードイン
    茅森スライド
    和泉フェードイン + スライド
    背景 スライド
    茅森スライド
    和泉フェードアウト + スライド
    背景 スライド

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  89. ©WFS
    ホットリロード

    View Slide

  90. まとめ
    ©WFS

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  122. ©WFS
    スキルTimelineの実例紹介

    View Slide

  123. ©WFS
    スキルTimelineの実例紹介

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  135. ©WFS
    スキルTimelineの実例紹介

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  144. [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で繋がるシステムと演出

    View Slide

  145. [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で繋がるシステムと演出
    引数で入力する部分

    View Slide

  146. [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で繋がるシステムと演出
    マーカーに
    持たせたい変数を定義

    View Slide

  147. 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で繋がるシステムと演出

    View Slide

  148. 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で繋がるシステムと演出
    ヒットマーカーを
    集めてきて

    View Slide

  149. 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を格納

    View Slide

  150. 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);

    }

    }


    View Slide

  151. 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);

    }

    }


    View Slide

  152. 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);

    }

    }


    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    処理

    Lua
    Battle
    敵がブレイク

    するまで待機


    View Slide

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

    バトルを

    一時停止

    Luaイベント

    終了まで待機

    Lua
    Battle
    敵がブレイク

    するまで待機

    バトル

    処理


    View Slide

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

    ADV

    再生

    カットシーン再
    生

    BGM
    フェード
    アウト

    Lua
    Battle

    View Slide

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

    ADV

    再生

    カットシーン再
    生

    BGM
    フェード
    アウト

    マーカー通知ま
    で待機

    Lua
    Battle

    View Slide

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

    ADV

    再生

    カットシーン再
    生

    BGM
    フェード
    アウト

    マーカー通知ま
    で待機

    BGM再生用
    マーカー通知

    BGM

    再生

    Lua
    Battle
    ©WFS
    Timeline

    View Slide

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

    終了まで待機

    カットシーン再生終
    了まで待機

    Lua
    Battle
    再生終了

    通知

    Timeline

    View Slide

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

    終了まで待機

    カットシーン再生終
    了まで待機

    第二形態バトル開
    始を通知

    Luaイベント

    終了まで待機

    Luaイベント終了を
    通知

    Lua
    Battle
    再生終了

    通知

    Timeline

    View Slide

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

    終了まで待機

    カットシーン再生終
    了まで待機

    再生終了

    通知

    第二形態バトル開
    始を通知

    Luaイベント

    終了まで待機

    Luaイベント終了を
    通知

    バトル
    再開

    Lua
    Battle
    Timeline
    ©WFS

    View Slide

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

    View Slide

  174. ©WFS
    まとめ

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  179. 179


    View Slide