Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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

gree_tech
October 25, 2022

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

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

gree_tech

October 25, 2022
Tweet

More Decks by gree_tech

Other Decks in Technology

Transcript

  1. 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ファイル
  2. 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
  3. 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
  4. 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
  5. 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
  6. 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
  7. 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
  8. 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
  9. 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
  10. 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ファイル
  11. 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人レイアウト]])
  12. -- 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
  13. -- 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
  14. -- 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
  15. -- 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
  16. 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人レイアウト]])
  17. シンプルな演出例 ©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人レイアウト]]) 茅森フェードイン 茅森スライド 和泉フェードイン + スライド 背景 スライド 茅森スライド 和泉フェードアウト + スライド 背景 スライド
  18. 目次 • Unity Timelineとは (ざっくり) • ヘブンバーンズレッドでの利用事例 ◦ Timelineで作るスキル演出 ◦

    Timeline Markerで繋がるシステムと演出 • おまけ: Luaで繋がるバトルとカットシーン • まとめ ©WFS
  19. • キャラクターの動き • カメラの動き • エフェクト再生 ◦ 再生するタイミングや位置を演出に合わせて調整するため • 効果音/ボイス再生

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

    ◦ 再生するタイミングを演出に合わせて調整するため • ヒットタイミング ◦ ダメージ量表示や体力ゲージの変動を演出に組み込むため、 攻撃が命中したタイミングにダメージを反映する必要がある Timelineで制御すべきスキルの構成要素 ©WFS
  21. [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で繋がるシステムと演出
  22. [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で繋がるシステムと演出 引数で入力する部分
  23. [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で繋がるシステムと演出 マーカーに 持たせたい変数を定義
  24. public class AttackPlayableTrack : TrackAsset
 {
 public void Awake()
 {


    // トラック内のマーカーに事前情報を渡す
 var hitMarkers = GetMarkers().OfType<HitMarker>().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で繋がるシステムと演出
  25. public class AttackPlayableTrack : TrackAsset
 {
 public void Awake()
 {


    // トラック内のマーカーに事前情報を渡す
 var hitMarkers = GetMarkers().OfType<HitMarker>().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で繋がるシステムと演出 ヒットマーカーを 集めてきて
  26. public class AttackPlayableTrack : TrackAsset
 {
 public void Awake()
 {


    // トラック内のマーカーに事前情報を渡す
 var hitMarkers = GetMarkers().OfType<HitMarker>().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を格納
  27. 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); 
 }
 }

  28. 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); 
 }
 }

  29. 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); 
 }
 }

  30. • ヘブンバーンズレッドではTimelineが様々なシチュエーションで活躍 ◦ スキル、カットシーン、登場演出など • Timelineで演出のほぼ全てを制御している ◦ モーション、カメラ、エフェクト、サウンド、ラベル表示など • Markerを活用して演出からシステム処理のタイミングを制御

    ◦ 演出側で指定したタイミングでダメージ処理やSE再生などを行う • Lua処理を走らせることでバトル中に自在に演出を再生 ◦ 会話パートやカットシーンを指定タイミングで差し込む まとめ ©WFS