Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Lua文化の伝承!? WFSにおけるイベントスクリプト活用術〜すべてはより良いコンテンツ制作のために〜

GREE Tech Conference 2020 で発表された資料です。
https://techcon.gree.jp/2020/session/Session-7

Related Books

Free with a 30 day trial from Scribd

See all
  • Be the first to comment

  • Be the first to like this

Lua文化の伝承!? WFSにおけるイベントスクリプト活用術〜すべてはより良いコンテンツ制作のために〜

  1. 1. Lua文化の伝承!? WFSにおけるイベントスクリプト活用術 〜すべてはより良いコンテンツ制作のために〜 2020/9/18 ⻄⽥ 綾佑
  2. 2. 西田 綾佑 (Nishida Ryosuke) WFS / Technical Direction @hosi_mo 2014年 東京大学大学院 情報理工学系研究科修了 株式会社 WFS
  3. 3. 消滅都市 (2014/5/26~) アナザーエデン (2017/4/12~) ほか複数 2014年2月21日設立 現在7年目のゲームスタジオ 株式会社 WFS
  4. 4. Lua
  5. 5. プレイ動画
  6. 6. 目次 • 既存資産のイベントスクリプトの紹介 • 新たなスクリプトシステムの構築
  7. 7. 目次 • 既存資産のイベントスクリプトの紹介 • 新たなスクリプトシステムの構築
  8. 8. フィールド 部屋の中へ移動 バルーンを表示 タップするとプレイヤーのHPを満タンにする
  9. 9. シナリオ ストーリーNまで到達していたら、 クエスト解放
  10. 10. バトル 特定のボスでHPが一定以上削れたら 特殊ギミック発動 等
  11. 11. デモ 実装を見てみましょう
  12. 12. 二度寝
  13. 13. 鬼ごっこ
  14. 14. 余談 鬼のようなミニゲームスクリプト
  15. 15. 製作体制 フィールド シナリオ バトル - シナリオ演出 - キャラ劇の演出 - フィールド作成 (マップエディタ) - フィールドギミック組み込み - フィールドパラメータ調整 - fogや天候調整 - シェーダプログラム調整 - レベルデザイン - バトルギミック設計 レベルデザインから 実装まで行う 演出から 実装まで行う フィールドデザインから 実装まで行う
  16. 16. 製作体制 チーム内に イベントスクリプト(Lua)を記述できる人が多数 人によってそれぞれに味わい深いイベントが組まれる
  17. 17. イベントスクリプト ランタイム(ゲーム) APIリスト ランタイムがLuaに公開してるAPI数 500以上 バトル カメラ制御 フィールドオブジェクト制御 UI アイテム サウンド、ムービー ポストエフェクト 移動 フラグ - 約 50 API - 約 30 API - 約 150 API - 約 20 API - 約 10 API - 約 20 API - 約 100 API - 約 30 API - 約 100 API
  18. 18. スクリプトの規模 リリース時(2017年4月) : 63万行 20170531 709077 +72238 20170630 804724 +95647 20170731 861798 +57074 1年で100万行以上のペース 現在(2020年9月) : 443万行
  19. 19. イベントスクリプトに 最大限の裁量を
  20. 20. 目次 • 既存資産のイベントスクリプトの紹介 • 新たなスクリプトシステムの構築
  21. 21. のまえに
  22. 22. 反省点
  23. 23. 反省点 • 習熟に時間がかかる • プログラマ向けのインターフェース • キャラクターと会話するために準備することが多い • 血塗られたバルオキー
  24. 24. 24 キャラクターと会話するためのコード
  25. 25. 25 — イベントシーンアクティベート用フラグ local talk_start = false — エリアに入ると呼ばれる local function regist(time) — フィールドの特定オブジェクトにタッチすると Object_setTouchCB( “オブジェクト名”, function () — フラグが上がる talk_start = true end ) end -- trueを返すとイベントシーン起動 local function activate(time, state) if state ~= "update" then return false end return talk_start end キャラクターと会話するためのコード
  26. 26. 26 — イベントシーンアクティベート用フラグ local talk_start = false — エリアに入ると呼ばれる local function regist(time) — フィールドの特定オブジェクトにタッチすると Object_setTouchCB( “オブジェクト名”, function () — フラグが上がる talk_start = true end ) end -- trueを返すとイベントシーン起動 local function activate(time, state) if state ~= "update" then return false end return talk_start end キャラクターと会話するためのコード
  27. 27. 27 — イベントシーンアクティベート用フラグ local talk_start = false — エリアに入ると呼ばれる local function regist(time) — フィールドの特定オブジェクトにタッチすると Object_setTouchCB( “オブジェクト名”, function () — フラグが上がる talk_start = true end ) end -- trueを返すとイベントシーン起動 local function activate(time, state) if state ~= "update" then return false end return talk_start end キャラクターと会話するためのコード
  28. 28. 28 — イベントシーンアクティベート用フラグ local talk_start = false — エリアに入ると呼ばれる local function regist(time) — フィールドの特定オブジェクトにタッチすると Object_setTouchCB( “オブジェクト名”, function () — フラグが上がる talk_start = true end ) end -- trueを返すとイベントシーン起動 local function activate(time, state) if state ~= "update" then return false end return talk_start end キャラクターと会話するためのコード
  29. 29. 29 — イベントシーンアクティベート用フラグ local talk_start = false — エリアに入ると呼ばれる local function regist(time) — フィールドの特定オブジェクトにタッチすると Object_setTouchCB( “オブジェクト名”, function () — フラグが上がる talk_start = true end ) end -- trueを返すとイベントシーン起動 local function activate(time, state) if state ~= "update" then return false end return talk_start end キャラクターと会話するためのコード
  30. 30. 30 キャラクターと会話するためのコード — イベントシーン開始されたらseqテーブルを実行 local function update(time) if seq:exec(time) == Enum_SequenceState.FINISH then return true end return false end — seqテーブル定義 local seq = Sequence_create("main", funcTable) — seqテーブル中身 local funcTable = { CustomSeq_faceTo(label_statue_man, CBO_PARTY1), CustomSeq_faceTo(CBO_PARTY1, label_statue_man), Sequence_setDirB(CBO_PARTY1), Sequence_invoke( function() Object_talkWithOptions( label_statue_man, [[なあ見ろよ アルド。この石像の 素晴らしさ! まさに人類の英知と 生命の崇高さに感動するね!]], {talkerDisplayName = "若者"} )
  31. 31. 31 キャラクターと会話するためのコード — イベントシーン開始されたらseqテーブルを実行 local function update(time) if seq:exec(time) == Enum_SequenceState.FINISH then return true end return false end — seqテーブル定義 local seq = Sequence_create("main", funcTable) — seqテーブル中身 local funcTable = { CustomSeq_faceTo(label_statue_man, CBO_PARTY1), CustomSeq_faceTo(CBO_PARTY1, label_statue_man), Sequence_setDirB(CBO_PARTY1), Sequence_invoke( function() Object_talkWithOptions( label_statue_man, [[なあ見ろよ アルド。この石像の 素晴らしさ! まさに人類の英知と 生命の崇高さに感動するね!]], {talkerDisplayName = "若者"} )
  32. 32. わかりましたね
  33. 33. わかりませんね 😷
  34. 34. 34 血塗られたバルオキー --[[ かくれんボーイ イベント フィーネさらわれている時以外&子供のかくれんぼイベントフラグが1-2の時 、かくれんぼ坊やをアクティベート ]]— if Common_getGlobalFlag(global_flag_baruoki_kakurenboy) == 1 or Common_getGlobalFlag(global_flag_baruoki_kakurenboy) == 2 then if Common_isStoryStepComplete(story_step_story_step_ch1_12) == false or Common_isStoryStepComplete(story_step_story_step_ch11_1) then Common_registAreaEvent("kakurenboy", false) end end
  35. 35. 35 血塗られたバルオキー --[[ かくれんボーイ イベント フィーネさらわれている時以外&子供のかくれんぼイベントフラグが1-2の時 、かくれんぼ坊やをアクティベート ]]— if Common_getGlobalFlag(global_flag_baruoki_kakurenboy) == 1 or Common_getGlobalFlag(global_flag_baruoki_kakurenboy) == 2 then if Common_isStoryStepComplete(story_step_story_step_ch1_12) == false or Common_isStoryStepComplete(story_step_story_step_ch11_1) then Common_registAreaEvent("kakurenboy", false) end end
  36. 36. 36 血塗られたバルオキー --[[ かくれんボーイ イベント フィーネさらわれている時以外&子供のかくれんぼイベントフラグが1-2の時 、かくれんぼ坊やをアクティベート ]]— if Common_getGlobalFlag(global_flag_baruoki_kakurenboy) == 1 or Common_getGlobalFlag(global_flag_baruoki_kakurenboy) == 2 then if Common_isStoryStepComplete(story_step_story_step_ch1_12) == false or Common_isStoryStepComplete(story_step_story_step_ch11_1) then Common_registAreaEvent("kakurenboy", false) end end
  37. 37. 37 血塗られたバルオキー
  38. 38. 目次 • 既存資産のイベントスクリプトの紹介 • 新たなスクリプトシステムの構築
  39. 39. 目次 • 既存資産のイベントスクリプトの紹介 • 新たなスクリプトシステムの構築
  40. 40. 論点 • アナザーエデンで得た教訓を生かす • より馴染みやすく • より書きやすく • こわくないよ
  41. 41. 言い換えると • 習熟度が低い人でも貢献できる
  42. 42. はじめてスクリプトを書く人でも大丈夫
  43. 43. はじめてスクリプトを書く人でも大丈夫 • 上から順番に実行されるだけの環境 • まずはアドベンチャーパートの制作を例に
  44. 44. 44 上から順番に進む記述方法 Log(“はじまり”) Wait(0.5) Log(“0.5秒たちました”) WaitTap() Log(“タップしましたね”) Exit()
  45. 45. 45 上から順番に進む記述方法 Log(“はじまり”) Wait(0.5) Log(“0.5秒たちました”) WaitTap() Log(“タップしましたね”) Exit()
  46. 46. 46 上から順番に進む記述方法 Log(“はじまり”) Wait(0.5) Log(“0.5秒たちました”) WaitTap() Log(“タップしましたね”) Exit()
  47. 47. 47 上から順番に進む記述方法 Log(“はじまり”) Wait(0.5) Log(“0.5秒たちました”) WaitTap() Log(“タップしましたね”) Exit()
  48. 48. 48 上から順番に進む記述方法 Log(“はじまり”) Wait(0.5) Log(“0.5秒たちました”) WaitTap() Log(“タップしましたね”) Exit()
  49. 49. 49 上から順番に進む記述方法 Log(“はじまり”) Wait(0.5) Log(“0.5秒たちました”) WaitTap() Log(“タップしましたね”) Exit()
  50. 50. 50 上から順番に進む記述方法 Log(“はじまり”) Wait(0.5) Log(“0.5秒たちました”) WaitTap() Log(“タップしましたね”) Exit()
  51. 51. 51 朝のシーン local aldo = Adv.character("Aldo", "Default") local fine = Adv.character("Fine", "Default") -- ADV開始 Adv.talk(“???”, [[起きて]]) Adv.talk(aldo, [[…ん?]]) Adv.talk(fine, [[起きてってば]]) -- 0.5秒まつ Wait(0.5) local select = Adv.question([[起きる?]], [[はい|いいえ]]) if select == 0 then Adv.talk(aldo, [[おはよう]]) elseif select == 1 then Adv.talk(aldo, [[zzz]]) end Exit()
  52. 52. 52 朝のシーン local aldo = Adv.character("Aldo", "Default") local fine = Adv.character("Fine", "Default") -- ADV開始 Adv.talk(“???”, [[起きて]]) Adv.talk(aldo, [[…ん?]]) Adv.talk(fine, [[起きてってば]]) -- 0.5秒まつ Wait(0.5) local select = Adv.question([[起きる?]], [[はい|いいえ]]) if select == 0 then Adv.talk(aldo, [[おはよう]]) elseif select == 1 then Adv.talk(aldo, [[zzz]]) end Exit()
  53. 53. 53 朝のシーン local aldo = Adv.character("Aldo", "Default") local fine = Adv.character("Fine", "Default") -- ADV開始 Adv.talk(“???”, [[起きて]]) Adv.talk(aldo, [[…ん?]]) Adv.talk(fine, [[起きてってば]]) -- 0.5秒まつ Wait(0.5) local select = Adv.question([[起きる?]], [[はい|いいえ]]) if select == 0 then Adv.talk(aldo, [[おはよう]]) elseif select == 1 then Adv.talk(aldo, [[zzz]]) end Exit()
  54. 54. 54 朝のシーン local aldo = Adv.character("Aldo", "Default") local fine = Adv.character("Fine", "Default") -- ADV開始 Adv.talk(“???”, [[起きて]]) Adv.talk(aldo, [[…ん?]]) Adv.talk(fine, [[起きてってば]]) -- 0.5秒まつ Wait(0.5) local select = Adv.question([[起きる?]], [[はい|いいえ]]) if select == 0 then Adv.talk(aldo, [[おはよう]]) elseif select == 1 then Adv.talk(aldo, [[zzz]]) end Exit()
  55. 55. 55 local aldo = Adv.character("Aldo", "Default") local fine = Adv.character("Fine", "Default") -- ADV開始 Adv.talk(“???”, [[起きて]]) Adv.talk(aldo, [[…ん?]]) Adv.talk(fine, [[起きてってば]]) -- 0.5秒まつ Wait(0.5) local select = Adv.question([[起きる?]], [[はい|いいえ]]) if select == 0 then Adv.talk(aldo, [[おはよう]]) elseif select == 1 then Adv.talk(aldo, [[zzz]]) end Exit() 朝のシーン
  56. 56. 56 local aldo = Adv.character("Aldo", "Default") local fine = Adv.character("Fine", "Default") -- ADV開始 Adv.talk(“???”, [[起きて]]) Adv.talk(aldo, [[…ん?]]) Adv.talk(fine, [[起きてってば]]) -- 0.5秒まつ Wait(0.5) local select = Adv.question([[起きる?]], [[はい|いいえ]]) if select == 0 then Adv.talk(aldo, [[おはよう]]) elseif select == 1 then Adv.talk(aldo, [[zzz]]) end Exit() 朝のシーン
  57. 57. 57 local aldo = Adv.character("Aldo", "Default") local fine = Adv.character("Fine", "Default") -- ADV開始 Adv.talk(“???”, [[起きて]]) Adv.talk(aldo, [[…ん?]]) Adv.talk(fine, [[起きてってば]]) -- 0.5秒まつ Wait(0.5) local select = Adv.question([[起きる?]], [[はい|いいえ]]) if select == 0 then Adv.talk(aldo, [[おはよう]]) elseif select == 1 then Adv.talk(aldo, [[zzz]]) end Exit() 朝のシーン
  58. 58. 58 local aldo = Adv.character("Aldo", "Default") local fine = Adv.character("Fine", "Default") -- ADV開始 Adv.talk(“???”, [[起きて]]) Adv.talk(aldo, [[…ん?]]) Adv.talk(fine, [[起きてってば]]) -- 0.5秒まつ Wait(0.5) local select = Adv.question([[起きる?]], [[はい|いいえ]]) if select == 0 then Adv.talk(aldo, [[おはよう]]) elseif select == 1 then Adv.talk(aldo, [[zzz]]) end Exit() 朝のシーン
  59. 59. 59 イベント発火を簡単にする function init() TouchEvent("ベッド", function() -- タッチ後にAdv開始 Yield(“lua/Adv/001.lua“) end) end
  60. 60. 60 イベント発火を簡単にする function init() TouchEvent(“ベッド", function() -- タッチ後にAdv開始 Yield(“lua/Adv/001.lua“) end) end
  61. 61. 61 イベント発火を簡単にする function init() TouchEvent(“ベッド", function() -- タッチ後にAdv開始 Yield(“lua/Adv/001.lua“) end) end
  62. 62. 62 イベント発火を簡単にする function init() TouchEvent(“ベッド", function() -- タッチ後にAdv開始 Yield(“lua/Adv/001.lua“) end) end
  63. 63. 63 local aldo = Adv.character("Aldo", "Default") local fine = Adv.character("Fine", "Default") -- ADV開始 Adv.talk(“???”, [[起きて]]) Adv.talk(aldo, [[…ん?]]) Adv.talk(fine, [[起きてってば]]) -- 0.5秒まつ Wait(0.5) local select = Adv.question([[起きる?]], [[はい|いいえ]]) if select == 0 then Adv.talk(aldo, [[おはよう]]) elseif select == 1 then Adv.talk(aldo, [[zzz]]) end Exit() 朝のシーン
  64. 64. 64 イベント発火を簡単にする function init() TouchEvent("オブジェクト", Sample2) end function Sample2() -- タッチ後の処理 Adv.Talk(“にしだ”, [[はーい]]) Wait(0.5) Adv.Talk(“にしだ”, [[0.5秒待ったよ]]) end
  65. 65. 65 イベント発火を簡単にする function init() TouchEvent("オブジェクト", Sample2) end function Sample2() -- タッチ後の処理 Adv.Talk(“にしだ”, [[はーい]]) Wait(0.5) Adv.Talk(“にしだ”, [[0.5秒待ったよ]]) end
  66. 66. 66 イベント発火を簡単にする function init() TouchEvent("オブジェクト", Sample2) end function Sample2() -- タッチ後の処理 Adv.Talk(“にしだ”, [[はーい]]) Wait(0.5) Adv.Talk(“にしだ”, [[0.5秒待ったよ]]) end
  67. 67. こわくないよ • IDEによるコード保管 • ライブテンプレート
  68. 68. ホットリロード • ゲーム起動中にShift + RですぐにLuaをリロード • Unity Editor再生したままでもok • インポート不要
  69. 69. ジャンプポイント • ゲーム起動中にescを押すことでDebugPoint()までジャンプ • 指定の箇所まで読み飛ばして • 修正した箇所から再生したい ジャンプ前のコードはすべて正常に動いていることを保証
  70. 70. 71 local aldo = Adv.character("Aldo", "Default") local fine = Adv.character("Fine", "Default") -- ADV開始 Adv.talk(“???”, [[起きて]]) Adv.talk(aldo, [[…ん?]]) Adv.talk(fine, [[起きてってば]]) -- 0.5秒まつ Wait(0.5) local select = Adv.question([[起きる?]], [[はい|いいえ]]) if select == 0 then Adv.talk(aldo, [[おはよう]]) elseif select == 1 then Adv.talk(aldo, [[zzz]]) end Exit() 朝のシーン
  71. 71. 72 local aldo = Adv.character("Aldo", "Default") local fine = Adv.character("Fine", "Default") -- ADV開始 Adv.talk(“???”, [[起きて]]) Adv.talk(aldo, [[…ん?]]) Adv.talk(fine, [[起きてってば]]) -- 0.5秒まつ Wait(0.5) local select = Adv.question([[起きる?]], [[はい|いいえ]]) if select == 0 then Adv.talk(aldo, [[おはよう]]) elseif select == 1 then Adv.talk(aldo, [[zzz]]) end Exit() 朝のシーン
  72. 72. 73 local aldo = Adv.character("Aldo", "Default") local fine = Adv.character("Fine", "Default") -- ADV開始 Adv.talk(“???”, [[起きて]]) Adv.talk(aldo, [[…ん?]]) Adv.talk(fine, [[起きてってば]]) -- 0.5秒まつ Wait(0.5) local select = Adv.question([[起きる?]], [[はい]]) Adv.talk(aldo, [[おはよう]]) Exit() 朝のシーン
  73. 73. 74 local aldo = Adv.character("Aldo", "Default") local fine = Adv.character("Fine", "Default") -- ADV開始 Adv.talk(“???”, [[起きて]]) Adv.talk(aldo, [[…ん?]]) Adv.talk(fine, [[起きてってば]]) -- 0.5秒まつ Wait(0.5) DebugPoint() local select = Adv.question([[起きる?]], [[はい]]) Adv.talk(aldo, [[おはよう]]) Exit() 朝のシーン
  74. 74. 75 local aldo = Adv.character("Aldo", "Default") local fine = Adv.character("Fine", "Default") -- ADV開始 Adv.talk(“???”, [[起きて]]) Adv.talk(aldo, [[…ん?]]) Adv.talk(fine, [[起きてってば]]) -- 0.5秒まつ Wait(0.5) DebugPoint() local select = Adv.question([[起きる?]], [[はい]]) Adv.talk(aldo, [[おはよう]]) Exit() 朝のシーン
  75. 75. 76 local aldo = Adv.character("Aldo", "Default") local fine = Adv.character("Fine", "Default") -- ADV開始 Adv.talk(“???”, [[起きて]]) Adv.talk(aldo, [[…ん?]]) Adv.talk(fine, [[起きてってば]]) -- 0.5秒まつ Wait(0.5) DebugPoint() local select = Adv.question([[起きる?]], [[はい]]) Adv.talk(aldo, [[おはよう]]) Exit() 朝のシーン
  76. 76. こわくないね • ホットリロード • ジャンプポイント
  77. 77. Luaのカバー範囲 • アドベンチャーパート • フィールド演出 • バトル • メインストーリー
  78. 78. ゲームエンジンとコンテンツ ゲームエンジン Advパート ギミック 消滅都市 Cocos2d-x Excel なし アナザーエデン Cocos2d-x Lua Lua ほか運営中タイトル Cocos2d-x Excel Luaほか • ほかタイトル群は権利の都合上表示しておりません
  79. 79. x
  80. 80. イベントスクリプト ランタイム(ゲームシステム) • アドベンチャーパート • 立ち絵 • UI • サウンド • フィールド • カメラ制御 • オブジェクト制御 • ポストエフェクト • バトル • タイムライン連携 • 会話演出 • メインストーリー • Scene制御 • ほか <- 大量のAPI -> <- 大量のAPI -> <- 大量のAPI -> <- 大量のAPI -> <- 大量のAPI -> <- 大量のAPI -> <- 大量のAPI -> <- 大量のAPI -> <- 大量のAPI -> <- 大量のAPI -> <- 大量のAPI -> <- 大量のAPI -> <- 大量のAPI -> <- 大量のAPI -> <- 大量のAPI -> <- 大量のAPI -> <- 大量のAPI -> <- 大量のAPI ->
  81. 81. 論点 • vs. ビジュアルスクリプティング
  82. 82. www.wfs.games
  83. 83. www.wfs.games
  84. 84. Lua
  85. 85. 引き続き、RPG開発に全力で挑みます!

    Be the first to comment

    Login to see the comments

GREE Tech Conference 2020 で発表された資料です。 https://techcon.gree.jp/2020/session/Session-7

Views

Total views

1,189

On Slideshare

0

From embeds

0

Number of embeds

657

Actions

Downloads

5

Shares

0

Comments

0

Likes

0

×