ホーム ブログ ページ 19

駆け出しプランナーからディレクターになるという目標を実現するために心がけていること (ゲームプランナー 高山 友貴さん)

ゲーム制作に携わっていれば、自分が考えたゲームを作りたいと思うのは当然のことだろう。常駐型の働き方をしながら、ゲームディレクターを目指す若手プランナーに、今心がけていることを聞いた。

■ 簡単に自己紹介をお願いします。

高山 友貴と申します。22歳です。ゲームプランナーをしています。

■ これまで携わってきたゲームタイトルについて教えて下さい。

最初は、アピリッツのブラウザゲーム式姫の庭で、プランナーのアルバイトとして携わらせていただきました。

そこで、一通りの仕事ができるようになり、正社員になると同時に他社常駐の働き方になりました。
初めて常駐したのは、有名IPのタップバトルRPGの案件でした。

その時の上司が、かなり仕事をまかせてくれる方で、少し戸惑ったときもあったのですが、結果として多くの業務経験を積むことができました。

■ なかなかない経験でしたね(笑)

そうですね。

ただ、先方の会社の業務整理の影響で、2ヶ月で契約終了になってしまいました。

その後、また別の会社に常駐することになりました。その会社が現在の常駐先なのですが、ここでは、マスターデータの反映、ゲームデータの作成、仕様書の作成、デザインの発注・監修、お知らせの作成、後輩の育成と多岐にわたって業務に携わらせてもらえました。

■ 後輩の育成をさせてもらえたのは、すごいですね!

そうなんです。
自分は若手の育成プロジェクトの一環での参画することになっていたんです。
その若手育成プロジェクトチームのリーダーを任せてもらえることになったんです。今では、自分を入れて8人のタイトル横断的なチームになっていて、そのメンバーのタスクマネジメントもさせていただいています。チーム運営業務についても、いろいろと経験させてもらえています。

※ 帰社懇親会のワークショップにて

■ 本当に幅広く経験させてもらえていますよね。何か特別な事をしていたりしたんですか?

実は、自分の夢を伝えるようにしていました。

自分には「20代後半にはディレクターになって、多くの人を動かしてゲームを作っていきたい」という夢があって、それを常駐先の方に伝えていたんです。

そうしたら、「まだ若いから、下準備として色々な経験をしないとね」と言われて。それで幅広い業務に携わらせてもらえるようになったんです。

だけど、最初はめちゃくちゃ厳しかったですね(笑)

「これ、今日までね」みたいなタスクが来たりして「もうちょっと早く言ってよー」みたいな(笑)ちゃんとやりましたけどね(笑)

■ 他にも意識していることはありますか?

常に大切にしているのは成長意欲です。

安定を求めず、常に色々な業務を身に着けようとする意欲が大切だと思っています。

スペシャリストではなく、ジェネラリストになるということが大切だと思っているので、とにかく幅広く様々な経験を積むことを常に意識していますね。

あとは、1回目にやるときよりも、2回目、3回目と業務を効率化させていくということを常に心がけるようにしています。

「1回目は2日間で終わったから2回目は1.5日でやってみよう」など、自分の中で目標を設定して業務に臨んでいます。常に100%以上のパフォーマンスを出していきたいと思っています。

■ そう思うきっかけが何かあったんですか?

そもそも自分の家族には、「お前なんかが、ゲーム業界になんて就職できないよ」って言われて、それが悔しくて。絶対成長してやろう、見せてやろうっていう気持ちになったんですよね。

だから、「俺ゲーム業界で働いてるぜ」って言いたくて。

例えば、ゲームのクレジットに自分の名前を載せてもらって、それを見せてやろうって。そういうのでギャフンといわせたいって思ってますね(笑)

その時に、常に成長していかないとなって思ったんですよね。

■ この前クレジットに載せてもらえましたよね!

そうなんです。2つの作品に載せていただきました。

本当に有難いですね(照)

■ 家族にはみせた?

みせました!スクショとってみせました!

でも、反応が薄かったんですよね〜(笑)
「もうちょっと良い反応をしてほしい」というのも自分のモチベーションになっています。

■ 自分の成長意欲を現場ではどうやって伝えているんですか?

「新しいことを学びたいです」というのは常に伝えるようにしています。

もし、上司とかが病気で倒れちゃったとして、会議とかで「誰かこの業務できる人いる?」って質問されたとき、周りの人が誰もできなくて「自分できます」って答えられたら、かっこいいじゃないですか!

そのためにも、常に「新しい業務ありませんか?」「仕事ください、あなたの仕事を楽にしますよ」と上長に伝えるようにしています。

■ アピリッツのサポートメンバーは高山さんにとってどんな存在ですか?

自分にとっては、お父さんやお母さんみたいな存在ですね。支えてもらっている、みたいな。困ったことがあったら相談できる存在です。気にしてもらえているな、というのを感じる機会は多いですね。

例えば、月に1回業務のヒアリングに来てくれるのですが、みんな笑顔だし(笑)

「将来どんな人になっていきたいの?」って常に目標をきいて聞いてくれたりするので、それが自分にとっての意識付けになっていますね。

あと、面白い方ばかりですね。

考え方鋭い方もいますし、同じことを聞いても色々な観点から意見がもらえたりするので、アドバイスが参考になることも多いです。

■ 常駐での仕事(正社員の派遣での仕事)ってどう思いますか?

個人的には、いろんな会社で、いろんなプロジェクトがあって、色々な経験をさせてもらえるという意味で、いい環境だと感じますね。

でも、個人的には、更新がくるかどうかは、毎回ドキドキしますね。更新が来ると「よかったぁ」って思います。

■ そこは毎回ドキドキするよね。サポートメンバーの私達も一緒に一喜一憂しています。
■ 高山さんの成長は、サポートメンバーも感じていますよ!

有難いですね!

■ その成長の背景に、こんな事を考えていたんだなと、知るきっかけになりました。インタビューありがとうございました。

ザ・インターネットカンパニー アピリッツ爆誕!経営幹部合宿 in 軽井沢

0

アピリッツでは普段の日常的なMTGで話し合いできない中長期の経営戦略について話し合うため、定期的に社長、執行役員、部長、次期部長候補などを集めて経営幹部合宿を行っています。今年の冬は軽井沢に行ってきました!今回はその経営幹部合宿の様子について紹介したいと思います。

合宿の目的とテーマ

今回の合宿の目的としては、日常業務から離れ会社の戦略や将来のことを考え、経営幹部の方向性を共有すること、また、次期幹部層の育成などです。今回のテーマは「会社のビジョンを再定義しよう」ということで、

1.当社のビジョン、ミッション、バリューの再定義

2.我々の誇り、当社の存在意義

3.社内外に対しての見せ方、方法

4.全ての事業、サービス、製品が向かうべき先のゴール

1~4について各自、事前に勉強し、考えを纏めて合宿に参加しました。

いざ合宿へ!

さて、合宿のスケジュールはこんな感じです。16時軽井沢駅集合。ヴィラに集まってみんなで宴会。翌日は朝から1日MTGをして夜自由解散。次の日が土曜日だったため2泊して次の日スキーやスノーボードを楽しむ方々も!

ヴィラに到着すると玄関の標識が社長の苗字である「和田」になっている…!!(笑)

ちなみに今回の宿泊場所でもありMTG会場でもあり宴会会場でもあるヴィラの中の様子はこちら!

リビングの吹き抜けの天井が開放的なメゾネット(2階建て)タイプ。部屋が3部屋ついていて部屋ごとにお風呂とトイレがついていました。テラスハウスを超えてくる豪華さです。。

ということで、和田家にお邪魔してまずは宴会からスタートです!

社長自らお肉を持参してくださり、焼肉が始まりました!

お肉って自然と人を笑顔にしますよねっ!!皆さんテンションも上がり大満足そうでした!軽井沢ビールやワインと一緒に美味しくいただいた後、みんなで温泉に入って1日目が終了しました。

2日目、合宿の本題であるMTGが朝から開始されました!

こういう会社であってほしいという願望、外のWebサイトに載せるミッション、ビジョン、行動指針、当社が社会に与える普遍的価値などについて、お昼休憩も交え夕方まで話し合いました。

どうやって、ビジョンを再定義しよう?と色々テーマを決めて話し合いましたが途中、ビジョンというと、ビジョンが先かミッションが先か、パーパス(存在意義)のがわかりやすい?….など、そもそも何を決めるのかも揺れた感じでしたが、Webに掲載する標語を創ろうと意見をまとめ、結論を出しました。

今回話し合って出た結論について

長い時間をかけ、紆余曲折しながら決まったアピリッツの標語は…

「ザ・インターネットカンパニー 〜セカイの人々に愛されるインターネットサービスをつくり続ける〜」

今後、社内の方針発表会で共有し、毎月の全社会でも共有し、周知させていくことになりました。また、名刺にも入れることになりました。

ちなみに、日本人的に「ジ」より「ザ」の方がしっくりくる、という意味も踏まえて本来の英語の発音とは変えています。アピリッツでは様々なインターネットサービスを扱っており、インターネットサービスといえばアピリッツという会社を目指していこうという方向性を示す意味でも、まさに当社にぴったりな一言だと思います!

また、副題の方の「セカイ」は、単なるグローバルという意味での「世界」ではなく、バーチャルやコミュニティという意味合いも入れたいと思いを込めて、カタカナで「セカイ」と表記することになりました。

そして行動指針はこれまでの「3C:Challenge、Chance、Change」をあらためて踏襲していくことに決定しました。

最後に記念撮影!

今回の合宿を通じて、チームワークが強化され、改めて会社の目指す方向性などが確認できたと思います。

また、日頃の視点からは見えない気づきも得て、得るものが多い合宿となりました!みなさんお疲れさまでした!


ActionCable実装しようwith webpack

0

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

Action Cableをイケてる構成のrailsで使いこなそう

ActionCable 使ってますか?

Rails5以降から導入されたActionCable
公式ドキュメントではチャットの実装などで使われていますが、色々な使い方ができそうです
sidekiqに投げた処理の結果を非同期で受信するとか

執筆時点では世の中に知見が多くなかったので実装体験記を残します

導入

rails g channel hoge_channelでActionCable導入に必要なアレコレが生成されるよ
ただしcoffee scriptだしSprocketsのアセットパイプラインに乗る形でフロントのスクリプトが吐き出される
(Rails5.2の場合)

うちの案件はwebpack導入してるしcoffe scriptなんて書いた事無い
そんな場合は下記コマンドでライブラリを導入しましょう

yarn add actioncable
または
npm install actioncable

そしてベースとなるcable.jsはこんな感じに
自分はapp/javascripts/channelsディレクトリに設置しました

# app/javascripts/channels/cable.js

import cable from “actioncable”;

let consumer;

function createChannel(...args) {
  if (!consumer) {
    consumer = cable.createConsumer();
  }
  return consumer.subscriptions.create(...args);
}

export default createChannel;

各チャンネルのjsはこんな感じ

# app/javascripts/channels/test_channel.js
import createChannel from “./cable”;    //cable.jsのパスは適宜変更してください
$(function(){
  const channel = createChannel(“TestChannel”, {
    received(message) {
      alert(mesage); # この辺に受信時の処理を色々書く、今回は受信したものを取り敢えずブラウザにアラートとして表示する
    }
  });

  // rubyのactioncableで実装した処理を()呼ぶ
  function method_name(message) {
     channel.perform(“method_name”, { message });
  }
});

ruby側の実装は公式ドキュメントで紹介されている実装から特に変更点はないです

設定

よ~し取り敢えず検証用にデータを受信したらブラウザのアラートに出す機能作ったので動かしてみるか~
おや…動かん…エラーが出てるな

設定に不足がある場合はブラウザのデバッグコンソールに下記のようなエラーが表示されます

WebSocket handshake: unexpected response code: 502

または

WebSocket connection to 'wss://example.com' failed: WebSocket is 'test channel~js'
closed before the connection is estrablished.

色々な記事にdockerで動かしている場合は専用のコンテナ建てろみたいなことを書いていましたが検証の結果不要でした
自分の場合 config.reload_classes_only_on_change が false になっていました
windiwsで開発していた時に変更がホットリロードされなかったのでここの設定弄ったのですがaction_cableの挙動にかかわるようです

その他設定

config/cable.yml でdevelop環境もredisを使用する

development:
  adapter: redis
  url: redis://10.10.3.153:6381 => 方法は自由ですがsidekiqやセッション情報とは異なるURLになるように工夫しましょう
  channel_prefix: appname_development

test:
  adapter: async

production:
  adapter: redis
  url: redis://10.10.3.153:6381
  channel_prefix: appname_production
config/environment/production.rbで自分ドメインからのリクエストを許可する

config.action_cable.allowed_request_origins = ['https://rubyonrails.com', %r{http://ruby.*}]
config/application.rb でrailsアプリケーション上にマウントするパスを指定する

class Application < Rails::Application
  config.action_cable.mount_path = '/websocket'
end 

config/routes.rbにマウントするパスを指定する方法を紹介する記事も見かけましたがたぶんこっちの方が良いと思います
viewファイルのヘッダーに<%= csrf_meta_tags %>を記載する

その他開発環境の設定や本番サーバーの構成によって何かしらの設定が必要になる場合があります
nginxのconfigファイルにリクエストヘッダーの設定書くとか…

うまくAction Cableが動けばRailsコンソール立ち上げて用意したchannelクラスのメソッドを呼んでみるなりなんなりすれば動くはずです
パターンが膨大すぎて個別の症状に応じて苦戦するのはなんだかインフラエンジニアっぽい感じでした
つまりググっても出てこないエラーとの戦いが結構大変だった

この頃のプルリクに対するレビュー

0

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

What is the difference between pull request and merge request?

自分がされたり、自分がしたり

そのクエリ参照する変数宣言、一定の時しか必要ないよね?

def create_current_banner_info(user,  now = Time.now)
  user_items = user.items.pluck(:item_code, :quantity).to_h
  BannerMaster.holdings(now).each_with_object([]) do |banner, res|
  if banner.user_condition_need?
    case banner.condition_type
    when :fulfill_item
      next unless user_items[banner.neccesary_item_code].to_i >= banner.nessesary_quantity
    when ...
      ...
    end
  end
  res << banner.as_json(:code, :banner_type, :banner_icon, ...)
end

アイテムを保持しているかで表示するかを分けるバナーって複数あったりもするだろうけど、いつもある訳じゃないよね?
だったら一番上のuser_itemsはnilで宣言しちゃって、 when :fulfill_item の中に入ったところで||=で宣言すれば無駄ないと思うよ。
いやそれよりも、もっと良い方法あるな。
そもそもさ、複数あるって言っても、pluckして必要なデータの取得を最低限にしてると言ってもさ、全部のアイテム参照してるのかなり無駄じゃん。
バナーを表示するかとレスポンス作っているところを一緒くたにしないでさ、先にアイテムが必要なバナーだけを抽出してしまえば、その参照すべきアイテムコード群を取れるよね。そうすれば本当に必要最低限しか取らずに済むよ。
で、今すぐマージ必要? 必要じゃないか、分かった。じゃあお願いします。

先にグルーピングしましょう

def past_events_user_data(user, now = Time.now)
  events = EventMaster.ended(now)
  user_events = user.user_events.where(event_code: events.pluck(:code))
  events.map do |event|
    user_event = user_events.find{ |user_event| user_event.event_code == event.code}
    create_past_event_response(event, user_event)
  end
end

ソースコードの重みってクエリ参照が一番でかいけど、コード自体の重みも結構馬鹿にならないんだよ。
ここだと、eventsの数だけfindの線形探索が走っちゃうから計算量がO(n2 ) であんまりよろしくない。
なのでuser_eventsを参照しているところで、index_by(&:event_code)をしておけば、後のfindを消せて、user_event = user_events[event.code] と綺麗に済ませられる。ハッシュ検索はO(1)だから、これなら計算量をO(n)まで落とせた。

混乱するかも

def lottery_apple(user, event_code, index)
  user_received_apples = user.user_apples.where.not(received_at: nil).where(event_code: event_code, apple_index: index)
  AppleMaster.lottery_no_duplicate(event_code, index, user_received_apples)
end

...

def lottery_orange(user, event_code, index)
  user_received_oranges = user.user_oranges.where.not(received_at: nil).where(event_code: event_code, orange_index: index)
  OrangeMaster.lottery_no_duplicate(event_code, index, user_received_oranges)
end

……あ、これ上のコードのindexと下のコードのindexの意味合い違うのね。
同じファイル内にあるソースならちゃんと変数名変えておいた方が良いと思うよ。

消すよりコメントアウトの方が時には良い

enum 1,  :new
enum 2,  :intensive
enum 3,  :course
enum 4,  :obliteration
enum 5,  :lenthal
enum 6,  :environmental
enum 8,  :search
enum 10, :end
enum 11, :alternate
enum 12, :dead_s

これ、7と9が抜けたの後から見たら理由が良く分からないしログ追うハメになるから、コメントアウトの方が良いかな。

後置ifはほどほどに

return long_method(argument1, argument2) unless too_long_long_methooooooooooooooooooooooood?(argument3, argument4)

流石にこれだと長いから、後置ifじゃなくて普通にif~endで囲もうか。その方が直感的な理解が早い。
というかrubocop怒られなかった? ……あら、無いのか。
付けようとすると……あー、駄目だ、案の定たっぷり出たわ出たわ。
issue化しておこうか(そして誰もやらない)。

良い催しは事前の準備から。第21回基本方針発表会&新春決起パーティ開催!

0

「基本方針発表会」、「新春決起パーティ」とは

アピリッツでは、1月に「基本方針発表会」、7月に「基本方針中間報告会」といった、全社員を集めて会社や各部の来期の方針や現況報告を伝える催しがあります。 発表会後は、美味しいお酒やお食事を頂きながら歓談できるパーティも…!

今回は1月なので、「新春決起パーティ」という名のパーティが催されました。

それではさっそく当日の様子についてご紹介していきます!

いい催しは事前の準備から

催しといっても、事前の準備が必要です。その準備次第で、いい催しになるかどうか決まるほど大事な部分でもありますよね!
今回は、19卒の面々が会場で様々な準備を行っていました!

クイズ大会で使用するビニールプールを膨らませていますね!

出欠確認や案内のために、事前に打ち合わせをしているようです。

P-Review’19の総括動画を流すようですが、音量や映り方のチェック作業をしています。放映は一度のみなので、明るさ調整など細かい指示を行っていました!

事前に案内版を作成し、早めに入り口で待機しているようです。

こういった事前の準備があるからこそ、スムーズに発表会が行われるのですね!

「第21期 基本方針発表会」の様子

事前準備のおかげで、無事に基本方針発表会が始まりました!
社長、そして執行役員の順に今後の方針が発表されていきます。まずは社長より…..

今回のテーマは「アピリッツの歴史」についてでした!2000年7月の創業から今年で20年になるということで、様々な歴史があり社員が自分の会社について知るとてもいい機会となったと思います。

続いては、執行役員の方々から各事業部の方針発表についてお話しをいただきました。

会社の今後に関わる重要なお話がたくさんありました!
和田社長、そして執行役員の堂々と発表する姿が、とても輝いていました♪

最後に第20期下期社員表彰!!今回は会社の業績に大きな貢献を残した社員を各事業部から選出しました。

今回は10名が選ばれました!!おめでとうございます!!!

受賞者の方々には賞状と金一封が授与されました。表彰が終わると新春決起パーティーに移ります。後ろのパーティー会場にみなさん移動します。

「新春決起パーティ」の様子

基本方針発表会の後は、社員同士で歓談できるパーティが始まります。
和田社長による乾杯の音頭と共に、皆さん大きく盛り上がりをみせました。

食事を楽しみながら部署やチームの垣根をこえて話ができる貴重な機会でもあります。

お酒が入ると話しやすいのか、皆さんいつも以上に話が盛り上がっていました!

今年はパーティーのビュッフェとは別にシュラスコを発注したそうで、すぐに行列ができていました….!

パーティーの中で内定者紹介も行われました!

19卒社員 頑張ってます

19卒のメンバーが、出し物としてクイズ大会を開催しました!
参加者はもれなく賞品を受け取れる優しい設計となっており、企画者やるな…という内容でした。

なんと景品にswitch liteも!!大変盛り上がりました。

なかにはこのような賞品も…!事前の準備で用意したビニールプールは、お菓子を入れるためのものだったんですね……!!

事前の準備もあってクイズ大会はスムーズに進行。出題内容もバラエティに富んだユニークなものばかりと、観戦している皆さんも含めとても盛り上がりました!

19卒の皆さん、本当にお疲れ様でした!!

いかがでしたでしょうか。


アピリッツでは定期的にこのような催しを行い、会社や各部の方針を明確に示すことで上層部との齟齬がなくなること。また、パーティでは部署や役職関係なく、誰とでもコミュニケーションを深めることができます♪


2020年も、「セカイに愛されるインターネットサービス」をお届けできるよう、第21期の基本方針のもと各自の目標に向けてがんばりましょう!

株式会社Appiritsでは一緒に働く仲間を募集しています!
アピリッツが気になった方は コチラの採用情報 もチェックしてみてくださいね。

【成果発表会】「”かんたん”な効率化でコストダウン!」ゲームデザインで今すぐマネしたいテンプレ化(動画付き)

今回は社内の成果発表会「P-Review ’19」にて発表した、デザイナ 復本 文人さんの発表を動画で紹介します。

復本 文人
2019年3月ゲームデザイン部デザイナにアルバイトとして入社。
入社後は「関ケ原演義」「幕末演義」担当する。 前職は郵便局職員 兼ストリートアーティスト。

「テンプレ化」と「データ整理」による作業の効率化

動画概要

復本氏は、2019年2月より当社に移管したソーシャルカードゲーム「演義シリーズ」の 2タイトル「関ケ原演義」「幕末演義」のデザイン業務を、1人で担当している新人デザイナです。

「演義シリーズ」3作がアピリッツに運営移管する前は、イラストを除くデザイナ業務だけで約5人/月かけており、運用する上で大きな負担になっていました。 前任者、ベテランデザイナの橘氏は、3作のデザイナ業務を少人数でこなせるようにPhotoshopの「アートボード」にてカードイラストを「テンプレ化」する仕組みをつくりました。

前任者を目標に「どうすれば3作を1人で担当できるのか」を日々意識し、さらなる効率化を目指したところ、3度の運営移管で混沌としてしまっていたハードディスクドライブの「データ整理」をすることで作業時間を大幅に短縮できそうな事に気づき、それを実施した事で3タイトル分を受け持つことができる作業効率まで到達することができました。

さらに、効率化したことでできた空き時間で外注していた新規イラストの一部内製化を提案。毎月の運用コストを節約させることにも繋がりました。 大好きなこのゲームをいつまでもユーザに遊んでほしい。その為には日々の業務を効率化して行く事が重要かもしれません。

毎日同じ仕事を続けるだけでなく、スタッフひとりひとりが「効率化」について考えていくと良いではないでしょうか。

「経験も知識もない新人デザイナが、1人で3つの演義タイトルを受け持てる」仕組みを作る!

担当したデザイン「疾風 幕末演義」を前に笑顔の復本氏
日本の夜明けは近い。疾風の如く幕末を駆け抜け、新時代の魁となれ!大人気アプリ「関ヶ原演義」の続編。「演義シリーズ」第2弾!

意識をすることで、効果的な効率化の方法を見つけられるんですね。すごいです!!
アイコの仕事も効率化できるかな?

発表資料

Preview発表用6-3

【成果発表会】「効果的な広告を打つ試み」小手先プロモーション

今回は社内の成果発表会「P-Review ’19」にて発表した、プランナ 栗山 春樹さんの資料を紹介します。
※記載内容はすべて個人の意見・研究内容であり、会社の見解ではございません。

栗山 春樹
2019年4月アピゲー部プランナとして入社
入社後は自社開発ゲーム「ゴエティア」を担当

PR19-H-kuriyama-3.pdf栗山さん-3

テーマ説明

(※挨拶省略)

早速ですが、こんなことに悩んだことはありませんか?

ゲームの「オモシロサ」には自信があるけど知名度がない……
広告を打ちたいけどお金が無い……
広告の効果に半信半疑だからお金をかけたくない……
  Etc……

特に広告とお金の心配って結構あると思うんですよ。
そこで僕は考えました。タダで効果的な宣伝を打てばいいんじゃないかと…
なので今回はこのテーマはこちらです。

「どうやって、タダで効果的な宣伝を打つか」

ただ、皆さんのほうが僕より何倍も経験があるため、新卒の自分の考えた方法はここで話しても説得力がないですよね。
なので今回は、海外のインディーズゲームから例をお借りしたいと思います。

インディーズというものが何か、わからない方もいるかと思うのでわかりやすく説明します。

インディーズとは

・日本でいうところの同人ゲーム
・開発は小規模で特定の企業には属しておらず、大手パブリッシャーとの契約は無い
・プロダクトアウト思考で基本的にマーケティングで何を作るかよりも先に製品を作っちゃう
・「Indiepocalypse」という過当競争が続いていており、インディーでも生き残っていけないという状態が続いている

ざっというとこの人たちは、製品以外何もない状態で戦っているという訳です。
そんな大変な状態の中でもゲームのヒット作品って結構生まれてるんです。

PLAYERUNKNOWN’S BATTLEGROUNDS/Goat Simulator/Guacamelee!/DESCENDERS

こういう人たちはどうして成功しているのか?どういう風に宣伝を打っているのか?
というのを気になって調べたのでそれを皆さんに共有していこうと思います。

友達にLINEでゲームを宣伝する

友達にゲームを宣伝するってことはわかりやすく”あなたのゲームの魅力”を伝えることだと思うんですよ
海外のインディーゲームの例を紹介します。

About a BlobGucameleee!は同じ開発会社が作ったものなんですけど、ざっくり見てください。

❖About a Blob
・スライムが主人公の横スクロールパズルゲーム!
・障害物を乗り越え、仲間を救い出しましょう
・24個のステージ
・沢山の実績とチャレンジを用意しました!

❖Guacamelee!
・決して戦闘が絶えることが無いアクションゲーム!
・2つの異なる世界、「死」と「生」の世界をボタン一つで行き来しましょう!
・魅力的な過去を持つボス、特殊な敵が大量に登場します。
・ニワトリ……沢山のニワトリが登場します。

About a Blobの広告は抽象的で、売りがちょっとわかりにくくないですか?
ゲームの売りが分からないから興味が湧きにくいと思います。

2つ目のGucameleee!は何を売りにしているのか、このゲームの面白さは何かを明確に伝えることができてると思います。

だからAbout a Blobに比べてGucameleee!は売り上げ差で言うと10倍くらい売り上げが違ったんです!
この事例から分かるのはゲームの面白さを明確に伝えることってめちゃくちゃ大事ということです。

特に勘違いされやすいのが、開発が考えるゲームの魅力とユーザーにとっての魅力って全然違ってHooker(引き)Kicker(掴み)っていうとらえ方をしてる海外の方が多いんですけど、まず広告で重要なのは引きをアピールすることが大事だと思うんですよ。

引きの大枠をとらえると直感的に理解・想像しやすいもの、次に他のゲームとあなたのゲームで明確に差別化できている点、そして既にゲームをリリースをしている場合はあなたのゲームをユーザーがどういう風に評価してくれているのかというのを理解して広告打つのが重要だと思います。

うちのゲーム、ゴエティアで参考の例を出してみます。

ゴエティア -千の魔神と無限の塔-

A.ひとりで遊ぶことはもちろん、多人数のプレイヤーで協力しながら同時に遊ぶこともできるマルチバトルRPGです。

B.好きなキャラクターも限界まで育成出来るマルチバトルRPGです。育成次第ではどんなに強い敵でも一撃で倒せます。

Aは実際にプレスリリースとかゲームの公式サイトに打ってある広告で、Bは僕が考えた広告です。

正直な話自分で言うのもなんですが、AよりもBのほうがもっと面白そうじゃないですか?
なぜならこのゲームの一番の面白さををより明確に伝えることができている
さらにそれに対して、ほんとに育成次第ではどんなに強い敵でも一撃で倒せるという他のゲームにはない面白さをしっかり提供できているんですよ

自分のゲームの面白さを明確に伝えるっていうことはすごく重要なことだと思います。

好きなキャラクターも限界まで育成出来るマルチバトルRPGです。育成次第ではどんなに強い敵でも一撃で倒せます。

貴族を仲間にする

皆さんPUBG(PLAYERUNKNOWN’S BATTLEGROUNDS)ってご存知ですか?

関連画像
PLAYERUNKNOWN’S BATTLEGROUNDS

今でこそインディーとは言えない規模に成長しましたが、はじめはすごい小規模な開発チームから始まり、広告費もほとんどなかったんです。

このゲームがやったすごいことはAlpha1~リリースまで全てストリーミングを許可したんです。
ここで重要だったのが、Alpha~Closed betaは会社側が招待しないと参加できなかったんです。
ですが、ストリーマーが参加したいと言ったらすぐにキーを渡していました。

この結果どうなったかというと・・・

Closed betaへ招待された方達は「自分は特別な存在なんじゃないか」と思いゲームに対して貢献してくれるロイヤルユーザーになったんです。

さらにAlpha~リリースまで、運営はユーザーの意見をちゃんと聞いてどんどんゲームが良くなっていってるのを生放送し、このゲームはどんどん向上していくんだなっていうことがユーザーに伝わ運営への信頼に繋がりました。

なので、ストリーマーがたくさんいて運営側も信頼されているということで、リリース前から既にゲームを遊びたいというユーザーが大勢いました。

インフルエンサーを味方にする

あなたのゲームを広告してくれる有名人を味方にすることってものすごく重要なんです。
何をするかというとインフルエンサーに特権を与えてそれを話題にしてもらうんです。

継続的に”あなたにしか与えていない特権”を提供することにより、その人を単純にゲームを広告しているという意識から、「私たちがこのゲームを面白くする役割を担っているんだな」という当事者意識をしっかり持ってもらうことができます。

さらにそれだけではなく、これに対して重要なのがインフルエンサー側にもしっかりとメリットを提示することで、あなたのゲームの面白さを伝えることで視聴者数が増えますよとか他にない特典を得ることができますよっていうのが重要です。

そういう人を味方にするのはどういうことかというと、勝手に宣伝してくれるんですよ
例えば声優さんなら「私のキャラクターこんなのが出ましたよ」とか、絵師さんだったらそのキャラクターの絵をいっぱい書いてくれたりとか、そういう風に勝手に宣伝してくれるのでまず損はないかと思います。

そして、友達ができる
これはほんとの友達というよりも、ビジネスパートナーができるということです。
一緒に作品を作っていくという上で、今後の作品も手伝ってくれるビジネスパートナーができるというのは非常に大きい点かと思います。
これらを用いて具体的に何をするのかというと僕はこんなことを思いつきました……。

声優さんにリリース前のキャラクターを先に渡す!!

この画像には alt 属性が指定されておらず、ファイル名は image-6.png です
(左から)コスプレイヤー:立花 はる(ふとんちゃん)様/声優:山根 絢様/声優:相沢 舞様

うちのゴエクロで生放送をしてくれている声優さんがいるので、その人達に新しいキャラが出る時は先に渡してそのキャラクターに対してツイートしてもらうとか……
生放送でこういう使い方があるんですよねっていうのを言ってもらうとか……
公式のアカウントよりも声優さんの方がフォロワー数が多いのでインプレッション数が稼げます。

そして、声優さんや絵師さんって絶対に1回は自分の書いたものを宣伝してくれるんですよ。
最初渡した時にまず宣伝してくれると思うので1回
さらにリリースされたタイミングでもう1回と、運が良ければ2回宣伝してくれるので効果はあると思います。

ユーザーと仲良くする。

Descendersという例を紹介します。
Discrdっていう海外のチャットツールでユーザーと運営が積極的にコミュニケーションをとりあったんです。

運営はユーザーのゲームに対するフィードバックに実際にテキストで答えて、さらにそのDiscrdに参加してる人に限定的な特権を与えました。

その結果何が起こったかというと、ユーザーに対するコミュニティが活性化して、新しいユーザーが一番最初に触れるコミュニティが盛り上がっているように見えたんです。

「今何が欲しいのか」」「どこが面白いのか」 ユーザーのニーズを正確に把握できるようになり、ユーザーが求めている提供ができる”良いゲーム”ということで、口コミからもゲームは広がっていきました。

ユーザーと仲良くすることは重要だよっていうことです。

ユーザーとの距離をなるべく近くする

・運営とユーザーがコミュニケーションを取れる場所を設けて、運営側からユーザーに問いかける
運営が「今何が欲しいですか」「今このゲームのあなたが楽しんでいるところは何ですか」と問いかけ、さらにそれに対して・・・

・ユーザーの意見を聞いていることをアピールする
一方的に答えをもらうのではなく、ちゃんと僕たちはあなたの話を聞いているんですよっていう感じで双方でコミュニケーションをとっていくことが重要だと思います。

実際にはこんなことが行われている

・日常的なことをキャラクターになりきって呟く
「今日は暑いですね~」とかキャラクターになりきってTwitterでtweetした。

・Twitterでゲームに興味があると呟いてる人にDMする
ここまでやるとすごいんですけどTwitterとかで「ゲームに興味があるよ」って呟いてる人がいたら「今僕たちのゲームはこんな特権与えますよ」とか「今このリンクから押せば10連ガチャ一回無料で引けますよ」みたいなことをDMしてユーザーを勧誘していた。

・ユーザーの絵をゲーム内のスタンプにした
ユーザーが実際に二次創作で描いてくれた絵を「これはすごく素晴らしいデザインなんでぜひともゲーム内に入れたい」と言ってゲーム内のスタンプにした。

派手なことのやり方3条

派手なことでとりあえず皆に周知してもらうっていう戦略をとりたがる人は多いと思います。
GoatSimulatorっていうのが、めちゃくちゃ派手な広告を打ったんですよ。

その広告のプロデューサが「この3か条をとりあえず守りましょう」と言いました。

1.ゲームを遊んでいる人にしか理解できないコンテンツは使わない
そのゲーム内でめちゃくちゃ価値のあるものを大量に配りますよって言ったところで結局あなたのゲームをやってないユーザーには効果がないんです。

2.ゲームと関係なさすぎることはあまり意味がない
実際の例になるんですけど、FPSを作っているのに「ネックレスを配りますよ」とかはたしかにTwitter内でRe tweetされると思いますが、それは”ネックレスが欲しい人”であってゲームに対して興味を持ってくれるユーザーではないのであんまり意味がないことです。

3.詐欺と思わせるようなことはやらない(騙す)
Twitterにて、「このtweetが1万リツイートいけば箱が開いて中から特別なものが出ますよ」と言い、いざ開いたら期待外れなものを出してしまったんです。
それに対してユーザーは騙されたと思い、ゲームを遊んでないのにゲームに対する悪い印象が定着しちゃったんですよ。

そういうことは絶対にやらない。

まとめ

皆さん、このプレゼンなにか新しい発見はありましたか?

……正直なにもないと思います。
だって考えたらこれ、めちゃくちゃ普通のことなんです。

ユーザーと触れ合ったり、声優さんと仲良くしたり、宣伝を打つ文言を考えるなんて、ユーザー視点からするとめちゃくちゃ普通のことなんですよね。

ですが、これはどのゲーム会社でも、なかなかできることではないと思います。
だってユーザーに毎回DMを送ったり、お問い合わせに丁寧に返したりするのって正直面倒くさいことで、とにかく大変なんですよね。
ですがそういう大変で地道なことを、しっかりやってるところのゲームが結局売れるんですよね。

僕が調べた海外のプレゼンターが毎回言ってました「別に僕の言ったことは面白いことじゃない、めちゃくちゃ当然なことだけど、そういう当然なことをやろう」ざっくりいうとゲームを売りたいんだったらどんなに大変でもやらなきゃいけないよ!!っていうことです。

僕は今回発表したような方向で、効果的な広告を打つ試みをしていきたいと思ってます。

ご清聴ありがとうございました。

ポーズを頼むと箱ティッシュを持ち出したり、謎のサービス精神がある栗山氏。

当然なことだけど、そういう地道なことが大切なんですね……。
アイコも地道に頑張るぞー!

アピリッツのクリスマスの様子をお届け!

0

こんにちは!皆さんクリスマスはどのようにお過ごしでしたでしょうか。

アピリッツではクリスマスが近づくとオフィスにクリスマスツリーが登場します!

玄関とオフィスの中にもツリーがあるのでご来社頂いたお客様や社員のみなさんにクリスマス気分を味わってもらえます!

クリスマス当日は社長自ら近くのケーキ屋さんに行ってお菓子を買って社員全員に配っていました。

写真には入りきらなかったのですが全部で32箱(笑)

店員さんがとても喜んでいました♪(笑)

ちなみにアピリッツのオフィスは原宿(明治神宮前)のため、帰りに表参道、原宿の綺麗なイルミネーションを見ることができます!

来年のクリスマスはサンタ(社長)から何が届くでしょう………♪

毎年冬が来るのが楽しみですね!
株式会社Appiritsでは一緒に働く仲間を募集しています! アピリッツが気になった方はコチラの採用情報もチェックしてみてください。

【成果発表会】「自分の魅せたいものを作る」Live2Dの表現と技術共有

今回は社内の成果発表会「P-Review ’19」にて発表した、デザイナ 森田 彩加さんの資料を紹介します。
途中でSpineについてなど、正確とは言いづらい情報が記載されていますが、そのままの掲載とさせていただきます。ご留意くださいませ。

森田 彩加
2019年4月コンテンツデザイン部(現:ゲームデザイン部)にデザイナとして入社
入社後は主にLive2Dを利用したキャラモーション作成やUIデザインなどを担当

Live2Dの表現方法

テーマ選定理由

(※挨拶省略)

テーマ選定理由なんですが、私はこの会社に入って初めてLive2Dを触りました。

ですが、社内にLive2Dの知識を持っている人が少ないなというのを感じ、簡単でもいいのでLive2Dのことを少しでも知って欲しいと思い、このテーマを上げさせていただきました。

もう一つの理由は、Live2Dには沢山の表現方法があり、発想次第でかなり自由に作ることができます!
ですが、自由度が高いゆえに指定されたものがない限り、何をどう見せたいかわからなくなってしまうことがあります。

そうならないために、今回参考として表現方法をいくつかご紹介したいと思います。

Live2Dって?

まずLive2Dとは、株式会社「Live2D」が作成しているPC用ソフトウェアのことを指します。

正式名称は「Live2D Cubism」という名前です。

2Dの、一枚絵のイラストをPhotoshopなどのイラストツールで頭や腕などのパーツ分けを行い、それを立体的に動かす技術です。
Live2Dにはモデルを作成するモードと、そのモデルにアニメーションをつけるモードがあります。

用語説明


まずメッシュ、アートメッシュというのは3Dでもよく見るポリゴンのようなものがあります。

そしてLive2D特有のもので、デフォーマというものがあります。簡単に説明すると、複数のパーツをデフォーマと呼ばれるフォルダに入れて、そのフォルダごと動かす事ができるツールです。

こちらのメッシュ、アートメッシュ画像にそのまま動きをつけることもできるのですが、そうすると動きに制限がついてしまい、後々困ることにもなったりもします。
なので、基本的にはデフォーマを使って動きを付けていきます。
※アートメッシュのにそのまま動きをつけたほうがいい場合もあるため、場合によります。

続いてLive2Dのパラメータというものがあります。これはモデルの動きの幅を規定するものです。


パラメータに点を打ち、パーツやデフォーマを設定することでモデルを動かすことができます。

Live2Dにはデフォルトで既に入っているパラメータがあるのですが、動かしたいパーツ名のパラメータがない場合があります。
その場合、腕や足はデフォルトにもともとないので新しく作るという方法がとれます。

2Dアニメーション作成ツール

ここでLive2D以外の2Dアニメーション作成ツールをご紹介します。

こちらのSpriteStudioはアピリッツでも2Dキャラを扱うときなどに使われています。

もうひとつのSpineなんですが現在海外向けのものしかなくて国内でも少しだけ使われている例もありますがそこまで浸透してはいません。

それでは本題のLive2Dでどんなものが作れるのかの説明に移ろうと思います。

表現方法の紹介

大まかに三つを紹介いたします。

①全身立ち絵を動かす
一番多くみられるのは一つのモーションをつけ、それをループさせて作成することが多いです。

②アニメのような演出
Live2D表でカメラワークなどを使ってアニメのように見せる演出があります。
例えば最近のゲームにて、カットインやガチャ演出などに多く使われています。

③Vtuberモデル
FaceRigというソフトを使い、自分の顔にカメラで対応されたモデルをVtuberとして使っています。ほぼ顔をメインとしたモデルとなっています。

3つ紹介致しましたが、制作する上での相違点も併せて紹介します!

制作相違点

3つとも2Dイラストをパーツ分けする点は同じです。

ただ①全身立ち絵を動かす③Vtuberモデルはパラメータのつけ方が似ています。
違う点はアニメーションの制作があるかないかになっています。

②アニメのような演出はこの2つとは全く違う作り方をしております。
これに関しては私もまだあまり研究ができていないのですがアニメのように魅せるためのパラメータ等、その他差分など作成する必要があります。

①~③は流用できる部分はあるのですがは、一つのモデルを作ったからと言って全部の表現ができる訳ではないというのが注意点です。

おおまかにどのような表現を先に作るかを決めることで、より動きに違和感のないモデルが作成できます。

最近のLive2D作成ポイント

続いて最近私が作ったLive2D作成のポイントをご紹介致します!
ここからはLive2Dを少し触ったことがある方向けです。

「AIチャットボットAiCHO」アイコちゃんのLive2Dモデルを作成いたしました。

■『心にひびくAIチャットボット AiCHO』とは
アピリッツでは、「接客・応対品質の向上」「顧客との対話」「業務の自動化」を目的としたチャットボットとして『心にひびくAIチャットボット AiCHO』(以下AiCHO、読みアイチョ)を開発しております。

今回はVtuberモデルということでFaceRig対応をしたモデルとなっています。
ポイントとして2つご紹介いたします。

顔の角度XY

顔の角度XYは簡単に言えば顔の上下左右のことです。

顔の角度は、パラメータに8つの点を打ちます。
正面を真ん中として、その他に8つの角度から見え方を作成するという形になっています。

ただ初めてのLive2Dでの作成で、最初からどの角度から見てもおかしくないようにするのはとても難しいと思います。

私のやり方は好きなアニメから「この角度良いな」みたいな角度を持ってきて、その角度を理想として真似をするような形で作成しました。
理由は既に世に出ているものなら、たくさんの人が見ている・評価しているものが多いので良いものを作る近道になると考えました。

特に顔周りは目線が集まりやすいところなので、これでもか!というくらい丁寧に作ることが大切です。

FaceRig対応

まずFaceRigというのは、主にVtuberなどに使われているソフトでウェブカムというカメラを使い2Dや3Dのキャラになりきれるソフトのことです。

気を付けた点は4つあります。

①自分で動きを付けるのではなく人に適応した動きをする

FaceRigは作成者がアニメーションで動かすのではなく、カメラでに映る人に対して動くのでチェックをしつつ調整を重ねることに気を付けました。

②FaceRigに対応するLive2Dパラメータ

Live2Dのパラメータは初期からあるパラメータもありますが、FaceRigにデータを持って行った時に対応しないものや自分の予想と違う動きになるものがあります。

■FaceRigでウェブカム対応が無い
通常通りに体の動きを「体の回転XY」に付けてしまうと反応しない。
正確には体の回転Xには対応しているが、マウス操作やショートカットキーを使用しなければならない。

■対応策
顔に追従するように、顔のパラメータ「角度XY」に体の動きを入れると良い。

③何を一番に魅せたいのか

簡単に言えばどの部位を一番見てほしいかです。
アイコちゃんの場合、FaceRigで動くということで顔に重点を置き、目のハイライトや口の動きに気を付けました。
胸などの女性的な部分よりも表情でかわいらしさを表現したかったため、あえて胸揺れは付けていない方を採用しました。

胸揺れなし(採用)
胸揺れあり

色気の強いキャラ、またはそういう作風なら顔にプラスして胸や足に重点を置いてもいいと思います。

④プロジェクトに沿った可動域を考える

キャラの可動域は制作時間などの制限がない限りどこまででもつけられます!!

ですが、やりすぎてしまってここはいらないと言われるのも悲しいので、一番最初に何に重点を置くか考えておくことはとても大切です。

今回伝えたかった事

色々な表現が、アプローチができるLive2Dってほんとにすごい!!

しかしその点を踏まえながらもまだ新しいソフトなので、まだまだ研究が必要です。
色々なものを見て、自分から表現の幅を広げていくことが大事だと思っています。

今回表現方法を3つご紹介しましたが、まだまだたくさんの表現があり、新しい表現や技術などはネットで調べたりするほうが多く出てきたりします。

そちらも参考にし、研究段階の足掛かりとして表現の種類・魅せたいポイントを決めて、その中でより魅力的な見せ方を考えることが大事だと思っています。

そうすることで、自由度の高いLive2Dでも自分の魅せたいものを作ることができます!

自分の表現したいものを追求できるLive2Dってすごい・・・
アイコもアイコを動かせるようになりたいです~!

お仕事中の森田さん(Appirits公式Facebookより)

「興味を発信することで知識が集まる」小さな活動から意識を変えるミニカンファレンスの仕組み

山田アイコ、社内のイベントを発信します!

今回は、自社ゲーム開発をしているアピゲー部のグループマネージャー『吉田 啓紀』さんから、4月より開催しているミニカンファレンスについてお話を伺ってみました。

吉田さん、今日は宜しくお願いします!

インタビューを通してミニカンファレンスの活動を周知していただけると伺いました。是非、宜しくお願いします!

仕事中の吉田氏

吉田 啓紀
2012年にWebのエンジニアとして入社
現在は、アピゲー部のグループマネージャーとして新作ゲームのディレクションと、
いくつかの既存ゲーム運営メンバーのマネージメント業務を行っている。
業務の傍ら、2019年4月より『ミニカンファレンス』を発足。

『定期的』にアウトプットする場を作りたい

まず、ミニカンファレンスを始めたきっかけや目的を教えてください。

社員の中でもっとアウトプットする場がほしいという話がもともとあったんですよ。

後は、部署が結構増えてきて部署間での情報共有が難しくなってきたなと、僕自身も感じていました。

アウトプットする場が、社内に少なかったということでしょうか?

少ないというよりは「あったけれど定常化はされていない」状態でした。
今までは突発的なもので『LT会』とか皆で技術書読もうみたいな『読書会』はありましたが、なかなか続けてやる題材がなかったんです。

開催者も「思いついた時にやる」みたいな感じだったのでほんとにまばらでした。

良い会なのにもったいないですね、そこで定期的な会を作りたいと……。

はい、いつ開催されるか分からない会に向けて目標を建てるのって難しいと思うんです。

前に開催したもので『LT』って名前を使っちゃったし、それなら名前を新しくして定期的にやる会を作ろうと思って始めたのが”ミニカンファレス”です。

ハニカミながらインタビューに答える吉田氏

なるほど!
定期的に発表があった方が、みんなが目標建てしやすくなっていいですね。

そうですね。
「今期あったことをミニカンファレンスまでに整理して発表ができれば」という話も最近ぽつぽつと耳にしていて、良い目標作りのきっかけになっているんじゃないかと思っています。

新技術を導入し、他のタイトルにも取り入れる

ミニカンファレンスでの印象深いエピソードはありますか?

直近だと、「発表した内容がもう明日から使えるね」と話がまとまって、プロジェクトにそのまま入れることになりました。

すごく早いですね!その新作ゲームにはどのように取り入れましたか?

発表した人自身がもうゲームに実装しているものだったんですが、「じゃあそれを他のプロジェクトにも入れよう」とすぐに決まりました。

どんなゲームなのでしょうか。良ければ宣伝していきませんか?

実装元は『ゴエティアクロス』というタイトルで、それとは別に新しく開発しているタイトルに取り入れようということになりました。
開発中のゲームについてはまだ詳しくお知らせできませんが、来年にはユーザーの皆様にお届けできるかと思います。

私もゲスト出演している『悪魔少女✕マルチプレイRPG ゴエティアクロス』。
是非、プレイしてみてくださいね!

アウトプットする事で得られる変化

ミニカンファレンスが会社に与えた変化はありましたか?

社員側が『定期的にアウトプットの場がある』ことを認識するようになったことで、目標設定として「次のキーワードを何か自分で研究してみて、それをミニカンファレンスで発表します。」と声が聞こえてきた事に関しては、すごくいい変化に感じました。

以前からあるブログ(DoRubyなど)ではアウトプットを目標として設定できなかったのでしょうか?

設定してる部署もあればしてないところもありました。ブログっていうのは結局「見る人は見るけど、見ない人は見ない」ものなので難しいと思います。

あと実際に人が前に立って発表することと、システムに書いて打ち込んで媒体として出すっていうのは、結構”行為”として違うなっていう風に思っていて、どちらも学んだことをまとめるっていう意味では同じなのかもしれないですけど、やっぱり個人的には前に出て発表するっていうのをもうちょっとやってもらいたいなと。

真剣な表情で語る吉田氏

吉田さんが考える最終的なゴールはどこですか?

社内のカンファレンスにも出てもらって発表して「俺たちすげえじゃん」みたいなとこまでいけるとゴールかなっという風に思っています。

明確で解りやすいゴール設定ですね!
ちなみに初回のミニカンファレンスでの反応はいかがでしたか?

まあ、「良かったね」という感じでした。マイナスな印象はなく、発表することに対して前向きな人が結構多かったです。

何か課題など出てきましたか?

発表する人が固定化されてきちゃうかなってところですね。
もうちょっといろんな人に発表してもらえればいいんですけど……。

特にどんな人に発表してほしいと思っていますか?

社内ではいろんな技術使っていて、実は業界的に見るとすごいことをやってるんだけど、その場に技術を持ったまま留まっちゃってる「もったいない」状態の人がいるんじゃないかと気づきました。

その人達にいきなり社外に向けて発表してくださいっていうのは、ハードルも高いですし、そんな恥ずかしいことできないってなると思うんです。
なので、その人達にはこのミニカンファレンスを最初の踏み台としてやってもらえたらと思ってます。

吉田さん自身は最初のミニカンファレンスでどんな発表をなさいましたか?

自分はエンジニアの発表をしようと思っていたんですけどせっかくなので、誰もあんまり触れていない広告の話をしました。
そこそこ興味持って見てもらえたと思います。

広告回りをやってる人があまりいないっていうところで布教活動にも使いたくて発表しました。

同じものに興味ある人が集まるきっかけにも使えるんですね

そうですね、広告回りでいうと興味はあるけど具体的に何やってるか知らないっていう状態の人が結構多いので、その辺の共有にも使えます。

ミニカンファレンスを実際に行って吉田さんにとって良かったことはなんですか?

皆色々やってくれてるんですけど、なかなかそれを引っ張り出すことができないなと思っていました。

ミニカンファレンスを開催したことで「あ、そういうこと考えてるんだ!とか、そういうことやってたんだ!」っていう普段見ててもわからない所を引き出せたいいきっかけになってくれました。

一つの手段としてそういう場を設けられたのは良かったと思っています。

メンバーの体験を満面の笑みで喜ぶ吉田氏

発信してもらってより理解しあえたんですね

発信することで逆に周りからも興味や知識が集まって仕事に活かせたり、見る側としてもメリットが大きいと思います。

ありがとうございます。
最後に、今後はミニカンファレンスをどの様にしていきたいとお考えでしょうか?

最初にも言いましたが、いろんな人に自分の興味あるものや取り組んでいることをもっと発信して共有してもらいたい。

他の部署と連携してミニカンファレンスの様子をYouTube配信などもしましたけど、そんな風に一緒に協力してやっていけたらよりよくなっていくんじゃないかと思っています。

発表したスライドを前にいい笑顔を見せる吉田氏

社内で定期的な情報共有の場を作り、社員全体の知識レベルを上げる取り組みはとても重要ですね。
もちろん私達CTO室も共に盛り上げますよ!

アピリッツが気になった方は コチラの採用情報 もチェックしてみてくださいね。

どうしようもないけどやれるだけのことはやってどうにかしよう

0

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

がんばろう

貴方はプロジェクトにアサインされ、サーバーの負荷試験を依頼された

「頑張るぞ! ガルバンゾ!」

ここにコードがあった。それは見るからに不味そうなスパゲッティであった

「お? おお……」

ここにスケジュールがあった。それはスパゲッティが腐るまでに時間がない事を示していた

「おお……。お、おお……」

それは即ち、根本解決をするだけの時間的猶予がない事を示していた

「おおあああああっ、うっ、うぐぅっ」

貴方の使命は、せめて足掻く事であった

「……」

現状整理から始めよう

 コードがスパゲッティになる理由は幾らかあれど、そんな事を調査していても始まらない。
 やるべき事は罪を償わせる事ではない。罪を代わりに償う事だ。
 まずは一つ一つ現状を整理し、問題解決には何をすべきか考える事である。

 現状を調査した結果、まずはとても良い事が分かった。

テストを書く文化がある

 これが何を意味するかというと、コードの振る舞いがテストによって保証されているという事だった。
 コードが中でどんなに破茶滅茶な事をしていても、テストが通りさえすればそのコードは少なくとも挙動としては正しい。
 即ち、中を書き換えてもテストが通りさえすればその正当性は保証される。最後にQAなどを通す必要はあるが、これから行う改修に対しての強力な道しるべとなる。

使用している監視ツールはデータベースの重みを調査するものと、サーバーそのものの重みを監視する二つを利用していた

 確かにこれで重い部分がある程度は分かる。ある程度ではあるが。
 これではコード全体の重みは一部見逃される恐れがある。レスポンスの遅さとは、データベースのクエリ以上にコードそのものの重みが原因となる場合があるからだ。
 ただ、それは誰かに何かを依頼する事もなく調査、解決出来る。
 そしてその監視ツールがある事自体はとても有用である、というか無ければ負荷試験は出来ない。

最も問題であるコードは1APIの中で何度も同じデータを参照する事があった

 マスタデータやユーザーデータを、そのコードが呼ばれる度に参照、計算して返していた。
 1回の呼び出しでユーザーデータが多数変更され、また都度都度必要となるマスタデータは変わる。それが1APIの間で何度も発生する。
 そしてまた、これを根本的に解決する事ーー例えば1回の呼び出しに纏めるーーといった事などは影響範囲や再度掛かるQAへのコストなども鑑みると時間の都合上不可能。
 出来るのは1回1回で掛かるコストを出来る限り減らす事、それが最善だった。

改善方針をまず固めて共有しよう

 見ての通り改善すべき事はデータベース上の重みとしてもコードとしてもはっきりしている。
 ただ、改善するものの規模が大きいのならば、見切り発車で改善を決めるのではなく、一旦共有して見落としが無いかなどを第三者の目からも含めて判定した方が良い。

「ここの重みを削れれば、取り敢えず重みは60%位になります」
「60%でもまだ重いな……他の部分は?」
「まだ調査していないです。ただ、ここのコードの重みとしては、このAPIの負荷の割合を見てもデータベースの重みではなく別の部分である事ははっきりしているのと、今のツールでは調査しきれない部分もあるので先にやってしまおうと思います」
「……まあ、良いか。で、どうやって削る?」
「基本的に1API間でユーザーデータを使い回す、1スレッドで同じマスタデータを使い回す、という感じですね」
「どうやって使い回す?」
「ユーザーデータに関してはのユーザーのインスタンスが1APIで使い回されているので、そこにインスタンス変数として保持しまおうかと。
 マスタデータもメモ化してしまいます。
 まあ、要するにユーザーデータ、マスタデータ共にスレッドのメモリにキャッシュします」
「……メモリに負荷が掛かるだろうけどそこ辺りは」
「幸いそちらはまだ余裕があります」
「オーケー」

改修したが想定までは減らなかった

「まあ、そう簡単に減らせたら苦労しないよなー……」
 確かに、無駄なデータ検索量は減った。幸いマスタをメモリに持たせる施策も、強いメモリ負荷もなく実現出来た。
 しかしながらAPIの重みを見ると、データベース起因ではない不明な重みが大半を占める事となっていた。
 これは即ち、コード自体に問題があるという事だ(掛けている負荷が適切であれば)。

 アプリケーションに対するプロファイリングツールを導入し確認してみると、膨大な配列から必要なデータを参照してくる部分に大きな問題があった。
 既存の部分だけではなく、自身が改善したユーザーデータやマスタデータを使い回す処理でも、その引っ張ってきたデータから目的のデータを探り当てる処理で重みが発生してしまっていた。
「データをDBからメモリ上にデータを乗せるだけではまだ駄目か……」
 こうなると、その検索処理などをどうにかするしかない。
 コードを読み込み、どのようにデータが扱われているかを確認していく。
・ある条件に当てはまるユーザーデータは、特殊な条件の場合にしか参照されない事が分かり、基本的に検索から抜いても良い事が判明した。
・同じマスタの中でも種類が膨大にあり、複雑な検索条件でもメモ化の形を工夫すれば配列検索の回数を減らせる事が分かった(マスタを分けろよという愚痴は心の奥底に仕舞った)。
 そんな事からユーザーデータやマスタデータを分類分けしていくと、何とかなりそうな目星がついてきた。

※ruby_on_railsだとindex_byやgroup_byがとても有用。単純にデータ検索をするにしても配列からfindを仕掛けるよりもハッシュにしてキーが存在するかを判定した方が確実に早いです。
サンプルに以下。

1.

array = (1..1000).to_a
begin
  now = Time.now
  1000.times do |n|
    array.include?(n + 1)
  end
  p Time.now - now
end
=> 0.0051824

2.

hash = (1..1000).each_with_object({}){|n, res| res[n] = true}
begin
  now = Time.now
  1000.times do |n|
    !!hash[n + 1]
  end
  p Time.now - now
end
=> 0.0001501

かなり改善された

「ここまで改善出来ました!」
「これなら耐えうるな、で、コードは?」
「こうなりました!」
「……複雑だな……」
「……そうなんです。まあ、テストコードがあったので振る舞いは保証されています」
「そうだな、そうか(テストコードの網羅率って100%だったかな……)」

 根本原因を解決出来ずに上部だけで対応しようとすると、そうなります。
 できる事はスパゲッティを美味しく作り直す事ではなく、防腐剤を振りかける事、ただそれだけだったのですから。
 しかしながら、そうしなければいけない時もあるのです。きっと。

美味しいスパゲッティの作り方

  1. 厚切りの芽を抜いたニンニクと唐辛子をたっぷりのオリーブオイルで常温から弱火で炒めましょう
  2. ニンニクはきつね色になったら取り出しましょう。唐辛子も焦げる前に取り出しましょう
  3. 好きなキノコを好きなだけ入れて塩を振って炒めましょう
  4. 塩をきつめに入れた熱湯で茹で上げたスパゲッティを和えましょう
  5. 皿に盛り付けてから取っておいたニンニクと唐辛子と、あればパセリを意識高めに散らしましょう
  6. 食べよう

カスタマーサポートの極意 2019 #3 レポート

0

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

セミナー概要

・カスタマーサクセスを意識したカスタマーサポートの方法
・顧客から高い満足度を得続けるカスタマーサポートの方法
・カスタマーサポートに適したサービスの選定方法

登壇者

  • 弁護士ドットコム:三浦陽菜様「クラウドサインの新しいカスタマーサポートの方法」
  • ベルフェイス:森本真伍様

クラウドサインの新しいカスタマーサポートの方法

  • クラウドサインとは
    • クラウド型電子契約サービス
  • リレーションの現状
    • チャットサポートの目標
      • 速さ
      • 満足度
    • 体制と測定方法
      • 3人体制
      • チャットの最後の満足度を聞く
    • 結果
      • 初回連絡60秒以内(満足する結果)
      • 満足度90%以上(満足する結果)
  • 満足度とチャーンの結果
    • 満足度が低い顧客が解約しているわけではない
    • 解約している顧客はサポートに問い合わせがない
  • サポートの意味は?
    • 関係を築き続けることが大事
  • 関係を築き続けるには?
    • CX(カスタマーエクスペリエンス)の向上が大事
  • CXとは?
    • 顧客に手間を取らせない(1回の連絡も手間)
  • 手間を取らせない方法
    • 1.疑問が生まれない
    • 2.疑問を自分で解決できる
    • 3.疑問は聞けば分かる
  • 「疑問を自分で解決できる」の現状
    • ヘルプセンターで解決できる
  • 問題点
    • 存在の認知度が低い
    • チャットの方がラク
  • 問題点の解決策
    • elevio
  • elevioについて
    • 開発せずにツールチップ(はてなマーク)を設定可能
    • intercomと連携できる
    • elevioからアドバイスをいただける
  • elevioをを使った今後の予定
    • データ収集
  • まとめ
    • ユーザーに努力をさせないこと
    • 爆速で解決できること

ベルフェイスのカスタマーサポート

  • ベルフェイスとは
    • インサイドセールスサービス
  • カスタマーサポートの極意とは
    • 個に頼る
  • KPIの達成状況
    • チャットの初動時間:60s以内(コツは画面と音)
    • 満足度:95%以上
    • (こちらからのアンケートなどに対しての)回答率:40%
  • KPIと行動指針
    • カスタマーファースト
    • オーナーシップ
    • ハイスピード
    • 改善
    • 細部を大事に
    • メンバー内協力
  • 具体的な対策
    • よかったことの共有
    • 満足度が低い対応への改善検討(メンバー全員で)
    • チャット対応のポイントを共有(docbase)
  • 課題
    • 数値を正しくとる
  • まとめ
    • 行動指針をCS向きにする

Search Engineerring Tech Talk 2019 Spring レポート

0

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

Search Engineering Tech Talk 2019 Spring レポート

Search Engineering Tech Talk とは

検索技術勉強会の目的は、「検索」/「検索システム」にまつわる技術や手法に関して共有できる場を提供すること

登壇者

  • ナビタイム 小式澤 篤 :「安心な移動」のためのPOI(Point-of-Interest; 地点)検索
  • 菅谷 信介 :社内ドキュメント検索システム構築のノウハウ
  • @818uuu :料理動画アプリ「クラシル」の検索について

「安心な移動」のためのPOI(Point-of-Interest; 地点)検索

  • POIとは
    • point of interest
    • 移動の目的地となる場所
    • 緯度軽度や名前や住所などの情報
  • POIの特徴
    • 短文か単語が多い
    • 文章が少ない
    • 要素が多い
  • 経路探索におけるPOI検索の立ち位置
    • 目的地検索ができなければ、そのあとのナビゲーションができない
  • 安全な移動のためのPOI検索
    • ほとんどの人が上位5位を選択している
    • キーワードとの一致率が大事
  • 問題点1
    • いろんな県のものが出てくる
      • ホテル椿山での検索でホテル椿山東京以外のホテル
      • ディズニーでの検索で東京ディズニーランド以外のもの
      • スカイツリーも同様
  • 対策1
    • 人気なもの(有名度)が高いものを優先して上位に表示 →大方解決
  • 問題点2
    • チェーン店などでの検索で、人気の店舗が上位されてしまう
    • 「コンビニ」での検索で、「セブンイレブン 新宿駅」が表示されるなど
  • 対策2
    • 距離が近いものも優先して上位に表示 →大方解決
  • まとめ
    • 並び順優先順位
  1. 有名度
  2. 適合率
  3. 距離
  4. 再現率

社内ドキュメント検索システム構築のノウハウ

  • 企業内検索とは
    • 企業内の情報を検索する(ファイルサーバーやウェブウェイトなど)
  • fessとは
    • オープンソースの全文検索システム
    • elasticsearchを使っている
  • 企業内検索を構築する際のよくある課題
    • クロール対象が大規模
    • セキュア
    • 業務システムとの連携
    • ファイルが様々
  • 大規模化について
    • ドキュメント数は数千万くらい
    • クラスタにして分散検索
    • クロールする際の工夫
    • 更新ファイルリストを生成し、更新されたもののみをクロールする(全件だと1日のクロールで終わらないため)
  • セキュアについて
    • 認証状態により検索結果を出し分ける
      • ad連携
      • ログイン
    • クロール時に権限情報を付与する
      • 閲覧することができる権限をクロール時に取得するため
    • シングルサインオン
    • 検索システムにも自動でログインする
      • リバースプロキシがた
      • windows統合認証
      • openid connectなど
  • 業務システムとの連携
    • データはデータベースにあるため、sqlを検索サーバーに入れる
  • ファイルの種類について
    • ツールを使って文字列の抽出
      • ms office po
      • pdfbox
      • tika など

料理動画アプリ「クラシル」の検索について

  • クラシルについて
    • 検索対象:動画
    • ドキュメント?数:約2万5千本
  • 同義語対策について
    • 日々検索キーワードをチェックし手動で登録
      • ある程度効果はある
    • 同義かどうかが難しい場合がある
      • 「インゲン豆」「さやいんげん」など
  • 0件ヒット対策
    • 0件のヒットとなっているキーワードのレシピを日々チェック
    • 新生ワードを早期発見するために日々リサーチ(twitterなど)
  • 運用中気になったこと
    • 食材クエリとメニュークエリで離脱率が異なる
    • androidとiosでクエリが異なる
    • テレビの影響は大きい
    • 検索数が少ないキーワードへの対処優先度
  • KPI
    • 最初は離脱率
    • その次、検索経由のCV
      • ただし検索した後、買い物に出かけるなど、タイムラグがあるため純粋なCVを測ることが難しい(検討中)

Rails & Webpack案件でのgmo paymentのトークン決済

0

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

webpackの仕様を理解しないとたまにトラブルが起きるという話

GMOペイメントゲートウェイのトークン決済とは

ECサイトで決済処理を実装するときにGMOペイメントゲートウェイを活用する事になりました。
多様な決済方式に対応していて便利ですね。

GMOペイメントゲートウェイにはトークン決済という方式があります。
クレジットカード情報をJavaScript上でAPIに送信し、トークン化する事でセキュリティ的な観点で安心して決済処理を行う事ができる、というものですね。

実装

トークン化はコントローラを介さずJavaScriptで行います
必要なライブラリはCDNで配布されています。
本番用と開発用があるのでお間違えの無いように

<!-- トークン決済開発用javascriptを読み込み -->
<%= javascript_include_tag 'https://stg.static.mul-pay.jp/ext/js/token.js' %>
※URLが変更されている場合があるので必ず最新のドキュメントを参照してください

Multipaymentが定義されるのでカード情報をsubmitする際に利用します。

Multipayment.getToken({ 
   パラメータ 
   }, 任意のコールバック関数); 

の形式です

公式ドキュメントのコードサンプルは以下の通り
最低限の機能なので、カード情報の入力に不足がある場合はアラートを表示して送信しない等の処理を挟むのをオススメします。

<script type="text/javascript"> 
  function execPurchase(response) { 
   if (response.resultCode != "000") { 
    window.alert("購入処理中にエラーが発生しました"); 
   } else { 
    // カード情報は念のため値を除去 
    document.getElementById("cardno").value = ""; 
    document.getElementById("expire_year").value = ""; 
    document.getElementById("expire_month").value = ""; 
    document.getElementById("securitycode").value = ""; 
    document.getElementById("tokennumber").value = ""; 
    // 予め購入フォームに用意した token フィールドに、値を設定 
    //発行されたトークンは、有効期限が経過するか、一度 API で利用されると、無効となります。 
    //複数のAPIでトークンを利用される場合は、tokenNumberにてトークンを複数発行してください。

    document.getElementById("token").value = response.tokenObject.token; 
    // スクリプトからフォームを submit 
    document.getElementById("purchaseForm").submit(); 
   } 
  } 

  function doPurchase() { 
   var cardno, expire, securitycode, holdername; 
   var cardno = document.getElementById("cardno").value; 
   var expire = 
   document.getElementById("expire_year").value + document.getElementById("expire_month").value; 
   var securitycode = document.getElementById("securitycode").value; 
   var holdername = document.getElementById("holdername").value; 
   var tokennumber = document.getElementById("tokennumber").value; 
   Multipayment.init("tshop00000001"); 
   Multipayment.getToken({ 
    cardno : cardno, 
    expire : expire, 
    securitycode : securitycode, 
    holdername : holdername, 
    tokennumber : tokennumber 
   }, execPurchase); 
  } 
</script> 

問題点

このサンプルでいうexecPurchaseにあたるコールバック処理は、
グローバルスコープに無いとCDNから取得したライブラリ側で認識されないのですが、

webpackがスコープを管理する都合でサンプルをこのまま記述してもexecPurchaseはundefinedになってしまいました。

解決策

window.execPurchase = execPurchase;

上記をスクリプトに追記し作成したコールバック関数をwindowオブジェクトに持たせることで解決しました。
package.jsonに設定を記載する等の手段がありそうでしたが上手くいかなかったのでこの手法に落ち着きました。

更なるトラブル

Uncaught ReferenceError: r is not defined

おっ解決したと思いきやデプロイしたら動かん…

何故ならwebpackはproduction環境では変数名等を変換して難読化させた状態でビルドするからです。

window.execPurchase = r;

Multipayment.getToken({ 
    cardno : cardno, 
    expire : expire, 
    securitycode : securitycode, 
    holdername : holdername, 
    tokennumber : tokennumber 
   }, r); 

上記のようにコールバック関数がr一文字に変換されていました
window.execPurchase = execPurchase;で宣言してもこれでは動かない…
今回の場合、幸いなことにコールバック関数は文字列を渡しても問題なく処理してくれるので

Multipayment.getToken({ 
    cardno : cardno, 
    expire : expire, 
    securitycode : securitycode, 
    holdername : holdername, 
    tokennumber : tokennumber 
   }, "execPurchase"); 

のようにコールバック処理を指定する引数を文字列にすることで解決できました。
変数名は勝手に変換されますが文字列はそのまま残ります。

普段意識しないですがwebpackはこういう変換処理をしてくれているんですね。
トラブルが起きない分には素晴らしいですがたまにこういう事が起きるという話でした。

Rails & Webpack案件でのgmo paymentのトークン決済

0

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

webpackの仕様を理解しないとたまにトラブルが起きるという話

GMOペイメントゲートウェイのトークン決済とは

ECサイトで決済処理を実装するときにGMOペイメントゲートウェイを活用する事になりました。
多様な決済方式に対応していて便利ですね。

GMOペイメントゲートウェイにはトークン決済という方式があります。
クレジットカード情報をJavaScript上でAPIに送信し、トークン化する事でセキュリティ的な観点で安心して決済処理を行う事ができる、というものですね。

実装

トークン化はコントローラを介さずJavaScriptで行います
必要なライブラリはCDNで配布されています。
本番用と開発用があるのでお間違えの無いように

<!-- トークン決済開発用javascriptを読み込み -->
<%= javascript_include_tag 'https://stg.static.mul-pay.jp/ext/js/token.js' %>
※URLが変更されている場合があるので必ず最新のドキュメントを参照してください

Multipaymentが定義されるのでカード情報をsubmitする際に利用します。

Multipayment.getToken({ 
   パラメータ 
   }, 任意のコールバック関数); 

の形式です

公式ドキュメントのコードサンプルは以下の通り
最低限の機能なので、カード情報の入力に不足がある場合はアラートを表示して送信しない等の処理を挟むのをオススメします。

<script type="text/javascript"> 
  function execPurchase(response) { 
   if (response.resultCode != "000") { 
    window.alert("購入処理中にエラーが発生しました"); 
   } else { 
    // カード情報は念のため値を除去 
    document.getElementById("cardno").value = ""; 
    document.getElementById("expire_year").value = ""; 
    document.getElementById("expire_month").value = ""; 
    document.getElementById("securitycode").value = ""; 
    document.getElementById("tokennumber").value = ""; 
    // 予め購入フォームに用意した token フィールドに、値を設定 
    //発行されたトークンは、有効期限が経過するか、一度 API で利用されると、無効となります。 
    //複数のAPIでトークンを利用される場合は、tokenNumberにてトークンを複数発行してください。

    document.getElementById("token").value = response.tokenObject.token; 
    // スクリプトからフォームを submit 
    document.getElementById("purchaseForm").submit(); 
   } 
  } 

  function doPurchase() { 
   var cardno, expire, securitycode, holdername; 
   var cardno = document.getElementById("cardno").value; 
   var expire = 
   document.getElementById("expire_year").value + document.getElementById("expire_month").value; 
   var securitycode = document.getElementById("securitycode").value; 
   var holdername = document.getElementById("holdername").value; 
   var tokennumber = document.getElementById("tokennumber").value; 
   Multipayment.init("tshop00000001"); 
   Multipayment.getToken({ 
    cardno : cardno, 
    expire : expire, 
    securitycode : securitycode, 
    holdername : holdername, 
    tokennumber : tokennumber 
   }, execPurchase); 
  } 
</script> 

問題点

このサンプルでいうexecPurchaseにあたるコールバック処理は、
グローバルスコープに無いとCDNから取得したライブラリ側で認識されないのですが、

webpackがスコープを管理する都合でサンプルをこのまま記述してもexecPurchaseはundefinedになってしまいました。

解決策

window.execPurchase = execPurchase;

上記をスクリプトに追記し作成したコールバック関数をwindowオブジェクトに持たせることで解決しました。
package.jsonに設定を記載する等の手段がありそうでしたが上手くいかなかったのでこの手法に落ち着きました。

更なるトラブル

Uncaught ReferenceError: r is not defined

おっ解決したと思いきやデプロイしたら動かん…

何故ならwebpackはproduction環境では変数名等を変換して難読化させた状態でビルドするからです。

window.execPurchase = r;

Multipayment.getToken({ 
    cardno : cardno, 
    expire : expire, 
    securitycode : securitycode, 
    holdername : holdername, 
    tokennumber : tokennumber 
   }, r); 

上記のようにコールバック関数がr一文字に変換されていました
window.execPurchase = execPurchase;で宣言してもこれでは動かない…
今回の場合、幸いなことにコールバック関数は文字列を渡しても問題なく処理してくれるので

Multipayment.getToken({ 
    cardno : cardno, 
    expire : expire, 
    securitycode : securitycode, 
    holdername : holdername, 
    tokennumber : tokennumber 
   }, "execPurchase"); 

のようにコールバック処理を指定する引数を文字列にすることで解決できました。
変数名は勝手に変換されますが文字列はそのまま残ります。

普段意識しないですがwebpackはこういう変換処理をしてくれているんですね。
トラブルが起きない分には素晴らしいですがたまにこういう事が起きるという話でした。

wheneverで時刻を設定する際システムで設定されているのとは別のタイムゾーンを使用する

0

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

Railsアプリケーションのバッチ処理を [whenever](https://github.com/javan/whenever) でスケジュール設定する際、デプロイ先のタイムゾーンの設定がUTCなんだけど、スケジュール設定は開発者にわかりやすく日本時間としたいケースがあったので、その対応方法について。

問題と背景

  • デプロイ先のサーバのタイムゾーンの設定は UTC となっている(本当はJSTにしておいて欲しかったが、今更サーバの設定を変更したくない)
  • ただし whenever のスケジュール設定(config/schedule.rb)は日本時間で記述したい
  • whenever の schedule.rb ではタイムゾーンを指定することができず、システムで設定されているタイムゾーンを使用する。

対応方法

  • schedule.rb で時刻設定時にタイムゾーンを変換する関数を用意。その関数使って任意のタイムゾーンからシステムのタイムゾーンに変更する
# config/schedule.rb

# Time クラスの拡張を利用するため ActiveSupport を require する
require 'active_support/core_ext/time'

# 時刻の文字列を日本時間で解釈して、システムのタイムゾーンに変換
def jst(time)
  Time.zone = 'Asia/Tokyo'
  Time.zone.parse(time).localtime($system_utc_offset)
end

# 日本時間の 午前 2:00 => UTC の 17:00 にバッチをスケジュール
every 1.day, at: jst('2:00 am') do
  runner 'HogeFugaBatch.execute'
end

参考

データをIDで水平分割する時の方式を選ぼう

0

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

舎利子色不異空空不異色色即是空空即是色受想行識亦復如是 舎利子是諸法空相不生不滅不垢不浄不増不減

そもそも水平分割って何さ

同じ情報を複数のテーブルやデータベースに分けて分割する事です。
ユーザー情報が一つのデータベースに入っていたとしたら、

DB1
|ID|name|profile|
|1|イチ|弟達の世話で大変ですが愛おしいです|
|2|ニ|教授は美味しかったです|
|3|サン|お兄ちゃんに良く噛まれます|
DB1
|ID|name|profile|
|1|イチ|弟達の世話で大変ですが愛おしいです|
DB2
|ID|name|profile|
|2|ニ|教授は美味しかったです|
DB3
|ID|name|profile|
|3|サン|お兄ちゃんに良く噛まれます|

こんな風に複数のテーブルやデータベースに情報を分けて保存する事です。

どうして水平分割なんてやるのさ

データベースのレコード検索はインデックスを張る事で高速化が出来ますが、それでも数が莫大になってくると遅くなってきます。
それならば、保存する場所を複数に分ける事で1データベースのレコード数を絞り、その中で検索を掛ける事によって検索が遅くなる事を防ごうという訳です。

どうやって水平分割をするのさ

用途によって必ずしもそうではありませんが、基本的にテーブルの主キーであり、一意の数値であり、連番となるIDを元に保存/検索などをする対象先を判別するようにします。
ここでは、そのIDを元に水平分割をする場合の主に使われる2つの手法を紹介します。

範囲分割

IDを範囲分けして、対象先を判別します。
IDが1から100まではDB1に、
IDが101から200まではDB2に、
IDが201から300はDB3に、
そして301から400まではまたDB1に…という風に分割します。

DB1
|ID|name|profile|
|1|イチ|弟達の世話で大変ですが愛おしいです|
|2|ニ|教授は美味しかったです|
|3|サン|お兄ちゃんに良く噛まれます|
...
|100|ごますり|操られていただけなので勘違いしないで頂きたい|
|301|マンモス|何か話題になりました|
...

DB2
|ID|name|profile|
|101|目覚まし時計|友よ|
|102|サノスおばさん|燃え尽きたぜ…真っ白にな…|
|103|応援部隊その1|ソイヤッ!|
...

DB3
|ID|name|profile|
|201|王様|目覚まし時計が強過ぎて我が家を失いました|
|202|女王様|得意技は磔です|
|203|緊急脱出|ホールインワンしました|
...

剰余分割

IDをDB数で割った余りで対象先を判別します。DBが3つならば、
余りが0ならばDB1に、
余りが1ならばDB2に、
余りが2ならばDB3に、
分割します。

DB1
|ID|name|profile|
|1|イチ|弟達の世話で大変ですが愛おしいです|
...
|102|サノスおばさん|燃え尽きたぜ…真っ白にな…|
...
|201|王様|目覚まし時計が強過ぎて我が家を失いました|
...
DB2
|ID|name|profile|
|2|ニ|教授は美味しかったです|
...
|100|ごますり|操られていただけなので勘違いしないで頂きたい|
...
|103|応援部隊その1|ソイヤッ!|
...
|202|女王様|得意技は磔です|
...
|301|マンモス|何か話題になりました|
...
DB3
|ID|name|profile|
|3|サン|お兄ちゃんに良く噛まれます|
...
|101|目覚まし時計|友よ|
...
|203|緊急脱出|ホールインワンしました|
...

どっちが良いのさ

範囲分割と剰余分割、それぞれに利点欠点はあります。


範囲分割

利点:
対象先のデータベースを増やすといった時に設定の追加が簡単。
欠点:
設定を書くのが多少面倒。想定されるID数までの範囲を分割して、そしてそれを設定として全て書かなければいけない。
また、データの追加が大量に走った場合に処理が1つのデータベースに集中する。
ソーシャルゲームだと、ユーザー登録が沢山走った場合などには1データベースの性能がボトルネックとなる場合がある。


剰余分割

利点:
設定が簡単。IDを割った値で分割するだけ。
範囲分割での欠点のような、データの追加が大量に走る場合でも処理が複数のデータベースに分散されて一つに集中しない。
欠点:
対象先のデータベースを増やしづらい。また、増やしても設定が複雑になってしまう。
このIDまでは3で割った余りで対象先を判別し、このIDからは4で割った余りで分割する…とか、そんな事をしなくてはいけない。


まあ、一長一短ですね。
ただ、汎用性があるのは範囲分割の方だと思います。剰余分割の 対象先のデータベースを増やしづらい。また、増やしても設定が複雑になってしまう。 という事柄がかなり大きいので。他の手法であるハッシュ分割なども似たような形です。
そんな訳で、それぞれの利点欠点を理解した上で、プロジェクトに合った分割方法を選びましょう。

Rails Console から Sidekiq のジョブを操作する

0

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

Sidekiq の Web コンソールが重いので Rails console からサクッとジョブのリトライとかしたい場合のメモ

キューの一覧を取得

 Sidekiq::Queue.all

キューに登録されているジョブの一覧を取得

 q = Sidekiq::Queue.new('キュー名')
 q.entries

ジョブが失敗した場合はリトライのキューに移る

リトライの一覧の確認〜ジョブの強制リトライ

 rs = Sidekiq::RetrySet.new
 rs.size
 rs.entries
 rs.entries[0].retry

リトライ待ちのジョブの削除

 rs.entries[0].delete

Dead (リトライ期限が過ぎたジョブ)

ds = Sidekiq::DeadSet.new
ds.size
ds.clear

docker-compose で開発者毎に異なる設定を使いたい場合 | その他 | DoRuby

0

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

開発環境として Docker を使っていると、開発者の環境固有の問題に対応するためにその環境固有の設定を追加したい場合がある。そのような場合のTips

背景

  • ローカル開発環境を Docker で構築して開発者間の環境の差異をできるだけ少なくしている
  • ただし、ホスト環境が Windows, Mac, Linux と様々で、個人毎に設定を微妙に変えたいケースがある

追記(2019/6/27)

どうも docker-compose はデフォルトで docker-compose.override.yml を読み込むようなので、以下のように環境変数の設定は不要だった。

対応方法

  • 環境変数 COMPOSE_FILE を使用するexport COMPOSE_FILE=docker-compose.yml:docker-compose.override.yml のように設定すると、 docker-compose.ymldocker-compose.override.yml 両方の設定がマージされる
  • いちいち export ... を打つのが面倒な場合、 direnv を導入すると良い

ローカル環境で’LOADING Redis is loading the dataset in memory’が頻発する

0

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

表題の件についてのメモ

背景

  • Rails アプリケーションのローカル開発環境を Docker で構築
  • その環境には Sidekiq の worker が稼働する、 worker コンテナ、Sidekiqがバックエンドとして使用している Redis が稼働している redis コンテナが存在する

現象

docker-compose によるコンテナの立ち上げ時、以下のエラーが発生して worker コンテナが落ちる

worker           | LOADING Redis is loading the dataset in memory
worker           | /app/vendor/bundle/ruby/2.5.0/gems/redis-4.0.3/lib/redis/client.rb:124:in `call'
worker           | /app/vendor/bundle/ruby/2.5.0/gems/redis-4.0.3/lib/redis/client.rb:107:in `block in connect'
(略)
worker           | /app/vendor/bundle/ruby/2.5.0/gems/redis-4.0.3/lib/redis.rb:278:in `info'
worker           | /app/vendor/bundle/ruby/2.5.0/gems/sidekiq-5.2.3/lib/sidekiq.rb:113:in `block in redis_info'
worker           | /app/vendor/bundle/ruby/2.5.0/gems/sidekiq-5.2.3/lib/sidekiq.rb:95:in `block in redis'

原因

redisコンテナ起動時に --appendonly yes が指定されていた。

LOADING Redis is loading the dataset in memory というエラーは、 AOF(Append-only File)からデータをメモリ上に展開している間に更新(SET)などのコマンドを実行しようとした際に発生するらしい。

RDB/AOFファイルをローディングしている間、SETコマンドのようなものを実行すれば “(error)LOADING Redis is loading the dataset in memory”このようなエラーを出すが、infoコマンドは実行されます。 したがって、infoコマンドでローディング中なのか確認することができます。
REDIS INFO [section] コマンド

対処その1

Append-only File は、プロセスの予期しない終了等からデータを保護するための機構であるが、ローカル開発環境においてはそのような考慮は不要(消えても構わない)ので、 --amendonly yes オプションを取り除く。

   redis:
     image: redis:latest
     ports:
       - 6379:6379
     volumes:
       - redis:/data
-    command: redis-server --appendonly yes
+    command: redis-server

対処その2

あるいは、ローカルで保持しているRedis内の情報が消えても構わないのであれば一旦 redis コンテナに割り当てている Volume を削除してあげるという方法もある。

ただし、一時的なものなので AOF が肥大化するにつれていずれ問題が再発する。(と思われる)

ボリュームを削除する手順は以下(コンテナを停止している状態で実施する)

<ボリュームの一覧確認>
docker volume ls

<ボリューム削除>
docker volume rm xxxxxx_redis

CloudFront + API Gateway + Lambdaの環境で生きた無属性のペアとそのこと

0

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

API Gateway のエラー時のレスポンスのメッセージは必ずしもその原因と直結するような内容となっていないことが多々あり、原因の特定に時間がかかりがちなのでメモっておく。

※随時追加予定

※「レスポンス」の内容は、curl -vでのリクエストに対するレスポンスを多少加工して掲載している。

401 ‘Unauthorized’

レスポンス

< HTTP/2 401
< date: Wed, 22 May 2019 08:43:33 GMT
< content-type: application/json
< content-length: 26
< x-amzn-requestid: xxxxxxxxxxxxxxxx
< x-amzn-errortype: UnauthorizedException
< x-amz-apigw-id: xxxxxxxxxxxxxxxxxxx

{"message":"Unauthorized"}

原因

  • カスタムオーソライザに必要な情報(特定のリクエストヘッダ等)がリクエストに含まれていない
    • 例えば Cookiesession_idを使ってAPIへのアクセスを認可するオーソライザを設定している場合、Cookieヘッダがリクエストに含まれていない場合に前述のリクエストが返ってくる

403 ‘Missing Authentication Token’

レスポンス

< HTTP/2 403
< date: Wed, 22 May 2019 08:31:54 GMT
< content-type: application/json
< content-length: 42
< x-amzn-requestid: xxxxxxxxxxxxxxxxx
< x-amzn-errortype: MissingAuthenticationTokenException
< x-amz-apigw-id: xxxxxxxxxxxxxx

{"message":"Missing Authentication Token"}

原因

  • API Gateway のエンドポイントのパスが間違っている
    • 例えば正しいエンドポイントのURLが以下であった場合
      • https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/stage/api/v1/{+proxy}
    • 以下のように間違っている(/apiが抜けている)と前述のようなレスポンスが返ってくる
      • https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/stage/v1/xxxxxx

403 Forbidden

(思い出したら書く)

403 ‘User is not authorized to access this resource with an explicit deny’

レスポンス

< HTTP/1.1 403 Forbidden
< Date: Wed, 22 May 2019 08:56:27 GMT
< Content-Type: application/json
< Content-Length: 82
< Connection: keep-alive
< x-amzn-RequestId: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
< x-amzn-ErrorType: AccessDeniedException
< x-amz-apigw-id: xxxxxxxxxxx

{"Message":"User is not authorized to access this resource with an explicit deny"}

原因

  • カスタムオーソライザから DENY のポリシー文書が返却された場合に返ってくるレスポンス
    • カスタムオーソライザの呼び出し自体には成功しているので、大体のケースにおいては正常動作と言える(正常でない場合はカスタムオーソライザをデバッグすることになる)

503 (HTML形式で) ‘ERROR: The request could not be satisfied’

レスポンス

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<HTML><HEAD><META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<TITLE>ERROR: The request could not be satisfied</TITLE>
</HEAD><BODY>
<H1>502 ERROR</H1>
<H2>The request could not be satisfied.</H2>
<HR noshade size="1px">
CloudFront attempted to establish a connection with the origin, but either the attempt failed or the origin closed the connection.
We can't connect to the server for this app or website at this time. There might be too much traffic or a configuration error. Try again later, or contact the app or website owner.
<BR clear="all">
If you provide content to customers through CloudFront, you can find steps to troubleshoot and help prevent this error by reviewing the CloudFront documentation.
<BR clear="all">
<HR noshade size="1px">
<PRE>
Generated by cloudfront (CloudFront)
Request ID: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
</PRE>
<ADDRESS>
</ADDRESS>
</BODY></HTML>

原因

  • オリジン(API Gateway)に対して http でアクセスしている
    • CloudFront のオリジンの設定(Origin Protocol Policy)が HTTP Only のようになっていないか確認する
    • API Gateway は HTTPS しかサポートしていないので、 HTTPS Only とするべき

最近人気な記事