ホーム ブログ ページ 28

LiferayとSolrの連携

0

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

LiferayとSolrを連携する方法をご紹介します。

前提

各バージョン

  • Liferay:6.2 CE GA2
  • LiferayサーバのOS:ubuntu 14.04.5
  • Solr:4.10.2
  • SolrサーバのOS:CentOS 6

※LiferayサーバとSolrサーバは、ローカル環境(Mac)にvagrantで構築しています。
※Liferay本体はLiferayサーバのvagrantユーザのHOME直下に配置しています。

Liferay CE Solr 4 Search Engineインストール

Liferayの公式サイトのマーケットプレイス( https://web.liferay.com/ja/marketplace )でLiferay CE Solr 4 Search Engineをダウンロードし、Liferayのdeployディレクトリに配置します。

※Liferayサーバでの作業

$ cp /vagrant/Liferay\ CE\ Solr\ 4\ Search\ Engine.lpkg ~/liferay-portal-6.2-ce-ga2/deploy/

置いた瞬間に自動的にTomcatにデプロイされます。
念のためLiferayを再起動しておきます。

$ ~/liferay-portal-6.2-ce-ga2/tomcat-7.0.42/bin/shutdown.sh
$ ~/liferay-portal-6.2-ce-ga2/tomcat-7.0.42/bin/startup.sh

デフォルトファイルを念のためバックアップ

※solrサーバでの作業

$ cd /usr/local/solr/liferay/solr/collection1/conf
$ sudo cp -a schema.xml schema.xml,20170925
$ sudo cp -a solrconfig.xml solrconfig.xml,20170925

以下のファイルをsolrサーバにコピー

コピー元(liferayサーバ)

ファイル1:~/liferay-portal-6.2-ce-ga2/tomcat-7.0.42/webapps/solr4-web/WEB-INF/conf/schema.xml
ファイル2:~/liferay-portal-6.2-ce-ga2/tomcat-7.0.42/webapps/solr4-web/WEB-INF/conf/solrconfig.xml

コピー先(Solrサーバ)

ファイル1:/usr/local/solr/liferay/solr/collection1/conf/schema.xml
ファイル2:/usr/local/solr/liferay/solr/collection1/conf/solrconfig.xml

※念のため、コピー後にschema.xmlとsolrconfig.xmlの所有権が他のファイルと同じになっているか確認してください。

solrサーバ再起動

※solrサーバでの作業

$ sudo service solr restart

solr接続先設定変更

※liferayサーバでの作業

$ vim ~/liferay-portal-6.2-ce-ga2/tomcat-7.0.42/webapps/solr4-web/WEB-INF/classes/META-INF/solr-spring.xml
---- ここから ----
<!--<property name="url" value="http://localhost:8080/solr" />-->
<property name="url" value="http://192.168.33.10:8983/solr" />
---- ここまで ----

Liferay設定変更

IPでアクセスしている場合、そのIPへのリダイレクトを許可しないと、インデックス作成時に「Forward does not exist」というエラーが発生するため、サーバのIPをリダイレクト許可に追加します。

参考:https://issues.liferay.com/browse/LPS-60292

※liferayサーバでの作業

$ vim ~/liferay-portal-6.2-ce-ga2/portal-setup-wizard.properties
---- ここから ----
redirect.url.security.mode=ip
redirect.url.domains.allowed=
redirect.url.ips.allowed=127.0.0.1,192.168.33.10
---- ここまで ----
※上記3行追加
$ ~/liferay-portal-6.2-ce-ga2/tomcat-7.0.42/bin/shutdown.sh
$ ~/liferay-portal-6.2-ce-ga2/tomcat-7.0.42/bin/startup.sh

インデックス作成

Liferayの管理画面( https://192.168.33.10/ )→システム管理→コントロールパネル→サーバ管理→検索で利用するインデックスを再構築する。を押下

正常にcommitされているか確認

Solrの管理画面( http://192.168.33.11:8983/solr/#/ )→Core Selector→collection1→Query→[Execute Query]
※response/numFoundが1以上であればOKです。

もし、numFoundが0のままの場合、以下を確認してみてください。

  • インデックス作成時にSolrのログが出力されること $ tail -f /var/log/solr.log
  • Solrの再起動が成功すること。以下のExceptionが表示され正常にSolrが停止されない場合は、killで強制終了 java.net.ConnectException: Connection refused (Connection refused)
  • 手動コミットを試してみる http://192.168.33.11:8983/solr/update?stream.body=%3Ccommit/%3E

以上です。Liferayの日本語ドキュメントは少なめなので、少しでも参考になれば幸いです。

コンストレイントでモーション作業を楽にする

0

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

存在は知っているけどコンストレイントって一体どこで使うんじゃという人や、キャラクターに道具を持たせてニメーションさせてあげたいという方に読んでほしい記事です。筆者は毎回「親と子どっち先に選択するんだっけ?」とド忘れしてしまいます。

はじめに

こんにちは。
今回は、難航しているモーション作業を楽にしてくれるかもしれない、シンプルなのに役に立つMayaのコンストレイント機能を紹介したいと思います。

10月に入ってからモーションデザイナーとして3Dアニメーションをメインにやるようになって制作経験のない動きを考え、作る機会が多くなりました。
現場では周りのデザイナーと武器に見立てた小道具を使いながらあーでもないこーでもないとかっこいい動きを考える毎日です。

やり始めていきなり苦労したのが重量のある斧を扱うキャラのモーションです。
斧の回転制御軸がコロコロ変わったり、両手で握ったり、持ちかえたり、肩に担いだり…工夫が必要な場面が多々あります。

モーション作業の面倒な場面

「物を持つ」だけではなく、「物を投げる」「物にぶら下がる」「物に乗る」などの複雑な動きをあまり時間をかけずに作るにはキャラ本体のリグだけでは当然足りません。

3Dでアニメーションを作る際、キャラクターが持つ武器はキャラクター本体の持ち手の骨(ジョイント)にアタッチされており、手の動きに追従して武器も勝手に座標が動くようにセットアップすることが多いと思います。

手を動かせばいいだけなので基本はこの構造がベターです。

しかし、持つ物やモーションによっては支点がアタッチされた手の座標とは違う場所に来る場合もあります。

例として、重量のある大きな斧で攻撃して地面に突き刺さった場合は斧の支点は地面に刺さった部分になります。

enter image description here

通常のリグでは手首の回転が斧の角度に大きな影響を与えてしまいます。
なので斧が地面に刺さった状態で体のアニメーションをつけたいときは手の位置と角度を全く動かさないよう注意しながら作るか、手首の回転と斧の接地面を毎フレーム確認して微調整しなくてはいけなくなります。
はっきり言って面倒で、モーション自体も汚くなりがちです。
こんな時にコンストレイントが役立ちます。

コンストレイントとは

コンストレイントとは
コンストレイントによって、1 つのオブジェクトの位置、方向、スケールを、もう 1 つのオブジェクトのトランスフォーム設定によって駆動できます。

簡単に言うとある物体のtransform、rotate、scaleを別のオブジェクトで制御できるようにする機能です。

mayaにはペアレント、ポイント、方向、スケール、エイム、極ベクトル等があります。
この中でも自分がよく使うのが、
・ペアレントコンストレイント
・ポイントコンストレイント
・方向コンストレイント

の3種類です。

ペアレントコンストレイントはデフォルトではコンストレイント対象のオブジェクトと親子関係を持ち、コンストレイントした時点での位置関係を保った状態で親のオブジェクトを中心点として回転、移動で子オブジェクトの動きを制御できます。

ポイントコンストレイントでは対象オブジェクトのピボット(中心軸)を親オブジェクトのピボット位置に常に来るように親子関係を保ちます。親が回転しても子は回転しません。

方向コンストレイントでは対象オブジェクトの回転値を位置が離れていても親オブジェクトの回転値と同じになるよう親子関係を保ちます。親が移動しても子は移動しません。

コンストレイントを使う例

先ほど挙げた、「大きな斧で攻撃して地面に刺さった状態」で柄を握ったままアイドリング動作をして数フレーム後に地面から斧を抜くという一連の流れの中でどう使えるか考えてみます。

地面に突き刺さる直前まで:斧の動きの親となるのは武器を振り回しているキャラクターの手です。

突き刺さった瞬間~斧を抜き取る直前まで:地面と斧の接地点が動きの支点になり、手は常に斧の柄を握っているため逆に手が斧の柄の部分の子になると作りやすいです。

斧をひっこ抜く瞬間:また斧の親が手に移ります。

・斧と地面の接地点にあたる部分
・斧の柄の部分
計2か所に親となるダミーオブジェクトが必要になります。

具体的に説明すると
刺さった瞬間のフレームで斧を、あらかじめ接地点に作ったダミーオブジェクトへのペアレントコンストレイント(コンストレイントA)に切り替え

さらに、キャラクターの手のジョイントを斧の柄の部分にペアレントしていたオブジェクトへのポイントコンストレイント(コンストレイントB)に切り替えます。

こうすることでコンストレイントAで回転制御している斧の持ち手の移動に対して常に手がコンストレイントBのおかげで追従するようになります。これで斧を握った状態でのアイドリング中は手首の回転の調整のみで済みそうです。

コンストレイントの使い方、切り替え方は以下の動画が分かりやすいです。

【第10回アニメーション講座 コンストレインの仕組み】

終わりに

このように一連のモーションの中で物体の動きの中心軸がどう遷移するか常に意識して、重ね掛けしたコンストレイントを切り替えていくことで思った動きに近づけることができます。

またキャラクターのセットアップにもコンストレイントを使うのでもう少し踏み込んで勉強すればアニメーターだけでなく、リガ―も目指せちゃいますね(3D分業の中でも特にMayaの知識が問われるのかもしれませんが)!

こういったMayaのコンストレイント機能を使った「工夫」はアニメーターによって様々であり、決まった正解のないアイデア次第の部分だと感じているのでモーション作業の魅力の一つに感じます。

ただ最初のうちはどう動くかなんてのはやってみないとわからない部分もあるので、とりあえず適当にアタッチしてそれぞれのコンストレイントの出来ること、出来ないことを理解してみると頭の中でもどこで何が使えるか設計できるかと思います。

最後まで目を通してくださりありがとうございました。

Rubyで速いプログラムを書くために

0

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

Rubyが遅いというのはよく聞く話ですが、ボトルネックがRubyそのものでもないのにRubyのせいにするのは非常にナンセンスです。最低限Rubyで読みやすく速いコードを書いてからRubyのせいにしましょう。本記事では何が速いコードなのかを知る手助けとなる色々なBenchmarkの取り方と、それを使ったString同士の結合メソッド比較を紹介します。

Measure Don’t Guess

はじめに

今年5度目の武道館公演で灼熱に飲まれました、くろすです。
鍋食べにいくなら喜んでついていきます。

コードに対する感性やセンスは一朝一夕で身につくものじゃありません。
それを磨くためには、たくさんの良いコードに触れ、良いコードとは何かを理解する必要があります。
速いコードが良いコードというわけではありませんが、遅いコードは悪いコードなので測り倒して抹殺しましょう。

benchmark をとる

最も単純なベンチマークの取り方というと以下のようなコードになると思います。

time = Time.now
'hoge'
puts "Benchmark : #{Time.now - time}s"

#=> Benchmark : 2.0e-06 s

これをそのまま使うのはあまりにもナンセンスなので普通なら以下のようなコードになるでしょう。

def benchmark
  time = Time.now
  yield
  puts "Benchmark : #{Time.now - time}s"
end

benchmark { 'hoge' }

#=> Benchmark : 2.0e-06 s

ただ、このbenchmarkメソッドもあまりセンスよくないです。
だってrubyにはBenchmarkモジュールがあるから。

require 'benchmark'

puts "Benchmark : #{Benchmark.realtime { 'hoge' } s}"

#=> Benchmark : 1.00000761449337e-06 s

ミリ秒で値が欲しいなぁという場合はこちら
ActiveSupportの中で拡張されています。

require 'active_support'
require 'active_support/core_ext/benchmark'

puts "Benchmark : #{Benchmark.ms { 'hoge' } ms}"

#=> Benchmark : 0.00200001522898674 ms

自分で作ったbenchmarkメソッドとruby標準のBenchmark.realtimeメソッドの違いですが、
Benchmark.realtimeの方はTime.now に当たる部分がProcess.clock_gettime(Process::CLOCK_MONOTONIC) になっています。
この Process.clock_gettime(Process::CLOCK_MONOTONIC)とTime.nowの違いは面倒くさくて追いきれてないので気が向いたら追記します。(どっちもsource_locationでnilが帰ってくるのでパッと調べるのがめんd..)

ちなみにですがruby標準のbenchmarkにはbenchmarkメソッドが存在します。

require 'benchmark'

Benchmark.bm do |x|
  x.report('hoge') { 'hoge' }
end

#=>
       user     system      total        real
hoge  0.000000   0.000000   0.000000 (  0.000002)

いろんな情報見れて便利ですよね。

さて、少し話が変わりますがRailsのcontributorになりたいと思ったことはありますか?
そう思ったら使わなきゃいけないgemがある(Benchmark Your Code)って知ってますか?
benchmark-ipsです。

require 'benchmark/ips'

Benchmark.ips do |x|
  x.report('hoge') { 'hoge' }
end

#=>
Warming up --------------------------------------
                       244.639k i/100ms
Calculating -------------------------------------
                          8.302M (± 6.6%) i/s -     41.344M in   5.002986s

gemの説明にもある通りiterations per secondを返してくれます。
1秒間に何回回るかってことですね。ウォーミングアップもやってくれるんで使い勝手が非常に良いです。

ここまでrubyのbenchmarkについて追ってきました。

ちょっと待ってくれ、Rails環境下でbenchmark do的な感じでbenchmark使ったことあるけど、あれはなんだ?と思った人にはこちら
ActiveSupport::Benchmarkableのインスタンスメソッドにbenchmarkがあります。
ソース読めばわかりますが、内部的には前述のBenchmark.msを使ってるだけですね。
オプションでloggerを切れるので、詳しい情報が必要じゃないけど時間だけ知りたい場合のRails環境下だとこっちの方が使い勝手いいです。

String同士の結合メソッド比較

さて、本題は終わったのでここからは実際の使用例的な感じで調べてみます。
ここで注意しておいてもらいたいのはString#concatString#<<のaliasであるということです。
リファレンスの説明もまとめられています。

require 'benchmark/ips'

Benchmark.ips do |x|
  x.config(time: 5, warmup: 2)
  x.report('String#+') { '' + 'hoge' }
  x.report('String#<<') { '' << 'hoge' }
  x.report('String#concat') { ''.concat('hoge') }
  x.compare!
end

#=>
Warming up --------------------------------------
            String#+   197.224k i/100ms
           String#<<   187.204k i/100ms
       String#concat   141.180k i/100ms
Calculating -------------------------------------
            String#+      4.383M (± 6.3%) i/s -     21.892M in   5.014485s
           String#<<      4.608M (± 6.0%) i/s -     23.026M in   5.016537s
       String#concat      2.983M (± 5.2%) i/s -     14.965M in   5.031350s

Comparison:
           String#<<:  4608081.5 i/s
            String#+:  4382769.8 i/s - same-ish: difference falls within error
       String#concat:  2982639.2 i/s - 1.54x  slower

'''hoge'のString.newのコストは全て同じため考えません。
こうやってみると単純な結合にはString#+String#<<どっちも良さそうですね。
String#+String#<<の比較はこちらが参考になりますが、メモリ確保の挙動と破壊的か非破壊的かが違うようです。
どうもString#concatは単発で呼ぶとaliasの分String#<<より遅いようですね。
ただ個人的は、Stringに<<を生やすのは間違えてArray触っている気持ちになって気持ち悪いのでconcatを使いたいです。

それでは次のbenchmarkをみてみましょう

require 'benchmark/ips'

@a = ''
def a
  @a += 'hoge'
end

@b = ''
def b
  @b << 'hoge'
end

@c = ''
def c
  @c.concat('hoge')
end

Benchmark.ips do |y|
  y.config(time: 5, warmup: 0)
  y.report('String#+')      { a }
  y.report('String#<<')     { b }
  y.report('String#concat') { c }
  y.compare!
end

stringをどんどん長くしていくコードですね。
今回はウォーミングアップの時間を作ると@a @b @cの中身が書き変わりフェアではなくなるので直で実行するために、configで時間を設定しています。
結果はこちら。

#=>
Warming up --------------------------------------
            String#+     1.000  i/100ms
           String#<<     1.000  i/100ms
       String#concat     1.000  i/100ms
Calculating -------------------------------------
            String#+     74.156k (±146.7%) i/s -     70.018k in   4.972166s
           String#<<    986.464k (±10.0%) i/s -      2.885M
       String#concat    980.709k (±11.8%) i/s -      3.182M

Comparison:
           String#<<:   986463.5 i/s
       String#concat:   980708.6 i/s - same-ish: difference falls within error
            String#+:    74156.1 i/s - 13.30x  slower

こうやってみてみるとString#<<が優秀ですね。

まとめ

普段から触っているStringのメソッドにもこのような違いがあるのですから、普段書いているプログラムが常に最善の中での最速になるわけありません。
プロファイリングツールを使い全体のボトルネックを把握しつつ、このような細かい部分は自分でベンチとって蓄積していきましょう。
計って測って図り倒して自分のセンスを磨いていきたいところです。

最後に

Measure Don’t Guess

やったもん勝ち精神でド新人が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日あたりのユーザー数を調べる
・リニューアル日(特定の日)から何日経っているかを調べる

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

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

最近人気な記事