ホーム ブログ ページ 29

やったもん勝ち精神でド新人がISUCON7に参加しました

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

先日(10月22日)ISUCON7の予選に参加しましたので「ISUCONってなに?」「私でもできるじゃろうか?」というようなことを書いていきます。

ISUCONとは

isucon.png

お題となるWebサービスを決められたレギュレーションの中で限界まで高速化を図るチューニングバトル、それがISUCONです。
ISUCON公式Blog

3人一組でチームを組んで、8時間の制限時間以内に与えられたWebサービスの動作を早くする競技です。
コンテスト会場があるわけじゃなくて、各々パソコンを持ち寄ってチームで集まりオンラインで参加します。
お題として速度に問題があるWebサービスサーバーが与えられて(全チーム同じ内容)そのサービスの動きがスムーズになるようにいろいろします。

チューニングって具体的にどんなことするの?

  1. ベンチマークを回して現在のスコアを確認
  2. サービスのどこがボトルネックになってるか分析
  3. 問題の解消方法を考える
  4. プログラムや設定ファイルの修正
  5. 再びベンチマーク回す

(繰り返し)

ベンチマーク
大会運営側が用意しているスコア採点用のバッチ。
サービスに負荷がかかるような動作を行い
1分間でどれくらい処理が進むか見ている。

下のはISUCON6の過去問で行ったベンチマークです。
これはスコアが4435点!この数字を上げていくことが目標になります。

isucon@ubuntu-xenial:~/isucon6q$ ./isucon6q-bench -target http://127.0.0.1
2017/10/26 12:31:17 start pre-checking
2017/10/26 12:31:23 pre-check finished and start main benchmarking
2017/10/26 12:32:17 benchmarking finished
{"pass":true,"score":4435,"success":1751,"fail":0,"messages":[]}

なんだか難しそう…

そもそも、私がこのISUCONに参加したのは会社の「や先輩」に誘っていただいたことがきっかけでした。

や先輩「最悪ただ座ってるだけでも。」

と言うので、座ってるだけなら私もできる!椅子コンか!と思い参加表明。
しかし、話をよくよく聞くと椅子関係ないし難しそう…

お題となるWebサービスを決められたレギュレーションの中で限界まで高速化を図るチューニングバトル、それがISUCONです。

高速化?チューニング?
ヨクワカラナイ…
レギュレーションは日本語にして規則って言って欲しい…

3人のメンバーは
言い出しっぺの や先輩 と
競プロ経験者の わ先輩 と
ド新人の私。

お二人ともデキる人なので足手まといになりはしないかとガクブルしてました…

自分ができることからしよう

チューニングの右も左も分からない私がいきなり全てをわかろうとするのは無理です。
や先輩に役割分担を割り振ってもらったので、その中で頑張ることにしました。

3人の役割分担

わ先輩: アプリケーションの修正

や先輩: ミドルウェア周りチューニングとあと全部

 私 : プロファイリングと他サポート

プロファイリング?

プロファイラとは、動作中のプログラムがどの処理をどういった順序で実行したかを監視するプログラム。
プログラムの障害を見つけるために用いられるよりも、プログラムの余計な部分を削るなどして高速化するために用いられることが多い。
http://e-words.jp/w/プロファイラ.html

プロファイリングすることでどこがボトルネックになって遅くなっているのか分析します。
分析ができないと戦略が立てられないので競技開始直後からやることが沢山です。
とにかく自分が足手まといになることを恐れ、や先輩に聞いて最低限やればいいことをメモしました。

具体的に私がやること

  • バックアップ体制の構築
  • プロファイラの追加
  • ログの解析

バックアップ

大会の運営側から渡されたサーバーを手直ししていくのがこの競技ですが、最終的なフロント側の挙動が変化しないようにしなければいけません。

何かの間違いで設定ファイルが破損した場合、バックアップをとっていないと元の挙動にも戻せなくなります。

競技開始直後、/etc/mysql や /etc/nginx などのディレクトリをまるまる別のサーバーにコピーして保存しました。
Webアプリのファイルも同様にバックアップを取って、アプリケーションの修正がやりやすいようにgit管理しました。

プロファイラの追加

htop

サーバー内でどのアプリケーションがCPUを食っているかほぼリアルタイムで見られます。
topコマンドは使ったことがありましたがhtopは知りませんでした。
htopはtopより見やすくてカッコいいです。

top_htop.gif

ベンチマークを回しながらhtopを監視します。ここで、mysqlがcpu使用率90%とかになっていれば「スロウクエリでもあるのかな?」ということになります。

pt-query-digest

mysqlのクエリログを解析して処理が遅い順番に順位をつけてくれます。

pt-query-digest.png

例えば、SELECT文1回の処理に時間がかかる場合は、indexを貼ることで解消できるかもしれません。
逆に、1回のSELECT文はそれほどかからなくても同じSELECTを何度も繰り返している場合は、「そもそも無駄な処理をしているんではないか?」「N+1が発生している?」と考えることができます。

クエリを分析することでその後の対処法を考えることができます。

ログ解析

pt-query-digestを使うためにはmysqlでlogを出力することが必要です。
nginxでaccess.logを出すことで、アプリ側のどの処理が重いのか確認することができます。
ただ、ログを出力するのにも多少なりと時間がかかるため、最終スコアを出す前にログを出力しない設定に戻す必要があります。
ほんの少しでもスコアを上げるための工夫ですね。

そして大会当日ーー

tahihu.png

土砂降りの中どうにか や先輩 のお家へ到着…
足ひっぱんないようにできることをやるぞ!と心に決めたのですが「お家に帰れるかな?」状態。

それでも開始までやるべきことリストを何度も読み返しながら準備をします…
しかしーー
isogasii.png

大会の運営というのは大変なんですね…お疲れ様です!
こちらとしては落ち着いて準備する余裕ができよかったです…
時間ができたのでやることチェックをしたり、マリオカートをしました。
その際Joy-Conストラップを逆向きにつけてしまい焦りました。

Joy-Conストラップを逆向きに付けたときの外しかた

そして競技開始ーー

やることを繰り返し確認したおかげか、以外とスムーズに作業を進めることができました。
ただ、問題もあってmysqlのログが出ない…
設定ファイルに書くか、うまくいかなかったらmysqlクライアントにrootで入って設定することにしてたのですが、設定ファイルが見つからない(後から探せば普通にあった)&mysqlにrootで入れない!
焦った私はプチパニックに…

や先輩 「パニックになるにはまだ早いよ」

ですよね…
mysqlのログはや先輩にやってもらうことになりました。
代わりに他のプロファイラを入れようとしたり(結局入れてない)ベンチマークを回している最中にhtopを監視したりしていました。
そしてーー

kazehiita.png

途中から鼻水が止まらなくなって や先輩 の家のティッシュがどんどんなくなる…

や先輩 から「DBにBLOB形式(バイナリデータ)で保存されている画像データを実データに変換する」というお仕事を任していただき、「いくたくんはそれが終わったらもう帰りな」と言っていただきました。
わわわ、すみませんんん…

競技開始4時間後、私は途中離脱という不甲斐ない結果でこのISUCONを終えることとなったのです。残念。

参加してよかったの?よくなかったの?

kazehiita.png
  • プロファイリングってどんなことをするのか基本的なところを知ることができた
  • Webサービスの高速化という新しい視点を持てた
  • 自分より上のレベルの や先輩 のさらに上のレベルの人がいることが分かった(ISUCONの予選を突破するのはほんの一握りだそうです)
  • 自分が何もできないわけじゃないことがわかった

特に最後の項目が大きいかと思います。
よく考えたら1年前はssh接続でサーバーに入ることすら怪しかった私が、微力とはいえサーバーサイドのことができるようになりつつある段階まできました!
や先輩に誘っていただかなかったら気付けなかったと思うので感謝です。
というか、一人では参加できないので わ先輩 も含め、皆さんに感謝です。

まとめ

難しそうなことに飛び込んでチャレンジすることで、自分が今どれくらいの実力か多少測ることができました。
結論から言うと「まだまだ」ですが、それでも少しづつ成長できてるかなーと嬉しく思っています。

皆さんも「難しそうだからやめておこう」と思っていることに飛び込んでみてはいかがでしょうか。
意外とできたり全然できないかもしれませんが、やったもん勝ちという言葉があります。
私もまた何かに挑戦できたらいいなと思ってます。

それでは、今日はこの辺で。
読んでいただき、ありがとうございました。

キャラクターでストーリーを動かす②

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。


前回の記事 キャラクターでストーリーを動かす①の続きです。
登場人物を決めた後は、実際にキャラクターを動かしてみましょう。

前回のおさらい

前回の記事では、ゲーム内に登場するキャラクターから、イベントに登場させるキャラクターを決めました。
enter image description here

前回もお話しましたが、今回のイベントの主役はあくまで①の明るい女の子
ストーリーを動かす役割は③のスピリチュアルさんにやってもらいますが、主役が潰れてしまわないように気を付ける必要があります。

個性を活かしてストーリーに組み込む

キャラクターの個性を活かすために、それぞれの役割りを明確にしましょう。
過去のかけあいなどで出している設定とにらめっこです。
(キャラクター名やキャラクターイラストを出しても良いとのお達しをいただいたので、今回は分かりやすいように実際のキャラクター名とイラストを使用して解説していきます。)

①明るい女の子「アミー」…女子大生系お姉さん。自分が別物になる・姿が変わってしまうことを恐れており、自分は自分であるという確信を得るために、特別な呼び名が欲しいと思っている。
enter image description here

②中二病の子「ウァラク」…かっこいい響きのものに惹かれる、中二病全開の女の子。愛されいじられ系。割とノリが良い。
enter image description here

③スピリチュアルさん「ベレト」…引っ込み思案で少し口下手。占いが得意。
enter image description here

④生真面目さん「キメイエス」…基本的に真面目だが、少し子供っぽいところがある。大人っぽく振る舞おうと奮闘中。
enter image description here


話を動かす役割というと「狂言回し」や、「トリックスター」といったものがありますが、今回は動かす役割の「ベレト」が引っ込み思案な性格で、なかなか自分からアクションを起こそうとはしません。
ですので、「ベレト」を、「ウァラク」と 「キメイエス」にサポートしてもらいます。

プロットを立てる

ようやくプロットを立てます。
以前に記事を書いた頭の中のアイデアをアウトプットするでも使用した「Xmind」を用いて、簡単なプロットを作成しました。

enter image description here

今回のイベントでは十五夜がテーマですので、月を連想させるワードなどを使っていきます。ちょうど、ベレトが得意とするカード(タロット)の占いには月のカードが存在するので、有効活用させてもらいました。

季節イベントの場合はキャラクター達を魅力的に見せることが重要なので、プロット通りに進行させることは絶対ではありません。
なので、ざっくりと書いて先に進んでしまいます。
(本編の重要な設定が出てくる場合などは適当に書くと後で大変なことになってしまうので、しっかりとメモを残しつつプロットを立てて概ねプロット通りに書きましょう)

実際に書く

あとはプロットを見つつ本文を書いていき、出来上がったものの1シーンがこちらです。

enter image description here
enter image description here

ということで、前回・今回の記事は、株式会社アピリッツが提供する「ゴエティア -千の魔神と無限の塔-」において2017年9月20日(水) ~ 2017年9月26日(火)の期間に開催されていたイベント「想いは月夜に揺れる」を例にとって執筆させていただきました。

enter image description here

ダンジョン内の夜空に浮かぶ満月の背景イラストがとても素敵なイベントでした。
実際にこちらのイベントをプレイしていた方は、ゲーム内機能の「ストーリー」からイベントのかけあいを見返すことが出来ます!

おわりに

enter image description here

現在「ゴエティア -千の魔神と無限の塔-」では、ハロウィンをテーマにしたイベント「スウィートトリック・ナイト」が開催されています。
こちらのイベントは2017年10月24日(火) ~ 2017年11月7日(火)までとなっております。
比較的静かで穏やかに進行していた「想いは月夜に揺れる」とはまた違い、ハロウィンらしい賑やかで楽しげなイベントに仕上がったかと…。
お時間がありましたら是非プレイして、ゴエティアの世界に触れていただければと思います。

どこでもフォントデザインが確認できるwordmark

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。


ウェブ上でフォントデザインが確認できるサイトwordmarkの紹介

こんにちはtokinです。
皆さんはどのようなフォントをよく使いますか?デザイン制作をしていく中で文字においても、読みやすさ、目立ちやすさ、なじみやすさなどといったことに注目してどのフォントを使うか選択しているかと思います。

今回はそんな様々なフォントを簡単に確認することができるサイト「wordmark」についてご紹介していきます。

enter image description here

さて、どのようなサイトかといいますと、
打ち込んだ文字をPC内にダウンロードしてあるフォントで確認できるサイトです。
各フォントで表示された文字が一覧となって表示される為、それぞれのフォントの具合が見やすくかつ比較しやすいシンプルなサイトです。
試しにトップページにある欄に文字を打ち込んでみます。

enter image description here

するとどうでしょう、PCに今までダウンロードしてきたフォントで変換された文字達が画面いっぱいに表示されます。
更にwordmarkの便利なところはアルファベット入力であれば上の欄で小文字、頭文字だけ大文字、大文字の三種類にクリック一つで変更できるところ。文字を打ち直すことなく簡単に確認することができます。また、白から黒への反転、文字の拡大縮小などもボタン一つで行えたりと様々な状態のフォントを簡単に確かめることもできます。
enter image description here

フォントを幾つかに絞って確認したいときはFilter selectedで選択したフォントのみを表示して確認することも可能です。選択したフォントのみを表示するFilter selected画面ではイメージ保存(画面キャプチャ)やイメージの印刷も行えます。
enter image description here

既に幾つかのペイントソフトでもフォント選択欄で打ちだした文字をフォントで表示確認することができますが
出先や打ち合わせでその場でフォントを確認したいときなどにオススメのサイトです。タイトルやロゴなど、短い文章や単語だけでなく、ある程度の長文まで変換に対応しているためサイトやゲームのイメージに合った文章フォントを探すのにもおすすめです。ぜひ利用してみてください。

information_schemaでMySQLの情報取得

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

現状のmysqlで、どれくらいのデータが保存されているか等を調べる際に、information_schemaが便利でした。

Information_schema リファレンス

information_schemaとは

データベースやテーブル等に関するメタデータを格納している物になります。

上記のリファレンスや、データベースの中身を見れば、どのようなものが格納されているかはわかりますが、一部を紹介したいと思います。

Information_schema SCHEMATA

データベース自体のメタデータを格納しているテーブルです。
テーブルの名前や、文字コードを確認することが出来ます。

mysql> select * from information_schema.SCHEMATA\G
*************************** 1. row ***************************
              CATALOG_NAME: def
               SCHEMA_NAME: information_schema
DEFAULT_CHARACTER_SET_NAME: utf8
    DEFAULT_COLLATION_NAME: utf8_general_ci
                  SQL_PATH: NULL
*************************** 2. row ***************************
              CATALOG_NAME: def
               SCHEMA_NAME: test_database
DEFAULT_CHARACTER_SET_NAME: latin1
    DEFAULT_COLLATION_NAME: latin1_swedish_ci
                  SQL_PATH: NULL

Information_schema TABLES

テーブルのメタデータを格納しているテーブルです。

TABLE_SCHEMAにデータベース名、TABLE_NAMEにテーブル名が格納されているので、それを利用して絞り込みが可能です。

(今回は、テーブル名を指定しました。)

mysql> select * from information_schema.TABLES where table_name = "test_table"\G
*************************** 1. row ***************************
  TABLE_CATALOG: def
   TABLE_SCHEMA: test_database
     TABLE_NAME: test_table
     TABLE_TYPE: BASE TABLE
         ENGINE: InnoDB
        VERSION: 10
     ROW_FORMAT: Compact
     TABLE_ROWS: 1
 AVG_ROW_LENGTH: 16384
    DATA_LENGTH: 16384
MAX_DATA_LENGTH: 0
   INDEX_LENGTH: 0
      DATA_FREE: 0
 AUTO_INCREMENT: NULL
    CREATE_TIME: 2017-10-19 12:14:34
    UPDATE_TIME: NULL
     CHECK_TIME: NULL
TABLE_COLLATION: latin1_swedish_ci
       CHECKSUM: NULL
 CREATE_OPTIONS:
  TABLE_COMMENT:

TABLE_ROWSが行数になるので、これを取得してあげれば手軽にデータ量が確認できます。ただ、InnoDBを利用していると概数でしか出てこないようなので、注意が必要です。(後述)

また、 DATA_LENGTH・INDEX_LENGTHを足してあげれば、どれくらい容量を使っているかも確認が出来ます。

Information_schema COLUMNS

カラムのメタデータを格納しているテーブルです。

TABLESと同様に、データベース名やテーブル名で絞り込みができます。

mysql> select * from information_schema.COLUMNS where table_name = "test_table"\G
*************************** 1. row ***************************
           TABLE_CATALOG: def
            TABLE_SCHEMA: test_database
              TABLE_NAME: test_table
             COLUMN_NAME: id
        ORDINAL_POSITION: 1
          COLUMN_DEFAULT: NULL
             IS_NULLABLE: NO
               DATA_TYPE: int
CHARACTER_MAXIMUM_LENGTH: NULL
  CHARACTER_OCTET_LENGTH: NULL
       NUMERIC_PRECISION: 10
           NUMERIC_SCALE: 0
      DATETIME_PRECISION: NULL
      CHARACTER_SET_NAME: NULL
          COLLATION_NAME: NULL
             COLUMN_TYPE: int(20)
              COLUMN_KEY: PRI
                   EXTRA: auto_increment
              PRIVILEGES: select,insert,update,references
          COLUMN_COMMENT:

Information_schema KEY_COLUMN_USAGE

キーの制約情報を格納しているテーブルです。

今回のテーブルではidをプライマリーキーにしているので、その情報が出てきます。

mysql> select * from information_schema.KEY_COLUMN_USAGE where table_name = "test_table"\G
*************************** 1. row ***************************
           CONSTRAINT_CATALOG: def
            CONSTRAINT_SCHEMA: test_database
              CONSTRAINT_NAME: PRIMARY
                TABLE_CATALOG: def
                 TABLE_SCHEMA: test_database
                   TABLE_NAME: test_table
                  COLUMN_NAME: id
             ORDINAL_POSITION: 1
POSITION_IN_UNIQUE_CONSTRAINT: NULL
      REFERENCED_TABLE_SCHEMA: NULL
        REFERENCED_TABLE_NAME: NULL
       REFERENCED_COLUMN_NAME: NULL

テーブルの行数を見る時の注意点

上記のinformation_schema TABLESを利用した時に得られる行数は、InnoDBだと概算値になってます。

自分はこれを知らなくて、どういうことなんだと悩みました。
行数確認の際、select文を走らせなくて良い分手軽ですが、InnoDBを利用している場合はこれに留意しておかないといけないようです。

実際にダミーデータを使って確認してみたのが以下になります。

#information_schemaでtable_rowsを見た時
mysql> select table_name, engine, table_rows from information_schema.tables where table_schema = "test_database";
+-------------+--------+------------+
| table_name  | engine | table_rows |
+-------------+--------+------------+
| user_innodb | InnoDB |      16400 |
| user_myisam | MyISAM |      16384 |
+-------------+--------+------------+

#select文でカウントした時
mysql> select (select count(*) from user_myisam) user_myisam, (select count(*) from user_innodb) user_innodb;
+-------------+-------------+
| user_myisam | user_innodb |
+-------------+-------------+
|       16384 |       16384 |
+-------------+-------------+

実際は16384個データがあるのですが、InnoDBのInformation_schemaでは16400個ということになっていました。
より大きなデータで利用する際は、誤差も無視できないレベルになりそうです。

まとめ

MySQLのメタデータを調べる際に便利な、information_schemaについて記述しました。

上記にピックアップした物以外にも、多くのメタデータが格納されているので、様々な状況で活用出来るかと思います。

自分でもまだ完全に把握できていないので、少しづつ調べていきたいと思います。

scpコマンドでファイルをリモートサーバからローカルへコピーする

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

リモートサーバからローカルへファイルをコピーするscpコマンドについて備忘録としてまとめました。

はじめに

 約3週間ぶりの投稿となります。贔屓球団がクライマックスシリーズのファイナルステージまで勝ち進み、まだ野球が見れる喜びを感じているむらさきです。
 さて今回は、リモートサーバーにあるファイルをローカルに持って来て取り出したいということがあり、その時に使ったscpコマンドについて備忘録として記事にしたいと思います。

1.scpコマンドとは

 scpコマンドはLinux系で使用できるコマンドで、sshを使用してネットワーク間でファイルをやり取りすることができます。
SSHを使用するため通信が暗号化されて安全にやり取りすることができるのが特徴です。

2.scpコマンドの使い方

scpコマンドの基本の形は以下になります

scp [オプション] [コピー元] [コピー先]

リモートサーバからローカルサーバにコピーしたい場合は

scp [オプション] ユーザ名@リモートサーバのホスト名(or IPアドレス):送りたいファイルのパス ローカル側の保存したいファイルのパス

となります。

例えば下記の状態だったとします

ユーザ名: violet
リモートサーバのホスト名: scp.command.test.jp
送りたいファイルのパス: /public/necessary_data.csv
ローカル側の保存したいファイルのパス: /media/sf_share_test/

この場合なら

scp [オプション] violet@scp.command.test.jp:/public/necessary_data.csv /media/sf_share_test/

とすることでコピーすることができます。

3.scpコマンドのオプション

次にscpコマンドのオプションはどんなものがあるのかを見ていきたいと思います。

・公開鍵ファイルを指定する

リモートサーバへの接続にSSH鍵が必要な場合は鍵をローカルサーバに保存して【 -i 】オプションで接続することができます

鍵の位置: ~/.ssh/violet_key.pub
scp -i ~/.ssh/violet_key.pub violet@scp.command.test.jp:/public/necessary_data.csv /media/sf_share_test/

・接続するポートを設定する

ポート番号を指定して接続する場合は【 -P 】オプションを使用します(大文字のPです)

ポート番号: 55555
scp -P 55555 violet@scp.command.test.jp:/public/necessary_data.csv /media/sf_share_test/

・ディレクトリごとコピーを行う

ディレクトリごと再帰的にコピーしたい場合は【 -r 】オプションを使用します

scp -r violet@scp.command.test.jp:/public/ media/sf_share_test/

おわりに

 いかがだったでしょうか。大学時代にscpコマンドを1度だけ習っていたので使えることは思い出したのですが、コピー元とコピー先のどちらが先か…などが思い出せなかったので備忘録として今回の記事を作成しました。
 一度学んだら忘れないが欲しいですね。 ん…?脳?……脳といえば前回、 指を動かして脳を活性化!?手と脳の関係とは? という記事を書いたのでまだ読んでない方は是非読みましょう!view数が増えると僕がちょっとだけ嬉しくなります。

第5回「ゲームプランナーはコミュニケーション能力が大事!」

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

みなさんお久しぶりです!Lionです。
前回の記事いかがだったでしょうか?
今年の東京ゲームショーは怒涛の神ゲーラッシュでしたね!
さて、本日は「ゲームプランナーはコミュニケーション能力が大事!」について話させていただきます。

私が考えるゲームプランナーは、制作チームやお客様の、それぞれの要求を聞きながら、ゲームのアップデートやリリースまでに様々な企画や仕様の作成やゲームバランスの調整が主な役割だと考えています。
それらを円滑に行うために、制作チーム内で必ず正確で分かりやすいコミュニケーションが必要となってきます。
しかし、コミュニケーションにも様々な方法があり、それは人の数だけあるかと思います。
今回は、ゲームプランナーに最低限必要なコミュニケーション能力を2個程、書いていきたいと思います!

考えや伝えたいことを可視化しよう!

enter image description here
1つ目ですが、ゲームプランナーはプロデューサーやディレクター、お客様の要望などから色々な仕様を考えたりすることが多いです。
私自身も色々なご意見を参考にしながら、ゲーム内にアップデートする仕様書を作成しています。
さて、仕様書はできました。ただ、仕様書ができただけではゲームはできません。
ここからが非常に重要で、自分が考えた仕様書を「開発メンバーと共有し認識を一致させる」必要があります。
もし、認識がずれてしまっていたら、認識のずれたままのゲームが完成してしまい、チーム内や受注先のお客様から「思っていたものと違うじゃないか!」と言われ、変更をしなければならないことになります。
過去の経験上です。

では、共通認識の誤差がでないためには?防ぐ方法は何か?を話していきます。
お客様やプロデューサーが考えている「○○をやればゲームが面白くなる。」「売れるようになる。」などを、落ち着いて一旦自分の頭で整理します。
整理が終わったら、文字や絵など使って、整理した内容をアウトプットする能力が必要となってきます。
アウトプットした内容を目で見える形にすることで、相手が思い描いていた考えをより簡単で分かり易く認識のすり合わせをすることができます。
これにより、お互いに認識誤差が発生しなくなります。

要するに、目に見えない頭の考えを目に見える形で具体的に表現することで、共通認識が取りやすくなります。
もし認識が間違っていても、「どこに認識の間違いがあったか」もすぐお互いで分かりますね!
当たり前のことですが、可視化ってとっても大事なのです!

面白いゲームを実現化させるために交渉力が大事!

enter image description here
2つ目ですが、ゲームプランナーのあるある現象ですが、「これ面白そうなんですけどどうっすか!」と案を大量に出す人が多いです。というか自分もその一人です。
しかし、考えて出てきた案は99%くらいの確率で大概没になります。
そこで、必要になってくる能力が「交渉する能力」となります。
ゲームを製作する際に色々なしがらみがあります。

・予算
・人員
・スケジュールの都合
・運営上のルール
・ゲームのコンセプト

ぱっと言えばこの5個くらいですね。
どんなに面白い企画書や仕様書でも実現できない場合が多々あります。これは、誰かが悪いと言うわけではなく、仕方のないことなのです。(大人の事情というやつです…)
しかし、「そっか…無理か…。」ってあきらめるのは嫌ですよね?そこで、交渉する力が問われます。
その企画や仕様をゲームに落とし込めることで「どんな良いことが起こるか?」をプレゼンし、交渉します。
例えば、「ポポロンファンタジー」とかいうスマホRPGがあるとし、今年で3周年を迎えるくらいの年季はあるゲームのゲームプランナーだとします。
そのゲームで新たな要素として「倒したモンスターを仲間にして育成し、戦闘を一緒に行う」という仕様を入れようと考えます。
※ちなみに仮定としてゲーム内モンスターは1000体いるとします。
「ゲーム内バランス」「仲間にしていいモンスターとしてはいけないモンスターの選別」などを考えるとかなり工数が高い実装になります。また、エンジニア側の工数も莫大です。
これを「これ面白そうなのでいれましょう!」なんて言う理由だけで持っていくと秒速でアウトをくらうでしょう。
しかし、ここで交渉の力を発揮するのです。
もし自分が交渉するのであれば、おおざっぱに書きますがこんな感じです。

「ゲーム内でモンスターが仲間になるという新たなギミックを導入することで、新たな戦闘スタイル、キャラに変わる新たな育成要素、モンスターを探すという楽しさがあります。」
「育成要素の部分は、沢山モンスターを集めて育成をしたい人向けに育成短縮の課金アイテムやレアモンスターが仲間になりやすくなる課金アイテムなどを入れて売り上げを向上させることもできると思います。」
「また三周年といこともありますので、ユーザーの目を引くチャンスでもあります。この新実装をSNSや広告バナーなどで幅広く宣伝し、新規ユーザーを獲得。長期的にログインしていないユーザーにカムバックメールを送り、大々的に告知するのはどうでしょうか?」

と交渉します。ここで大事になってくる要素は3点あります。
・新たな実装でユーザーにどんな変化がおこるか?
・売り上げに貢献できるのか
・ユーザーが増えるのか

などがポイントになります。もっと簡単に言えば○○をするとこんないいことあるんです!と伝えるのです。

ただ、メリットだけ言うのはダメなのです。ここで、実装する際の工数などの説明しなければなりません。
続いて書くと…。
「ただ、ゲームのモンスター数は1000体。中にはとても強いモンスターもいるため仲間にできるモンスターの選別に時間がかかりそうです。また、モンスターの育成、モンスターと戦闘などの新ギミックを導入するためエンジニアの工数もかなり高くなってしまいます。」
と起こりうる懸念点もきちんと話さなければなりません。
※良いことばっかり言ってるだけは胡散臭いなで終わります。

要するに「もたらせるメリットとデメリットをきちんと話、それでもメリットの方が恩恵が高い」と思わせたらいいのです。
ただ、「面白そうなのでどうですか?」などでは全然交渉にはならず、結局それが良い案だったとしても無くなってしまいます。
そういう風にならないためにも常に施策に対するメリット、デメリットを考えていると、交渉する時もすごく楽になると思います。

まとめ

まとめですが、自分は上記のことが50%くらいしかまだできません(笑)
人に物事を正確に伝える、達成したいことを交渉する能力は、実際やってみるとこれが超難しいのです。
でも、コミュニケーションの方法や交渉術を書いてある本を読んだりして勉強すれば誰にだって習得できるものだと思っています。
自分ももっとできるように精進していきたいです(。◕ˇдˇ​◕。)/

さて、来月は「第6回 本場のプランナーって仕事場で実際何してるのー?」を話していきたいと思います。
寒くなってきましたので、皆さん風邪をひかないようにお過ごしください!では!(/・ω・)/

Kotlinの便利機能

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

Kotlinでよく使う便利機能を紹介します

lateinit

遅延初期化に使います。

class Sample : AppCompatActivity() {
    private lateinit var name: String

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        name = intent.getStringExtra("WAGASHI") ?: "Daihuku" // エルビス演算子
        println(name)
    }
}

みたいに使います。
Javaだとメンバ変数で

private String name = ""

みたいな書き方をすると思うんですが、これだと再代入する前に呼び出されると空文字が返ってしまうのでよくないですね。
ちなみにKotlinで初期化する前にnameを呼び出すと落ちます。

オプショナル

val name = null
name.toUpperCase()

みたいなことをすると落ちます。

val name = null
name?.toUpperCase()

にすると何も起こらないため落ちないです。

他にも、

val num = 1
num as String   //落ちる
num as? String   //nullになる 

?を使うとこんな感じになります。

型を調べる

Javaだと、.getClass()で確認できました。
KotlinにはgetClass()が無いので、

val num = 1  
println(num.javaClass) //Javaの型が取得できる  
println(num.javaClass.kotlin) //Kotlinの型が取得できる  

.javaClassだとどんな型か調べることができました。
特定の型かどうかを調べたいときには、 is が使えます。

val num = 1
println(num is Int) // True

これはJavaではinstanceOf()を使っていた処理です。

テンプレート式

文字列の中に変数を入れられます。

// Java
Integer num = 1;
println("num is " + num);

// Kotlin
val num = 1
println("num is $num")

// 長い処理
println("num x2 is ${num * 2}")

AndroiStudioのConvert Java File to Kotlin File

これはKotlinの機能ではないんですが、Android StudioにはJavaをKotlinに変換する機能があります。
Code -> Convert Java File to Kotlin File
これが凄く便利です。
何か処理をしたくてサンプルコードを探したけどJavaしかないってときにJavaのコードをKotlinに変換します。
コンパイル時や実行時にエラーが出ることは経験上ないのでおすすめ。

まとめ

Kotlinを使うとNPEが起こりにくい書き方ができます。

また、同じ処理でもJavaに比べると簡潔に書けるのが魅力です。

今回は書きませんでしたが、コレクションの種類や操作関数も豊富なのでコード量が減らせたり安全な設計ができます。

Apacheを最新版に更新したらnagiosのhttp監視がBad Requestになった話

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。


こんにちは。アピリッツの本多です。 今回は先日apacheを最新版に更新した際に発生した事象についてご紹介します。 こちらはAWS上で稼働しているEC2インスタンス(OSはAmazon Linux)にて発生したものですが、他の環境でも発生した際のご参考になればと思い、本記事を投稿させて頂きます。

エラー発生の経緯

2017-09-18にAmazon Linux向けに「重要度:重要」のapache(httpd)のアップデートが配信されました。
https://alas.aws.amazon.com/ALAS-2017-896.html

そのため先日、稼働中のwebサーバー(以降server_01と表記)にてapacheの更新を実施したのですが、更新直後からnagios(server_01とは別のサーバー上で稼働)の監視項目の1つとして設定しているhttp監視からアラートが発生するようになりました。

手動で確認してもこの通り400 Bad Requestが返ってきます。
apacheのアップデート前までは200 OKでした。

/usr/lib64/nagios/plugins/check_http -H server_01 -u /
=> HTTP WARNING: HTTP/1.1 400 Bad Request - 513 bytes in 0.002 second response time |time=0.001673s;;;0.000000 size=513B;;;0

nagios設定

nagiosサーバーのhostsの設定にサーバー名を書いて、nagiosのcfgファイルでaddressの部分にそのサーバー名を指定していました。

# hosts
10.0.0.1 server_01
10.0.0.2 server_02

# cfgファイル
define host{
    use                     linux-server
    host_name               server-01
    alias                   server-01
    address                 server_01
    }
~
define service{
    use                             generic-service
    host_name                       server-01
    service_description             HTTP
    check_command                   check_http! -u /
    notifications_enabled           1
    }

原因

サーバー名「server_01」にアンダーバーを使用していた事により、エラーが発生していました。

汎用JPドメイン名登録等に関する技術細則
https://jprs.jp/doc/rule/saisoku-1-wideusejp.html

「ドメイン名」は、ラベルをピリオド(”.”)で区切って連結した文字列であ
る。

「ラベル」は、本技術細則により定められるASCIIラベルおよび日本語ラベル
により構成される。

「ASCIIラベル」は、英字(”A”から”Z”)、数字(”0″から”9″)、ハイフン(”-“)
からなる文字列である。ただし、ASCIIラベルの先頭と末尾の文字はハイフン
であってはならない。

ドメイン名については上記のように定義されているのですが、どうやら今回のapacheのアップデートにて、それを厳密にチェックする判定が導入されたようです。
そのため、ドメイン名に上記の文字列以外(今回のケースではアンダーバー)が入ると、400 Bad Requestを返すようになったと思われます。

nagios設定 修正後

おとなしく、nagiosのcfgをIPアドレスに変更する事でエラーが解消されました。

# cfgファイル
define host{
    use                     linux-server
    host_name               server-01
    alias                   server-01
    address                 10.0.0.1
    }
~
define service{
    use                             generic-service
    host_name                       server-01
    service_description             HTTP
    check_command                   check_http! -u /
    notifications_enabled           1
    }

手動で実行した際の結果もこの通りです。

/usr/lib64/nagios/plugins/check_http -H 10.0.0.1 -u /
=> HTTP OK: HTTP/1.1 200 OK - 352 bytes in 0.016 second response time |time=0.016100s;;;0.000000 size=352B;;;0

まとめ

今回はapacheのエラーという形で表面化しましたが、もしかすると他のミドルウェアとの連携でもハイフンが原因で正常に動かない等の問題があるかもしれません。
サーバー名を設定する際にアンダーバーを使うのはリスクがあるため今後は使わないようにしようと思います。

シェルスクリプトと戦う〜その2〜

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

前回はシェルスクリプトの始まりの部分とディレクトリパスの確認を行いました。
今回はif文と引数、変数を利用していきます。

if文を書いてみる

---checkVariable.sh

#!bin/bash
if [ "$1" == "" ]; then
  echo "引数が設定されていません。"
else
  echo "引数は$1です。"
fi

 
enter image description here

 
if文を使う場合はifで始まり、fiでif文全体の終わりを示します。
(条件分岐にはcase文もありますが使用頻度は……)

---listVariables.sh

#!bin/bash
if [ $# == 0 ]; then
  echo "引数が設定されていません。"
else
  for variable in $@
  do
    echo "引数の1つ:$variable"
  done
fi

 
enter image description here
 
入力した引数を順々にvariableに格納し、表示していきます。ただし、引数を設定していない場合は「設定されていない」旨のメッセージを表示します。
さて、$#$@$1といった変数は、シェル実行時点で初期値が決まっています。入る値は以下の通り。
 

$#:実行するシェルファイル名の後に続く引数の数(半角スペース区切り)
$@:実行するシェルファイル名の後に続く引数全体
$1:実行するシェルファイル名の後に続く引数の1つ目。2つ目以降は$2$3・・・となる。

これがおおよそわかっていれば、引数の順序について規則を設けた上で対応した変数を利用することができます。

少し本格的に書いてみる

set -euを利用していますが、こちらを参考に。(コメントも大事なことが。)
シェルスクリプトを書くときはset -euしておく

--- copyFile.sh

#!bin/bash
#bash記述のため、cd等実行エラー時強制終了するため保護
#想定外の事故をなるべく回避するため記載
set -eu
if [ $# != 2 ]; then
  echo "引数不足、または引数過多です。コピー前とコピー後のファイル名を入力してください。"
  exit
fi

#シェル自身のディレクトリを格納する。
dirPath=$(dirname $(readlink -f $0))

#半角スペースが含まれるディレクトリの場合は""でくくることで引数の区切りとして認識されなくなる
cd "${dirPath}"

if [ -e "$1" ] && [ ! -e "$2" ]; then
  cp -a "${dirPath}/$1" "${dirPath}/$2"
  echo "${dirPath}/$1を${dirPath}/$2にコピーしました。"
elif [ ! -e "${dirPath}/$1" ]; then
  echo "コピー元のファイルがありません。"
else
  echo "コピー先のファイルが既に存在します。上書きを防ぐため、実行を中止しました。"
fi

 
enter image description here
 
引数が2つぴったりでないと実行しない、ファイルをコピーするシェルスクリプトです。引数が2つ以外の時は、最初のif文で警告文を表示し、その後exitで実行を終了します。
処理するファイルの基準はシェルの置いてあるディレクトリとします。そのためシェルのディレクトリ位置を取得し、 cd コマンドで移動しています。
 
2つ目のif文ではコピー元のファイルが存在すること、及びコピー先に指定した名前のファイルが存在しないことを確認しています。分岐が3つになったので、else ifにあたるelifを使用しています。
 

変数記述に注意

引数を順々に表示するシェルと違い、自身で設定した変数(dirPathなど)に{ }が付いています。このスクリプトでは{ }はなくとも正常に動作するのですが、例えば、

--- wrongCopy.sh

#!bin/bash
# -u オプションは後解説用にわざと外しています
set -e
dirPath=$(dirname $(readlink -f $0))
cd "${dirPath}"

if [ -e "$1.txt" ] && [ ! -e "test_$2.txt" ]; then
  echo "===== コピー開始 ====="
  #一度$2をtargetに格納する
  target=$2
  target_today=wrong

  #ここだけ外す
  cp -a "$1.txt" "test_$target_today.txt"
  echo "===== コピー終了 ====="
  echo "test_${target}_today.txt が作成されました。"
  ls -la test_*
fi

 
拡張子前の名前を引数として、これを実行した際に以下のようになります。
 
enter image description here
 
想定と違うファイルが生成されましたね。
これはtargetという変数ではなく、target_todayという変数で展開されているためです。一方で、先に出した例では/が存在するために変数名がその手前で終了していると認識されたため、正常に動作してしまいます。
$1$2なども同様の現象に陥りそうですが、こちらは元々ユーザ側では先頭が数字の変数名をつけられないため、別に認識してくれるようです。
 
正常に動作するパターン、正常に動作しないパターンを覚えてもいいですが、それよりも変数名を{ }でくくるようにした方が予期せぬ動作を起こしにくくなると思います。
また、今回target_todayの値を設定していますが、未定義の場合はtest_.txtとして生成されます。(未定義の場合にも終了させるset -uオプションがあります。)
 

存在確認前に引数の例外処理を実行

少し判定文を付け加えます。

--- copyFile.shに追加

〜省略〜
dirPath=$(dirname $(readlink -f $0))

# /と/homeだけ弾く例
if [[ "${dirPath}" =~ ^/(home/*)?$ ]]; then
  echo "現在置かれているシェルの場所では実行できません。実行するユーザのディレクトリ以下の場所に配置してください。"
  exit
else
  echo "${dirPath}"
fi

if [[ "$1" =~ "\.\." ]] || [[ "$2" =~ "\.\." ]]; then
  echo "予期しない動作を行う可能性があるため、処理を終了します。"
  exit
fi

cd "${dirPath}"
if [ -e "$1" ] && [ ! -e "$2" ]; then
〜省略〜

 
enter image description here
cpコマンド前に終了したので生成されない
 
enter image description here
実行したくないディレクトリなのかを確認
 

簡易的ですが、このディレクトリでは絶対に処理させたくないことを示すif文を最初に記載しています。前回にも書きましたが、テスト用の環境構築を行ってから実行しましょう。2つ目は1つ上のディレクトリに行く記述を禁止しています。
また、判定文として正規表現を利用しています。[の数が変わる等、変化しているので注意してください。
 

終わりに

前回締めに書いた通り、今回はif文と変数を利用した、上書きできないコピーを行うシェルを紹介しました。正規表現もパスの確認のために少し利用しました。これくらいの内容であれば、普通にコマンド入力した方がはやいのですが、様々な処理を盛り込んだり、一部同じ値を使いまわしたりする場合は、単純なコピーでも作成すると楽になるのではないでしょうか。
記事を書いている本人もしっかりしたものを書けるわけではないですが、興味や参考になればと思います。
 


関連記事:
シェルスクリプトを書くときはset -euしておく
シェルスクリプトと戦う〜その1〜

Googleデータスタジオでハイパーリンクを設定する方法

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

Googleデータポータルだと表のハイパーリンクが設定されない・・・。そうお思いですか?実はGoogleアナリティクスのように表のURLから対象ページにリンクするだけでなくより複雑なリンク指定もできるんです。

この記事でまとめられていること

こんにちは。株式会社アピリッツでアナリストをしているssekiです。
最近GoogleデータポータルとGoogleアナリティクスを交互に見ることが多くなりました。

毅然としてGoogleアナリティクスを使い続ける理由はいくつかあるのですが、こと定点観測においてもデータポータルに比べてGoogleアナリティクスで便利だなぁと感じる点がひとつあります。

それは・・・、
「表から直接URLにリンクできる」ことです。

しかしながら、その考えは間違いでした。
実は、関数を使えばGoogleデータポータルでもハイパーリンクはできるんです!!!!

ということで、今回はGoogleデータポータルの表中でハイパーリンクを使用する方法についてご紹介いたします。

HYPERLINK関数を使ってハイパーリンクをつけよう

Googleデータポータルにハイパーリンク機能をつける関数、その名もHYPERLINK関数です。
名前そのまんまなので覚えやすいですね!

早速HYPERLINK関数の使い方を見てみましょう。以下はヘルプページからの引用です。

HYPERLINK(URL、リンクのラベル)
パラメータ:
 URL – URL として評価されるフィールドまたは式。
 リンクのラベル – フィールド、式、またはリテラル値。

あいかわらず、ヘルプページは簡潔すぎて意味がよくわからないですね・・・。
では、実際に簡単な例を通して使い方をご紹介いたします!

GoogleアナリティクスのようにURLをリンクさせる方法

Googleアナリティクスでは行動>サイトコンテンツ>すべてのページなどに表示されるディメンション「ページ」の表にハイパーリンクが設置されていますよね。
enter image description here

Googleデータポータルで同じような表を作成しても、デフォルトではハイパーリンクになりません。
そこで、HYPERLINK関数の出番です。計算フィールドで以下のディメンションを作成しましょう。

HYPERLINK(ページ,ページ)

URLの部分にページを指定、リンクのラベルの部分にもページを指定してみます。
そうすると、表ではこのように表示されますね。
enter image description here
ページ(リンク可能)と書いてある列が作成したディメンションです。
察しの良い方はお分かりでしょうが、これではリンクは機能しません。

見た目上同じなのになぜ?と思うかもしれませんが、URLに指定されている部分が”/”で始まっているので、リンク先のURLとして正しくない状態です。

そこで、URLの部分を次のように書き直してみましょう。

HYPERLINK(CONCAT("https://shop.googlemerchandisestore.com",ページ),ページ)

これで見た目は同じで、リンクが機能するようになりました。

えっ?初めてみる関数がある?
そうです。Googleアナリティクスデータを用いる場合、CONCAT関数を使って初めてHYPERLINK関数は機能するのです。

CONCAT関数で複数のものを結合させよう

CONCATはconcatenationの略で、日本語にすると「連結、結合」を意味します。
その名の通り、何かと何かをつなげる役割をする関数です。

使い方はいたって簡単。
CONCAT(A,B,C,・・・)のように、連結したい文字をパラメータに指定することで「ABC・・・」という文字列が生成されます。

先ほどHYPERLINK関数の利用例では、ドメイン部分をCONCAT関数によって前部分につけ、正しいURLに差し替えたというわけです。

クロスドメイン設定をしている場合はそのままでOK

ちなみに、先ほど誤用例として挙げた式ですが、全く使えないわけではありません。
実は、Googleアナリティクスでクロスドメイン設定をしている場合には、そのまま有効なリンクになるのです!

なぜなら、クロスドメイン設定時にはディメンション「ページ」がドメインから始まる正しいURLになるためです。

むしろ、この場合Googleアナリティクスの方でハイパーリンクが機能しない分、Googleデータポータルの方が優秀ですね。恐れ入りました。

※クロスドメイン設定とは、Googleアナリティクスでクロスドメイントラッキングを設定し、レポートビュー上でクロスドメインのフィルタをかけたものを指しています。

リンク表示名を入れ替えることも可能

HYPERLINK関数を用いれば、リンク表示名(ラベル)を変更することもできます。
例えば、こんな感じ。

HYPERLINK(CONCAT("https://shop.googlemerchandisestore.com",ページ),ページ タイトル)

enter image description here

ページ(リンク可能)の列に表示されている文字列がページタイトルになっていますが、正しくリンクすることができます。

HYPERLINK関数の2番目のパラメータは表示する内容(ラベル)を表すため、文字を入れ替えて利用することもできるのですね。

ますます、Googleアナリティクスよりも便利だということが露呈してしまいました・・・。

まとめ:この機能はどんな人におすすめか

今回はHYPERLINK関数とCONCAT関数を用いて表中のURLをハイパーリンクにする方法をご紹介いたしました。
どちらの関数もGoogleアナリティクスでは難しい表示を実現するために使い勝手の良いものになっています。

特にGoogleデータポータルでレポート管理をしている方にとって、重宝するのではないでしょうか?
ぜひ使い方を覚えて、活用してみてください!

Gooagleデータポータルでハイパーリンクを設定する方法

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

Googleデータポータルだと表のハイパーリンクが設定されない・・・。そうお思いですか?実はGoogleアナリティクスのように表のURLから対象ページにリンクするだけでなくより複雑なリンク指定もできるんです。

この記事でまとめられていること

こんにちは。株式会社アピリッツでアナリストをしているssekiです。
最近GoogleデータポータルとGoogleアナリティクスを交互に見ることが多くなりました。

毅然としてGoogleアナリティクスを使い続ける理由はいくつかあるのですが、こと定点観測においてもデータポータルに比べてGoogleアナリティクスで便利だなぁと感じる点がひとつあります。

それは・・・、
「表から直接URLにリンクできる」ことです。

しかしながら、その考えは間違いでした。
実は、関数を使えばGoogleデータポータルでもハイパーリンクはできるんです!!!!

ということで、今回はGoogleデータポータルの表中でハイパーリンクを使用する方法についてご紹介いたします。

HYPERLINK関数を使ってハイパーリンクをつけよう

Googleデータポータルにハイパーリンク機能をつける関数、その名もHYPERLINK関数です。
名前そのまんまなので覚えやすいですね!

早速HYPERLINK関数の使い方を見てみましょう。以下はヘルプページからの引用です。

HYPERLINK(URL、リンクのラベル)
パラメータ:
 URL – URL として評価されるフィールドまたは式。
 リンクのラベル – フィールド、式、またはリテラル値。

あいかわらず、ヘルプページは簡潔すぎて意味がよくわからないですね・・・。
では、実際に簡単な例を通して使い方をご紹介いたします!

GoogleアナリティクスのようにURLをリンクさせる方法

Googleアナリティクスでは行動>サイトコンテンツ>すべてのページなどに表示されるディメンション「ページ」の表にハイパーリンクが設置されていますよね。
enter image description here

Googleデータポータルで同じような表を作成しても、デフォルトではハイパーリンクになりません。
そこで、HYPERLINK関数の出番です。計算フィールドで以下のディメンションを作成しましょう。

HYPERLINK(ページ,ページ)

URLの部分にページを指定、リンクのラベルの部分にもページを指定してみます。
そうすると、表ではこのように表示されますね。
enter image description here
ページ(リンク可能)と書いてある列が作成したディメンションです。
察しの良い方はお分かりでしょうが、これではリンクは機能しません。

見た目上同じなのになぜ?と思うかもしれませんが、URLに指定されている部分が”/”で始まっているので、リンク先のURLとして正しくない状態です。

そこで、URLの部分を次のように書き直してみましょう。

HYPERLINK(CONCAT("https://shop.googlemerchandisestore.com",ページ),ページ)

これで見た目は同じで、リンクが機能するようになりました。

えっ?初めてみる関数がある?
そうです。Googleアナリティクスデータを用いる場合、CONCAT関数を使って初めてHYPERLINK関数は機能するのです。

CONCAT関数で複数のものを結合させよう

CONCATはconcatenationの略で、日本語にすると「連結、結合」を意味します。
その名の通り、何かと何かをつなげる役割をする関数です。

使い方はいたって簡単。
CONCAT(A,B,C,・・・)のように、連結したい文字をパラメータに指定することで「ABC・・・」という文字列が生成されます。

先ほどHYPERLINK関数の利用例では、ドメイン部分をCONCAT関数によって前部分につけ、正しいURLに差し替えたというわけです。

クロスドメイン設定をしている場合はそのままでOK

ちなみに、先ほど誤用例として挙げた式ですが、全く使えないわけではありません。
実は、Googleアナリティクスでクロスドメイン設定をしている場合には、そのまま有効なリンクになるのです!

なぜなら、クロスドメイン設定時にはディメンション「ページ」がドメインから始まる正しいURLになるためです。

むしろ、この場合Googleアナリティクスの方でハイパーリンクが機能しない分、Googleデータポータルの方が優秀ですね。恐れ入りました。

※クロスドメイン設定とは、Googleアナリティクスでクロスドメイントラッキングを設定し、レポートビュー上でクロスドメインのフィルタをかけたものを指しています。

リンク表示名を入れ替えることも可能

HYPERLINK関数を用いれば、リンク表示名(ラベル)を変更することもできます。
例えば、こんな感じ。

HYPERLINK(CONCAT("https://shop.googlemerchandisestore.com",ページ),ページ タイトル)

enter image description here

ページ(リンク可能)の列に表示されている文字列がページタイトルになっていますが、正しくリンクすることができます。

HYPERLINK関数の2番目のパラメータは表示する内容(ラベル)を表すため、文字を入れ替えて利用することもできるのですね。

ますます、Googleアナリティクスよりも便利だということが露呈してしまいました・・・。

まとめ:この機能はどんな人におすすめか

今回はHYPERLINK関数とCONCAT関数を用いて表中のURLをハイパーリンクにする方法をご紹介いたしました。
どちらの関数もGoogleアナリティクスでは難しい表示を実現するために使い勝手の良いものになっています。

特にGoogleデータポータルでレポート管理をしている方にとって、重宝するのではないでしょうか?
ぜひ使い方を覚えて、活用してみてください!

【C#/Unity】コルーチン(Coroutine)とは何なのか

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

コルーチンについて理解を深めるためにざっくりと調べてまとめてみました。

はじめに

Unity、と言うよりC#など幾つかの言語には「コルーチン」という仕組みが備わっているのですが、実は今まであまり触れたことがありませんでした。
私のプロジェクトでは普段使う場面も無く、使わないまま苦手意識が芽生えてしまうのは非常にまずいので、調べてみてDoRubyの記事としてまとめることで理解を深めようと思います。

本記事で扱うコルーチンは、Unityの仕様として提供されているコルーチンを指します。

What is Coroutine

そもそもコルーチンがどんな仕組みなのか分かっていなかったので、そこから整理してみます。
Unityの公式マニュアルによると、

コルーチンとは実行を停止して Unity へ制御を戻し、ただし続行するときは停止したところから次のフレームで実行を継続することができる関数です。

とのこと。

つまりコルーチンは、「フレームを跨いで処理を中断・再開させることが出来る仕組みを持った、関数の亜種みたいなもの」という認識で良さそうです。

How to use Coroutine

マニュアルによると、IEnumerator型を戻り値とした関数を定義することで、その関数をコルーチンとして扱うことが出来るようです。
IEnumerator型はコレクションを扱うためのインターフェースなので、
using System.Collections; しておく必要があります。
定義したコルーチンを実行したい場合は、

StartCoroutine("コルーチン名")
StartCoroutine(IEnumerator型の変数)

のように呼び出すことで実行できます。(MonoBehaviourを継承する必要あり)

同様に、

StopCoroutine("コルーチン名")
StopCoroutine(Enumerator型の変数)

でコルーチンを停止(≠中断)できます。

使い方も分かったところで、適当にコルーチンを使うコードを書いてみました。

using System.Collections;
public class Test : MonoBehaviour{
    void Start(){
        IEnumerator coroutine = TestCoroutine();
        StartCoroutine(coroutine);
    }
    IEnumerator TestCoroutine(){
        Debug.Log("First.");
        yield return null;
        Debug.Log("Second.");
    }
}

StartCoroutineでコルーチン(TestCoroutine)が呼ばれると、コルーチンの中身が実行されてUnityのコンソールに「First.」と表示されます。
次の行に yield return null; と書いてあるのが、コルーチンの中断処理です。
これが呼ばれるとコルーチンは処理を中断するため、Unityは別の処理を行えるようになります。
そして、次のフレームになるとコルーチンの処理が再開され、先ほど中断した行の次の行から実行されます。
すなわち、「Second.」と表示されるわけです。
このように、yield return を挟むことで処理を一時中断し、次のフレームまで待機することが出来ます。

When to use Coroutine

コルーチンをどんな時に使えばいいのか考えてみます。

コルーチンを使って実現できるのは、「一連の処理を複数フレームに跨って実行させること」です。
例えば、横スクロールアクションゲームでありがちな「穴から敵が垂直に飛び出てきて攻撃して戻っていく」処理を例とすると、

①何らかの条件を満たすことで、敵が飛び出すフラグがオンになる
②60フレームかけて敵を上に移動させる
③5フレームかけて敵が攻撃するアニメーションを行う
④60フレームかけて敵を下に移動させる
⑤フラグを消す

といった5つのステップが考えられます。(フレーム数は適当です)
UnityではMonoBehaviourクラスを継承することでオーバーライドしたUpdate関数が毎フレーム呼び出されるので、Update関数を使ってフレーム処理を行うのが良さそうです。

ということでコルーチンを使わずに安直に実装したのがこちらです。

int frameCount = 0;
void Update(){
    if (moveFlag) {
        if (frameCount < 60) {
            // 上に移動させる処理
        } else if (frameCount < 65) {
            // 攻撃アニメーション処理
        } else if (frameCount < 125) {
            // 下に移動させる処理
        } else if (frameCount == 125) {
            moveFlag = false;
            frameCount = 0;
        }
        frameCount++;
    }
}

moveFlagがtrueの時だけUpdate関数の中でframeCountをインクリメントし、その値に応じて各処理を行います。
しかし、この書き方ではif文やswitch文による条件分岐が多くなりがちで、フレーム数をカウントするためだけの変数が必要になります。(しかもどこかのタイミングで0に戻す必要がある)
65や125といった数字も前の数字に依存するため、上に移動する処理を40フレームに変更しようとした場合、それに伴って65を45に、125を105に直す必要が出てきます。
(変更しやすいように各ステップに必要なフレーム数を予め定数にしておいた場合でも、
65の部分には60を表す定数と5を表す定数を加算する式が入ることになり、冗長になります)

そもそもこのようにUpdate関数を使う場合、moveFlagがfalseの間も毎フレームmoveFlagの判定を行ってしまい、無駄が多くなってしまいます。

そこで、同じ処理をコルーチンを使って書くことで、無駄を省いた上に分かりやすいコードを書くことが出来ます。

IEnumerator MoveAndAttack(){
    for (int i = 0; i < 60; i++) { // 60フレームだけ実行
        // 上に移動させる処理
        yield return null;
    }
    for (int i = 0; i < 5; i++) { // 5フレームだけ実行
        // 攻撃アニメーション処理
        yield return null;
    }
    for (int i = 0; i < 60; i++) { // 60フレームだけ実行
        // 下に移動させる処理
        yield return null;
    }
}

yield return null; にたどり着くまでの処理 = 1フレームで行われる処理なので、for文で60回実行させれば60フレーム分の処理が行えます。
このコルーチンを、前述のmoveFlagがtrueになるタイミングでStartCoroutineで呼び出してあげることによって、Update関数で無駄な判定を行うことなく必要な処理を必要な分だけ実行することが出来ます。

さいごに

初見ではややこしいと感じていたコルーチンでしたが、基本的な使い方が理解できれば便利さも実感できました。
ちなみに、フレームごとに処理を実行するだけでなく、指定した秒数ごとに実行させることも可能なようで、
その場合 yield return null; のところを yield return new WaitForSeconds(秒数); と変えることで実現できます。
また、StopCoroutineでコルーチンを止めた場合でも、全く同じコルーチンを再度StartCoroutineすると、StopCoroutineで停止した状態の続きから実行されてしまうようです。

悪い例

IEnumerator coroutine = ExampleCoroutine();
StartCoroutine(coroutine);

// 何らかの処理が挟まる

StopCoroutine(coroutine); // ExampleCoroutineが停止することを期待するが...
StartCoroutine(coroutine); // 完全に停止せず、続きから実行されてしまう

 
これはIEnumerator型の変数に、同じコルーチンを再度代入することで回避できます。
 

良い例

IEnumerator coroutine = ExampleCoroutine();
StartCoroutine(coroutine);

// 何らかの処理が挟まる

StopCoroutine(coroutine); 
coroutine = ExampleCoroutine();
StartCoroutine(coroutine); // coroutineの指す対象が変わり、正常に最初から開始

 
コルーチンは画面上のオブジェクトの動きをコントロールするのに非常に便利だと感じました。
これから少しずつ使い慣れて行きたいと思います。
機会があれば、今度は「カスタムコルーチン」についても記事を書いてみようと思います。

【C#/Unity】LINQを使ってみたらとてもスマートだった

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

コレクション操作をする際にLINQを使ってみたところ、素晴らしさに気付けたので紹介したいと思います。

はじめに

皆様は統合言語クエリ(Language INtegrated Query)すなわちLINQというものをご存知でしょうか?
私の場合、Unity業務に携わってC#を触り始めて1ヶ月ぐらいで先輩に教えてもらい、ようやく知った概念でした。
LINQを知らない・よく分からないといった方々にLINQの便利さを知ってもらえるように記事を書いてみました。

余談ですが、記事を書くにあたりおさらいをするために「LINQ」でググったのですが、福岡県を中心に活動しているアイドルグループが出てきました。
私はアイドルに詳しくないんですが、全員エンジニアのアイドルグループとかあったら個性強そうですね。というかありそう。

統合言語クエリって何?

簡単に言うと、SQLなどで馴染みの深いSELECTやWHEREといった単語を用いてコレクションのデータを操作できる仕組みです。

C#やVBなど公式にLINQに対応している言語以外にも、JavaやPHPなどメジャーな言語で有志によるLINQ風のライブラリが開発されています。

LINQを使うとどう変わるのか

Unityでゲーム(私の場合はソーシャルゲーム)を作っていると、「リストの中から特定の条件に合うものだけを表示する」という処理をすることが多々あります。

例えば、所持アイテムの中から戦闘中使用可能なアイテムだけを表示したり、全ユニットの中から進化可能なユニットだけを表示したり…。
一般的にユーザーが持つデータの数が増えれば増えるほど、条件による絞り込み表示の有用性は高くなると思います。

例として挙げた「所持アイテムの中から戦闘中使用可能なアイテムだけを表示する」処理を実装するときに一番単純明快なのは、以下のコードのようにforeachを使ってリストの要素を片っ端から見ていき、条件に一致するかどうか判断することだと思います。
(本来C#の命名規則はほとんどPascalCaseのようですが、最も書き慣れているので自分の所属しているプロジェクトの規則に則ることをお許しください。)

List<Item> usableItemList = new List<Item>();

foreach(var item in itemList){ // itemList = 全所持アイテムのリスト
    if (item.usableInBattle) { // アイテムが戦闘中使用可能かどうかのフラグを見る
        usableItemList.Add(item);
    }
}

foreachでリストの各要素を見ていき、if文で判定を行うという至ってシンプルな処理です。
本筋でないので省略しますが、もちろんこの後にusableItemListの中身を表示します。

このコードをLINQを使って書いてみると、こうなります。

List<Item> usableItemList = itemList.Where(item => item.usableInBattle);

たった1行で、先ほどの5行の処理を表せます。
Whereを使うことで、SQLのように条件を指定して絞り込みを行っているわけです。
5行から1行になったことは視覚的に大きな変化ですが、それ以外にもネストが減ることによる可読性の向上などの恩恵も受けられます。
ちなみに、LINQを使うためには using System.Linq; する必要があります。

メソッド構文とクエリ構文

先ほどの例では、Whereをメソッドのように使用してLINQを記述しました。
このような記法をメソッド構文と言い、それ以外にLINQにはクエリ構文というものがあります。

先ほどのコードをクエリ構文を用いて書く場合、

List<Item> usableItemList = item in itemList
                            where item.usableInBattle;

このようになります。

どちらを使っても同じ処理が出来るので、最終的には好みに依ると思うのですが、私はC#っぽさ(?)を重視してメソッド構文を使っています。
そもそも私のように生のSQLを書くことに慣れていない身としては、メソッド構文の方が圧倒的に書きやすく直感的でした。

LINQの主なメソッド

LINQには、もちろんWhere以外にもメソッドが用意されています。
どれも便利なので、私がよく使うものを簡単な例とともに載せておきます。
ちなみにLINQのメソッドはどれもIEnumerableクラスの拡張メソッドとして定義されています。

Where 条件指定して要素を抽出する

List<int> integerList = {0,1,2,3,4,5,6,7,8,9};
List<int> result = integerList.Where(value => value % 3 == 0); // {0, 3, 6, 9}

Select 要素に対して処理を行い、その結果を返す(射影)

List<int> integerList = {0,1,2,3,4,5,6,7,8,9};
List<int> result = integerList.Select(value => value * 10); // {0, 10, 20, 30, 40, 50, 60, 70, 80, 90}

Any 指定した条件を満たす要素が含まれているかどうか判定する

List<int> integerList = {0,1,2,3,4,5,6,7,8,9};
List<int> result = integerList.Any(value => value == 0); // true
List<int> result2 = integerList.Any(value => value > 9); // false

FirstOrDefault 指定した条件を満たす最初の要素を返す 無ければ要素の型の規定値を返す

List<int> integerList = {0,1,2,3,4,5,6,7,8,9};
List<int> result = integerList.FirstOrDefault(value => value > 5); // 6
List<int> result2 = integerList.FirstOrDefault(value => value < 0); // 0

ほぼ同じ処理をするメソッドにFirstがありますが、Firstは要素が見当たらなかった時に例外を発生させます。

OrderBy 昇順に並べ替えを行う / OrderByDescending 降順に並べ替えを行う

List<int> integerList = {3,0,2,8,6,9,4,7,1,5};
List<int> result = integerList.OrderBy(value => value); // {0,1,2,3,4,5,6,7,8,9}
List<Item> itemList = {item1, item2, item3, item4} 
// item1.price = 100, item2.price = 50, item3.price = 300, item4.price = 0 とする

List<Item> result2 = itemList.OrderByDescending(item => item.price); 
// {item3, item1, item2, item4}

まとめ

LINQを使うとコレクションに対する色々な操作が簡単に出来るよ!ということが伝わっていれば幸いです。
WhereやSelect、OrderByなど、戻り値がIEnumerable型のメソッドはメソッドチェーンして繋げることが出来るので、foreachやifで書くと長ったらしくて複雑な処理でも、LINQなら繋がっているメソッド名を見ることでなんとなく何をしているのかが分かりやすいのも強みだと思います。
他にも、LINQのメソッドの遅延実行によるメリットなど私もまだまだ理解が足りていない部分もありますが、基本的なメソッドと使い方を覚えるだけでforeachやifによるネストの回数がグッと減ると思います。

今回も長くなってしまいましたが、お付き合いありがとうございました。

【Unity】メニュー項目を増やして君だけの最強のUnityを作ろう!

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

MenuItem属性を使ってUnityのメニューをカスタマイズします。

はじめに

4月に入社し、配属先のプロジェクトのエンジニア業務を引き継いで早くも半年。
今までUnityで当たり前のように使っていた機能が、実は先輩方が作った機能だと知り驚きました。
Unityを使い始めたのも3月ぐらいなので、メニューに存在する項目は最初からあるものだと勘違いしていたのです。(無知)
…というより業務に関係する機能が多かったので、家でUnityを触っていても無いことに気付かなかったと言うのが正解です。

ということなので、今回はUnityのメニューに項目を追加して、便利なスクリプトをポチっと実行出来るようにしちゃおうという話です。

メニュー項目を追加する方法

さて、メニュー項目、つまりMenuItemの追加方法ですが、もちろんスクリプトで行います。
静的関数の定義時に[MenuItem]属性を付けることで、その関数がMenuItem(つまりメニュー項目)として登録されます。

[MenuItem("CustomMenu/MenuTest")]
static void MenuTest(){
    // なんかする
    Debug.Log("なんかした!");
}

こんな感じですね。
この場合、メニューの大項目に「CustomMenu」が追加され、その下に「MenuTest」という項目が追加されます。
クリックすると、MenuTest関数に記述した処理を実行できます。

enter image description here

[MenuItem("Window/MyWindow")] のように、既に存在する大項目の下に新たな項目を追加することも可能です。

ちょっと応用編

チェックマークをつける

Menu.SetChecked関数にメニュー項目のパスとtrue/falseを渡すことで、チェックマークが付けられます。
デバッグする際に切り替えたいゲーム全体に関わるフラグなどをここで変更できると楽ですね。
現在の状態を取得するのはMenu.GetChecked関数で行います。

[MenuItem("CustomMenu/Check")]
static void Check(){ 
    var path = "CustomMenu/Check";
    var checkFlag = Menu.GetChecked(path);   
    // 何らかのtrue/falseを切り替えたりする
    Menu.SetChecked(path, !checkFlag);
    Debug.Log("何らか is " + (!checkFlag).ToString() + "!");
}

ショートカットキーを設定する

追加した項目に対してショートカットキーを設定します。
MenuItemの引数に設定してるパスの最後に、「半角スペース」「修飾子キー」「文字」を入れることで設定できます。
以下の例では、#がShift、%がcommand(WindowsではCtrl)を表すので、
Shift + cmd + D で任意のスクリプトが実行可能になっています。

[MenuItem("CustomMenu/ShortCutTest #%d")]
static void ShortCutTest(){
    // なんかする
    Debug.Log("ShortCut!");
}
enter image description here

コンテキストメニューに追加する

右クリックや歯車マークのクリックなどで表示されるコンテキストメニューに、新たな項目を追加します。
選択中のアセットやコンポーネント等を取得し、それに対して処理を行うことも可能になります。

[MenuItem("CONTEXT/Component/ContextTest")]
static void ContextTest(MenuCommand menuCommand){
    // コンポーネントに対してなんかする
    Debug.Log(menuCommand.context);  
}

コンテキストメニューに項目を追加するには、MenuItem属性の引数であるパスの一番最初に CONTEXT/ を入れます。
上の例では、ComponentのコンテキストメニューにContextTestという項目を追加し、
MenuCommand型の引数を設定することで、その引数を使って取得したコンポーネントの情報を表示しています。
もちろん引数を取らなくてもメニュー項目を追加できます。
また、コンテキストメニューに対しても先ほどの方法と同じやり方でショートカットキーの設定が出来ますが、コンテキストメニューを開いている状態でないと有効になりません。

enter image description here

実際の使用例

私の所属しているプロジェクトでは、このMenuItem属性を使って、日々の作業を楽にしてくれるようなスクリプトをすぐに実行出来るようにしてあります。

AssetBundleのビルド
AssetBundleNameの設定
Unityエディタ上でゲーム実行時、AssetBundleのロードを行うかどうかのフラグ
選択したアセットを使っているプレハブやシーンを、GUIDをキーにして検索
JSONファイルからプレハブを作成

などなど、主にアセット管理の面で役に立つものが多いですね。先人に感謝。

さいごに

メニュー項目やコンテキストメニューの項目をカスタマイズすることで、自分のプロジェクトに適したUnityが構築できますね。

メニュー項目を増やして君だけの最強のUnityを作ろう!(タイトル回収)

BoxのAPIを使って見よう。(その5)~【実践編】Box-管理者-グループ管理-EXCELで管理-便利ツール

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

0.グループ情報のコントールには

グルーブ管理の権限が必要です。

EXCEL(VBA)から呼び出す方法は、過去記事を参照して下さい。

BoxのAPIを使って見よう。(その1)~概要と基礎情報
BoxのAPIを使って見よう。(その2)~実際にAPIでデータ取得
BoxのAPIを使って見よう。(その3)~VBAから呼び出す
BoxのAPIを使って見よう。(その4)~グループの関連情報を取得

1.その前に。

作成したVBAを動作させるとき「開発」画面を表示してイミディエイトに、リクエスト情報やレスポンス情報を表示させるのが面倒ですよね。
そのため、今回はひと手間として、テキストファイルでトレースログを取得してみます。

参考にさせて頂いたサイトは、こちらです。

エクスプローラーで表示した様子です。EXCELと同じ場所に出力します。
エクスプローラーで表示したとき

以下の要件を追加

・ファイルが無ければ、自動生成
 ⇒ 消してから確認しいって事ありますね?
・ファイルがロックされている場合は、エラーにしない。
 ⇒ エディタによっては開いた時にロックされてしまう場合があるので、その対策

ログ出力(プログラムサンプル)

Sub WLog(message As String)

    On Error GoTo ErrorHandler

    Dim LogPath As String
    LogPath = ThisWorkbook.Path & "\box_sample.log"

    Dim objFso As Object
    Set objFso = CreateObject("Scripting.FileSystemObject")

    If Dir(LogPath) = "" Then
        objFso.CreateTextFile (LogPath)
    End If

    With objFso.OpenTextFile(LogPath, ForAppending)

        .WriteLine Now & vbTab & message
        .Close

    End With

    Set objFso = Nothing

ErrorHandler:
End Sub

BOXのレスポンスJSONをログに出力するときの、ひと手間

いままでの記事でも紹介にさせて頂いて居る VBA-JSONを使用してログを整形して出力してみます。

Private Sub ResponseJSONLog(ByVal resJson As Variant)
    TraceLog.WLog "response JSON" + vbNewLine + JsonConverter.ConvertToJson(resJson, Whitespace:=4)
End Sub

上記の様な関数を用意しておいて、ログに出力してみます。
以下はメモ帳で表示して見たときです。

トレースログでJSONを整形して表示

2. 【本題】BOXでグループを操作

今回は以下のようなインターフェース画面を作成し簡単に管理できるような仕組みを構築してみます。

・グループ情報を取得して一覧表示
・新規にグループを追加
・既存グループに対して変更・削除

■初期画面
初期画面

■グループ一覧を取得してみよう。
グループ一覧を取得する場合

■グループ一覧を取得し実際に変更を加えてみよう。
各グループの追加・更新・削除を実施する場合

今回使用するBOX APIは

・Create Group
・Update Group
・Delete Group
・Get Enterprise Groups

Create Group

グループを新規に登録するAPI。※グループを管理する権限が必要です。

■接続方法(基本)

curl https://api.box.com/2.0/groups \
-H "Authorization: Bearer ACCESS_TOKEN" \
-d '{"name": "section-mmaster", "description": "section 2 group", "member_viewability_level": "admins_only"}' \
-X POST

■パラメタ内容

create params

■レスポンス

グループ情報が復帰します。

※fields に、必要なパラメタを指定しないと含まれません。
※同じグループ名が既に存在すると、、HTTP status codes が 409 conflict となります。

Update Group

グループを更新するAPI。※グループを管理する権限が必要です。

■接続方法(基本)

curl https://api.box.com/2.0/groups/GROUP_ID \
-H "Authorization: Bearer ACCESS_TOKEN" \
-d '{"name": "section-master"}' \
-X PUT

■パラメタ内容

update params

■レスポンス

グループ情報が復帰します。

※fields に、必要なパラメタを指定しないと含まれません。
※同じグループ名が既に存在すると、、HTTP status codes が 409 conflict となります。

Delete Group

グループを削除するAPI。※グループを管理する権限が必要です。

■接続方法(基本)

curl https://api.box.com/2.0/groups/GROUP_ID \
-H "Authorization: Bearer ACCESS_TOKEN" \
-X DELETE

■パラメタ内容

delete params

■レスポンス
正常にDELETEされた場合には、HTTP status codes が 204 no_content となります。

Get Enterprise Groups

企業のすべてのグループを取得するAPI。※グループを管理する権限が必要です。

前回の記事 を参考にしてください。

3.サンプル

サンプルを作ってみましたので、試したい方はご自由にどうぞ。
サンプルEXCELをダウンロード
※VBAのコードは、現時点では非公開です。
※操作可能なグループ数は25に制限されています。

サンプルデータのダウンロード画面

4.まとめ

次回は
グループにユーザとフォルダを追加・更新・削除する機能を作ってみたいと思います。

※アピリッツではBoxの販売パートナーもしてるようです。

代理店販売してますよ。

お問い合わせは、こちら へ

フリー素材を知っておこう

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

 ゲームを作るにはたくさんの材料が必要である。「こんなゲームを作りたい!」という気持ちや構想はもちろん大事だが、それを形作るための音楽や画像がなければ実現できない。

フリー素材を探す

 「音楽 フリー素材」のような感じで検索すればすぐに見つかるので素材を探すこと自体は非常に簡単。敵グラフィックだろうが、攻撃アニメーションだろうが使い切れないぐらいには見つかる。そして利用規約を守れば誰でも自分のゲームに組み込める。そう、誰でも。

誰でも使えることに気をつける

 誰でも使えるということは、すなわち他人のゲームと同じ素材を使う可能性が高いことを意味している。もし有名なゲームが既に使っていれば、フリー素材という事情を知らないユーザーからは最悪パクリだと思われて、叩かれるという悲劇が起こったりする。
 一方で、自分がプレイした他人のゲームに欲しい素材があったら、そのゲームのクレジットから素材を逆に辿る事も出来る。(パクったわけじゃないんだからね!)

素材の管理に気をつける

 配布サイト毎にダウンロードした素材は管理しておかないと、後で困ったことになる。例えば利用規約が変わって商用利用禁止になったとき、自分のゲームがまだ未公開で商用利用するつもりだったならそれらの素材はゲームから抜く必要のある場合がある。(サイト運営者に問い合わせるのが確実。)
 また、配布サイトが閉鎖してしまう場合に備えて利用規約はどこかに記録しておくといい。

有償素材も考える

 他人と素材が被るのを気にするなら、有償の素材を使うことも一つの手。今や数百~数千円程度でまとまった素材が買える時代なので、よほどお金に困っていなければこちらを選ぶとグッと気が楽になるし捗ったりする。

余談

 フリー素材がどんなものがあるか知っていると、「実は大ブームを起こした有名ゲームが
フリー素材を使っている」なんてことに気づくこともあったりする。グラフィックは自製で、決定音のような誰も気にしない部分はフリー素材を使うというのも基本的で有効な手段だとよく分かる。

配列同士のinclude

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

プログラムを書いていて少し迷ったことがあったので書いていきます。

arr_1 = [1, 2, 4, 5, 5, 8, 9]
arr_2 = [2, 5, 8]

arr_1の中のものからいくつか選んだものがarr_2に入っている」という状態で、
arr_2arr_1の中身の組み合わせで成り立っているかを調べる必要がありました。


rubyでは配列同士は「-」演算子で差分を取れるのを知っていたので

(arr_2 - arr_1).empty?

このようにチェックをしようとしたのですがこのやり方だと…

arr_1 = [1, 2, 4, 5, 5, 8, 9]
arr_2 = [2, 2, 2, 5]

arr_2 - arr_1
=> []

こんな感じに重複した値がすべて消し去られてしまいます。
自分の場合は重複したものも個別に判定する必要があったのでこれではまずいです。

どうせ便利なメソッドがあるだろうと探しましたが見当たらなかったので自分で作ることにしました。

class Array

  def include_of_array(arr)
    temp = self.dup
    arr.each do |value|
       idx = temp.index(value)
       # 見つからなければ終了
       return false if idx.blank?
       temp.delete_at(idx)
     end    
     return true
  end  

end
arr_1 = [1, 2, 4, 5, 5, 8, 9]
arr_2 = [2, 2, 2, 5]

arr_1.include_of_array(arr_2)
=> false

arr_1.include_of_array([1, 5, 5, 9])
=> true

一件落着です。

Googleデータポータルで1日あたりのトランザクション数を確認する方法

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

今回はGoogleデータポータルの対象期間における1日あたりの平均トランザクション数を計算する方法についてご紹介いたします。DATE_DIFF関数やTODATE関数といったヘルプページだけではわかりにくい関数の実用TIPSとしてご活用いただければ幸いです。

この記事でまとめられていること

こんにちは。株式会社アピリッツでアナリストをしているssekiです。
Googleデータポータルには数多くの便利な関数があるのですが、使い方がなかなかわかりにくいのが正直なところ。
僕もヘルプページをにらみながら日々勉強しています。

今回ご紹介するのは、日付に関する関数です。実例を交えながらご紹介いたします。
例えば、こんな事例ってよくありませんか?

Aさん「このECサイトの目標は日平均○○個売上を上げることなんだけど、これってデータポータルで定点観測できないかな?」

僕「えっ、それって1日あたりのトランザクション数を計測するってことですよね。うまいこと計測日数を計算できればいけそうだけど・・・。ちょっと調べてみます。」

・・・というわけで、日数を計算する方法を探してみた僕ですが、なんとか解決できたのでご紹介いたします。

データポータルで計測日数を計算するにはDATE_DIFF関数とTODATE関数を使おう

ある程度調べていくと、Googleデータポータルにおいて日付がどのように扱われているか、次のようなことがわかりました。

  1. 日付はYYYYMMDD形式で指定する必要がある。
  2. TODATE関数を用いることで、日付データを他の形式に変換できる。
  3. DATE_DIFF関数を用いることで、2つの日付間を計算できる。

ここまでわかってくると、後はDATE_DIFF関数とTODATE関数を組み合わせれば何とかなりそうですね。
早速、それぞれの関数の使い方を調べてみることにしました。

DATE_DIFF関数の使い方

まずは、2つの日付間を計算するための関数、DATE_DIFF関数の使い方からまとめていきます。
Googleデータポータルのヘルプには、このように記載されています。

説明:XとYの日数の違いを返します(X-Y)。
構文:DATE_DIFF(X,Y)

かなりシンプルですね。しかし、ここで四苦八苦しました。
例えば、次のように仮の日付を当てはめてみてもエラーが出てしまいます。

DATE_DIFF('20171011','20171010')

また、シンプルに「日付」のディメンションを入れても、当たり前ながらダメでした。

DATE_DIFF(日付,日付)

どうすれば計算できるの???と小一時間悩み続けていたのですが・・・、

「データポータルで使える日付の形式が決まっているなら、もしかして日付の出力形式をDATE_DIFF関数が使えるようにしないといけないんじゃないか?」とやっと気づいたのでした。

つまり、TODATE関数の出番ってことですね!

TODATE関数の使い方

というわけで、日付データの形式を他の形式に換える関数、TODATE関数についてまとめていきます。
またもや、ヘルプページはシンプルにこう書いてありました。

説明:UTCの指定された形式で日時の項目を返します。
構文:TODATE(X,入力形式,出力形式)

UTCというのは、世界各地の標準時を決めるときの基準「世界標準時」のことです。
パソコンのタイムゾーン設定でよく見かけますね。

TODATE関数のXの部分は、指標やディメンションなどのデータを入れます。
そのデータの入力形式を次に指定し、変換したい形式をその次の出力形式に当てはめるようです。


ここで使える入力形式と出力形式には制限があり、次の文字列が使用できます。

【入力形式】
・「‘BASIC’」 YYYY/MM/DD-HH:MM:SS
・「‘DEFAULT_DASH’」 YYYY-MM-DD[HH:MM:SS[.uuuuuu]]
・「‘DEFAULT_SLASH’」 rmat YYYY/MM/DD [HH:MM:SS[.uuuuuu]]
・「‘DEFAULT_DECIMAL’」  YYYYMMDD [HH:MM:SS[.uuuuuu]]
・「‘RFC_1123’」 表示例:Sat, 24 May 2008 20:09:47 GMT
・「‘RFC_3339’」 表示例: 2008-05-24T20:09:47Z
・「‘SECONDS’」 エポックからの秒数
・「‘MILLIS’」 エポックからのミリ秒数
・「‘MICROS’」 エポックからのマイクロ秒数
・「‘NANOS’」 エポックからのナノ秒数
・「‘JULIAN_DATE’」 エポックからの日数
・「‘DECIMAL_DATE’」 ’DEFAULT_DECIMAL’ と同じ
文字列として有効な strptime 形式

【出力形式】
“%Y” 年
“%m” 月
“%d” 日
“%W” 週番号
“%w” 曜日
“%H” 時
“%M” 分

詳細についてはヘルプページをご参照ください。

ひとまず、この形式に合わせて日付をコントロールすればよさそうだということがわかりました。

対象期間の始点・終点間の日数を計算する方法

実際に、DATE_DIFF関数に合う形式で日付を修正してみましょう。
指標「日付」、入力形式「’DEFAULT_DASH’」、出力形式「’%Y-%m-%d’」で計算してみるとDATE_DIFFに合う形になるようです。

下のコードを計算フィールドに入力することで、対象期間の始点・終点間の日数を計算できます。

DATE_DIFF(TODATE(日付,'DEFAULT_DASH','%Y-%m-%d'),TODATE(日付,'DEFAULT_DASH','%Y-%m-%d'))

ここで、やってみて不思議だったのですが、DATE_DIFFのXとYの位置に同じTODATE構文を入れるだけで自動的に始点と終点を見分けてくれていました。

おそらく、Googleアナリティクスデータがそのような形式になっているのでしょう。

とにもかくにも、目的の第一段階であった対象期間の日数をカウントすることができました。
あとひといきです。

1日あたりのトランザクション数を計算する方法

次に、1日あたりのトランザクション数を計算してみましょう。
指標「トランザクション数」を先ほど作ったDATE_DIFF構文で割るだけです。

トランザクション数/DATE_DIFF(TODATE(日付,'DEFAULT_DASH','%Y-%m-%d'),TODATE(日付,'DEFAULT_DASH','%Y-%m-%d'))

簡単ですね。
式が複雑になってわかりづらいという方は、さきほどのDATE_DIFF構文を別の名前で保存して使ってもよさそうです。

まとめ:この機能はどんな人におすすめか

というわけで、無事に1日あたりのトランザクション数を計算することができました。

今回の日数計算の関数利用は様々な利用が考えられそうですね。
例えば、
・1日あたりのユーザー数を調べる
・リニューアル日(特定の日)から何日経っているかを調べる

など、少し上げただけでも、多くのマーケッターにとって実用的な機能な気がします。

ぜひ、記事を参考にいろいろ試してみてください!

flattenで多次元を1次元に

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

今回は、rubyのflattenメソッドの紹介をしていきます。

flattenメソッドとは

flattenは、配列とハッシュに使用することができます。
配列に対してflattenメソッドを使用すると、2次元配列や3次元配列などの階層の深い配列を1次元配列に再構築してくれます。

[ [1, 2], [3, 4], [5, 6] ].flatten
=> [1, 2, 3, 4, 5, 6]

[ [ [1, 2], [3, 4] ], [ [5, 6], [7, 8] ] ].flatten
=> [1, 2, 3, 4, 5, 6, 7, 8]

ハッシュに対してflattenメソッドを実行すると、ハッシュのキーと値の一次元配列が返ります。

{ "apple" => 3, "grape" => 2, "peach" => 5 }.flatten
=> ["apple", 3, "grape", 2, "peach", 5]

しかし、ハッシュの値がハッシュである場合は値のハッシュは変換されません。

{ "red" => { "apple" => 3 }, "purple" => { "grape" => 2 } }.flatten
=> ["red", {"apple"=>3}, "purple", {"grape"=>2}]

mapメソッドとの組み合わせて使用する

ここで紹介しているflattenメソッドですが、mapメソッドと組み合わせて使用することで階層の深い関連データを1次元の配列で取得することができます。
例をProject Programmer Workモデルで説明します。目的は、Projectに関係のあるProgrammerのWorkの一覧を取得することです。
enter image description here

以下のコードがProjectに関係のあるProgrammerのWork一覧を取得するコードです。

project = Project.find(1)
project.programmers.map(&:works).flatten

このコードが何をしているのか説明していきます。
まず、project.programmersでprojectに紐づくprogrammerを参照しています。

project.programmers
=> [#<Programmer id: 1, name: "taro">, #<Programmer id: 2, name: "jiro">]

次にproject.programmers.map(&:works)でprogrammerに紐づくworkを参照しています。ただし、2次元配列で返ってきます。

project.programmers.map(&:works)
=> [[#<Work id:1, status: "new">, #<Work id:2, status: "fin">], [#<Work id:3, status: "fin">, #<Work id:4, status: "fin">]]

最後にproject.programmers.map(&:works).flattenで2次元配列を1次元配列に再構築しています。

project.programmers.map(&:works).flatten
=> [#<Work id: 1, status: "new">, #<Work id: 2, status: "fin">, #<Work id: 3, status: "fin">, #<Work id: 4, status: "fin">]

まとめ

eachなどのループを使用すれば、この記事で紹介したように階層の深いデータを取得することができますが、mapとflattenを使用すれば1行で納めることができます。自分の経験上、関連データの関連データを一覧で取得する機会があまりないですが、rubocopで行数を抑えたいときに使えるかと思います。

超高速通勤に注意!?未来に行く方法

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

またもや通勤がサブテーマですが、今回は社会人なら常に気を配りたい”時間”にまつわるお話です。

人によって流れる時間が違う!

時刻ピッタリに電車が発車したり、5分でも遅刻は遅刻であったり、期限を守らないと大変なことになったりと、現代日本はとても時間に正確な社会ではないでしょうか。
しかし、こんなにも社会の軸とされている”時間”というものが、そもそも人によって流れる速さが違うことをご存知でしょうか?

これは、超有名な物理学者アインシュタイン博士が発表した、「相対性理論」(正確には特殊相対性理論)によって導かれる結論です。
この理論によれば、運動している速度が速ければ速いほど、その人の流れる時間が遅くなるらしいのです。
例えば、ロケットに乗って1時間ぐらい超高速の宇宙お散歩をして帰ってくると、地球では1日経過していた、みたいなタイムスリップのようなことが起こるのです。
enter image description here
一見現実味がないようなこの理論ですが、実はすでに色々なものに応用されており、その身近な代表例がGPS時計です。
GPS時計は人工衛星を使って、いつでも正確な時刻を時計に送っているのですが、人工衛星は地球の周りを時速1万キロで回り続けているため、
速度による時間の遅れが無視できなくなってしまいます。そこで、内部で相対性理論に基づいた補正を行い、正確な時刻を出しているのです。

未来に行く!?

さて、すでに例で出したように、超高速のロケットに乗って、宇宙旅行を楽しんで帰ってくることで、地球ではロケットの中以上の時間が流れているため、結果として未来に行くことが出来ます。
ここでは、実際にどれぐらいの速度で走れば実感できるほど未来に行けるのかを計算してみたいと思います。
事前の計算でかなり速度が必要なことが分かったので、いきなりぶっ飛ばします。

①秒速269813.2122km
秒速約27万キロです。1秒あれば大体地球を7周ぐらいします。
この超高速ロケットに乗っている人の1秒は、地球の人の約2.3秒に相当します!
この速度を出せれば、このロケットの中の時間は、地球時間のおよそ半分の速度で流れることになります。
つまり、このロケットの中で5年過ごすと、地球では約10年が経過します。劇的ではないですが、まあまあなタイムスリップになるのではないでしょうか。

②秒速299792.158207542km
秒速約30万キロです。1秒で大体地球を7周半します。
この超高速ロケットの中の1秒間は、地球上の人のなんと707秒に相当します!
つまり、このロケットの中の時間は地球上の700分の1の遅さになるのです。
ここで1年過ごすだけで、地球では700年もの歳月が流れ、あっという間に浦島太郎状態です。

**考察**
この速さを実現するには1年のような長い時間をかけてロケットを加速し続ける必要があります。(ちなみに1時間ほどでこの速さまで加速しようとすると、体に8500G(体重の8500倍の力)が掛かります。注意してください)
従って、
①数年単位で加速し続けられるエンジンと燃料
②数年間人間が生活し続けられる環境
③途中の惑星などの影響をよけられるようなシステム
これらが実現できれば、この未来への旅行も現実味を帯びてくるのではないかと思います。未来に行ける世界は意外と近くまで来ているかもしれませんね。

補足:上の例で出している速度は、光の速さ(秒速299792.458km)を基準に、①その90%の速さ、②その99.9999%の速さ、で計算したものになります。
光の速度に近づくにつれて急激に時間が遅くなることが分かります。
(ちなみに、現在のロケットは、秒速数十km程度であるようなので、今のロケットに乗っても実感できるほど未来に行くには、まだまだ速さが足りません。)

過去には戻れるの?

未来に行くことが出来ることは分かりました。しかし、過去に行くにはどうすればよいのでしょうか。
相対性理論において、物体の速度をどんどん上げていくと、光の速度を超えたところで時間が逆転し、過去に進むようになります。
しかし、現在の物理学では、質量をもつ物体の速度は光の速度を超えることはできないとされています。
そのため、残念ながら現状では過去への行き方は分かりません。未来に行くときには、もう戻れないことに注意してくださいね。

終わりに

いかがでしょうか。通勤速度を追い求めるあまり、未来に行ってしまって結局遅刻する、のようなことが無い安全運転の通勤をしたいですね。

Rails×chart-js-railsで統計を表示する 1.

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

2は多分ある。

 ナポリタンの食品サンプルがデスクに増えました。

発端

「Railsでデータベース使って色々やってるんだから、それ使って簡単なグラフとか見れたら面白そうだな…..。Railsでグラフを表示する用のgem、探してみよう」
「今回はchart-js-railsを使ってみることにしよう。有名どころでも使われているみたいだし、このgem自体はjavascriptのライブラリのchart.jsをRailsで使う為に引っ張って来てるだけのものみたいだし」

使用方法

「Gemfileに一行付け足して、bundle installして、使う為には、application.jsに

//= require Chart.min

 を書き込む、と。
 それだけで、他にコマンドとかを打つ必要は全く無し。
 さて、まずは適当に新しいページ作ってchart.jsのサンプルページとか見ながら試してみるか……。
 新しくそれ用のコントローラ作ってroute.rbにルーティング付け加えて、それからviewを作成して」

Controller
graph_viewers_controller.rb

class GraphViewersController < ApplicationController
  def sample
  end
end

View
sample.html.erb

<canvas id="myChart" width="900" height="400"></canvas>
<script>
var ctx = document.getElementById("myChart").getContext('2d');
var myChart = new Chart(ctx, {
    type: 'bar',
    data: {
        labels: [1,2,3,4,5],
        datasets: [{
            label: "サンプル",
            data: [1,2,3,4,5],
            backgroundColor: 'rgba(255, 99, 132, 1.0)',
            borderColor: 'rgba(255, 50, 132, 1.0)',
            fill: false
        }]
    },
    options: {
        title:  {
          display: true,
          text: "タイトル"
        }
    }
});
</script>

bar graph test
「お、出来た。
で、これにデータやラベルを変数として組み込むには、配列部分を弄れば良さそうだな」

Controller

def sample
  pivotes = PiVote.pluck(:name, :vote_num)
  @labels = pivotes.map(&:first)
  @datas = pivotes.map(&:second)
  #@labels = ["◯っ◯◯","π","パイナップル", "ニシンパイ", "パイルドライバー"]
  #@datas = [9999, 3141, 2, 8, 1000]
end

View
「ラベルとデータ部分をそれぞれ弄って、と」

    data: {
        //文字列の入った配列やハッシュ等はこうしないと正しく表示されない
        labels: <%= @labels.to_json.html_safe %>,
        datasets: [{
            label: "投票数",
            data: <%= @datas %>,
            backgroundColor: 'rgba(255, 99, 132, 1.0)',
            borderColor: 'rgba(255, 50, 132, 1.0)',
            fill: false
        }]
    },

enter image description here
「おお、でけたでけた。
 一つ二つでも弄れば円グラフとか散布図とかも色々作れそうだし、オプション設定も色々あるし、夢が広がるぅ」

実際の使用感覚

 まあ、そんな感じで使ってみました。実際にデータが視覚化されると、特徴とかが分かりやすくなりますし、そこからプロジェクト等に対する改善策も見やすくなってくると思います。
 このchart-js-railsは実際、chart.jsをRailsで使えるようにしただけのgemみたいなものですが、Javascriptを余り深く知らない自分にとっては、Javascriptの勉強にもなって中々面白い所もあります。
 今もそれに関して主に弄っていて、グラフの色分けや、散布図の点をクリックしたらそのデータに関する画面に遷移したりとか、実際色んな事が出来るようになってきて、実際夢が広がるぅ、感じです。
 でも、一つ、難点として挙げるのならば、データベースから取ってきた値などを、javascriptにrubyとして組み込んでいくので、コードがとても汚くなりがちです。
 散布図を色分けやらしたい、なんて考えた時は、データを与える辺りのコードが以下のようになってしまいました。

  datasets: [
    <% @labels.each_with_index do |label, i| %>
    {
      label: "<%= label.html_safe %>",
      //色の設定。数に応じて変化。
      borderColor: "<%= "##{(16 * i / @labels.length).to_s(16)}f#{(16 * (@labels.length - (i + 1)) / @labels.length).to_s(16)}fff"%>",
      backgroundColor: "<%= "##{(16 * i / @labels.length).to_s(16)}5#{(16 * (@labels.length - (i + 1)) / @labels.length).to_s(16)}5ff"%>",
      pointRadius: <%= 4 + i %>,
      pointStyle: "<%= ['circle', 'triangle', 'rect', 'star', 'cross'][i].html_safe%>",
      showLine: false,
      data: [
      //データの配列。データの一つはx,y座標を格納した配列。
        <% @datas_array[i].each do |data| %>
        {
          x: <%= data[0] %>,
          y: <%= data[1] %>
        },
        <% end %>
      ]
    },
    <% end %>
  ]

enter image description here
まあ、もっと良いやり方はあると思うので、もっと色々試してみようとは思いますが。

最近人気な記事