ホーム ブログ ページ 31

プランナーがやる発注の仕方~敵キャラ編~

0

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

皆さんこんにちは
モツ太郎です。
そろそろ記事のネタが尽きてきました…
そんな中今回どんな記事を書こうかなーと思っていたのですが、せっかくなので最近から始めたプランナーが行う発注について書いていこうかなと思います。
※いつも通り個人のやり方なので悪しからず。

そもそも発注とは?

新しいマップやイベントを行う際には当然敵のキャラクターも場面にあったものを作らないといけないですよね。
そこで、こういう敵がほしいというのをデザイナーさんにお願いすることが、発注となります。

発注の要件

個人的に発注を行う際に必ず伝えているのが、以下の二つになります。
・モチーフは何か
・どういう形をしていてほしいか

この二つの中身についてちょっと解説していきます。
まず「モチーフは何か」というところですが、当然デザイナーさんにお願いするときには前述したとおり新しいマップやイベントの時にすることが多くなります。その時にそのマップやイベントのイメージを伝えます。
イベントが一番わかりやすいと思うのですが、例えば4月の「花見イベント」や8月だと「海イベント」など大枠のイメージを伝えます。

次に「どういう形にしてほしいか」というところですが、大雑把に言うと人型であるかそれとも動物の何かをモチーフにするのか、はたまた機械的な何かにするのかというのを指定します。例えばですが、4月の花見イベントの時の敵キャラを酔っぱらった人にしてほしいといったように書くための大まかな指針を立ててデザイナーさんにお願いします。

おわりに

今回はデザイナーさんへの発注について書き綴って見ましたが、いかがだったでしょうか?
今回の記事では最低限ここは抑えておくべきというところを書いていきました。
人によってはもっと細かく指定する場合もあります。ただ、自分の中では「餅は餅屋」という考えがあり、基本的には細かく指定しないでそのデザイナーの想像力に任せたいかなと考えています。(もちろん相手もプロであるという前提の上でですが…)
この記事を読んで、発注することがある人がいたら上記の最低の要件を抑えつつ自分なりにどこまで相手にお願いするかということを考えて発注をすればいいと思います。
今後こういう機会がある人の役に立てば幸いです。

bashをカスタマイズして快適なgit生活

0

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

gitを利用して開発する際に、branch名を入力するのが煩わしいというのが悩みでした。
何か良い方法は無いかと調べた結果、簡単に補完出来るようになることがわかりました。
また、合わせてプロンプト上にgitの情報を表示できるようになったので、そちらも紹介したいと思います。

参考にした記事:
MacのターミナルでGitのブランチ名を表示する
MacのGitで補完を効かせたりブランチ名を表示する設定

環境

CentOS release 6.5 (Final)

git-completion.bashの利用

git-completion.bashは、gitコマンドの補完スクリプトです。
こちらを利用することで、tab補完を利用することが出来るようになります。
gitのインストール時に既に準備されているので、そのまま利用することが出来ます。

$ find / -name "git-completion"
/usr/share/doc/git-1.7.1/contrib/completion/git-completion.bash

環境やgitの導入方法によって、パスは異なるようです。(自分はyumで導入しました)
また、個別に直接入手することも可能です。

git/contrib/completion

git-prompt.shの利用

git-prompt.shを利用することで、プロンプトでgitの情報を表示出来るようになります。
こちらも同様に、インストール時に準備されているものか、直接入手したものを利用します。

git/contrib/completion

利用方法はgit-prompt.shに記載されているので、今回は一部を紹介したいと思います。

$ find / -name "git-prompt"
/usr/local/git/contrib/completion/git-prompt.sh

bashrcへの記述

git-completion,git-promptを読み込むことで、補完やプロンプトへの情報表示を実現します。
それぞれを読み込んだ後に、__git_ps1を環境変数に加えれば、情報の表示が可能です。

今回は、色の変更も合わせて行いました。
また、__git_ps1には様々なオプションがあるので、用途に合わせてカスタマイズすることも可能です。こちらもgit-prompt.sh内に説明があります。

~/.bashrc

# git-completion,git-promptの読込
source /usr/share/doc/git-1.7.1/contrib/completion/git-completion.bash
source /usr/local/git/contrib/completion/git-prompt.sh

# __git_ps1のオプション指定

## unstaged fileがある時は*, staged fileがあるときは+を表示
GIT_PS1_SHOWDIRTYSTATE = 1
## stash fileがあるときは、$を表示
GIT_PS1_SHOWSTASHSTATE = 1
## untracked fileがある時は、%を表示
GIT_PS1_SHOWUNTRACKEDFILES = 1

# 環境変数PS1の変更
export PS1='\[\033[1;32m\]\u\[\033[00m\]:\[\033[1;34m\]\w\[\033[1;31m\]$(__git_ps1)\[\033[00m\] \$ '

実際の画面は以下のようになります。
デフォルトのまま使っていたので、だいぶわかりやすくなりました。
また、gitコマンドやbranch名もtab補完出来るようになっています。

利用例

おわりに

tab補完のおかげで、branch名の入力ミス等が減りだいぶ捗るようになりました。
色々調べていると、まだまだ作業に役立ちそうなカスタマイズ等を見かけるので、暇をみて試してみたいと思います。

イカしたSlackの使い方!知っていると役立つ3つの機能!

0

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

Slackを使っていてただチャットツールとして使っているだけでは気づきにくい、でも知っていると便利な使い方を3つご紹介します。

Slackって何?

“よくぞ聞いてくれました!Slack とは一連の業務の拠点となるデジタルワークスペースです。人々と組織、そしてツールをつなぐことで、作業効率を改善し、組織を活性化します。”※

Slackガイドより引用

ビジネス向けのチャットツールという認識の方が多いのではないかと思います。
使っていて楽しいこともあってビジネスに限らずコミュニケーションツールとしても有用です。
今回はSlackを使っていてただチャットツールとして使っているだけでは気づきにくい、でも知っていると便利な使い方を3つご紹介します。

自分と会話しよう!?

ダイレクトメッセージを打つところに自分の名前があるのは皆さんお気づきでしょうか?
そうです!なんとSlackでは自分と会話が出来るんです!
自分と会話出来たからなんやねん!って思った方、甘く見てはいけません!
To Doリストを作成できたり、リマインド設定することで簡易タスク管理も出来てしまいます!
工夫次第では他にも活用できるかもしれませんね。
enter image description here

Threadを活用しよう!

Threadとは話題ごとに発言を別途まとめることが出来る機能です。
チャットツールにありがちな発言が多くて前の話が流れてしまって反応しづらいなどといった状況を防ぐことができます。

実際どのようにして使うのか見ていきましょう。

Step1
enter image description here
使い方は簡単、発言のリアクションボタンの横の「Start a thread」を選択。

Step2
enter image description here
これだけでThreadが作成できました。
あとは「Reply…」にメッセージを打つだけ!

Step3
enter image description here
今回は一つ目に紹介した自分にダイレクトメッセージを送る機能と併用してタスクの詳細チェック表を作ってみました。

通知はどこに来るの?
enter image description here
ちなみに通知はAll Threadsにきます。
通知が来ると「All Threads」が「New Threads」に変わります。

大勢の人が参加しているチャンネルでもトピックごとに会話をすることが出来るのでまだ使ったことがない人はぜひ活用してみてください。

チャンネルを使い分けよう!

パブリックチャンネルであればJOINしなくても他のチャンネルを見ることができる。
これはSlackの大きな特徴の一つとも言えるでしょう。
すでにご存知の方も多いと思います。
既存チャンネルに入らなくても先に会話を見れることで入る前に自分もそのチャンネルに参加するかどうか考えることが出来ます。お試しに近いイメージですかね。(決して覗き見しているわけではありませんよ!)
この機能はコミュニケーションツールとしては非常にありがたく、またビジネスの観点からは同じ会社の仲間の仕事の状況の把握や、ノウハウの共有などに役立ちます。
どうしても見られたくない、社内の中でも一部の人間が知るべき情報などもあると思います。そんなときはプライベートチャンネル。JOINしていない人は見ることは出来ない設定もきちんと用意されています。
上手く情報を共有して、皆で活用していきたいですね。

まとめ

今回は
自分にダイレクトメッセージを送る機能の活用
Thread機能について
パブリックチャンネルとプライベートチャンネルを使い分ける

以上3点をご紹介しました。Slackを使い始めて数カ月と初心者であるため、基本的な内容になりましたがSlackは中々便利なツールだと感じています。まだまだ他にもbotやスラッシュコマンド、外部サービスとの連携など多くの機能があるようです。その辺り詳しいことはまた別の機会に書きたいと思います。
それでは皆さん良いSlackライフを!

※本記事の内容は個人の見解によるものを含みます。

夏の気だるさは暑さのせいじゃない?クーラー病とは

0

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

はじめに

最近都会独特の暑さにやられつつあるBe82Mです。
九州で生まれ育ち上京して今年初めての夏を過ごしていますが、都内は排熱やコンクリート等でこもったような暑さ。想像を超える辛さで会社でも家でもずっとクーラーがついた部屋で過ごしていますが、それでもけだるい。なんか頭痛い。いろいろ調べていたらクーラー病なるものを見つけたので、症状や原因、対策についてまとめてみようと思います。
 

症状と原因

人によってさまざまですが、頭痛やだるさ、肩こり、不眠症、食欲不振などが主な症状。これよりも症状がひどくなっていくと発熱、吐き気までしてくる人もいるようです。これは、自立神経の乱れが主たる原因だそうで、クーラーの温度を涼しく設定しすぎてしまうと体が冬と勘違いして寒さに対応しようとし、外に出ると暑いので今度は暑さに対応しようとします。これを繰り返すと自律神経が混乱してしまい、このような症状が出てきてしまうようです。
夏になるとなぜかこれらの症状が出てくる、という人はクーラー病の可能性が高いです。
 

対策

これらの症状を改善する方法をいくつか調べてみました。

外気と室内の温度差を5度以内に抑える

体感的に涼しさを感じるまで設定温度を下げてしまいがちですが、一番の原因はこれ。
温度差が激しすぎると先述したように自律神経を混乱させてしまう要因になります。

でも暑いから強くしてしまう…

クーラーの設定温度を下げず、他の方法をとりましょう。
※ どうしても暑い場合は熱中症の危険性も出てきてしまうため少しずつ温度を下げましょう。

  • 首元を冷やす
  • 扇風機を利用する
     寒いけど公共の場はどうにもできない…温度だけでなく、単に体を冷やさないようにするだけでも対策になるようです。
  • ブランケットや軽い羽織りものなどで体を冷やさないようにする
  • 温かいものを口にして体の内部から温める

外で汗をかいた後にクーラーの効きすぎた部屋に入ってしまうと夏風邪をひいてしまう原因にもなるので気をつけましょう。
 

食生活を改める

主に夏野菜といわれるものは体温を下げる効果があるため暑さを凌ぎやすくなるため、夏野菜を摂取するといいそうです。逆に、冬野菜は体温を上げる効果があるものが多いため、あまりよくないようです。
夏野菜:きゅうりなどのウリ科、トマトやピーマン、ナスなど
冬野菜:大根や白菜、ほうれん草など
また、夏の暑い日にキンキンに冷えた飲み物はとてもおいしいですが、体を急激に冷やすのもあまりよくないので、せめて寝る前は控えるようにしましょう。
 

最後に

夏の気だるさや頭痛は暑さが原因とばかり思っていましたが、暑さゆえの行動が原因だったようです。
今後は少しずつ生活を見直して夏を乗り切りたいと思います。

立ち絵を魅力的に描く②【体型による個性を考える】

0

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

はじめに

nocoです。夏らしからぬ涼しい気候が続いたかと思いきや最近また暑くなってきましたね。

前回に続き立ち絵を魅力的に描くためのポイントを共有したいと思います。
今回はキャラクターの体型による個性についてお話しします。

体型による個性とその特徴

一つの世界観を作り上げる中で、同じ年齢や体型のキャラクターばかりがいるわけではないと思います。
体型の異なる4人の男性のシルエットを並べてみます。
enter image description here

衣服を着用していないただの素体ですが、シルエットだけでもそれぞれの年齢がおおよそ伝わるのではないでしょうか。

シルエットを外しました。
enter image description here

子供は大人に対し線が細く体に対して頭が大きく、
太った人はお腹が突き出て少しだらしない立ち方に、
老人は腰が曲がり体のバランスを杖で支えたり…
年齢、体型によってさまざまな特徴があります。
日頃からプロポーションの比率を観察してみましょう。

背格好や肉の付き方に伴って立つ時の重心も異なってきます。
その点も意識して立ち絵に取り込むことでキャラクターの個性が強まります。

キャラクターデザインはシルエットだけで誰か判別できると良いと言われていますね。
髪型や服装はもちろんですが、体型やその特徴を意識して描き分けることでシルエットも自然と差別化されていきます。

体型の異なるキャラクターが並ぶことでお互いの個性を引き立て、そのコントラストによってそれぞれをより魅力的に見せてくれます。
ヒーローがいるからヒロインはより美しく、ヒロインがいるからヒーローはより格好良くなるものです。
イラストそのものの魅力も大切ですが、キャラクター同士のバランスも考えてデザインできると尚良いでしょう。

終わりに

趣味でイラストを描いているとどうしても自分の得意なタイプのキャラクターばかり描いてしまうのですが、業務で老若男女を描く中でそれぞれの個性の付け方について考えさせられました。
体型の描き分けだけでもキャラクターの職業や人柄を表現することができます。
前回お話したポーズや表情による表現とも合わせて、さらに魅力的なキャラクターを作っていきましょう。

立ち絵の描き方については今回でおしまいになります。
次回からまた別のテーマでイラスト制作に関する記事を書いていきたいと思います。

第3回「Lionがゲームプランナーになるきっかけを与えたゲームとは!?」

0

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

第3回「第3回!Lionがゲームプランナーになるきっかけを与えたゲームとは!?」を話します。 興味があれば、見ていってね!

みなさんお久しぶりです!Lionです。
前回の記事いかがだったでしょうか?
和製と海外製のゲームって何が違うのだろう?が少しでも伝わったのであれば、こちらとしても嬉しいです。

さて、本日は「第3回!Lionがゲームプランナーになるきっかけを与えたゲームとは!?」について話させていただきます。
今回は、自分語り的な内容となりますがお付き合いくださいませ(੭ु´・ω・`)੭ु⁾⁾

「自分の人生を変えたゲーム」

enter image description here
私の人生を大きく変えたゲームのタイトルは「ゼルダの伝説 時のオカリナ」です。
今じゃアクションRPGの金字塔と言われるほど人気の作品です。
※『ゼルダの伝説 時のオカリナ』は、1998年11月21日に任天堂より発売されたNINTENDO64用アクションアドベンチャーゲーム
ゼルダの伝説 時のオカリナの公式ページ
※Wii Uのバーチャルコンソールではありますが、公式ゲームPVがありましたので載せておきます。
Wii U ゼルダの伝説 時のオカリナ

少しゲームの概要を話しますと…
ゼルダの伝説シリーズの第5作目であり、今までの2D作品からシリーズ初の3D作品となったことが最大の特徴です。
3Dになったことにより、奥行きのある立体的な空間内をよりリアルな視点位置で見渡すことができるようになりました。もちろん、バトルや謎解きにも革新的な変化がもたらされました。

・3Dゲームに付きものだったカメラワークや操作性の問題点を軽減する「Z注目システム」
・Aボタンアクションでの特殊アクションの自動化

それ以外でも、カットインムービーも多数盛り込まれ、物語の臨場感を大幅に高めています。
シナリオ面でも、少年リンクと青年リンクに区分したことで、色々なリンクを見ることもでき、
猪の怪物だけだった魔王ガノンに「ガノンドロフ」という本名と人間の姿が与えられ、以後のゼルダ作品の要素・設定に引き継がれシリーズに与えた影響は大きかった模様。

完成度の高さと革新的なゲーム性は世界中から高く評価され、第3回CESA大賞や、第2回文化庁メディア芸術祭 デジタルアート部門 大賞など、日米欧で数多くの賞を受賞した。

いわゆる神ゲーである( ˘•ω•˘ )
参考資料:Wikipedia

「人生を変えるほどに面白かった理由とは?」

当時、このゲームを始めてプレイした時が、確か小学三年生くらいだったかなぁと思います。
あまりに面白すぎるor感動してしまい、父親に段ボールで剣や盾を作ってもらったほどです(笑

今でもニコニコ動画などの実況プレイ動画を見たり、友人と寝ずに全クリアできるかなど
徹夜で遊ぶほどです。

では、私を含めた世界各地のユーザーを魅了した「時のオカリナのパワーとは一体何か!?」を要素ごとに書いていきます。

「過去、現在、未来を行き来する圧巻のストーリー!」

enter image description here
ハイラルのコキリの森に住む少年リンクは、いたって普通の子供でした。
ただ、コキリの森に住む住人として、必要なパートナーである妖精を自分だけ持っておらず、仲間に馬鹿にされている日々。
そんなある日のこと、リンクのもとに妖精ナビィが訪れ、コキリ族の守護神である「デクの樹サマ」が危機に落ちていることを知らせにやってきます。
勇敢な少年リンクは、デクの樹サマの体内に入り、苦しませている原因の魔物を退治しました。
しかし、蝕まれたデクの樹サマの命までは戻らず、命尽き果てていくデクの樹サマから最後に森の精霊石を授けられ、ハイラルで「神に選ばれし姫」に出会うように言われます。

ここから、少年リンクの大冒険がスタートします。
「神に選ばれた姫」に出会いなさい。
子供のころこれにすごくワクワクとドキドキを隠せませんでした。

その後、ゼルダ姫に出会ったリンクは、彼女から魔盗賊ガノンドロフの野望と精霊石の秘密を聞かされます。
ガノンドロフは聖地に眠る「トライフォース」を得るため、その地への扉を開くために必要な「精霊石」を探していると…。
リンクはガノンドロフの野望を阻止するため、彼より先に精霊石を集めて聖地への扉を開こうとした。
そして、扉を開けることに成功した。しかし、扉の鍵である「マスターソード」を引き抜いた途端に、リンクの身体が封印されてしまうとう事態に!?
封印の末に大人に成長したリンクは封印が解かれ7年もの歳月を得て目を覚まします。
しかし、ハイラルは既に「力のトライフォース」を得たガノンドロフに支配されていた…
ガノンドロフを倒すためには「伝説の賢者」の力が必要であると聞かされたリンクは、賢者たちの協力を得るため、マスターソードを手にハイラルを救う勇者になり立ち向かうのであった!

これが大まかなストーリーとなります。
このストーリーは、この先のゼルダの伝説シリーズに多大な影響を及ぼしたと言っても過言ではありません。
少年時代、青年時代、とリンクのハイラル世界に対する視点も変わってくるのも面白さの1つです。
また、自分が子供の時にプレイしたのもあってか、少年リンクから青年リンクに変わって世界を救うという部分に「自分も大人になったら世界を救うんだ!」と心から思っていました(笑)
当時、自分と同じことを思った方も少なくはないんじゃないでしょうか?

「3Dだからこそできるゲームシステム!」

enter image description here
ゼルダシリーズおなじみの謎解きとアクションというゲームシステムは色あせない状態で、完全に3D用として制作されました。
「草や岩を持ち上げて任意の方向に投げる」、「ヒビの入った壁や石像を爆弾やハンマーで壊す」など過去作同様のアクションを3Dでもできるように開発!
また、このゲームにおいて一番大事なシステムである「Z注目システム」があります。

プレイヤーは対象となる物体を任意にロックオンすることができ、対象物に向かって移動したり、パチンコや弓矢などを確実に当てることを可能としたシステムです。
このシステムのおかげで、戦闘もしやすくなり、対象が見えなくならないようにカメラワークもセミオートで補正してくれるため、キャラクターの位置の把握や敵との間合いを取ることが分かり易い&容易となるのです。
このシステムによって、3Dアクションの難点とされていたカメラワークと操作性の問題が大きく改善され、以後多くの3Dアクションゲームに類似したシステムが搭載されることとなりました。

戦闘が苦手な人でも気軽に楽しめます。
自分も小学三年生でしたが、とても戦いやすかったのを覚えています。
また、フィールドを自由自在に走り回り、剣や弓矢で敵をバッサバッサとなぎ倒していくのもとても面白かったです。
当時、ここまで爽快感があるゲームはそうそうなかったと思います。

「全てのダンジョンの謎解きやギミックが面白い!」

enter image description here
ゼルダの伝説シリーズおなじみの謎解きやギミックですが、過去最高だとユーザーからの声も高いと思います。
ダンジョンで手に入れたアイテムや武器を使って謎を解き、そしてボスを倒すという過去作と同じですが、3Dになったことで、天井、床、壁と360度全体でギミックを仕込むことができます。
特に、皆様お馴染みである「水の神殿」の難易度は今になっても語り継がれていると思います。
神殿内の水位を変動させて、移動したり物を動かしたりするのが本当に難しかったです。
この神殿で手に入る「ロングフックショット」は、壁にフックをかけて、そこまで飛んで行ったり、遠くのアイテムを引き寄せたりできる神アイテムでした。
また武器にもできるというところも魅力の1つです。

ちなみに私は難しすぎて友人にクリア法を教えてもらった経験があります(笑)
今ではきちんとクリアできます。しかし、大人の方でも初見でプレイさせると確実に詰まると思われます。
それほどに難しいですが、プレイ応えがある最高のダンジョンでもあります。

「ここまで話してみて…」

どうでしょうか?少しでも「ゼルダの伝説 時のオカリナ」の魅力は伝わったでしょうか?
まだまだ、語ろうと思えば語れますが、際限がないのでこれくらいにしておきます。

そして、私がゲームプランナーになると決めた理由は、「自分自身で、ゼルダの伝説 時のオカリナのような面白い世界を作ってみたい!そして、自分が作ったゲームで自分が味わったこの感動を世界中の人たちに伝えたい!」と子供の頃に思ったからです。
実際、大人になってもこの思いは色褪せることはありませんでした。
現在はプランナーとして働いていますが、もっと上を目指して精進していく所存です!

今回の記事、いかがだったでしょうか?
「ゼルダの伝説 時のオカリナ」の魅力や筆者ってこんな人なんだなぁというのが少しでも伝わってくれたら嬉しいです。

次回は来月開催されるTGS(東京ゲームショー)についてお話します。
「第4回 TGSで日本ゲーム大賞に選ばれた作品たち!」についてお話します(੭ु´・ω・`)੭ु⁾⁾
※筆者は極度のファンです
では、みなさん!夏ももう終わりですが熱中症にはお気を付けください( ˘ω˘ )

N+1問題

0

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

 railsで開発しているとN+1 Queryというエラーに遭遇しました。このN+1 Queryについて調べてみると、別名N+1問題と呼ばれており、サービスのパフォーマンスを落とす問題の一つであることがわかりました。今回は、この問題を解決する為に調べたN+1問題の原因と解決方法について記事にしました。

N+1問題とは

 N+1問題を簡単に説明すると、クエリがデータ数よりも多くなります。データ数よりも多くのクエリが発行されることで、サービスのパフォーマンスを低下させます。少量のデータの場合は、問題を感じることは少ないと思いますが、実際にサービス運用では膨大なデータ量を扱うことがほとんどなので放っておくことはできません。

問題発生例

 実際に、どのような場合にこの問題が発生するのか例題を使って説明します。
 以下のようなハードウェアモデルとソフトウェアモデルが存在するとします。ハードウェア1つに対して複数のソフトウェアが結びつく関係です。

class Hardware < ActiveRecode::Base
  has_many :softwares
end

class Software < ActiveRecode::Base
  belongs_to :hardware
end

 この2つのモデルを使用したコードが以下にありますが、このコードがN+1問題を発生させてしまいます。このコードの処理は次のことを行っています。
1. ジャンルがアクションのソフトウェアのデータを取得
2. 取得したソフトウェアのデータ1つ1つに対してハードウェアのデータを取得

softwares = Software.where(genre: "action")
softwares.each do |software|
  software.hardware
end

1.の処理では、ジャンルがアクションのソフトウェアをまとめて取得している為、クエリの数は1つになります。
2.の処理では、取得したソフトウェアの数だけバードウェアを1つずつ取得しているので、クエリの数は取得したソフトウェアの数になります。
つまりこの2つの処理では、取得したソフトウェアの数をNと置き換えると、N+1のクエリを発行していることになります。

解決方法

 N+1問題の原因は、データ一覧取得後、取得していないデータを参照していることです。(上の例だと、ソフトウェアを取得している時にハードウェアを取得せずに、後から取得している。)この原因を解消する為に、データ一覧取得時にincludesで関連するデータをまとめて取得する方法があります。下のコードは、先ほどの例題にincludesを使用して改善したものです。

softwares = Software.where(genre: "action").includes(:hardware)

 このようにincludesを使用することで後からデータを参照した場合でもクエリが発生することはありません。includesを使用した場合のクエリ数は、ジャンルがアクションのソフトウェア取得の1回と取得したソフトウェアに結び付くハードウェアの取得の1回の計2回となります。

まとめ

 RailsエラーでN+1という文字を発見したら、クエリが余分に発行されていないか確認してincludesで対応しましょう。

CLIPSTUDIOvsPhotoshopここはこうなる仕様の違い②

0

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

イラスト作成ツールのCLIPSTUDIOPAINTとPhotoshopの各ツールやコマンドの仕様を比較してみました。

 こんにちはtokinです。
 前回の続きでイラスト制作ソフト「CLIPSTUDIOPAINTPRO」と「PhotoshopCC」の仕様の比較を行いました。大きな違いは勿論ありますが同じツールで異なる仕様も多数存在します。この記事は私がイラスト制作を行っている最中に気づいた微かな違いをご紹介していきます。

前回の記事はこちらCLIPSTUDIOvsPhotoshopここはこうなる仕様の違い①

今回も引き続き比較対象は

  • セルシス CLIP STUDIO PAINT PRO(クリスタ)
  • Adobe PhotoshopCC2017(フォトショップ)

この上記二点のソフトです。
 ちなみに文の中にクリスタEXが登場しますがこちらはCLIP STUDIO PAINT PROをバージョンアップさせたCLIP STUDIO PAINT EX というソフトです。こちらはPROよりも更に漫画作成に特化しているタイプのものでPROの仕様に加え様々なシステムが加わったソフトとなっております。
それでは続いていってみましょう。

キャンバスに右クリック

 クリスタでは右クリックを押している間だけスポイトツールが、フォトショップの右クリックではブラシサイズ設定のタブが開かれます。右クリックの設定については今のところどちらのソフトもカスタマイズは不可能な様子です(クリスタは右クリックの無効化なら設定可能です)。ちなみにフォトショップで一時的にスポイトツールを使用したいときはブラシやバケツツールを選択した状態でAltキーを押すと、クリスタのように押している間だけスポイトツールに代わります。

レイヤーを別キャンバスに貼り付け

 クリスタでは別キャンバスにレイヤーを移すときはコピー&ペーストを行います。逆にフォトショップでは、キャンバスを二面開いた状態で移動させたいレイヤーをそのままドラッグ&ドロップすることで内容を移すことができます。勿論コピー&ペーストで移動することもできるのですがその際はまずコピーしたいデータを選択範囲に入れる必要があります。コマンドだとCtrl+A(全面選択)→Ctrl+C(コピー)→Ctrl+V(ペースト)でできます。ちなみにドラッグやペーストしてデータを移動させる際、shiftキーを押しながらペーストすると移動する前のキャンバスと同じ位置にデータを配置できます。

直線コマンド

 クリスタもフォトショップもShiftキーを押しながら直線を引くことができます。直線の始まりに点を打ち、そこから引きたい線の終点をShiftキーを押しながら打つと直線が描けます。クリスタとフォトショップの違いはクリスタの場合は終点を打つ際にどんな線が引けるかガイドが表示されるところ。これによって描く前の線の角度、長さや太さをよりイメージしやすくしてくれます。
 勿論コマンドではなく直線ツールそのものも搭載されています。クリスタでは図形のサブツール内に、フォトショップではシェイプツールの設定を「シェイプ」→「ピクセル」に変更すれば通常レイヤーに直線が引けます。

色調補正

 クリスタにも色調補正は備わっていますがフォトショップは圧倒的です。写真編集ならではの「自然な彩度」、「露出量」や「カラーフィルタ」など画像データをさらに細かく補正・調整できるシステムが備わっています。
 驚いたのは色調補正レイヤーの存在でした。作画したレイヤー本体の色調は変えることなく、全体の色調の調整のみを色調補正レイヤーで行えるのです。試しに色味を変えてみたり様々な色調補正を重ねたり…これらが全てレイヤーで管理できてしまうのです。補正レイヤーは複製することもクリッピングマスクをかけることもできるので特定のレイヤーのみの調整も可能です。結合すれば補正内容はそのまま適用されます。

フィルタ・アクション機能

 デフォルト状態のクリスタPROにもさほど多くはないのですがぼかしやシャープなどのフィルタが存在しています。更にバージョンアップしたクリスタEXでは有志のユーザーが作り出したフィルタプラグインを導入することができます。一方写真加工に特化したフォトショップに実装されているフィルタはとても多いです。フィルタ一つで一味違った雰囲気の画像にしてしまえるので単体で使ったり重ね合わせたりして使ってみたいもの。デフォルトで備わっているのでCDジャケットのようなスタイリッシュな画像まで簡単に作り出すことが出来ます。
 また、クリスタPRO/EX・フォトショップの両方に備わっているアクション機能もオススメです(クリスタではオートアクション機能と呼ばれています)。これを使いこなせばフィルタを使ったような画像加工も一発でできるのです。というのもこちらはソフトでの一連の作業を記憶し、必要なときに再びそれを利用できるようにする機能なのです。幾つかの工程を記録させればワンクリックでそれらを自動で実行してくれるので短い時間でクオリティの高い加工が行えます。クリスタもフォトショップも、DLできるフィルタプラグインやアクション機能がネット上に多数存在しておりますのでぜひ自分好みの機能を探してみてください。

レイヤー効果

 クリスタのレイヤー効果のうち注目したいのがトーン効果です。レイヤーで描いた部分をそのままトーンに変更できてしまうのでこれ一つで漫画作成で大変だったトーン作業がとても楽になります。色の濃度はそのままトーンの濃さに反映されるので濃さに合わせてドットの大きさも自動で調整してくれます。効果を外せばもとの状態に戻すこともできドット数やドットの形も変更できます。またバージョンアップされたクリスタEXのみの実装なのですが線画抽出のレイヤー効果もそのまま漫画背景にできそうな線を抽出してくれるので話題となっています。enter image description here
 フォトショップのレイヤー効果は「グラデーション」から「ドロップシャドウ」、「べベルとエンボス」など平面を加工するものから影をつけ立体にみせる効果などバラエティに富んでいます。その富み具合たるや直接描き込まずとも効果のみでも数段クオリティをあげたものことが作れるほど。レイヤー効果のすばらしいところは拡大縮小しても効果自体は荒れないところ。UIボタンなどのサイズを変更する可能性の高いデータには効果での演出もオススメです。
enter image description here

素材

 どちらのソフトにもネット上に豊富にありますが、クリスタは漫画作成向けソフトということもあってペンツール・トーン素材が特に多く、写真加工からはじまり色彩に強いフォトショップはブラシ素材が豊富な印象を受けます。クリスタはCLIP STUDIO内のサービスから素材を検索・配布・DLすることができ、DLしたものは全てクリスタペイント内の素材欄に表示されますので後はペンツール内にドラッグして読み込みが完了します。フォトショップは個々のサイトからDLしたabr.データをフォトショップのブラシパレットから読み込み取り入れます。また、サイトの他にもAdobeのもつ「Adobe Stock」というサービスもあり、こちらからも素材の検索やDLが可能です。色を塗ったり、線を描いたりするペン・ブラシツールの中でも煙や泡、花やサラダなどスタンプに近いバラエティ豊かな素材も登場しているのでぜひ探してみてください。

最後に

 いかがでしたでしょうか。最後に前回今回と書き連ねました内容を表にまとめました。
enter image description here
 個人の趣味、業務範囲で使ってみて感じたレベルなので掘り出せばもっとそれぞれの似た点便利な点たくさんあると思います。今回はどちらの設定もデフォルト状態での比較でしたが中にはコマンド設定で差が気にならなくなったりカスタマイズでより自分に合う使い方に仕様変更できたりします。ソフトを移行したときに混乱しない為にも是非自分好みのソフトにカスタマイズしてみてくださいね。それでは。

【Unity】オブジェクトプーリングでオブジェクト節約術

0

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

オブジェクトを生成・破棄する際にかかるコストを、簡単なオブジェクトプーリングの仕組みを実装して軽減してみます。

まえがき

こんにちは。17新卒エンジニアのinoooooocchiです。
今まではUnity入門的なネットに溢れかえってるような記事をここ(DoRuby)に投稿していましたが、そろそろ多少は実用的な記事を書かなければいけないと気を引き締め、今回はオブジェクトの生成・破棄と”オブジェクトプーリング“について書いていきます。これを皮切りに、これから少しずつ技術的な話を書いていきたいと思います。(願望)

Unityにおけるオブジェクトの生成・破棄

Unityでは、オブジェクトを新たに生成したり、使い終わったオブジェクトを破棄したりする際、それぞれ Object.Instantiate や Object.Destroy を用いて行います。
一般的に、オブジェクトをスクリプトで生成する場合、UnityのPrefab機能を用いてオブジェクトのPrefabを作っておき、それをスクリプトに渡して Instantiate することで生成します。

この Instantiate・Destroy はゲームを動かす上で何百、何千、何万回も呼ばれるであろう関数ですが、処理が重いことで知られています。
(→ Debug.Log() や Instantiate() などの速度を計測してみる (外部サイト))

スマートフォン向けにゲームを作ってみよう!と思い立ち、いざUnityを立ち上げてゲームを作ってみると、意外とこの Instantiate・Destroy の負担は目に見えて現れます。
弊社で運営しているようなRPGベースのゲームでは頻繁に Instantiate と Destroy を繰り返すことは少ないですが、アクションゲームやシューティングゲームなどでは必然的に敵や弾などのオブジェクトの数が多くなるため、Instantiate や Destroy を行う回数は相当なものです。
特に筆者の好きな弾幕系シューティングゲームでは物によっては合計数百〜千程度の弾オブジェクトが生成されては消えを繰り返すため、スペック次第では処理落ちが発生し、プレイヤーの操作感を損なうことに繋がってしまいます。
(コルーチンならフレームを跨いで処理が行えるため、処理落ちを多少は回避できそうですが、今回は置いておきます)

オブジェクトプーリングしよう

そこで登場するのが、 “オブジェクトプーリング” という手法です。
オブジェクトプーリングとは、一定数のオブジェクトを溜めておく(poolしておく)ための貯蔵庫のような物を作り、オブジェクトが必要になったら取り出し、使い終わったら戻すことで、なるべく少ない数のオブジェクトを使い回してオブジェクト数及び生成・破棄のコストを抑えようというものです。
リサイクルみたいなもんです。

今回は、受け取ったPrefabを元にGameObjectを最初に一定数まとめてInstantiateで生成し、それらを使い回すといった簡単なオブジェクトプーリングを実装してみます。
また、最初に生成した数では足りなくなった時は、逐次生成してプールしているリストに加えます。

実装

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class ObjectPool : MonoBehaviour {
    private List<GameObject> _poolObjList;
    private GameObject _poolObj;

    // オブジェクトプールを作成
    public void CreatePool(GameObject obj, int maxCount){
        _poolObj = obj;
        _poolObjList = new List<GameObject>();
        for (int i = 0; i < maxCount; i++) {
            var newObj = CreateNewObject();
            newObj.SetActive(false);
            _poolObjList.Add(newObj);
        }
    }

    public GameObject GetObject(){
        // 使用中でないものを探して返す
        foreach (var obj in _poolObjList) {
            if (obj.activeSelf == false) {
                obj.SetActive(true);
                return obj;
            }
        }

        // 全て使用中だったら新しく作って返す
        var newObj = CreateNewObject();
        newObj.SetActive(true);
        _poolObjList.Add(newObj);

        return newObj;
    }

    private GameObject CreateNewObject(){
        var newObj = Instantiate(_poolObj);
        newObj.name = _poolObj.name + (_poolObjList.Count + 1);

        return newObj;
    }

}

オブジェクトを管理するオブジェクト(例: 大砲の弾を管理する大砲オブジェクト)でObjectPoolクラスのCreatePool関数にオブジェクトのPrefabと最初にまとめて生成する個数を渡すことで、オブジェクトプールがリストとして作成されます。
生成されたオブジェクトはSetActive(false)で非アクティブ状態にしておきます。
今回の場合、オブジェクトが使用中かどうかをアクティブかどうかで判断しているためです。
管理オブジェクトからGetObject関数が呼ばれた場合、リストの中から使用されていないものを探して返します。
リスト内のオブジェクトが全て使われていた場合、新たに1個だけオブジェクトを生成し、それを返します。

それでは、実際にプールするオブジェクトと管理オブジェクトを作り、動作を確認してみます。

オブジェクト管理クラス

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

[RequireComponent(typeof(ObjectPool))]
public class BulletCreator : MonoBehaviour {

    [SerializeField] private GameObject _bulletPrefab;
    private const int BULLET_MAX = 50;
    private int _state = 0;
    private float _originDirection = 0.0f;
    private bool _isReverse = false;
    private ObjectPool _pool;

    private void Awake () {
        _pool = GetComponent<ObjectPool>();
        _pool.CreatePool(_bulletPrefab, BULLET_MAX);
    }

    // Update is called once per frame
    void Update () {
        if (_state % 30 == 0) {
            var way = 16; // 16方向
            for (int i = 0; i < way; i++) {
                var bullet = _pool.GetObject();
                if (bullet != null) {
                    bullet.GetComponent<Bullet>().Initialize(_originDirection + i * (360.0f / way), _isReverse);
                }
            }
            // 発射のたびに10度ずつ回転し、弾の回転方向を反転
            _originDirection = (_originDirection + 10) % 360;
            _isReverse = !_isReverse;
        }
        _state++;
    }
}

オブジェクト

using UnityEngine;
using System.Collections;

public class Bullet : MonoBehaviour {

    private float _speed = 4.0f;
    private float _direction;
    private bool _isReverse;
    private Rigidbody2D _rigidBody;

    public void Initialize(float direction, bool isReverse){
        _direction = direction;
        _isReverse = isReverse;
        _rigidBody = GetComponent<Rigidbody2D>();

        if (_isReverse) {
            GetComponent<SpriteRenderer>().color = new Color(1.0f, 0.3f, 0.3f); // 赤っぽい色
        } else {
            GetComponent<SpriteRenderer>().color = Color.white;
        }

        SetDirection();
    }

    public void Update(){
        if (_isReverse) {
            _direction = (_direction - 1) % 360;
        } else {
            _direction = (_direction + 1) % 360;
        }
        SetDirection();
    }

    public void OnBecameInvisible(){
        gameObject.SetActive(false);
        ResetPosition();
    }

    private void SetDirection(){
        Vector2 v;
        v.x = Mathf.Cos(Mathf.Deg2Rad * _direction) * _speed;
        v.y = Mathf.Sin(Mathf.Deg2Rad * _direction) * _speed;

        _rigidBody.velocity = v;
    }

    private void ResetPosition(){
        _rigidBody.velocity = Vector2.zero;
        transform.localPosition = Vector3.zero;
    }
}

動作確認

enter image description here

画像サイズ上限の都合上短いですが、上記ソースコードでは最初に生成するオブジェクトプールを50個に設定しており、数が足らなくなったことで追加で2個生成されています。
管理オブジェクトは、30フレームに一度、16個の弾を発射するようになっています。
画像は管理オブジェクトが弾を発射する様子を5秒程度切り取ったものなので、本来なら160個のオブジェクトが必要なところ、オブジェクトプーリングを導入することで52個のオブジェクトで済むようになっています。
これが1分2分と続いていくとしても、弾が定期的かつ規則的に発射される限り、52個前後のオブジェクトで本来なら数千個必要なオブジェクトを賄えるということになります。
すなわち、数千回分のInstantiate・Destroyを削減出来たわけです。

最後に

今回の実装は、オブジェクトプーリングの形だけを作りましたが、実際にゲームで運用するためには幾つか改善すべき点があります。

  1. ObjectPoolがオブジェクト管理オブジェクトと1:1の関係になっているため、同じオブジェクトを生成する管理オブジェクトが複数ある場合、ObjectPoolを共有できず無駄が多い
  2. 最初に生成しておくオブジェクトの数を大きくし過ぎると、最初だけ処理が重くなる  →描画コストなどとの兼ね合いですが、基本的には逐次生成が良さそうです

実際はゲームの種類やシステム、オブジェクトの特徴などによって、どのような形式のオブジェクトプールを使うのか考える必要がありますね。
また、オブジェクトプーリングの考え方はUIにも応用出来ます。
例えば何らかのオブジェクトを一覧表示してスクロールさせるUIでは、最初に幾つかオブジェクトを生成しておき、スクロールに応じて中身を入れ替えることで擬似的にたくさんのオブジェクトが並んでいることを表現できそうです。

以上、「オブジェクトプーリングでオブジェクト節約術」でした。

誰が買うゲーム?~顧客について考える~

0

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

自分の作りたいゲームを作る!という人は「関係無い」と思うかもしれないが、 整理しておくと困ったときに役立つのでそういう人も一度しておきたいのが顧客についての研究だ。勿論、売り上げを出すには必須となる。

どんな顧客がターゲット?

 売る相手となる顧客から考えるか、作りたいゲームがあってそこから考えるかはそれぞれの製作状況によるが、とにかく「どんな人がターゲットなのか」はなんとなくでも想像はついているはず。
 もし、顧客について考えずに単に「RPGを作る!」とだけ思って作り始めてしまうと「あれもいれたい、これもいれたい・・・」と右往左往するのは目に見えている。RPGと一口に言っても男性女性、子供、成人など色々な層が存在しているのだから、しっかり考えるのは重要だ。
 今回は「小学生の男の子向け冒険活劇RPG」を例にして考えてみる。総務省の統計によれば平成27年(2015年)4月1日時点で日本の小学生(6~11歳)の男の子の人口は330万人となっている。こういった役所から調べられる情報はきちんと収集しておきたい。

参考:http://www.stat.go.jp/data/jinsui/topics/pdf/topics89.pdf(我が国のこどもの数 – 総務省統計局)

ターゲットとなる顧客について考える

 年齢と男女が決まったので、本格的にターゲットとなる顧客について考えていく。その顧客がどんな性質かを見極めるのには様々な観点、手法があるが、今回は「整理したい項目を並べてそれらについて考える」とする。なお、ネットアンケートをとるなどの手法は有効だが、費用がかかるので今回は割愛させていただく。
 今回の例では小学生の男の子が対象なので、例えば「難しい表現や漢字は分からない」といった事を最終的に整理した結果として得ることとなる。以下のように簡単にExcelでまとめてみた。内容や項目は適当なので、詳しくは「マーケティング 基本」などで調べてみるといいだろう。
enter image description here

顧客からゲームを考える

 さて、小学生の男の子の特徴をまとめたところで、どんなゲームにすれば良いかを考えてみる。ここまで特に触れていなかったが、ゲームの形態としては「携帯ゲーム機での買い切りゲーム」としている。
 所得の少なさや相場から考えて価格は5000円程度に抑えることは必須そうだ。趣向を「冒険活劇」としているが、小学生の男の子でも冒険活劇が好きではない子もいるだろう。こういった「携帯ゲーム機を持っている」「冒険活劇が好き」といった項目によって実際の顧客対象となる人口は減っていくこととなる。両方を満たすのは330万人の5割と想定すると165万人となる。(実際には何かしらの手法でどれぐらいの割合となるかは調べる必要がある)
 次に、知識や経験の少なさから、「難しい表現や漢字は使わない」「チュートリアルは丁寧に」「カッコいい演出やストーリーにする」などといった事が決まってくる。プレイ環境は複数人プレイが容易なので、ゲーム構成によっては通信対戦などを取り入れることも出来る。こういった「どんなゲームにするか」の方針も顧客の特徴を掴むことで立てられる。

まとめ

 いかがだっただろうか。きちんと顧客について想定をしていないと、「思ったより全然売れなかった」なんていうことが起こってしまう。仕様に迷ったとき、「どんな顧客か」に立ち返れば多くのことはどうするのが最善か分かるので、しっかりと整理して記録しておきたい。それでは、良い製作ライフを。

Rails時短の巻 -seed編-

0

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

いちいち本番で1からデータを作る必要なんてありません。既存seedに時間がかかるならまず使うことをやめましょう。DBのダンプと差分管理と並列処理で本番へのデータ投入を爆速にするのです。

はじめに

みなさま、お久しぶりです。新卒エンジニアのくろすです。
素手人間になって久しいですが、たまーにオレンジ折りたくなる衝動に襲われます。

今年こそは一番綺麗な空を飛べると信じて初日参加します。
推しが出るので2日目も参加します。
3日目はチケットがありません。チケットが、ありません。

データ投入用高速seed

localでダンプしたデータを差分管理しつつ直接本番に流し込める高速seedをyaml_dbparallelを用いて作成したのでその紹介です。

差分を管理してデータ更新を高速化する、seed_fu:expressのご紹介
を参考に差分管理を実装し、
RailsのDBの初期データ(rake db:seed用)をyamlで美しく管理する方法
をヒントにデータをyaml_dbで管理することを思いつきました。
実際にはテーブル毎のダンプが出来さえすればいいのでmysqldump等でも問題ないと思いますが、sql管理に比べデータの視認性が良い、手修正がしやすいという2点からyaml_dbを使っています。
まだブロックスタイルでしかダンプできていないのであまり使い勝手はよくありませんが、フロースタイルでダンプできるようにできれば手修正はより容易になると思います。

背景

弊社ではExcelで作成していることが多いゲームのマスターデータですが、これはそのまま使うものもあればdbに投入する際にプログラム側で使いやすくするため加工するものもあります。
このデータを加工する作業というものは、ゲーム運営が長くなりデータが増えていくに従い時間を食う作業になっていきます。
また、このデータの作成に他のモデルを参照しながらデータを作るような書き方をしていると各masterに依存関係が生まれ並列処理できないため、既存seedプログラムにがっつり手を入れなければ高速化のしようがない…なんてこともあります。

新卒としてアピリッツに入社しrubyを書き慣れるにつれ、DRYに精神を乗っ取られ始めました……
スゥパァドゥルァァァァイ
今回の場合Don’t Repeat Ourselfでいきましょう。
なにもデータ作成までDBサーバーで行う必要なんてどこにもありません。
データ作成はlocalのみで行い本番はそのデータだけを流し込んではいけない決まりなんてありません。
すっぱりきっぱりと既存のseedを捨て去る覚悟をしましょう。

投入用データ作成

とりあえずdb:seedの後にダンプを行うrake taskを作ります。

namespace :gundum do
  desc "gundum:seed"
  task :seed => :environment do
    Rake::Task["db:seed"].invoke
    Rake::Task["db:dump_masters"].invoke
  end
end

できました!!!ガンドゥムSEEDです!!!!
この高速seedを作ってる時にseedにinvokeを生やそうと[ seed invoke ]でググってしまったあの日からこれをやろうと心に決めていました。

さて、ここで呼んでいるdb:dump_mastersですが中身はyaml_dbの拡張である以下のプログラムをロードするようになっています。

module YamlDb
  module SerializationHelper
    class Base
      # @Override
      def dump_to_dir(dirname, *dump_table_names)
        Dir.mkdir(dirname) if Dir[dirname].empty?
        tables = if dump_table_names.present?
                   dump_table_names
                 else
                   @dumper.tables
                 end
        tables.each do |table|
          File.open("#{dirname}/#{table}.#{@extension}", "w") do |io|
            @dumper.before_table(io, table)
            @dumper.dump_table io, table
            @dumper.after_table(io, table)
          end
        end
      end
    end
  end
  module RakeTasks
    MASTER_TABLES = ["fizz_master", "buzz_master"].freeze
    def self.dump_masters
      SerializationHelper::Base.new(helper).dump_to_dir("#{Rails.root}/db/masters/yml", *MASTER_TABLES)
    end
  end
end

#--------------------------
YamlDb::RakeTasks.dump_masters

テーブルを指定したダンプが行えるよう拡張して、マスターのみダンプする新しいRakeTaskを追加しています。

データ投入と差分管理

一番最初に紹介した
差分を管理してデータ更新を高速化する、seed_fu:expressのご紹介
を参考に差分管理しつつデータ投入を行えるようyaml_dbを拡張していきます。

require 'parallel'
module YamlDb
  module SerializationHelper
    class Base
      def load_masters_from_dir(dirname, truncate = true)
        updated_masters = []
        Dir.entries(dirname).each do |filename|
          next if filename =~ /^[.]/
          master = MasterVersion.find_or_create_by(master: File.basename(filename, ".yml"))
          checksum = Digest::MD5.file("#{dirname}/#{filename}").to_s
          if master.chesksum == checksum
            p "✔ "
          else
            p "✖"
            updated_masters << master
          end
        end

        if updated_masters.present?
          Parallel.each(updated_masters) do |master|
            filename = "#{master.name}.yml"
            begin
              ActiveRecord::Base.transaction do
                @loader.load(File.new("#{dirname}"/#{filename}", "r"), truncate)
                master.save!
              end
            rescue => e
              p "✖"
              raise e
            end
            p "✔ "
          end
        end
      end 
    end
  end

  module RakeTasks
    def self.load_masters
      SerializationHelper::Base.new(helper).load_masters_from_dir(dump_dir("/masters/yml"))
    end
  end
end

#--------------------------
YamlDb::RakeTasks.load_masters

テーブル情報の管理にMD5を使っています。
MD5の衝突耐性は容易に突破され、ハッシュ値から入力値を求めることができるようになっていますが、テーブル管理に使うくらいなら問題ないでしょう。
手元のファイルからハッシュ値を計算しているため、管理対象のテーブルがタイムスタンプを持っている場合は削除しておきましょう。
seedで更新されるデータのの更新時間なんてそんな使うもんじゃありません。

後はこれを標準のrakeから使えるようにrake taskを作ります。

namespace :gundum do
  desc "gundum:seed_destiny"
  task :seed_destiny => :environment do
    load "gundum/seed_destiny.rb"
  end
end

U・N・M・E・I 感じちゃいますね

まとめ

今までローカル反映にすら15分ほどかかっていましたが、一度誰かが取り込み済みのデータなら全データ入れ替えても1分30秒で済むようになりました。
既存seedが遅い原因は富豪プログラミングによるString.newが走りまくることだったので、それも解決。
結果としてデータ入れ替えながらの作業が捗るようになりました。
gundum:seed_destiny最高ですね。

BOM付きUTF-8で文字化けしないCSV出力

0

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

Railsで出力したCSVファイルがExcelで文字化けを起こしてしまった場合にBOMを付けて対応する方法をまとめました。

はじめに

 またまた1か月ぶりの投稿となります、むらさきです。探していたSwitchも無事購入できて同期と日々インクを掛け合ってます。
さて、データベースに入っているデータをCSVファイルにUTF-8で出力してExcelで見られるようにしたい!と思い実装してみたところ盛大に文字化けしていた…なんて経験誰もが1度は体験したことがあると思います。
文字化けした文字を解読して読んでやる!…と挑戦した経験も1度ではないと思います。
 ですが、僕に文字化けを解読する能力は備わってなかったようなので、今回はUTF-8の文字コードにBOMを付けて、出力する段階で文字化けしないようにする方法をまとめようと思います。

目次

  1. BOMとは?
  2. CSVをUTF-8で出力する
  3. 終わりに

1.BOMとは?

 BOMとは、バイト・オーダー・マーク(Byte order mark)の略で、Unicodeの符号化形式で符号化したテキストの先頭に着ける数バイトのデータのことです。
 ExcelはCSVファイルを開くときデフォルトでShift-JISで開きに行ってしまいUTF-8では確実に文字化けしてしまいます。ですがUTF-8で出力する際にこのBOMを付けることでExcelにUTF-8で書かれていると認識させることができます。

2.CSVを出力する

 今回は適当な日本語をBOM付きUTF-8とBOM無しUTF-8でCSV出力してExcelで文字化けしているかどうかを確認するところまでやりたいと思います。
まずはBOM無しCSVを出力します。

require 'csv'
def csv_export
  File.open(path + 'test_doruby.csv', "w:UTF-8") do |f|
    csv_data = CSV.generate do |csv|
      csv << csv_text
    end
    f.write(csv_data)
  end
end

def csv_text
  [
    "これは",
    "UTF-8の",
    "BOMなしの",
    "testです"
  ]

 このメソッドを実行すれば、pathに指定した場所に”test_doruby.csv”がUTF-8で作成されるのでそれをExcelで開いてみます。
enter image description here

 案の定日本語の部分が無駄に画数の多い漢字の羅列に置き換わりました。先ほど述べたようにUTF-8をShift-JISで開きに行っているからです。

では次に先ほどのコードを書き換えてBOMを付けます。

require 'csv'
def csv_export
  File.open(path + 'test_doruby_bom.csv', "w:UTF-8") do |f|
    bom = "\uFEFF"
    csv_data = CSV.generate(bom) do |csv|
      csv << csv_text
    end
    f.write(csv_data)
  end
end

def csv_text
  [
    "これは",
    "UTF-8の",
    "BOMありの",
    "testです"
  ]

bom = "\uFEFF"
と宣言し、CSV.generateの引数に渡すことでBOMを付けることができます。

BOMを付けたファイルをExcelで開いてみると
enter image description here
文字化けなく表示することができました。

3.おわりに

 いかがだったでしょうか、今回紹介した方法以外にもWindowsのメモ帳で表示してから保存し直すなど、BOMを付ける方法はありますが何度も出力する必要がある場合はこちらの方が手間が1度でいいので良いと思います。自分の状況にあった方法を選択してください。

姿勢の悪さからも目疲れはくる

0

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

こんにちは、楽しいことはあっという間に終わってしまうなすちゃです。

はじめに

仕事や趣味などディスプレイを眺める環境が日常化している現代ですが、
長時間パソコン作業やスマホ操作をしているとどうしても目への負担が出てきます。

光による刺激や目の乾燥などありますが、使用中の環境も関係しています。
ブルーライトをカットする眼鏡や保湿の高い目薬など様々なアイテムがありますが
今回は自身で改善できることを調べてみました。

目への負担

なぜ姿勢が悪いと目へ影響がでるのか。
長時間不安定な姿勢でいると両目の視点のバランスが崩れ、片目だけに負担がかかり視力低下にも繋がるそうです。
目疲れが続くと頭痛・肩こりといった眼精疲労の原因にもなります。
眼精疲労が悪化すると頭痛・吐き気など身体にも悪影響を及ぼしてしまいます。

改善

座る際には椅子に深く腰掛け姿勢を正すことを意識します。
前かがみになり画面へ近すぎると目だけでなく首への負担もかかるため目線の移動範囲が広くならない距離に保ち、ディスプレイの高さは目線が真っ直ぐかまたはやや低い位置がいいそうです。

頻繁な視線移動で目の周りの筋肉へ負担がかからないようにしましょう。

最後に

忙しいと目疲れ程度でと思いがちですが、軽いストレッチで体の筋肉を解したり遠くを眺めたりと目を休ませてあげるのが大切だと思います。

コマンドラインから英和/和英辞書を引く

0

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


こんにちは。いくたです。

コードを書く上で気をつけるべき最も基礎的なこと…
それは「スペルミス」です。

一文字間違えると今まで動いていたスクリプトがうんともすんとも言わなくなる。
ミスに気付かないと永遠に時間が取られる。
そして何よりスペルミスは、めっちゃしょうもない

スペルミスしちゃうのは英単語のせい…

クラス名やメソッド名に使われている英単語の意味がわからないと、コードはただの英数字の羅列になってしまいます。
コードを書きながら英単語の意味をすぐに調べられたらいいですよね。

そこで、コマンドラインから英和・和英辞書を引けるようにしました。

概要

  1. rubyスクリプトでオンライン英語辞書の weblio http://ejje.weblio.jp から検索結果を取ってきます
  2. 実行するときに検索キーワードを引数に渡すことでコマンドラインから検索できるようにします
  3. bashのエイリアスを設定して、libコマンドを作ります

準備するもの

  • Ruby(ver 2.1.0)
  • gem: open-uri → URL先のデータを普通のファイルと同様に扱えます
  • gem: Nokogiri →  HTMLやXMLの構造を解析して特定の要素を抽出できます

open-uri や Nokogiri の使い方については私が前々回書いた記事でWebスクレイピングについて書いたので割愛します。
【前々回の記事】アニメ名探偵コナンの新一が出てる回だけを見たい

weblioの検索結果を取ってくる

weblioはhttp://ejje.weblio.jp/content/のURLの後ろに検索ワードをくっつけることで検索結果のURLとなります。

(例) rubyの検索結果
  → http://ejje.weblio.jp/content/ruby

このURLのページをnokogiriで分解して「主な意味」の項目をXpathで指定して取り出して表示します。

エラーに対処する

エラーが出ないようにコードを書くのは大切ですが、実行時に実行時に引数を入力しない・検索結果が存在しない等エラーの発生が予想されます。
そうなった時にNoMethodError等を出さないようにあらかじめ逃げ道を作っておきます。
エラーとなる原因をメッセージで表示することで、正しい使い方ができる手助けになります。

例えば、スペルミスをして検索結果が無かった場合、「一致する見出し語は見つかりませんでした。」と表示しておけばスペルミスに気づくことができます。
スペルが不安な単語を検索にかけることで、ミスに気づくことができますね。

実装スクリプト

引数をキーワードにweblioの検索結果を表示する

# weblio.rb
require 'open-uri'
require 'nokogiri'

LIB_URL='http://ejje.weblio.jp/content/'.freeze
XPATH="//*[@id=\"summary\"]/div[2]/table/tbody/tr/td[2]".freeze

# 引数を入れてなかったらメッセージを表示する
if AVGV.empty?
  puts '検索ワードを入れてください。' 
  return
end

# 引数ARGVの検索ワードからURLにする
# `shell script` など複数の単語を組み合わせることができるように'+'で結合しておく
# => 'http://ejje.weblio.jp/content/shell+script'
url = URI.encode(LIB_URL + ARGV.join('+'))

# URLから検索結果ページを取ってくる
html = open(url).read
doc = Nokogiri::HTML.parse(html)
# 検索結果の「主な意味」の項目を抜き出す
result = doc.xpath(XPATH).text

# resultが無かった場合メッセージを表示する
if result.empty?
  puts '一致する見出し語は見つかりませんでした。'
else
  # 結果を表示する
  puts result
end

bashのエイリアス設定

# ~/.bash_profile
alias lib='ruby ~/scripts/library_command/weblio.rb'

使ってみよう!

$ lib ルビー
ruby

$ lib apend
一致する見出し語は見つかりませんでした。

$ lib append
(…に)添える、付加する

$ lib
検索ワードを入れてください。

$ lib お腹すいた
I'm hungry.、hungry

これでスペルミスが少なくなりそうです!
写し間違えなどを気をつけるのが第一ですけどね。

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

アニメーションキーのスマートな打ち方

0

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

「とりあえずSキー」に心当たりがある方は今までよりもアニメーション作業が捗るかもしれません。 ※筆者作業環境:Maya2016

はじめに

独学で3DCGを学ぼうとすると、
実際は作業上あまり使われることのない知識が身についてしまうことが多々あります。

今回は独学で身につくであろうキーの打ち方に関するお話です。

『Sでキーを打つ』というやり方

Webでチュートリアルなどを探して何もわからない状態からアニメーションを学ぶときに高い確率で身につくであろう、この「Sでキーを打つ」というアクションは、
アニメーションさせたいオブジェクトをビュー上で動かしてSを押せばアニメーションキーを打ってくれるので、確かに便利ですし非常にシンプルで初心者にもわかりやすいです。
私も以前は、キャラクターを細かく動かしてはSキーを連打していました。

しかし、仕事上でアニメーション作業を教わってからは使うことがなくなりました

なぜ使わなくなったのでしょうか。
そして実際はどのようにキーを打つとスマートなのでしょうか。

使わない理由

無駄な項目にまでたくさんキーを打ってしまっている
これにつきます。

実際にバウンドする球体のアニメーションキーをSを用いて打ってみます。
すると画面右側、チャネルボックスの移動Xから可視性という項目までの数値が赤くなりました。赤くなった部分にキーが打たれたということになります。
Sキーでキーを打つと...

アニメーションの内容としては球体がZ軸方向にバウンドしていくというものなので、
移動Yと移動Zにだけキーが打たれていれば良いのですが、グラフエディタを開いてみるとこのようにたくさんの無駄なキーが打たれてしまっていることがわかります。
実際のアニメーションカーブ

例に挙げたモーションはZ軸移動とY軸移動に限定したものでしたが、
様々な方向、角度に動かすモーションを作っていく中で、いちいちこのやり方でキーを打ってしまうとグラフエディタを開いたときスパゲッティのように複雑なアニメーションカーブを目の当たりにすること間違いなしです。

先ほどのアニメーションの無駄をなくすとここまですっきりします。
これならタイミング調整などもしやすいですよね。
理想のアニメーションカーブ、キーもすっきりしていて編集しやすい

また、アニメーションカーブ自体も先ほどの画像のものと比べ曲線がきれいなことがわかるかと思います。
illustratorやphotoshop等の使用経験がある方ならイメージしやすいと思いますが、アニメーションカーブはベジェ曲線と原理は一緒なので頂点数が少ないほど、ソフト側できれいに補間してくれます。
アニメーション作業ではガタガタのカーブがそのまま見た目に現れてくるので綺麗なアニメーションカーブを作りたいところです。

ゲームなどに使われるモーションはできるだけ少ないキーでアニメーションを作り、一つ一つのデータサイズを小さくしていく必要があるためSキーによるキーの打ち方はゲームには不向きだと言えます。(特にコンシューマーゲームの場合はゲームソフトに入れられるデータ容量が決まっているので少ないキーフレームでモーションを作るスキルが必要と聞きます。)
単にアニメーション作品を作っているのであれば、最終的にはレンダリングで連番画像に書き出すだけなのでそこまでキーの削減に注力する必要はないのかもしれませんが、シーンサイズはできるだけ軽い方がストレスなく作業ができますよね。

ということで、出来るだけ動かしたい項目(移動軸、回転軸)だけにキーを打っていくようにしましょう。

キーの打ち方

私が普段使っている3通りのキーの打ち方を紹介します。

1.チャネルボックス上でのキーの打ち方

チャネルボックス上で特定の項目へキーを打つ場合は「選択項目のキー設定」からキーを打ちます。
方法はチャネルボックス上のキーを打ちたい項目の行にマウスカーソルを合わせ
右クリック→選択項目のキー設定
でキーを打つことが出来ます。
チャネルボックスからキーを打つ

2.グラフエディタ上での基本的なキーの打ち方
グラフエディタ上ではキーを挿入するといった方がわかりやすいです。あらかじめチャネルボックスなどで打ったキー(アニメーションカーブ)を選択して

Iキー+中マウスボタン

で任意のフレームにキーを挿入できます。
グラフエディタ上でキーを挿入する

3.自動キーフレームを用いたキーの打ち方
自動キーフレーム機能はオブジェクトをマニピュレーターで動かしたときに動かした軸にのみ自動でキーを打ってくれるもので、個人的にとても便利だと感じている機能です。
画面右下にあるこのボタンが自動キーフレームのON/OFFボタンです。このボタンが青く表示されていると、自動キーフレームがONの状態です。
自動キーフレーム機能を使ってキーを打つ

グラフエディタと同様、あらかじめキーが打たれていないとどんなに動かしてもキーを打つことができないので一つ目のキーはチャネルボックスで打ちます。
自動キーフレーム機能を使ってキーを打つ2

3通りのキーの打ち方について紹介してみました。
特定の項目に対してのみ任意のフレームにキーを打つという目的は一緒ですが、
場面によって使いやすさが変わってくるのでうまく使い分けていくと作業の単純化、効率化にもつながるかと思います。

終わりに

記事を書きながら、
「やる気があれば独学で3Dコンテンツくらい作れるだろう。」と高を括り、インターネットの力だけを頼って学んでいた頃を思い出して、よくSキーのみでアニメーション作れていたなと思いました。就活でそのアニメーションを見せて回っていたことに若干恥ずかしさを感じています。

最近はスカートや振袖、髪の毛等の「揺れ物」にアニメーションを付ける作業を始めました。体の動きとは違い、しなやかさや遅延表現など様々な要素を考慮して付ける一段難しい作業ですが、キャラクターのモーションがより華やかになる大切な作業なので非常に達成感が強いです。

考え事の場をつくる

0

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

はじめに

一日中ゲームをしていたら、いつの間にかお盆休みが終わっていました。
休みの間に旅行に行った方、帰省した方、仕事だったという方、私のようにゲーム三昧だった方……
色々な過ごし方をされてきた方がいらっしゃることでしょう。
そんな中でふとした瞬間、考え事をすることはありませんでしたか?
今回はその考え事について、少し書いていこうと思います。

発想の場といえば……

enter image description here

皆さんは日々何気なく考え事をしながら生活していると思いますが、自分にとってどこが最も考え事に適した場所なのか……そんなことを考えたことはありますか?
私の周囲では、「煮詰まったからファミレスに行って作業をする!」という話をよく耳にします。
お洒落で静かなカフェ、薄いノートPCを持ったイケてるサラリーマンたちと女子高生でごったがえすカフェ、近所のファミリーレストラン……
こんなところでキーボードをカタカタさせていたら、格好良いに違いありません。
少し憧れていましたが、残念なことに私はカフェなどで頭脳が活性化するタイプではありませんでした。

自分に合っている場所を見つける

enter image description here

私が集中して考え事をできる場所……それは、「お風呂」でした。
お風呂やトイレなどの、一人でいられる場所が最も集中できるという人はかなり多いと思います。
湯船にゆったりと浸かっている時も良いですが、私の場合は頭からシャワーを浴びている時が絶好調になります。
すぐにでもメモを取って忘れないようにしたいところですが、現実は非情です。
シャンプーを洗い流しているうちに半分くらい忘れています。
お風呂でアイデアが浮かんでも、メモを取れない!とお悩みの方は少なくないと思います。私もその一人です。
なんとかバスタイムを終えるまで、頭の中に残っているよう祈るしかないですね。

考え事をしやすい場所を見つける・気付くことができれば、より良いアイデアが生まれてくるかもしれません。

終わりに

最近では耐水ノートというとても便利なものが登場しています。
これがあれば、お風呂の中でも好きなだけメモを取れますね。
耐水といっても、お風呂に沈めると流石にまずいので、気を付けましょう。

ホワイトボードで代用するのも良いかもしれないです。

select2 で “No Results Found” などのメッセージを変更する

0

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

jquery を拡張し selectボックス(プルダウンメニュー、ドロップダウンリスト、リストボックス)に対してデザインを整えたりタグやプレースホルダテキストや検索の機能を加える select2 があります。検索が成功していないときに表示されるメッセージを変更したくて時間を費やしたので記載します。

参考

本体
https://select2.github.io/

使用例
https://select2.github.io/examples.html

基本的な使い方

jquery, select2 プラグインと関連のcssを読み込んでおく。
検索機能やスタイルを付与したいセレクトボックスをjqueryのセレクター ‘$()’ で指定して select メソッドを呼ぶだけです。select に引数(連想配列的な何か)を渡すことでカスタマイズできます。検索の時にちらっと表示されるメッセージもそこで上書きできます。

例 (coffeescript)

$(document).ready(->
  $('#select_box').select2()

こんなことをしました。

$(document).ready(->
  $('.select2').each(->
    $select = $(this)
    options = {}
    options.language = {}
    options.language.noResults = ->
      '該当するものがありません。' # 0件ヒットのときのメッセージ : No Results Found
    options.language.searching = ->
      '検索中' # 検索中のメッセージ :  Searching...
    options.language.errorLoading = ->
      '検索中' # 検索が失敗しているときのメッセージ: Error loading results
    $select.select2(options)

※ 文字列を返すのだけれど文字列を入れるのではなく文字列を返す関数を入れるところに気をつけましょう。

1台のWindowsで複数Firefoxを起動させる

0

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

1台のWindowsで、複数Firefoxを起動させる(いわゆる多重起動)方法。

できると何が嬉しいか

  • cookieや閲覧履歴、保存パスワードなどが独立する
  • 複数のアカウントを分けてログインするというような事を Firefoxだけでできるようになる
  • それぞれ個別に設定する事ができる(設定違いのFirefox)
  • アップデートしたらアドオンや設定がおかしくなるかどうかを事前確認できるようになる

前提

Firefox自体はインストール済であるとする。(これを「メイン」とする。)

手順

  1. Firefox Portable をダウンロードし、任意の場所に展開する(これを「サブ」とする)
  2. 展開されたディレクトリ下の Other\Source\FirefoxPortable.ini を展開されたディレクトリ直下にコピーする
  3. コピーした FirefoxPortable.ini を以下の通り編集する
  • 変更前
AllowMultipleInstances=false
  • 変更後
AllowMultipleInstances=true

これでメインとサブを同時に干渉せず起動する事ができるようになる。

補足

FirefoxPortable は何個あっても問題ないので、この方法を使えば、サブ側はいくつでも起動可能となる。

オプション

任意で以下のような事もできる

FirefoxPortable の起動時にスプラッシュ画面を表示しない

FirefoxPortable.ini を以下の通り編集する

  • 変更前
DisableSplashScreen=false
  • 変更後
DisableSplashScreen=true

サブ側のアイコンを変える

拡張子が .ico である任意の画像ファイルを用意し、 App\Firefox\browser\chrome\icons\default\main-window.ico として上書き保存する

EXCEL(VBA)から、HTTP通信でファイルをアップロードしてみよう。

0

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

はじめに

EXCEL(VBA)で、JSONデータをHTTP通信する方法に関しては、前回の記事を参考して下さい。
EXCELでファイルリストを管理し、PCに格納しているファイルもアップロード出来れば、作業効率が図れると思ったのがキッカケです。
それを実現する簡単な方法をご紹介します。

※実際は、そんなに簡単ではありませんでした(汗)

参考にさせて頂いたサイト
・WSH + VBScriptでのサンプルを参照して下さい。
・boundaryに関してのこちらの仕様を参照して下さい。

ファイルを送信するには

1.まずHTMLを使ったファイルを送信する処理を確認してみる

ファイルを送信するHTMLは、以下のようになります。
enctype="multipart/form-data" を付ける事でファイルを送信することが可能となるのです。

<form action="http://localhost:8080/" method="POST" enctype="multipart/form-data">
  <input type="text" name="id" /><br />
  <input type="text" name="description" /><br />
  <input type="file" name="file" /><br />
  <input type="submit" name="submit" value="send"/>
</form>

HTMLイメージ

HTMLイメージ

選択しているテキストファイルの内容

選択しているテキストファイルの内容
※使用しているサクラエディタは、こちら

2.HTTP通信の内容はどうなるの?

HTMLのフォームを使ったファイルアップロードの仕様はRFC1867を参照して下さい。
実際に送信し、proxyツールfiddler を使った、HTTP通信内容を確認した様子が以下の図となります。
※HTTPの構文に関しては、こちらのページが参考になります。

リクエスト内容

HTTPヘッダ部分の content-Type が、 multipart/form-data となっていますね。
ここでポイントとなるのは、 boundary= に設定されている”境界線”です。

3.multipart/form-data の構造

詳しくはこちらのサイトを参照して下さい。その中の一部を抜粋して説明します。

■HTTPヘッダ部

Content-Typeの内容

boundaryは、1文字以上70文字以下でなければなりません。
ブラウザによっては、40文字程度の場合もあります。

■ボディ部

ボディ部は、以下の3種類に分類されています。

フォームデータ(赤枠参照)

boundary は、先頭に--HTTPヘッダのboundaryCRLF

テキストのパラメタ

ファイルデータ(バイナリデータも同様)(赤枠参照)

boundary は、先頭に--HTTPヘッダのboundaryCRLF

ファイルを選択時のパラメタ

フッタ(赤枠参照)

boundary は、先頭に--HTTPヘッダのboundary+後尾に--CRLF

フッタ内容

実際にEXCEL(VBA)からHTTP通信にて画像等のファイルを送信

実際簡単ではなかったポイントが、送信するファイルが、テキストではなく画像やEXCELファイル等のバイナリデータの場合です。
送信するmultipart/form-dataパラメタが、テキストであればString型の結合で対応できるのですが、画像やEXCELファイルはByte型になり、容易にはString型Byte型の結合ができませんでした。


解決方法として、上部でも記載さしている参考サイトと同様に、ファイル操作等に使用するADODB.Streamをパラメタ生成用の領域として使用することで、String型Byte型結合を実現しています。


ちなみに、今回もJSONを生成している部分は、こちらを使用させて頂いて居ます。

■サンプルプログラム

①メイン処理

Const adTypeBinary = 1
Const adTypeText = 2

Const adBTypeContent = 1
Const adBTypeBody = 2
Const adBTypeFooter = 3

Public Function UploadFile() As Boolean


    Dim FilePath As String: FilePath = "d:\証明写真サンプル.jpg"

    Dim strMethod As String: strMethod = "POST"
    Dim strUri As String: strUri = "http://localhost"
    Dim strResult As String

    '---------------------------------
    ' リクエストパラメタ用の領域を生成
    '---------------------------------
    Dim tempParamStream As Object
    Set tempParamStream = CreateObject("ADODB.Stream")
    tempParamStream.Open

    '---------------------------------
    ' リクエストパラメタ作成
    '---------------------------------
    Dim FileName As String
    FileName = Dir(FilePath)

    Dim JsonObject As Object
    Set JsonObject = New Dictionary

    JsonObject.Add "name", FileName
    JsonObject.Add "parent", New Dictionary
    JsonObject("parent").Add "id", 0

    If SetNomarlParameter(tempParamStream, "attributes", JsonConverter.ConvertToJson(JsonObject)) Then
    End If

    If SetFileParmater(tempParamStream, "file", FilePath, "application/octet-stream") Then
    End If

    If SetEndParameter(tempParamStream) Then
    End If

    '---------------------------------
    ' リクエストパラメタ取得
    '---------------------------------
    Dim snedParameter As Variant
    GetSendParameter snedParameter, tempParamStream

    '---------------------------------
    ' リクエスト
    '---------------------------------
    Dim objHTTP As Object
    Set objHTTP = CreateObject("msxml2.xmlhttp")
    objHTTP.Open strMethod, strUri, False
    objHTTP.setRequestHeader "Content-Type", "multipart/form-data; boundary=" + getBoundy(adBTypeContent)
    objHTTP.send snedParameter

    statusCode = objHTTP.status

    strResult = StrConv(objHTTP.responsebody, vbUnicode)
    Set objHTTP = Nothing

    UploadFile = True
End Function

②データフォームのパラメタ設定

Private Function SetNomarlParameter( _
                    ByRef tempParamStream As Object, _
                    ByVal fname As String, _
                    ByVal fvalue As String) As Boolean

    If fvalue <> "" Then

        ChangeStreamType tempParamStream, adTypeText

        Dim params As String
        params = ""
        params = params + getBoundy(adBTypeBody)
        params = params + "Content-Disposition: form-data; name=""" + fname + """" + vbCrLf
        params = params + vbCrLf
        params = params + fvalue + vbCrLf

        tempParamStream.WriteText params

    End If

    SetNomarlParameter = True
End Function

③ファイル(バイナリデータ)のパラメタ設定

Private Function SetFileParmater( _
                            ByRef tempParamStream As Object, _
                            ByVal fname As String, _
                            ByVal fvalue As String, _
                            ByVal fct As String) As Boolean

    '-------------------------------------
    ' テキストデータ
    '-------------------------------------
    ChangeStreamType tempParamStream, adTypeText

    Dim params As String
    params = ""
    params = params + getBoundy(adBTypeBody)
    params = params + "Content-Disposition: form-data; name=""" + fname + """; filename=""" + fvalue + """" + vbCrLf
    params = params + "Content-Type: " + fct + vbCrLf
    params = params + vbCrLf

    tempParamStream.WriteText params


    '-------------------------------------
    ' バイナリデータ
    '-------------------------------------
    ChangeStreamType tempParamStream, adTypeBinary

    Dim fileStream As Object
    Set fileStream = CreateObject("ADODB.Stream")
    fileStream.Type = adTypeBinary
    fileStream.Open
    fileStream.LoadFromFile fvalue

    tempParamStream.Write fileStream.Read()

    fileStream.Close
    Set fileStream = Nothing

    SetFileParmater = True
End Function

④フッタのパラメタ設定

Private Function SetEndParameter( _
                    ByRef tempParamStream As Object) As Boolean

    ChangeStreamType tempParamStream, adTypeText
    tempParamStream.WriteText getBoundy(adBTypeFooter)

    SetEndParameter = True
End Function

⑤送信するパラメタを取得

Private Function GetSendParameter( _
                    ByRef parameter As Variant, _
                    ByRef stream As Object) As Boolean

    ChangeStreamType stream, adTypeBinary
    stream.Position = 0
    parameter = stream.Read

    stream.Close
    Set stream = Nothing

    GetSendParameter = True
End Function

⑥パラメタ用の領域の状態を変更する

最初に、p = stream.Positionで現在のポジションを取得しているのは、状態を変更したことでポジションが変わってしまうためです。

Private Function ChangeStreamType( _
                    ByRef stream As Object, _
                    ByVal adType As Integer) As Boolean
    Dim p As Long
    p = stream.Position
    stream.Position = 0
    stream.Type = adType

    If adType = adTypeText Then
        stream.Charset = "UTF-8"
    End If

    stream.Position = p

    ChangeStreamType = True
End Function

⑦Boundy 情報取得

Boundyは、同じ文字列を使用することになるので、変数をstaticにすることで、1度生成した文字列を使って、用途に合わせたBoundyデータを復帰するようにしています。
各種参考させて頂いたサイトでは、Boundyデータは固定の文字列としていますが、折角なのでランダム文字列を生成年月日時分秒までを追加した文字列生成するようにしてみました。

Private Function getBoundy(ByVal adType As Integer) As String

    Static sBoundy As String

    If sBoundy = "" Then

        Dim multipartChars As String: multipartChars = "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
        Dim boundary As String: boundary = "--------------------"

        Dim i, point As Integer

        For i = 1 To 20
            Randomize
            point = Int(Len(multipartChars) * Rnd + 1)
            boundary = boundary + Mid(multipartChars, point, 1)
        Next

        sBoundy = boundary + Format(Now, "yyyymmddHHMMSS")

    End If

    Select Case adType
    Case adBTypeContent
        getBoundy = sBoundy
    Case adBTypeBody
        getBoundy = "--" + sBoundy + vbCrLf
    Case adBTypeFooter
        getBoundy = vbCrLf + "--" + sBoundy + "--" + vbCrLf
    End Select

End Function

送信パラメタの生成の経過

サンプルプログラムでは、"d:\証明写真サンプル.jpg" と画像ファイルを指定していますが
説明しやすいように、別のバイナリファイル(d:\test.bin)を用意して生成過程を確認してみます。

テキストデータを設定する場合には、tempParamStreamをテキスト状態に変更
ファイル(バイナリデータ)を結合する場合には、tempParamStreamをバイナリ状態に変更することで結合(パラメタを生成)を実現します。

test.bin の内容

テスト用バイナリデータ

テキストパラメタ設定時

    If SetNomarlParameter(tempParamStream, "attributes", JsonConverter.ConvertToJson(JsonObject)) Then
    End If

上記を実行したタイミングでは、tempParamStreamのは以下の通り
※先頭の0xEF 0xBB 0xBF はBOM付き(UTF-8)の場合を表すコードとなります。

テキストパラメタまでの送信パラメタ

ファイルパラメタ設定時

    If SetFileParmater(tempParamStream, "file", FilePath, "application/octet-stream") Then
    End If

上記を実行したタイミングでは、tempParamStreamのは以下の通り

バイナリデータ設定までの送信パラメタ

フッタまで設定時

    If SetEndParameter(tempParamStream) Then
    End If

上記を実行したタイミングでは、tempParamStreamのは以下の通り

フッタを追加した時の送信パラメタ

まとめ

BOX API にて、もしもEXCEL(VBA)でファイルをアップロードさせるには今回の作成した処理を使って送信することが出来るはずです。
という事で、次回は、またBOX API に戻りPOST送信する処理を考えてみようと思っています。

英語弱者が送る翻訳サイトのススメ

0

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

はじめに

エンジニアに必要なもの。それは英語です。
コードを打つにも英語、リファレンスを読むにも英語、とにかく必要なのです。
いまどきはどんな人でも必要と言われているかもしれませんが特に我々エンジニアと英語は切っても切り離せない存在です。

ですが英語の勉強をしていなくても英語を訳すことができますよね。
そう、翻訳サイトです。わからない単語があったら翻訳サイトですぐに意味を調べることができます。
ある時僕が翻訳サイトを使用していて、イマイチしっくりくる翻訳が出来なかったので試しに他の翻訳サイトで訳してみるとニュアンスがかなり違って理解できた、なんてことがありました。


検証しよう!

そこで英語のことわざをいくつか翻訳サイトで訳してどれくらい違いがあるかを調べてみました。

その過程で「最後のわら一本がラクダの背中を折る」という、少しでも限度を超えると大変なことになる。といった意味の英語のことわざがそれぞれのサイトの特徴が出ていていい感じだったので、それを訳した文と僕が受けたそれぞれのサイトの特徴をまとめてみました。


・翻訳前の文
The last straw breaks the camel’s back.


Bing翻訳

最後のわらはラクダの背中を壊します。

いくつかの言葉を訳していて、一語一語の意味がわかりやすいと感じました。
「You」という単語が文の中に入っていても訳した文には「あなた」とは入れずに文を簡潔にまとめてくることが多かったです。わからない単語が多い時に使うといいかもしれません。


Google翻訳

最後のストローはラクダの背中を壊す。

ことわざをいくつか訳していた限りでは、あまり変な訳はでてきませんでした。さすがGoogle翻訳ですね。
ですが「kettle」をヤカン、釜ではなくケトルとしたり上記の例でも藁をストローと訳しています。
日本で通じる単語は何かに変換せずそのままカタカナにしてくれるのでわかりやすかったりそうじゃなかったりしました。


Weblio翻訳

我慢の限界は、後ろにラクダのものを壊します。

《諺》 ぎりぎりのところまで重荷を負ったラクダはその上わら 1 本でも積ませたら参ってしまう 《たとえわずかでも限度を越せば取り返しのつかない事になる》

日本語的には微妙な感じに翻訳されることが多かったのですが、ことわざが登録されていると直訳とは別にことわざの意味も表記してくれることがありました。
直訳は間違ってなさそうだけど意味はわからない!なんて時にはWeblio翻訳を使うといいのでは。


excyte翻訳

ぎりぎりの重荷を負ったラクダはわら1本でも積ませたら参ってしまう。

ことわざを訳していた限りではexcite翻訳が一番元の意味に近く翻訳してくれました。今回のように話し言葉のような形で翻訳してくれることも多かったので、ことわざや冗談を訳す時はexcyte翻訳に任せたいと思いました。


InfoSeekマルチ翻訳

我慢の限界は、ラクダの背中に怪我をします。

他のサイトとは単語の訳し方が違ったことが多かったです。
上記の例でも「怪我」という訳し方をしたのはここだけでした。
他のサイトで翻訳した文が微妙にしっくりこなかったりした時にはここに尋ねるといいでしょう。


おわりに

いかがでしたでしょうか。思ったより翻訳に差が出て調べている間結構楽しかったです。
よく考えたら翻訳が全く同じ内容なら翻訳サイトが複数あるなんてこと起こりませんよね。

ちなみに翻訳をお金を払ってやってもらうこともできます。
送った文章を機械じゃなく人力で翻訳してもらうことができたり、より精度の高い翻訳が有料だったりします。

……英語勉強してきます。

cURL(カール)ではなく、httpie を使用しBOX APIの確認をしてみる。

0

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

はじめに

box api referenceでは、cURL(カールと読む)でのリクエスト方法が記載されていますが、
このcURLを使ったの確認の場合、JSONデータが整形されないため、使いにくい。
その事から、別のコマンドラインHTTPクライアントのhttpieを使う事にしてみます。

cURL と、httpie での比較

BOX のフォルダ情報取得処理(Get Folder Info)で比較してみます。

cURL の場合

 > curl https://api.box.com/2.0/folders/0 -H "Authorization: Bearer fAFzgyluzN5JUkjUFIF3jWjGQl48ucvx"
curl の実行イメージ

httpie の場合

 > http -v GET https://api.box.com/2.0/folders/0 "Authorization: Bearer fAFzgyluzN5JUkjUFIF3jWjGQl48ucvx"

シンタックスハイライトも付いているので、見やすい!!

httpie の実行イメージ

httpie をインストールしてみる。

会社からアテンドされているPCもwindow という事で、windows 環境にインストールしてみます。

1.python ダウンロード

httpieを使うには、python が必要です。

こちらのサイトから、python をダウンロードして下さい。

python のダウンロード画面

2.python インストール

インストール画面で、デフォルトのInstall Now を選択してもいいですが、
Customize installation を選択し、インストールする場所を指定する事をオススメしています。
この後説明する、環境設定のパスがOSのエディションなどにもよるが、ほとんどの場合はPATH環境変数の最大長は約2000文字以下に制限されているためです。

インストール時の選択画面1
インストール時の選択画面2

3.環境変数(path)の設定

Windows 10 で環境変数を設定するための流れは
Windowsキー → 設定 → システム → バージョン情報 → システム情報 → システムの詳細設定

仮に、python をインストールしたのが、c:\python の場合
システム境変数 の Path に以下の2つを追加して下さい。

  c:\python
  c:\Python\Scripts
システムプロパティ画面
環境変数設定の画面

4.python の確認

コマンドプロンプト画面を表示し、python とだけ実行すると以下の様に表示されます。

python のバージョン表示

5.httpie をインストール

インストールには、Python のパッケージ管理(pip)を使います。
以下のコマンドを実行して下さい。

 > pip install --upgrade pip setuptools

 > pip install --upgrade httpie

httpie の オプション紹介

一番使いそうな出力オプションのみをご紹介。
詳しくは、httpie使用方法を参照して下さい。

1.出力オプション

-h  応答ヘッダーのみが出力
-b  レスポンスBodyのみが出力
-v  HTTP交換全体(要求と応答)を出力
-p  HTTP交換の一部を選択

 http -v https://api.box.com/2.0/folders/0

1.1.HTTP交換のどの部分を出力するか指定

H  要求Headers
B  要求Body
h  応答Headers
b  応答Body

  http -p=hb https://api.box.com/2.0/folders/0

まとめ

BOXのAPIを簡単に確認出来るようになった気がします。

最近人気な記事