Slide 1

Slide 1 text

Goの標準機能で簡易システムを 低コストで作成するテクニック Shimada Yuji

Slide 2

Slide 2 text

自己紹介 島田 裕司 / Shimada Yuji 株式会社CyberAgent -> QualiArts スマートフォン向けゲームの開発を担当 Goは今のプロジェクトから使い始めました (2年程度)

Slide 3

Slide 3 text

ゲーム開発におけるデバッグツール ● テストプレイやデバッグのため、特定の状態を作りたいケースが多い ○ ユーザーのレベルを50に変更 ○ 回復アイテムを100個付与 ○ クエストをステージ10までクリア ● 状態操作を簡単に行えるツールが必要 ⇒ 低コストで追加開発できるデバッグツール基盤の事例を紹介

Slide 4

Slide 4 text

用語説明 ● 弊社ではデバッグツールのことをデバッグコマンド(デバコマ)と呼称 ● ソースコード中での表現 ○ Command ■ 「ゲームユーザーのレベルを操作する」といった単一の操作、およびそれに関する情報 ■ ツール画面上のメニューにあたる ○ CommandGroup, Group ■ 「カード」や「クエスト」等の区分で、複数のCommandをまとめた情報 ■ ツール画面上のカテゴリにあたる

Slide 5

Slide 5 text

ツールイメージ type UpdateRarityRequest struct { UserID string `label:"ユーザーID"` CardID string `master:"card" type:"select"` Rarity int32 `enum:"rarity" type:"radios"` } func (h *handlerImpl) GetBaseCommandGroup() *Group { return &Group{ Name: "カード", Commands: command.Commands{ { HandlerFunc: h.UpdateRarity, URL: "/updateCardRarity", Name: "レアリティ操作", Model: UpdateRarityRequest{}, }, }, } }

Slide 6

Slide 6 text

3. フォーム一覧をリクエスト 4. フォーム一覧情報を返却 5. デバコマ実行をリクエスト 6. 実行結果を返却 処理の流れ 2. 起動時にフォーム情報を生成 1. フォーム定義を記述

Slide 7

Slide 7 text

3. フォーム一覧をリクエスト 4. フォーム一覧情報を返却 5. デバコマ実行をリクエスト 6. 実行結果を返却 処理の流れ 2. 起動時にフォーム情報を生成 1. フォーム定義を記述

Slide 8

Slide 8 text

ポイント 1. 共通インターフェースとStructタグでフォーム定義を記述 2. リフレクションを用いてStructタグからフォーム情報を生成 3. フォームの一覧情報を返却するAPIを定義

Slide 9

Slide 9 text

ポイント 1. 共通インターフェースとStructタグでフォーム定義を記述 2. リフレクションを用いてStructタグからフォーム情報を生成 3. フォームの一覧情報を返却するAPIを定義

Slide 10

Slide 10 text

共通インターフェースの実装(1) type CardHandler interface { BaseHandler UpdateRarity(c echo.Context) error } type handlerImpl struct{} type BaseHandler interface { GetBaseCommandGroup() *Group } ● ハンドラのインターフェースに、 ベースインターフェースを埋め込む ● ハンドラは、ベースインターフェー スのメソッドを実装 ● ハンドラ = APIの受け口となるメ ソッド

Slide 11

Slide 11 text

共通インターフェースの実装(2) func (h *handlerImpl) GetBaseCommandGroup() *CommandGroup { return &CommandGroup{ Name: "カード", Commands: []*Command{ { HandlerFunc: h.UpdateRarity, URL: "/updateCardRarity", Name: "レアリティ操作 ", Model: UpdateRarityRequest{}, }, }, } } ● CommandGroupを定義 (≒ カテゴリ) ○ カテゴリ名 ○ Commandの一覧 ● Commandを定義 (≒ 各メニュー) ○ ハンドラメソッド ○ URL ○ デバコマ機能名 ○ フォームの基になる構造体

Slide 12

Slide 12 text

Structタグによるフォームの設定 type UpdateRarityRequest struct { UserID string `label:"ユーザーID"` CardID string `master:"card" type:"select"` Rarity int32 `enum:"rarity" type:"radios"` } func (h *handlerImpl) UpdateRarity(c echo.Context) error { var r UpdateRarityRequest err := c.Bind(&r) // 実際の処理… } ● リクエストパラメータをバインドす る構造体にタグを設定 ● Structのフィールドに対してメタ情 報を付加できる ○ jsonタグが身近

Slide 13

Slide 13 text

ポイント 1. 共通インターフェースとStructタグでフォーム定義を記述 2. リフレクションを用いてStructタグからフォーム情報を生成 3. フォームの一覧情報を返却するAPIを定義

Slide 14

Slide 14 text

リフレクションでStructタグを取得 func CreateSchema(c *Command) *Schema { // 構造体の型情報を取得 rt := reflect.TypeOf(c.Model) var schema Schema for i := 0; i < rt.NumField(); i++ { // フィールド毎にタグ情報を取得 f := rt.Field(i) schema.Label = f.Tag.Get("label") // 各タグを処理… } return &schema } ● リフレクションによりタグ情報を取 得しフォーム情報を生成 ○ type:テキスト、セレクトボック ス、ラジオボタン... ○ label:フォーム名 ○ required:必須チェック ○ enum, master:次スライドで説明 ● 型ごとにデフォルト値も設定してあ るのでタグは指定しなくてもOK

Slide 15

Slide 15 text

セレクトボックスの選択肢を作成 func CreateValues(f *reflect.StructField) Values { // Enum指定 if e := f.Tag.Get("enum"); e != "" { return CreateEnumValues(e) } // マスタデータの指定 if m := f.Tag.Get("master"); m != "" { return CreateMasterValues(m) } return Values{} } ● セレクトボックスやラジオボタンの 選択肢もタグを基に生成 ● enumタグ ○ 指定Enumからリストを作成 ○ (例)アイテムタイプ定義のEnum ● masterタグ ○ 指定マスタデータからリストを作成 ○ マスタデータ = レベルやクエストと いったゲームパラメータ ○ (例)クエスト一覧

Slide 16

Slide 16 text

ポイント 1. 共通インターフェースとStructタグでフォーム定義を記述 2. リフレクションを用いてStructタグからフォーム情報を生成 3. フォームの一覧情報を返却するAPIを定義

Slide 17

Slide 17 text

Commandの初期化とルーティング handlers := []BaseHandler{card.New(), …} groups := make(CommandGroups, 0, len(handlers)) for _, h := range handlers { group := h.GetBaseCommandGroup() // フォーム情報の生成とルーティング InitCommand(group) for _, c := range group.Commands { e.POST(command.URL, c.HandlerFunc) } groups = append(groups, group) } // フォーム情報の一覧を返す API e.GET("/api/list", func(c echo.Context) error { return c.JSON(http.StatusOK, groups) }) ● サーバー起動時に各Commandの初期 化を実行 ○ フォーム情報の生成 ● 各デバコマをルーティング ● フォームの一覧を返すAPIを登録

Slide 18

Slide 18 text

まとめ ● StructタグやInterfaceを活用し、追加開発しやすい基盤を実装 ○ 共通インターフェースとStructタグでフォーム定義を記述 ○ リフレクションを用いてStructタグからフォーム情報を生成 ○ フォームの一覧情報を返却するAPIを定義 ● 補完が効かない・記述ミスに気づきにくいデメリット ● 開発するシステムの要件に合わせて実装方法を検討

Slide 19

Slide 19 text

ご静聴ありがとうございました!