ember.js tokyo event 2014/09/22 (japanese)

27
An example of game developing with Ember.js and WebGL Yuki Shimada 2014/09/22

Upload: yuki-shimada

Post on 01-Dec-2014

204 views

Category:

Technology


2 download

DESCRIPTION

2014年9月22日に行われたEmber.js Tokyoイベントでの発表資料です。 http://emberjs.doorkeeper.jp/events/14856 すごく個人的なネタ、かつEmber.jsの単なるバッドノウハウのカミングアウトと言われる可能性アリですが(汗) 一応公開します。 少しでも参考になれば幸いです。 (英語版: http://www.slideshare.net/yukishimada1/emberjs-tokyo-event-20140922-english )

TRANSCRIPT

Page 1: Ember.js Tokyo event  2014/09/22 (Japanese)

An example of game developing with Ember.js and WebGL

Yuki Shimada2014/09/22

Page 2: Ember.js Tokyo event  2014/09/22 (Japanese)

About me

• 東邦大学でコンピューターグラフィックスを専攻。

• 東大のベンチャー企業で、 PS3 向けのゲームエンジンの開発に従事。

• 株式会社ソピア ( 現アクセンチュア ) に SEとして勤務。

• 某 VFX Studio にて、レンダラーの開発とWebGL サイトの開発案件を担当。

• 現在はフリーランス (Web & CG).

Page 3: Ember.js Tokyo event  2014/09/22 (Japanese)

Demo

• ゲームプレーヤーhttp://youtu.be/0iRTk_2Wjp8

• マップエディターhttp://youtu.be/u6F3wGJpnUo

Page 4: Ember.js Tokyo event  2014/09/22 (Japanese)

My Web Service• Web 上で簡単に RPG が作れる Web サービス。

• 想定開発者をベーシック開発者とアドバンスド開発者の2つに分けている。

• アドバンスド開発者は RPG をゲームシステムの段階から開発することができる。– ゲームコードは TypeScript で記述してもらう。– 将来的には、ノード接続によるビジュアルプログラミングに対応したい。– UI のカスタマイズも SCSS で可能。

• ベーシック開発者は、アドバンスド開発者が開発したゲームシステムの中から好きなものを選び、その枠内でマウス操作だけで簡単にRPG が作れる。

Page 5: Ember.js Tokyo event  2014/09/22 (Japanese)

本 Web サービスの構造• ゲームプレーヤー部分と制作ツール部分の2つから

構成される。• 全体が Ruby on Rails 上で構築されている。– Rails の View 、 Controller は殆ど使っていない。– Ember.js で全てを制御。 Rails はモデルデータを提供するだ

け。– ビューの記述には Emblem を使用。

http://emblemjs.com– Rails からの Ember へのモデルデータ伝達には、 Ember Data

と ActiveModel::Serializers を利用。• 参考 URL : @ursm さんの以下のサンプルが参考になりました。

http://ursm.jp/blog/2013/12/03/ember-js-and-rails/

Page 6: Ember.js Tokyo event  2014/09/22 (Japanese)

使用言語• 制作ツール側は CoffeeScript で、ゲームプ

レーヤー部分は TypeScript で記述。• CoffeeScript (制作ツール)、 TypeScript

(ゲームプレーヤー)双方から、 Ember.jsを操作。

• 全体の Router 定義は CoffeeScript 側で行っている。

Page 7: Ember.js Tokyo event  2014/09/22 (Japanese)

Using of Ember.js

• UI handling• Data transfer of Rails Model -> Ember Data• Data transfer of Ember Data -> WebGL• Ember.js Observer and WebGL• Computation of Game character’s parameters

Page 8: Ember.js Tokyo event  2014/09/22 (Japanese)

UI Handling

Page 9: Ember.js Tokyo event  2014/09/22 (Japanese)

UI handling

• UI は HTML(Emblem)+CSS(SCSS) でできている– クリエーターは SCSS で見た目をカスタマイズ可能

• スクリーン (‘uiScreen’)-> テーブル (‘uiTable’)の2段階構造のモデルで UI を管理。

• スクリーン= Ember.js のテンプレートEmber.js でテンプレートを切り替えて、スクリーンの切り替えをしている。

• スクリーン配下のテーブルの表示・非表示はjQuery で。

Page 10: Ember.js Tokyo event  2014/09/22 (Japanese)

uiScreen と uiTableテーブル(’ uiTable’ )

スクリーン(’ uiScreen’ )

Page 11: Ember.js Tokyo event  2014/09/22 (Japanese)

Data transfer ofRails Model -> Ember Data

Page 12: Ember.js Tokyo event  2014/09/22 (Japanese)

Between Rails and Ember Data

trait :as_2 do id 2 ui_screen_id 1 screenIdentifier 'battle' tableIdentifier 'character_0' tableName ' 0 :' tableName_binding 'Tool.EScenarioCharacter.0.name' selectable false trs "[" + "{"+ "'columns':[" + "{" + "'item':'HP:'," + "'binding': 'Tool.EScenarioCharacter.0.hp'" + "}" + "]" + "}," + "{"+ "'columns':[" + "{" + "'item':'MP:'," + "'binding': 'Tool.EScenarioCharacter.0.mp'" + "}" + "]" + "}" + "]" end

var uiTableDefinition = { screenIdentifier: DS.attr('string'), tableIdentifier: DS.attr('string'), tableName: DS.attr('string’), tableName_binding: DS.attr('string'), selectable: DS.attr('boolean'), trs: DS.attr('uiTableTrs'), …};

Definition of Ember Data Model A fixture of Rails Model (Factory Girl)

window.Tool.UiTableTrsTransform = DS.Transform.extend( { deserialize: function(value:any) { var json:any = null; eval("json = " + value); return Ember.create(json); } });

Transform of Ember Data Model

複雑または構造の変更が多いデータは、 JSON テキストデータとして Rails モデルのカラムに保存。 Ember Data の Transform を使い、 JavaScript で eval して、 Ember オブジェクト化している。

Page 13: Ember.js Tokyo event  2014/09/22 (Japanese)

Question : Ember Data モデルの一対多関係を Rails のモデルからどう復元する?• Rails と Ember Data の一体多のデータ表現では、一部異なる点がある。• Rails では、子モデルが親モデルの参照を持つだけで、親モデルから子モデルのリス

トにアクセス可能。• Ember Data では、親モデルが子モデルの id 配列を明示的に持つ必要がある。• 上記の違いがあるため、両者のやりとりには変換処理が必要。

App.Post = DS.Model.extend({ comments: DS.hasMany('comment', {async: true})});

{ "post": { "comments": [1, 2, 3] }}

App.Comment = DS.Model.extend({ post: DS.belongsTo('post')});

{ "comment": { "post": 1 }}

create_table ”posts", force: true do |t|end

create_table ”comments", force: true do |t| t.integer "ui_screen_id”end

class Post < ActiveRecord::Base has_many :commentsend

class Comment < ActiveRecord::Base belongs_to :postend

Page 14: Ember.js Tokyo event  2014/09/22 (Japanese)

Question : Ember Data モデルの一対多関係を Rails のモデルからどう復元する?

var uiTableDefinition = { screenIdentifier: DS.attr('string'), tableIdentifier: DS.attr('string'), tableName: DS.attr('string’), tableName_binding: DS.attr('string'), selectable: DS.attr('boolean'), trs: DS.attr('uiTableTrs'), …};

var uiScreenDefinition = { identifier: DS.attr('string'), config: DS.attr('uiScreenConfig'), uiTables: DS.hasMany('ui-table') };

Definition of ‘uiScreen’ which has many uiTables

Definition of ‘uiTable’ which belong to ‘uiScreen’

create_table "ui_screens", force: true do |t| t.string "identifier" t.text "config" t.text "uiTables”end

create_table "ui_tables", force: true do |t| t.integer "ui_screen_id" t.string "screenIdentifier" t.string "tableIdentifier" t.string "tableName" t.string "tableName_binding" t.boolean "selectable" t.text "trs”end

class UiScreen < ActiveRecord::Base has_many :ui_tablesend

class UiTable < ActiveRecord::Base belongs_to :ui_screenend

Ember Data Rails Model

Page 15: Ember.js Tokyo event  2014/09/22 (Japanese)

Answer : Rails の Controller で Ember Data 向けに has many id 配列を生成する

class Api::UiScreensController < ApplicationController skip_before_action :verify_authenticity_token

def index uiScreens = [] UiScreen.all.each do |uiScreen|

uiTablesStr = "[" uiScreen.ui_tables.each do |uiTable| uiTablesStr += uiTable.id.to_s + ",“ end

uiTablesStr.chop! uiTablesStr += "]"

uiScreen.update_attribute(:uiTables, uiTablesStr) uiScreens.push(uiScreen) end

jsonHash = JSON.parse(uiScreens.to_json) for item in jsonHash item["uiTables"] = eval(item["uiTables"]) end jsonWrapper = {} jsonWrapper["ui_screens"] = jsonStr = jsonWrapper.to_json jsonHash = JSON.parse(jsonStr) render :json => jsonHashend

uiScreen に belong to している全ての uiTable のid を配列文字列に追加。

Ex:”[1, 2, 5, 6, 7]”

そして、 JSON 化して出力{ "ui_screens": [ { "config": "{'firstUI':'command','hiddenUIs':['physics', 'magic', 'target', 'magic-fake'],'calculatePosition': true}", "created_at": "2014-09-08T02:07:53.215Z", "id": 1, "identifier": "battle", "uiTables": [1, 2, 5, 6, 7] } ]}

Page 16: Ember.js Tokyo event  2014/09/22 (Japanese)

Data transfer ofEmber Data -> WebGL

Page 17: Ember.js Tokyo event  2014/09/22 (Japanese)

WebGL へのデータ受け渡し• WebGL を直接触っているのではな

く、 Three.js を利用。http://threejs.org

• tmlib というゲームライブラリを使用。http://phi-jp.github.io/tmlib.js/

• tmlib は three.js と統合されている。

• 今回は、マップの描画を例に取って解説。

Page 18: Ember.js Tokyo event  2014/09/22 (Japanese)

マップの描画のためのマップデータの受け渡し

Tool.PlayRoute = Ember.Route.extend model: (params, transition) -> return Ember.RSVP.hash({ map: @store.find('squareGrid3dMap', params['id']) textures: @.store.find('squareGrid3dMapTexture') tiletypes: @.store.find('squareGrid3dMapTileType') uiScreens: @.store.find('uiScreen') uiTables: @.store.find('uiTable') })

trait :as_2 do id 2 name ' デバッグテスト ' width 5 height 5 type_array "1 N,1 W,1 N,1 W,1 W\n" + "1 N,1 W,1 N,1 W,1 W\n" + "1 N,1 N,1 N,1 N,1 N\n" + "1 W,1 W,1 N,1 W,1 W\n" + "1 W,1 W,1 N,1 W,1 W\n"

height_array "0 1,0 1,0 1,0 1,0 1\n" + "0 1,0 1,0 1,0 1,0 1\n" + "0 1,0 1,0 1,0 1,0 1\n" + "0 1,0 1,0 1,0 1,0 1\n" + "0 1,0 1,0 1,0 1,0 1\n" end

RailsMap Data (Factory Girl)

Tool.SquareGrid3dMap = DS.Model.extend name: attr('string') width: attr('number') height: attr('number') type_array: attr('string') height_array: attr('string')

Ember Data

Page 19: Ember.js Tokyo event  2014/09/22 (Japanese)

マップの描画のためのマップデータの受け渡し

Tool.PlayIndexRoute = Ember.Route.extend model: -> @modelFor('play')

afterModel: (model, transition)->

window.WrtGS.main(model)

function systemMain(err:any, args:any){

window.WrtGS = new system.System(args);

}

module system { export class System {

public main(model:any) { this.map = new map.Map(model.map.get(‘type_array’), model.map.get(‘height_array’), model.texture.get(‘gametex_url’));

trait :as_2 do id 2 name ' デバッグテスト ' width 5 height 5 type_array "1 N,1 W,1 N,1 W,1 W\n" + "1 N,1 W,1 N,1 W,1 W\n" + "1 N,1 N,1 N,1 N,1 N\n" + "1 W,1 W,1 N,1 W,1 W\n" + "1 W,1 W,1 N,1 W,1 W\n"

height_array "0 1,0 1,0 1,0 1,0 1\n" + "0 1,0 1,0 1,0 1,0 1\n" + "0 1,0 1,0 1,0 1,0 1\n" + "0 1,0 1,0 1,0 1,0 1\n" + "0 1,0 1,0 1,0 1,0 1\n" end

trait :as_1 do id 1 name ' メタル ' gametex_url ‘metal.jpg’

Page 20: Ember.js Tokyo event  2014/09/22 (Japanese)

あとは、ただのテキストデータなので、独自マップテキストフォーマットを解析

し、 Three.js のジオメトリ作成、テクスチャ画像割り当てを行う。

// 1 頂点目 geom.vertices.push( new THREE.Vector3(x-1, this.maxCeilingHeight, y) );

// 2 頂点目 geom.vertices.push( new THREE.Vector3(x-1, ceilingHeight, y) );

// 3 頂点目 geom.vertices.push( new THREE.Vector3(x-1, ceilingHeight, y-1) );

// 4 頂点目 geom.vertices.push( new THREE.Vector3(x-1, this.maxCeilingHeight, y-1) );

// 表三角形の1個目 var face1 = new THREE.Face3( verticesStride+0, verticesStride+1, verticesStride+2 ); face1.normal = new THREE.Vector3(1, 0, 0); geom.faces.push( face1 );

geom.faceVertexUvs[ 0 ].push( [ new THREE.Vector2( 0.75, 0 ), new THREE.Vector2( 0.75, this.texcoordOne * (this.maxCeilingHeight - ceilingHeight) ), new THREE.Vector2( 1, this.texcoordOne * (this.maxCeilingHeight - ceilingHeight) ) ] );

Page 21: Ember.js Tokyo event  2014/09/22 (Japanese)

Ember.js から WebGL(Three.js) へのデータ受け渡しは、そんな特別なことはしていない。

Ember Data モデルで、 Fulfilled されたデータから文字列を取り出し、それを文字列解析して、 Three.js のジオメトリ構築用の関数に渡しているだけ。

マップの描画のためのマップデータの受け渡し

Page 22: Ember.js Tokyo event  2014/09/22 (Japanese)

Ember.js Observer and WebGL

• 敵がダメージを受けると、敵画像が揺れたりしているが、これは Ember.js の Observerを使っている。

• 敵や味方のステータス値も Ember オブジェクトとして管理しており、 HP が減ると Observer が起動、 Three.js の敵ポリゴンの座標値を振動させている。

Page 23: Ember.js Tokyo event  2014/09/22 (Japanese)

Computation of Game character’s parameters

RPG では、あるパラメーターから他のパラメーターを算出する、というケースが非常に多い。→Computed Properties で解決!

本制作ツールでは、キャラクターのパラメーターを自由に新設し、パラメーター間の数学関係を自由に記述できる。

Page 24: Ember.js Tokyo event  2014/09/22 (Japanese)

Ember.js と Web ゲームの親和性• UI の表示・更新に最適!

– 初期バージョンは jQuery のみで処理していたが、やはり地獄だった。今はEmber.js のお陰でかなりスマートになった。

– UI の数が多い RPG などには特に向いている。使わない手はない。

• 双方向バインディングが便利。– 大型のゲームになると、複数の場所間でデータを同期する必要が生じるが、同期処理の面倒はすべて Ember.js が見てくれる。

• オブザーバーも便利。– ゲーム UI では単なるテキストではなく、画像を使用することも多い。内部デー

タの再計算だけにデータバインディングを使用し、アニメーションは別描画ライブラリを併用することも可能。表示の更新のタイミングは Observer で。

• RPG など、パラメーター同士の計算が多いゲームでは Computed Properties が活躍する。

Page 25: Ember.js Tokyo event  2014/09/22 (Japanese)

Ember.js最高!• 以上、 Ember.js は本 Web サービスのゲー

ム部分、制作ツール部分双方の根底を支えている。

• Ember.js がなければ、ここまでの開発効率とコードの見通しの良さは考えられなかった。

Thanks! Ember.js!

Page 26: Ember.js Tokyo event  2014/09/22 (Japanese)

最後に• Ember.js を学びたい方。– Ember.js公式ガイド日本語訳を公開していま

す!https://github.com/emadurandal/emberjs-guides-japanese-translation

– Developers.IO の「シリーズ Ember.js入門」も必見。(渡辺さん素晴らしい記事をありがとう!)http://dev.classmethod.jp/series/getting-started%E2%80%8E-emberjs/

Page 27: Ember.js Tokyo event  2014/09/22 (Japanese)

Thank you!