ホーム ブログ ページ 35

pythonで梅雨対策

0

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

pythonで梅雨対策をします。
技術力が低いので、役に立つ技術記事はかけません。
その代わり、ちょっとだけ便利なものを手軽に作ります。

ざっくり概要

  1. APIから天気情報を取得
  2. いろいろする
  3. slackに投げる

結果

一時間後の天気がザーザー降り以上ならslackに通知するbotができました。
enter image description here

ちなみに、強さレベルで言うと
ザーザー<土砂<バケツ<滝<恐怖
となっています。恐怖が最強です。

APIから天気を取得

お天気APIは色々あって、OpenWeatherMapやDark Sky APIなど海外のものも触ってみたのですが、日本の詳細な天気が時間単位で取れなかったり、SSHがうんたらで怒られたりしたので、結局Yahoo! JAPANさんのYOLP 気象情報APIを使います。
ちなみに緯度経度が使えるので、大雑把に会社の近くの緯度経度にしました。
安心です。

色々する

APIから天気情報を取得して色々します。
簡単に言うと、APIからrainfallを抜いてきます。
rainfallは、どうやら降水強度という指標らしいです。
降水強度では分かりやすい目安がすぐに見当たらなかったので、似たような概念の時間降水量を参考に、rainfallをわかりやすくします。

気象庁に分かりやすい指標がありました。
上に書いた雨の強さレベルもこれに基づいてます。
http://www.jma.go.jp/jma/kishou/know/yougo_hp/amehyo.html

slackに投げます

Incoming Webhookというものを使います。

雑記

コード自体は単純ですが、APIの選定が地味に面倒でした。
せっかく作ったので、cronで回して、折りたたみ傘でも買って会社内でレンタル傘屋でもやろうかと思ったのですが、YOLPさんは商用利用に申請が必要そうなのでやめました。
では、良き梅雨を。

以下コード

メイン

# get_rainfall.py
import requests
import slack_post


def main():
    url = build_url()
    json_data = request_json(url)
    in_one_hour_rainfall = extract_rainfall(json_data)

    if in_one_hour_rainfall >= 10:
        rainfall_notice = notification_level(in_one_hour_rainfall)
        slack_post.post(rainfall_notice)


# url組み立て
def build_url():
    APP_ID = "YOLPのAPIキー"
    BASE_URL = "http://weather.olp.yahooapis.jp/v1/place"
    COORDINATES = "139.709008,35.669968"  # 緯度経度
    OUTPUT = "json"

    url = BASE_URL + "?appid=%s&coordinates=%s&output=%s" % (APP_ID, COORDINATES, OUTPUT)

    return url


# リクエスト
def request_json(url):
    req = requests.get(url)
    json_data = req.json()

    return json_data


# 降水強度の取得
def extract_rainfall(json_data):
    weather = json_data['Feature'][0]['Property']['WeatherList']['Weather']
    in_one_hour_rainfall = weather[1]['Rainfall']

    return in_one_hour_rainfall


# 降水レベル
def notification_level(in_one_hour_rainfall):
    if in_one_hour_rainfall >= 80:
        return "恐怖"
    elif 50 <= in_one_hour_rainfall < 80:
        return "滝"
    elif 30 <= in_one_hour_rainfall < 50:
        return "バケツ"
    elif 20 <= in_one_hour_rainfall < 30:
        return "どしゃ"
    elif 10 <= in_one_hour_rainfall < 20:
        return "ザーザー"


if __name__ == "__main__":
    main()

スラックになげる処理

import requests
import json


# slsckにpostするスクリプト
def post(text):
    requests.post('slackのAPI関連', data=json.dumps({
        'text': "一時間後に " + text + "降りの雨が降ります",  # 投稿するテキスト
        'username': 'baguette',  # ユーザー名
        'icon_url': 'http://2.bp.blogspot.com/-mhOboAyB0eA/VfS6MnLrttI/AAAAAAAAxOM/rqBqjtoezJ4/s800/fashion_ame_zubunure.png',  # プロフィール画像
        'link_names': 1,  # メンションを有効にする
        })
    )

フリップフロップと聞いてRubyを思い浮かべる方法

0

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

フリップフロップと聞いて、いきなりRubyやPerlの話を始める人はいないと思います が、今回の記事はRubyのフリップフロップのお話です。 論理回路のフリップフロップもRubyのフリップフロップも一度に確認できちゃうお得な記事にしたいと思います。

はじめに

みなさん初めまして、新卒エンジニアのくろすです。
今回の話、決してFlip Flopやフリップフラッパーズ、RWBYの話ではございませんのでご注意を。
個人的には3Dモデリングとかやってる人にRWBYの話とかして欲しかったりしますが。

そもそも フリップフロップ とは

flipflop とは (洗濯物・旗・サンダルなど)パタパタなる音、とんぼ返り、宙返り、(意見などの)急変、豹変(ひょうへん)、フリップフラップ [出典:Weblio]

なんのこっちゃわかりませんね。
とりあえず今回は普通の人(?)ならまず思い浮かべるであろうフリップフロップ回路の話から始めたいと思います。
要するにフリップフロップを使うと電源がある間は情報の保持が可能であるって話。
いきなり回路の話ですので、興味ない方は読み飛ばしておいていただけると。

メモリ って?

まずはエンジニアでない方でも聞いたことがあるであろうメモリの話から始めたいと思います。
メモリとはなんぞ?って質問にはとりあえずパソコンに入ってる4GBとか8GBとかの方のやつ。
くらいに思っておいてもらえればいいかと思います。

ざっくり言うとメモリには2通りの種類があり揮発性メモリと不揮発性メモリに分けることが可能です。

  • 不揮発性メモリ 電源の供給がなくても情報の保持が可能であるメモリ
  • 揮発性メモリ 電源の供給がないと情報の保持ができないメモリ

この揮発性メモリにはまた種類がありその中にはDRAMとSRAMと呼ばれる種類のものがあります。
DRAMは今回の話にあまり関係ないのでざっくりとしか紹介しませんが、
DRAMとはDynamic Random Access Memoryの略でコンデンサに電荷を蓄え、その電荷の有無で1bitの情報を記憶する半導体メモリです。この電荷をリフレッシュと言う作業で蓄え続けることによって情報を維持しています。
よくパソコンに刺すメモリなんかはこちらが多いかと思います。
そしてSRAMですがDRAMと比較して、回路が複雑でコストが高いがリフレッシュ作業がない分高速で消費電力が小さいと言う特徴を持っています。
SRAMとはStatic Random Access Memoryの略で、リフレッシュ作業がないことからStaticとの名前がついています。

フリップフロップ

さてこのSRAMの記憶部にはフリップフロップ回路というものが使われています。
ようやく出ましたね、フリップフロップくん。
最も基本的なフリップフロップはRSFFと呼ばれるもので以下の回路図で表されます。

enter image description here

Sがセット、Rがリセットで各入力に対する結果は以下のようになります。

SRQ¬Q
00前の状態を保持前の状態を保持
0101
1010
11禁止禁止

上の結果を見てもらえればわかりますが、セットSが立ち上がると無条件で出力は1に、リセットRが立ち上がると無条件で出力は0になります。出力の状態により(0, 0)を入力した際の状態が変わりますが、出力は直前の出力を保持します。
これがフリップフロップ回路が1bitの情報を記憶するメカニズムです。
一瞬の電気信号を回路自身がデータとして保持し続けるということなのですね。
※ ちなみにSRFFで(1, 1)入力は禁止です。これは入力を(1, 1)から(0, 0)に変化させた場合に、出力が(1, 0)で安定するか(0, 1)で安定するかが定まってないため禁止となっています。

Rubyのフリップフロップ

文字数稼ぎのはずが筆が乗りました。
ようやく本題です、むしろ本題の方がしょっぱい気さえします。

とりあえず、以下のコードを見てください。

string = "これからもよかったらかくりよの門をよろしくお願いします"
string.each_char do |char|
  if (char ==  'よ')..(char == 'よ')
    print char
  end
end

このコード実行するとこんな結果が帰ってきます。

よよよ

まぁ、見た感じそうなるでしょうって結果ですね。
次に上のコードそっくりだけどドットの数が違うだけの以下のコードを実行してみます。

string = "これからもよかったらかくりよの門をよろしくお願いします"
string.each_char do |char|
  if (char ==  'よ')...(char == 'よ')
    print char
  end
end

すると結果はこうなります

よかったらかくりよよろしくお願いします

ややっ、これは由々しき事態です。
どうしてこうなるのか、というのはだいたいリファレンスに書いてありますが説明していきます。

範囲式とは

リファレンスを読む限りではRangeクラスのインスタンスを生成する式のことを範囲式と言っているように思えます。

(a) 式1 .. 式2
(b) 式1 ... 式2

条件式の外側で使うと式1から式2までの範囲を表すオブジェクトを返してくれます。
また、(b)の書き方をすると範囲オブジェクトは式2の値を含みません。
Rangeクラスのリファレンスを見てもらえるとわかりますが、上の2つはRangeクラスのインスタンスとして宣言することも可能です。

(a) Range.new(式1, 式2, false) # default値がfalseなので実際はfalseいらない
(b) Range.new(式1, 式2, true )

ここで覚えておいて欲しいのは(b)式の場合は範囲オブジェクトが終端を含まないという点です。

条件式として範囲式を使う

さて、今回問題としているのは条件式として範囲式を使った場合です。
先に説明した、フリップフロップ回路の特性である「回路自身が状態を記憶する」ことと、範囲オブジェクトの終端の話を念頭に置くと理解しやすいかと思います。
少なくとも私は理解しやすかったです。
先ほども登場した範囲式についてのリファレンスには以下のように書いてあります。

「..」の場合:
1. 初期状態では式1だけを評価し、式1が真を返すまでは false を返します。
2. 式1が真を返すと true を返します。式2が真なら初期状態に戻ります。
3. この後は式2だけを評価し、式2が真を返すまで true を返します。
4. 式2が真を返すと true を返したあと、初期状態に戻ります。

「…」の場合:
1. 初期状態では式1だけを評価し、式1が真を返すまでは false を返します。
2. 式1が真を返すと true を返します。
3. この後は式2だけを評価し、式2が真を返すまで true を返します。
4. 式2が真を返すと true を返したあと、初期状態に戻ります。

つまりこの2つの違いは式1が真を返した時式2をその場で評価するかにあります。

(b)式の場合は終端である式2をその場で評価しないのですね。
そして、式2がtrueを返すまでの間は範囲式自身がtrueであるという状態を記憶してtrueを返します。
RSFFでいうなら、式1が入力S、式2が入力Rと考えながら見ると入力が(1, 0)
Rubyだとnilとfalse以外は全てtrueなのでこれはマズイですね。
入力が(true, *)の時に出力はtrueとなり範囲式としてはtrueを維持するようになります。
次からは式2を使い値を評価した結果、入力が(*, true)ならリセットが立ち上がり、
その場では範囲式の出力としてtrueを返した後に、範囲式の出力をfalseとして保持する
と言った具合です。

まとめ

(a) 式1 .. 式2
(b) 式1 ... 式2

今回の話は結局RSFFで禁止されていた(1, 1)入力、つまり(true, true)に対する出力とその時保持する値をどう定義したか?
っていうのに繋がる話なんですよね。

  • (a)式の場合 trueを返しfasleを保持している だから次に評価されるのはセットである式1
  • (b)式の場合 trueを返しtrueを保持している だから次に評価されるのはリセットである式2

以上のように定義されているのがrubyのフリップフロップというわけです。

ここまで読んでいただけたら、フリップフロップからRubyを連想できるようになり、かつ「式」という漢字がゲシュタルト崩壊を起こしていることでしょう。

BoxのAPIを使って見よう。(その1) ~概要とAPIのことについて

0

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

Box API を使う上でのポイント4つ

  1. RESTful API
  2. JSON
  3. OAuth2.0
  4. Box Developer アカウント

1.RESTful API とは

ウェブシステムのHTTPでの呼び出しインターフェース
GET、POST、PUT、DELETE等のHTTP標準のメソッドを使い、「何を」「どうする」というようにシンプルな表現されのが特徴です。

a.URL

https://api.box.com/2.0/files/123456789
  files の中の ID:123456789 のファイルを取得
https://api.box.com/2.0/files/123456789/versions
  files の中の ID:123456789 のファイルのバージョンを取得

b.HTTPメソッド(他にもありますが、今回は以下の4つ)

GET
  リソースの獲得
POST
  リソースの作成
PUT
  リソースの更新(状態変更)
DELETE
  リソースの削除

c.curl(データを転送するライブラリ) を使った例

$ curl -X GET https://api.box.com/2.0/files/123456789 

curl については、こちら 見て下さい。

2.JSON とは

JSONとはJavaScript Object Notationの略で、XMLなどと同様のテキストベースのデータフォーマットです。
{} (カッコ)で括られている文字で、整形すると下記のようになります。

  {
      "entries" : [
          {
              "created_at": "2012-08-20T10:20:30-07:00",
              "id": "672259576",
              "modified_at": "2012-11-28T13:14:58-08:00",
              "modified_by": {
                  "id": "183732129",
                  "login": "sean+apitest@box.com",
                  "name": "sean rose",
                  "type": "user"
              },
              "name": "Dragons.js",
              "sha1": "359c6c1ed98081b9a69eb3513b9deced59c957f9",
              "size": 92556,
              "type": "file_version"
          }
      ],
      "total_count": 1
  }

3.OAuth2.0 とは

a.概要

HTTP上で認可を行うためのプロトコル
通常、ログインが必要なサービスを利用する際はログインID/パスワードの情報が必要になります。
下図のようなサイトの場合、あまり問題にはなりませんが、

enter image description here

下図のようにウェブサーバ経由で、BOXへの操作を行う際、
ウェブサーバにBOXのID/パスワードを格納してしまうのは、セキュアな状態とは言えません。
それを解決するための仕組みとして使われるのが OAuth2.0 です。

enter image description here

b.一般的なフロー

利用者によって、BOXのログイン認証を行い、そこで得られるアクセストークンを使用して、
BOX のAPIを使って、各種操作を実行することで、ウェブサーバにID/パスワードを格納する必要がないのです。

enter image description here

4.Box Developer アカウント

Boxの開発を行う上で、デベロッパーサイトよりアカウントを作成して、アプリケーションを作成するため初期設定を行いましょう。

(1)Box Developers サイトより、アカウントを作成して下さい。

enter image description here

(2)マイアプリより、アプリの新規作成を選択

enter image description here

(3)アプリの種類を選択(ここでは、企業統合を選択しておきます)

enter image description here

(4)認証方法を選択(ここでは、標準OAuth2.0(ユーザー認証)を選択しておきます)

enter image description here

(5)アプリの名前をつけて、完了(あとで変更も可能です)

enter image description here

アクセストークン取得に必要な情報

構成情報から、クライアントID、クライアント秘密コード、リダイレクトURLをコピーして下さい。

enter image description here

BOX API reference

BOXでは、下図のようなリファレンスサイトが用意されています。

enter image description here

まとめ

次回は、BOXのAPIを使って、実際に簡単な情報取得処理を作成していきます。

キーフレームアニメーションに触れて

0

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

はじめに

初めてまして、新卒で2DCGのモーションを勉強中の初心者です。
現在使用しているSpriteStudioを使い、自分が分からなかった点、興味深かったことなど復習も兼ねてまとめていきます。

キーフレームアニメーションについて

一般的な手描きアニメーションとは異なり
手足や顔、胴体など各パーツごとにキャラクターを制作しボーンとして繋ぎ合わせ動かしていきます。
味のあるイラストが感情を持って動くと見栄えが豪華にみえ、
可愛らしい動きから、凝ったエフェクト効果など様々な表現ができます。

キャラクターを動かす点で意識すること

しかしいざ動かしてみると、イメージしていても流れるような動作がでず
ペラペラした動きになってしまったため先輩からアドバイスをいただきました。
【キャラの溜め】
人の動きを表現するにあたって重要になってくるのが「重力」
次のアクションへの溜めが必要になってきます。

例えば、ジャンプをさせる場合、
アクションゲームには欠かせない動きの一つです。

構えから、しゃがみ込み、足に力を入れ、地を蹴る

その際、キャラクターの足にも気を付ける必要があります。地から離れる動作は着地場所に
もっていく工程も作っていきます。

空中で飛んでいる瞬間、地に足がつく、足元へ体重がかかる

ジャンプという動作から流れをイメージし、コマ割りのように分けていきます。

まとめ

実際に自身で動いたり、動画などを見ながら細かな流れを観察していくことが重要でした。
自分自身未熟なのでこれからも精進していきます。

参考URL:http://photoshopvip.net/75555

文章で感情を表現する

0

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

株式会社アピリッツにてゲームシナリオを書いている者です。
エンジニアの方々が技術的な記事を書いている中で恐縮ですが、私はシナリオ関連の話をさせていただきます。
今回のお題は、とても初歩的な感情の表現方法です。
少しゲームシナリオ寄りのお話になります。

はじめに

皆さんは普段、何気なく「楽しいな」、「悲しい……」などと感じていると思います。
(ここで「そんなの感じたことないよ……」と思った場合はブラウザを閉じて回れ右しましょう。)
皆さんがそういった気持ちを感じるということは、物語の中で生きているキャラクターにもその感情がある、ということですね。
ですが、自分の感情をわざわざ声に出して言うものでしょうか?
パッと思いつく個人的な例になりますが、自覚した瞬間に声に出してしまうのは「痛い!」「かゆい!」くらい。
たまに「嬉しい~!」と言うこともありますが、これは相手に「いま嬉しいと思ってるぞ」ということを伝えるために”わざと”言っているので、もしかしたら本心ではあまり嬉しいと思っていないかもしれません。
少なくとも、「悲しい……」「いま怒ってます」等を口に出すことは滅多にないかと。
人間同士の対面会話であれば、表情や声のトーンでその時々の気持ちを判断することもできますが……
感情を文字のみで表すとき、どのようにすればいいのでしょうか?

行動や仕草・状況を書く

人間が何かを感じたときの行動・仕草は、ほぼ普遍的です。
よく言われているのは、「嘘をついている時に目が泳ぐ」や、「何かを考えている時に視線が上を向く」などですね。
目の動きを文章化するだけでも、感情を表せるのです。
人間の体の部位はたくさんありますから、これらを活用した特徴的な仕草を地の文に入れるだけで、そのキャラクターが今何を思っているのか、スマートに表現できます。

……といっても、過剰に地の文だらけのお話というのは、よほど文章が上手でない限り、冗長になってしまい面白みに欠けてしまいます。
ノベル形式のゲームシナリオの場合は尚更ですね。
むしろゲームのシナリオでは、地の文がなく会話文だけで進行するものも多いですが……
二つが混合している形式の場合、大切なのは地の文と会話文のバランスです。
両者のバランスをよく考えて、気持ちよく読める文章になるように私も日々勉強しています。
地の文をどのようにするかで物語全体の雰囲気も左右されるので、作品に合った文体になるよう心がけるのも重要です。

「怒り」の感情を例にして、文章を書いてみます。

~例~
「もういいよ……」
少年は、眉間に皺を寄せたまま俯いた。
渾身の励ましが逆効果となっていたことに狼狽える。
その反応が、更に少年の機嫌を損ねてしまったらしい。
恐る恐る肩に伸ばした手は、無慈悲にもぴしゃりと跳ね除けられた。

突貫工事の拙い文章ではありますが、少年が静かに怒っていること、もう相手と会話をしたくないと拒絶している様子を表しています。
更に、『相手に自分の気持ちを察してほしかった。けれど相手は鈍感なので狼狽しているだけであり、それに呆れてしまった』というニュアンスもなんとなく含んでいます。
控えめな子供は感情を口に出せない傾向があるので、仕草の描写で表現します。

快活で幼いキャラクターであれば、あえて感情を言葉にさせることで、幼さ・未熟さを表現できます。

~例~
「もぉ~、怒ったー!ぜったいぜーったい、許さないから!」

幼女キャラクターにありがちですね。

怒りにも様々な種類がありますから、キャラクターによって「怒鳴る」「黙る」「暴れる」など、たくさんの怒りを使い分けられます。

ゲームならではの表現方法

少しお題から逸れてしまいますが……
先ほど「ゲームでは地の文なしで会話文だけで進行するものが多い」と述べました。
通常の小説であれば、会話文だけで進行する文章は、よほど魅力的な見せ方をしていない限り、あまり推奨されません。
ですが、ゲームでは演出次第で地の文がなくとも何の違和感もなく進行させることが出来るのです。
例えば、「立ち絵」です。
キャラクターが口角を上げて目を細めている立ち絵を表示するだけで、わざわざ文字にしなくても「笑っている」ということを表現できてしまうのです。
普遍的な感情を表す表情イラストを予め数パターン用意しておけば、その分の感情表現は文字にする必要がなくなります。
地の文有りの作品の場合でも、会話の応酬のシーンではテンポを崩さないためにあえて地の文を削り、立ち絵で細かな表情の変化を見せることができます。
これは、立ち絵を使用できるゲームシナリオならではの表現方法です。

例えば、「そうなんだ」という台詞の場合
enter image description here enter image description here
この二つの表情では、「そうなんだ」というセリフに含まれる意味が全く異なってきますね。
左の表情では同意やこちらの話への興味が見て取れますが、
右の表情では疑念、または話にそれほど興味がない……といった様子になる、といった感じです。
これを応用することで、例えば”口では納得しているけれど、本心はあまり乗り気ではないので表情が暗い”などの描写を簡単に示すことができます。
ゲームならではの表現方法に、もう一つ「選択肢」がありますが、これ以上脱線するわけにもいかないので、またの機会にお話ししたいと思います。

キャラクターごとの感情

感情というのは十人十色なので、そのキャラクターの個性によって抱く感情は変わります。
ですが、物語を書いている私は一人しかいないので、私が表現し得る感情には限界がありますね。
では、どうやってその様々なキャラクターの感情を表現すればいいのでしょうか?

自分自身とは全く違う常識や価値観、信念を持つキャラクターを表現する時に、経験、知識、想像力が生きてくるのだと思っています。
ここでいう経験とは、今までの人生の中で出会ってきたバリエーション豊かな人々のこと。
よく「人間観察」などと言いますが、まさしくそれですね。
自分の身の回りにいる人々が、意外なことに創作のヒントになったりすることがあります。

次に、知識とは、数多ある作品に触れて新たに出会ったキャラクターや世界などのことです。
幼いころから自由帳に自分の世界を書きなぐっていた人などであれば分かるかと思いますが、幼稚園生~小学生の頃に考えたキャラクターなどは、経験も知識もないまま作ったキャラクターなので、悪く言ってしまえばお粗末なものですね。
小学校高学年~中学生あたりになると、自由帳の中のキャラクターに悪魔の角やら天使の羽やらが生えてくる頃合いかと思います。色々な作品を見て、知識を得たからこそ生み出せたキャラクターですね。

そして、想像力とは、いかにして「自分ではない誰か」を想像し、その者の心を想像することが出来るのか・自分が経験したことのない空想の世界をどこまで広げられるのか、という力です。
例えば、善良な一般人A(……?)である私は、猟奇的なキャラクターを物語に出す時、どのような人物にさせるか非常に迷います。
このキャラクターにとって何が快楽であり、何が嫌悪の対象なのか……普段はどういった生活をしているのか、などをイメージします。
更に、とある一つの事象を前にした時、そのキャラクターはいったいどのような反応をするのか……とにかく、とことん考えます。
考えても考えても分からない場合がほとんどなので、ここでテンプレート化された「猟奇的なヤツ」を参考にします。
色々な作品に、たくさんいますよね。
その後、自己流のアレンジを加えて、個性あふれるキャラクターになればそれで良いのです。
そういった意味では、想像力は、経験と知識を積み重ねた先にあるのかもしれません。

終わりに

私自身はキャラクターに感情移入をして書くタイプなので、想像力がとても重要です。
文章を書く人の中にも色々なタイプの人がいると思いますので、より良く表現できる方法を模索していきましょう。

オススメの書籍

感情類語辞典
アンジェラ・アッカーマン、ベッカ・パグリッシ 著 / 滝本杏奈 訳
発売日:2015年12月25日
http://filmart.co.jp/books/playbook_tech/emotion-thesaurus/

この記事を最後まで読んでいる人の中には既にご存知の方もいらっしゃるでしょうが、シナリオを書く際に非常に役立つ一冊です。
感情に付随する仕草や感覚、サインなどが多く掲載されています。
「○○の感情を表したいけど、いつも同じような表現になってしまう……」という悩みがある方は、是非どうぞ。

小さなテクスチャ領域でも見た目に説得力を与える!UV展開のポイント

0

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

はじめに

2017年新卒で3Dのデザイナーをやっています、SYです。
アピリッツでは入社する前から内定者インターンとして実際に業務を経験できる環境が設けられています。私は昨年の9月中旬からインターンとしてチームに配属され、3DCGを扱うチームの一員として様々な事を先輩社員の方々から教えていただきました。
今回はそのインターンを通して教わってきた事の中でもUV展開のお話をしたいと思います。

UV展開とは

簡単にUV展開とは何ぞやという説明をさせていただきます。

立体的なモデルに模様などのテクスチャを表示させるにはまずUV展開という作業を行い3Dを2Dへと展開していきます。
これで立体に対して、展開された平面が張り付けられている状態になりました。
あとはフォトショップなどのペイントソフトなどを用いテクスチャを書き込んでいき、画像を参照することで2Dで描いた具体的な模様などが立体上に乗るという仕組みです。

enter image description here
私は画像にある網目状のテクスチャを張り付けた状態でUVに歪みがないかなど確認をしながら作業を行っています。こうすることで2Dで描いた模様を3Dに張り付けた際も歪まないようになります。

細い線を入れる部分はUVをなるべく直線に

私がインターンを始めてから一番最初に作ったモデルのUV展開を行った際指摘を頂いたポイントです。
なぜ、ローポリモデリングにおいてUVはなるべく直線を心がけるべきなのでしょうか。
理由の一つは、
無駄なくスッキリとUVの配置がしやすいというメリットがあるからです。

しかしもう一つ、
テクスチャに書き込んだ直線が綺麗に表現できるという利点があります。

テクスチャに描いた直線が、モデルに貼り付けた際にしっかりと直線として表現されてほしい理由としては

・形状の縁に沿って入る細いハイライト
enter image description here

・パーツが重なった部分や溝などに必然的に入る一段暗い影
enter image description here
などを綺麗に見せたいからです。

なるべくデータを軽く押さえてゲームを作る時、ポリゴン数だけでなく、一つのモデルに使われるテクスチャサイズもそこまで大きいものではありません。現在私がプロジェクトで制作しているモデルも顔以外のパーツを一枚のUVに収め512px × 512pxの大きさで作っています。同じ太さの直線を一本書くにしても真っ直ぐか斜めに書くかでは大きく見た目が変わってきます。
実際にハイライトが入る部分のUVを直線にしてテクスチャを描いた際と直線にはせず展開ツールを使っただけの状態で描いた物の仕上がりを見てください。

enter image description here

enter image description here
いかがでしょうか。
こうして比較してみると、ハイライト部分を斜めに展開してしまった方はテクスチャのドットが目立ってしまい汚いハイライトになってしまいました。
一方、直線を心がけて展開した方はギザギザしていない綺麗なハイライトを表現する事ができました。

恥ずかしながらインターンに参加したばかりの自分は知りもしなかったポイントでした。

少ないテクスチャ領域でも展開時のひと工夫でこれだけ見た目に差が出てきます。
特にツルツルとした質感のものに入るハイライトや影がざらざらとしてしまっていては説得力に欠けてしまいますね。

(※ただ今回は単色の扇子でしたが、複雑な模様やロゴが描かれている場合、UVは少なからず歪んでおり元の模様をそのまま乗せてしまうと変形してしまいます。ロゴなんかは変形してしまうとマズいですよね。そんな時はテクスチャ上で変形させるなどして調整する必要が出てくると思います。)

終わりに

今回は少ないテクスチャ領域でも意識することで見た目に説得力を持たせられるポイントを紹介しました。
今後もこのように備忘録も兼ねた気づきや学んだことを書いて、これから3DCGに挑戦してみたい!という方のお役に立てれば幸いです。

Linuxでファイルやプログラムコードを検索する ~findコマンド~

0

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

こんにちは、新卒のプログラマーです。

初歩的な話ではありますが、findコマンドを利用することで簡単にファイルやディレクトリ、ソースコードを検索出来るようになったので備忘録も兼ねて共有したいと思います。

findコマンド

findコマンドは、findは、ファイルやディレクトリを検索するコマンドです。

ファイル名だけでなく、ファイルサイズやタイムスタンプなどでも検索をすることが可能です。

findの記法は、以下のようになっています。

find [検索開始ディレクトリ] [オプション] [検索したいファイル名]

オプションや開始ディレクトリを省略すると、カレントディレクトリを検索します。

$ find sample1.rb  
  find: 'sample1.rb' そのようなファイルやディレクトリはありません
$ find hogehoge/sample1.rb
  hogehoge/sample1.rb 

オプションの利用

findコマンドには、多くのオプションがありますが、自分はよくnameオプションを利用しています。

ワイルドカードを利用できるので、ファイル名が正確に分かっていなくても検索をすることが可能です。

$ find ./hogehoge/ -name "*"  
  ./hogehoge/
  ./hogehoge/sample2.rb
  ./hogehoge/sample3.rb
  ./hogehoge/sample1.rb  

また、typeオプションを利用することで、検索対象をファイルかディレクトリか指定することが出来ます。

find -type f: ファイルを検索

find -type d: ディレクトリを検索

$ find ./hogehoge/ -name "*" -type f 
  ./hogehoge/sample2.rb
  ./hogehoge/sample3.rb
  ./hogehoge/sample1.rb
$ find ./hogehoge/ -name "*" -type d
  ./hogehoge/

その他にも多くのオプションがあるので、findのマニュアルをご覧ください。

xargs,grepコマンドとの組み合わせ

findコマンドは他のコマンドと組み合わせることで、より便利に扱うことが出来ます。

自分はプログラムコードを検索する時などに、よくxargsとgrepコマンドを組み合わせて利用しています。

xargsコマンドは、ファイル名の一覧を標準入力として受け取って任意のコマンドに引数として渡すコマンドです。

また、grepコマンドは、ファイル名を与えることでファイル内を指定文字列で検索するコマンドです。

そのため、findで得られたファイル名のリストをgrepに渡すようにコマンドを書いてあげると、ファイル内の記述も簡単に検索することが出来るようになります。

$ find ./hogehoge/ -name "*" -type f | xargs grep "Hello"
 ./hogehoge/sample1.rb:print "Hello, World!\n"

終わりに

初歩的な話ではありますが、findコマンドについて普段自分が利用している方法をまとめました。  

まだまだ有効活用できていないコマンドやオプションも多いので、日々精進していきたいと思います。

初心者がRailsのマイグレーション作成でやりがちな3つの失敗

0

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

マイグレーション作成時にやりがちな失敗について

最近、自室用にHHKB pro2をまた買ったよ。オフィスで使ってるTypeSとは違いノーマルの方だけど。TypeSに比べると少し音が煩い代わりに打鍵感はほぼ同じでとても良い。新卒のみんなも初任給で10個くらい買おう。

はい、というわけで今日は マイグレーション作成時にやりがちな失敗 についての話。

RDBMSの予約語をカラム名にしてしまう

気づきにくい、発覚しにくい、忘れやすい。三拍子揃ったうっかりポイント。
DBにはシステム上で識別子として使われるいくつかの単語がある。これらは予約語とされており原則としてカラム名やテーブル名として使うのを避ける必要があるが、案外うっかりカラム名として使ってしまうことがある。

そんな単語は使わないって? ところが予約語はDBの種類によって差がある上にその数も多い。例えばMySQLなら予約語は 231個 ある。23個じゃないよ。(mysql 5.6)

しかも結構使いたくなってしまうような語が多い。 interval、option、key、usage、release、condition ……全部ダメだ。(意外なことにcountは予約語ではない)

それならマイグレーション生成する時に警告でも出してくれればいいが残念ながらそんなものは出ない。ご不満なら自分でissueでもプルリクでも立てよう、今日から君もOSSコミッターだ。

で、結局これをやってしまうと何が起こるんだ? 答えは何も起こらない。 少なくとも普通にRailsアプリケーションで使っていく上ではまず問題にならない。が、例えばMySQLコンソールでこんなSQLを書けばエラーが返る。

mysql> SELECT key FROM nekos;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'key from nekos' at line 1

なんで? Railsでは動くよ?
その理由はActiveRecordが発行してくれるSQLは全部テーブル名まで指定した上で引用府で囲まれるため。こうしてやることで予約語であってもSQLは無事に通る。

SELECT `nekos`.`key` FROM `nekos`;

Railsで無事に使えるならいいじゃん? 良くないよ! ActiveRecordだけがそのデータベースを使うとは限らないし単純にDB設計として推奨されない。一度作成して使い始めてしまったカラム名を後から変更するのは非常に困難だ。作成時に避けよう。

デフォルト値や制約を設定しない

さて、nekosテーブルのbrushing_flagというブーリアン型(true/false)のカラムについて考えてみよう。

SELECT COUNT(*) FROM nekos;
=> 3

SELECT COUNT(*) FROM nekos WHERE brushing_flag = true;
=> 1

SELECT COUNT(*) FROM nekos WHERE brushing_flag != true;
=> ?

?の部分はどうなるかな。正解は、 わからない。
いや、ふざけている訳ではなくて「わからない」というのが正解だ。
何故ならSQLのWHEREではbrushing_flagにnullが入っている場合、trueともfalseともしない。それどころか brushing_flag != trueという条件にも引っかからないからだ。論理的に考えるとなるほど正しい。

だからbrushing_flagがnilであるレコードの数に応じて結果は0から2まで不定だ。nullの値を条件の対象に出来るのは IS NULLの構文だけであり、ガス室に入った猫ちゃんよろしく 「trueであるともfalseであるとも言えない」 のだ。

このため、既に稼働しているアプリでブーリアン型のカラムを追加してrailsのコードを書いていると面食らうことがある。rubyの感覚ではnilであればfalseだからだ。しかし、Neko.not.where(brushing_flag: true)と書いても期待している結果にはならない。直感的じゃないなぁ…。

期待する挙動を得るためにはNeko.where(brushing_flag: [false, nil])と書く必要がある。しかし、これはあんまりイケてない。 そもそもnilをfalse扱いにしたいならカラムの初期値をfalseにすべきだからだ。

初期値が設定できない理由やnullを許可する明確な理由がなければ可能な限り入れて行きたい。特にブーリアン型は必須と言えるかも知れない。新しいカラムを追加したりテーブル構成を弄る時には既存データとの整合性を慎重に考慮する必要があるが、それも初期値設定を活用したい。

これをモデルのvalidateではなくマイグレーションで行う意味は、前述したようにDBをRailsアプリだけが使うとは限らないからだ。仮に別のRailsアプリが使ったとしてもモデルが一致するとは限らない。DBレベルで避けるのが安全だ。

さもなければ思いがけないところで条件漏れが起きたり突然 undifined method error for nil:NilClass と怒られてしまう。

既存マイグレーションを書き換える

やるべきではない。既に保守フェイズに入っているアプリでこれをやるのは考えただけでもクラっとする。

ただ例外として初期の開発フェイズにおいては話が変わる。データベースの仕様変更が頻繁でマイグレーションで愚直に管理して行くことが却って混乱を招くため、単一の初期マイグレーションファイルとしてまとめて、変更が生じる度に書き換えてDBをリセットしていくやり方もある。

整合性を保持しなければいけないデータもまだないのでこれで良い。万事、状況に応じてベストプラクティスは変わる。

その他

他にもいくつか思うことがあるが、それらについては賛否両論あるため省略する。例えばserialize/storeの是非やインデックスの付与タイミングなど。

railsのマイグレーションに限らずSQLのDB設計アンチパターン(不適切なやり方)については 『SQLアンチパターン』 などの書籍で詳しく扱っているので未読なら読むと良いです。この本に書かれている内容に比べるとこの記事の話は局所的で瑣末なこと。

最後に強調しておきたいのが 「アンチパターンの回避や正規化は未然・早めに行うこと」 。既に数十万件以上のものレコードが積み上がって複雑に関係しあった状況からシステムを止めずに正規化したり改修するコストは半端じゃない。わずかな手間を嫌がると驚きの複利で返済不可能な負債が積み上がる。

他の初心者向け記事

エンジニア見習いが最初に覚えるべき4つの習慣

キーボードだけで生きて行く

UnityでゲームのUI作りたい! 〜 物並べ編①

0

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

17新卒エンジニアがUnity関係で学んだことの備忘録を書きます。

はじめに

お初にお目にかかります、17新卒エンジニアの HN:inoooooocchi と申します。
私は4月からエンジニアとして入社し、とあるゲームプロジェクトに配属され、サーバー側(Ruby及びRuby on Rails)とクライアント側(Unity(使用言語はC#))の両方の実装を担当させて頂いております。

ところが私は、RubyもC#もUnityも、入社するまで(正確には3月にインターンシップで配属されるまで)一切触ったことがありませんでした。
そんな中、ここ2、3ヶ月で様々なことを学び、そろそろ整理しておきたいな、と思った矢先にDorubyに記事を書くことになりました。
従って私の記事では、私が仕事をしていて最も楽しい瞬間である「UnityでのUI制作」について、今まで学んだことの備忘録としての意味合いを兼ねてご紹介させて頂きます。

ご注意

本記事では、前章で述べた通りのUnity初心者中の初心者が試行錯誤して覚えた内容を載せています。
従って、凄腕Unityユーザーの方々の中には、「もっと効率的な方法があるのに…」「本当にユーザーの事考えてる?」「え?入社してから今まで何やってたの?w」と思われる方がいらっしゃるかもしれません。
優しい目で見守って頂けると幸いです。

前書き

  • Unityって?
    それでは、早速本題に入っていきましょう。…と言いたいところですが、「そもそもUnityってよく聞くけど、どういうもんなの?」という方がいらっしゃるかもしれません。(私も1年ぐらい前までそうでした。)
    なので、かるーく説明したいと思います。
    Unityとは、ユニティ・テクノロジーズという会社が開発した、ゲームエンジン及びゲーム開発用プラットフォームのことです。
    とっても簡単に言えば、ゲームを作るための基盤を提供してくれる便利なツールってことです。
    GUI(マウスなどを用いて操作可能な直感的インターフェース)によって、ゲームを構成するオブジェクトの配置やオブジェクトを動かすためのスクリプトの設定が可能で、なんならプログラミングが一切出来なくてもゲームが作れてしまうという話もあります。
    (もちろん、自作のスクリプトを用いることでさらにゲームの幅が広がります)
    PC(Windows / Mac / Linux)、スマートフォン(iOS / Android)、さらにはPS4などのゲーム機、(Unityのプラグインを搭載した)Webブラウザなど、様々なプラットフォームに対応しているのも大きな魅力です。
  • UIについて
    ゲームを遊ぶためには、ユーザー(プレイヤー)が操作をする必要があります。
    もちろん、ユーザーがゲームを遊びやすくするために、開発側はなるべく見やすく・分かりやすく・操作しやすいようなUIを用意してあげなくてはなりません。
    例えば、1つのゲーム画面に全部で50個のボタンを配置する必要がある場合、「うまく入りきらないからめちゃくちゃ重なってるけど、頑張ってタップしてくれよな!」なんてUIは、「本当に遊んでもらう気があるのか?」と疑いたくなります。
    そこで、今回の記事では、そのようなふざけた画面を作らないために、「たくさんのオブジェクトを並べる」UIの作り方を書いていきます。

1. SceneとCanvasの作成

まず最初に、Unityでオブジェクトを配置するためには、SceneとCanvasが必要になります。

用語解説

Scene: 「.unity」という拡張子のファイルで、これが1つの画面に相当します。
     たくさんのScene(画面)遷移を繰り返し、ゲームが進行していきます。
     (例:タイトル→メニュー→ステージ選択→ステージ→リザルト→ステージ選択…)

Canvas: Scene上にUIオブジェクトを配置するための、地盤となるオブジェクト。

まずはSceneを作ります。Unityを開いたら、File -> New Sceneで、Untitledという無名のSceneが作られます。
New Sceneでシーン作成
作ったばかりの新しいSceneには、Main Cameraというカメラオブジェクトのみ存在します。

次に、Canvasを作ります。Hierarchyの上で右クリックし、UI -> Canvasを選択します。
Canvasを作成

すると、Canvas(とEventSystem)が生成されます。EventSystemはUIの動作を管理する偉いオブジェクトで、これが無いとクリックや文字入力、タップ操作などが反応しなくなってしまいます。消さないようにしましょう。

さて、このCanvasには、いろいろな仕組みが備わっています。Inspectorウィンドウで、選択したオブジェクトの情報を確認することができます。

Inspectorウィンドウでオブジェクトの詳細情報を確認

最初の設定として、カメラから見える位置にCanvas及びCanvas上のオブジェクトが来るようにします。

Canvas という機能のRender Modeという設定を選択し、デフォルトの「Screen Space – Overlay」から「Screen Space -Camera」にします。

Render Modeを変更
すると、カメラを指定する部分(Render Camera)が現れます。

Render CameraがNoneのままでは意味がないので、Sceneにもともと存在していたMainCameraをアタッチします。
アタッチとは、スクリプトやオブジェクトを別のスクリプトやオブジェクトの構成要素に追加し、アタッチされた側から干渉できるようにするための行為です。
今回の場合、MainCameraをCanvasにアタッチすることで、CanvasはMainCameraを認識し、使うことが出来ます。
ドラッグ&ドロップで、HierarchyウィンドウにあるMainCameraを、InspectorウィンドウにあるRender CameraのNoneの部分に持って行きます。

すると、無事カメラの設定が出来ました。
これで、Sceneの作成とCanvasの作成は完了です。ようやくUIを作っていけます。

2. オブジェクトを並べる領域を作る

それでは、オブジェクトを並べる領域をUnity上で作成します。

早速、先ほど作成したSceneのCanvasに、「オブジェクトを並べる領域」のオブジェクトを作ります。
Canvasの上で右クリックし、Create Emptyを選択します。

Create Emptyで空のオブジェクトを作成
すると、Canvasの下に、GameObject(デフォルト名)が子オブジェクトとして生成されます。
今回は、オブジェクトを並べる領域ということで、ObjectAreaなどの名前にしようと思いましたが、後々スクロール機能を追加したいので、InspectorウィンドウでScrollAreaという名前に変更します。
ScrollAreaがCanvasの子オブジェクトとして存在
生成されたGameObjectはデフォルトでは100×100pixelの小さな正方形なので、好きな大きさに変更します。サイズや位置の調整もInspectorウィンドウで行うことが出来ます。(マウスで掴んでびよーんと伸ばしたりも出来ますが、きっちりした値にするならInspectorがいいでしょう)
ScrollAreaのサイズを変更
今回は300×300の正方形にし、位置を左にずらしてみました。(x座標:0 → -160)
ここで、Inspectorの座標値の左側に、四角形の記号のようなものが見えると思います。
これは、Anchor(アンカー)といい、親オブジェクト(Canvas)に対して子オブジェクト(ScrollArea)の基準点がどこなのかを表しています。
現在は中央に設定されているため、ScrollAreaに取っての基準点(0 , 0)が、Canvasの中央座標になっていました。それを左にずらしたので、x座標が-160という値になったわけです。
本来ならプラットフォームごとの画面サイズの差を考え、このAnchorを調整しx,y座標が基準点から大きくずれないようにするべきですが、今回は省きます。
このAnchorはUIのレイアウトを決める上で非常に大事なものなので、詳細は別の記事を設けて書きたいと思います。

さて、これから、この300×300の領域にオブジェクトを並べていきます。

3. ScrollAreaの中身を作る

並べる領域が出来たので、今度はその中身を作ります。
今回は例として、何らかのステージを選択する画面を想定し、各ステージの名前が書いてあるボタンを作ります。
ボタンを作成するには、ScrollAreaの上で右クリックし、UI -> Buttonを選びます。
Button作成

SceneウィンドウでButtonが確認出来た

はい、ボタンが出来ました。Buttonって書いてありますし誰がどう見てもボタンですね。
本来なら、このボタンをクリックした時に別のSceneに遷移するなどのスクリプトを書いてアタッチするのですが、今回は省略します。
ちなみに生成直後のButtonには「Image」「Button」の二つの機能が最初から搭載されています。画像を変えたい時はImageのSourceImageに画像をアタッチすれば変更できます。(今回は省きます)

名前が「Button」じゃ寂しいので、適当に「StageButton」にしておきます。
同様に、ボタンに書かれている文章も「Button」じゃ何のボタンかさっぱりわからないので、適当にステージ名を入れてみます。

HierarchyのStageButtonの横に、▶︎があるのが確認できますよね。ここをクリックすると、Buttonの子オブジェクトを確認できます。

▶︎ をクリックして子オブジェクトを確認
UI -> Buttonオブジェクトは生成されると同時にTextという子オブジェクトを持っています。このTextにInspectorウィンドウで文章を入れてあげれば、お好みの文字列をボタンに表示できます。
また、領域の横幅が300pixelなので、違和感がないようにボタンの横幅も280pixel程度にしておきます。
ボタンのサイズを調整

これで、押してもうんともすんとも言わないボタンが出来ました。
今回の趣旨はボタンを押すことでは無く、ボタンを並べ(て、スクロールさせ)ることなので、並べるためにボタンを大量生産しましょう。

StageButtonの上で右クリックし、Duplicate(複製する)を選ぶと、ボタンが複製されます。
もちろん、CopyしてPasteしても問題ありません。何度もやるのが面倒な人はCtrl + C -> Ctrl + Vしまくるといいでしょう。

StageButtonを複製
10個まで増やしてみた

というわけで、StageButtonを9個増やして合計10個にしてみました。

10個あるはずなのに1個しか表示されない

…が、何故か表示されているボタンは1個だけです。何故でしょう?
答えは簡単、「元のStageButtonの座標まで丸ごとコピーしてるから」ですね。つまり、1個にしか見えないStageButtonには10個のStageButtonが重なってしまっています。

「え、じゃあオブジェクトを複製するたびに手入力で座標をずらしていかなきゃいけないの!?」

もちろん、Unityには便利な機能があるのです。先輩に教えて頂いて、Unityの力ってスゲー!ってなりました。次の章でご紹介します。

4. Vertical Layout Groupを使ってみよう

その便利な機能とは、「Vertical Layout Group」という機能です。
どんな機能か説明する前に、まずは試しに使ってみましょう。
…と、その前に、機能の効果を体感しやすくするために一度複製したボタンを全て削除してください。
複数選択して右クリックでDeleteすれば一発です。

さて、StageButtonの親オブジェクトであるScrollAreaオブジェクトのInspectorをご覧ください。
見事に何もないですね。そりゃそうです、Create Emptyで空のオブジェクトを作りましたから。
オブジェクトに新たな機能を追加する場合、Add Componentをクリックします。Add Componentでは、Unityに標準で備わっている機能や、自作したスクリプトなど、様々な構成要素(Component)を追加できます。

Add Componentで機能を追加
早速、Vertical Layout Groupを追加してみましょう。
Vertical Layout Groupを追加

何故か大きくなりました。

これはVertical Layout Groupの仕様であり、本記事の目的として不適切な動作なので、元に戻しましょう。
InspectorのVertical Layout Groupで、Child Force ExpandのWidth、Heightのチェックを外しましょう。

Child Force Expandのチェックを外した結果

何故か小さくなりました。(左上)

焦らないでください、これもそういう仕様なのです。
Vertical Layout Groupは、子オブジェクトのサイズを自動で調整してしまうのです。
もともとのサイズを維持したい場合、子オブジェクトに手を加える必要があります。

Layout Elementを選択
子オブジェクトのStageButtonのInspectorでAddComponentをクリックし、今度は「Layout Element」を選択します。
すると、このようなものが追加されます。
Layout Element設定
詳細はここでは省きますが、ひとまず「Preferred Width」「Preferred Height」にチェックを入れ、現れたテキストボックスに、維持したい縦幅・横幅を入力します。
280 x 30を記入

すると…

サイズが元通り

あれほど縮こまっていたStageButtonが、Layout Elementの力により、本来のサイズ(=先ほど設定したサイズ)を取り戻しました。
Layout Elementは、Vertical Layout Groupや、後述するHorizontal Layout Groupなど、
「子オブジェクトのサイズを自動で調整してしまう機能」に対し、「このサイズにしろ!」と強制できる機能なのです。

ちなみに、StageButtonが左上に寄ってしまっているのは、ScrollAreaのVertical Layout GroupのChild AlignmentをUpper Centerにすれば中央に戻せます。

Upper LeftからUpper Centerに

さらに、ScrollAreaに、「Content Size Fitter」という機能を追加します。

その後、Vertical Fitの部分をPreferred Sizeに設定してください。

Content Size Fitter導入

Content Size Fitterは、そのオブジェクトのサイズを、全ての子オブジェクトのサイズに合わせて自動で変化させてくれる機能です。これが無いと、子オブジェクトを複製しすぎた場合、ぎゅうぎゅう詰めになって子オブジェクトが潰れて表示されてしまいます。

さて、これで準備は整いました。Vertical Layout Groupは、ただ単に「子オブジェクトのサイズを勝手に変えてめちゃくちゃにする機能」ではなく、ここから彼(?)の本領が発揮されます。
先ほどと同じように、StageButtonを10個ほど複製してみてください。

StageButtonが並ぶ

Vertical Layout Groupは、子オブジェクトを縦方向に自動で整列させる機能です。
同様に、Horizontal Layout Groupは、子オブジェクトを横方向に自動で整列させてくれます。
このようにして、無事に(?)ボタンを縦にたくさん並べることが出来ました!

ただし、この状態で複製し続けると…
はみ出る

はみ出ます。(※Unity上のGameウィンドウです)
ここで、スクロール機能が必要になってきますね。
しかし、今回の記事がかなり長くなってしまうため、それはまたの機会にご説明いたします。

5. 今後の課題&まとめ

今回は、オブジェクトを並べる際の基礎の基礎をまとめてみました。
今回作った機能を実際のゲームで運用させるためには、まだまだ課題があります。

  • スクロールして、はみ出た部分をクリックできるようにする
  • 「Stage1」の文字列を、Stage2、3、4…のように自動で変えたい(Prefab、スクリプトの使用)
  • StageButtonをクリックした時に、何らかの処理を発生させる(スクリプトの使用)

次回からは、これらの課題を解決していきたいと思います。

このように、私の記事ではUnityによるUI作成をまとめて行きたいと考えていますので、
今回の記事を読んでUnityに興味が沸いた方、分からないことが解決した方(いるかな?)などなど、今後ともよろしくお願いします。

以上、17新卒エンジニアのinoooooocchiでした。

情報共有ならデータポータルがおすすめ!マイレポートと比較してみた

0

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

Googleアナリティクスで定点観測や社内共有をするためのレポート作成はできるだけ時間短縮したい。そんな方におすすめなのが、Googleデータポータルです。今回は、Googleデータポータルとマイレポートとの比較を交えて紹介していきます。

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

こんにちは。株式会社アピリッツでアナリストをしているssekiです。
Googleアナリティクスを使ってアクセス解析レポートの作成をしていると、「いつも同じ作業だから自動化したいなぁ」と思う反面、「APIとかは複雑で敷居が高いなぁ…」と結局断念してしまうことありますよね。

そんなアナリストのためにGoogleアナリティクスにはマイレポートという機能があります。
組織内での情報共有のためにマイレポート機能を常用されている方も多いかもしれませんね。
ただ、マイレポート機能にも難点が多く、自由度の低いレポートになってしまうことがしばしば。

そうした弱点から、私はマイレポート機能をあまり活用していなかったのですが、最近になって、Googleが新しいサービスを提供してくれるようになりました。
そのサービスがGoogleデータポータルです。

今回は、マイレポートより優れている(と私は思っている)ダッシュボードツールのGoogleデータポータルについて、マイレポートとの比較を交えてまとめてみました。

Googleアナリティクスのマイレポートとは

enter image description here
↑※画像はGoogleアナリティクスのデモデータを用いて作成しました。

マイレポートは、Googleアナリティクスの”自分専用”サマリです。
Googleアナリティクス上で見ることのできる表やグラフを1画面にまとめて閲覧できるようにすることがマイレポートの大きな役割といえます。
マイレポートの便利な点をいくつかまとめると、以下のようになります。

  1. Googleアナリティクス上の好きな表・グラフをマイレポートに”直観的に”追加できる
  2. どんなレポートにすればいいかわからなくても、”おすすめを”ギャラリーからインポートできる
  3. 一度マイレポートに保存すれば、期間を変えて再計測が”簡単に”できる

ある程度Googleアナリティクスを使っており、定点観測的なアクセス解析を行っている人にとって、マイレポート機能はとても優れた効果を発揮します。
しかし、冒頭で述べたようにデメリットも多く存在します。特に私が困っていたのは以下の点です。

  1. ウィジェットの大きさやスタイルを変更できない
  2. ウィジェット(表・グラフのこと)が1つのマイレポートに12個までしか配置できない
  3. コメントが追加できない
  4. 他のデータソースをインポートすることができない

総じて、マイレポートのカスタマイズ性が低いことによって、社内共有できるほどのレポートにならないのが難点です。
自分だけでサイトを運営している場合や、アクシデントが生じていないかのアラートとして利用する場合など、誰かと共有する必要が無いときは非常に使いやすいツールですが、企業でサイトを運営していると、レポートの提出や提案資料の作成などを行うことが多くあります。
そうした共有の場面では、マイレポートのレイアウトとデザインはカスタマイズ性が低いため、Googleデータポータルの方がいいんじゃないかなと思います。

Googleデータポータルとは

enter image description here
↑※画像はGoogleデータポータルのサンプルを使用しています。

Googleデータポータルは様々なデータを視覚化し、共有を促進するツールです。
BIツールとも呼ばれていますが、現段階で分析に使えるほどの性能はないため、一度に大勢にが同じ情報を共有できるダッシュボードツールと呼んだ方がしっくりくると個人的には思っています。
ダッシュボードツールという意味ではマイレポートと同じ土俵のツールですね。

元々、Googleデータスタジオ 360として有償版が提供されていましたが、最近になって無償版としてリリースされました。
現在はまだベータ版ですが、共有用として実用に足るレベルの機能を備えています。
少なくとも、私がマイレポ―トに抱いていた欠点を次のように補填することができています。

  1. ウィジェットの大きさ・スタイル・数などのカスタマイズが自由
  2. コメント追加可能
  3. 同じシート内で複数のページを作成可能
  4. 他のデータソース(Adwordsやスプレッドシートなど)も同時に使える

Googleデータポータルを使ってみて、まず最初に感動したのは自由度の高さです。
マイレポートがドキュメントレベルだとすると、Googleデータポータルはほぼパワポレベルです。
つまり、Googleデータポータルを使うことで、これまで一度Googleアナリティクスのデータをダウンロードしてエクセルで作業していた時間を短縮でき、かつパワポレベルの資料が作成できます。

多くの共有資料はパワポやPDFで作成されると思うので、ビジネスユーザーにとってとても利用しやすいツールになっています。

また、標準ではPDF化できないという欠点もありますが、Chromeの拡張機能を用いることでPDFにも対応可能です。

まとめ:Googleデータポータルはどんな人におすすめか

enter image description here

Google アナリティクスのマイレポート機能は、定点観測など決まったデータを取得してアラートのために閲覧したい人向けでした。
それに対し、Googleデータポータルはさらに共有できるレベルまでデータをきれいにまとめたい人向けです。

「これまでマイレポート機能を使わずわざわざエクセルで集計しパワポでレポートを作成していた人」はもちろんのこと、「マイレポート機能を使い慣れていて、共有する必要がある人」にとっても有用なツールといえます。

ちなみに、私はGoogleデータポータルを使って新卒の書いた記事のページビュー数を逐次集計し、新卒内で共有しています。
Googleアナリティクスに詳しくない人とも数値の共有をすることで、より見てもらえる記事の作成につながっていると感じています。
個人的には、こうした非アナリストとの情報共有に重きを置いているのがGoogleデータポータルなのだと思います。

ただし、Googleデータポータルは分析にはあまり向いていません。
なぜなら、Googleデータポータル上で四則演算など計算する機能がついていないためです。

そのため、もしGoogleアナリティクスから次の施策のための本格的な分析をしたかったり、さらに何をすべきかを洗い出したい場合はGoogleデータポータルではなく、TableauなどのBIツールを使うことをおすすめします。

【VBA】エクセルで指定列ごとに改行したデータを作成するマクロ

0

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

横に長いデータを指定した列数ごとに改行して縦長のデータに整理するマクロを(エクセル初心者に向けて)紹介します。

はじめに

 初めまして、新卒でゲームプランナーをやっている者です。プランナー職ではゲーム内のデータをエクセルで管理・編集するため、エクセルに慣れていないとつまずくことが多々あります。本記事では、エクセルをあまり使ったことがない人に向けて、検索しても出てこなかったエクセルの簡単なマクロを紹介したいと思います。

前置き

 ゲーム内の種々のデータ、例えばキャラやエネミーのパラメータ等の多くは、プランナーがエクセルで管理しています(マスターデータと言います)。スマホ向けゲームでもそのデータ量は膨大で、作業の多くはエクセルの関数などで機械的に行わないと、とてつもない時間が掛かってしまいます。これが、そもそも私がエクセルのマクロを使い始めた理由です。

エクセルのマクロ機能の紹介

 今回は私のような初心者の方を想定して、エクセルのマクロとは何なのか、というところから記しておこうと思います。
 エクセルには様々な機能がまとまっていますが、それらの機能を人が動かすのではなく、プログラムで動かしてやることができます。それがマクロです。正しく使えれば、人間では膨大な時間がかかるような作業を瞬時に行うことが可能です。
 エクセルのタブに開発というタブがあれば、すでにマクロが使える状態になっています。
enter image description here
出てない方は【ファイル】→【オプション】→【セキュリティーセンター】と進み、ここで設定を変更すれば表示されるようになります。
 マクロを組むということは、エクセルにどのような手順で作業をしてほしいかを指定することです。難しく感じるかも知れませんが、エクセルでは人の行動を記録してマクロを作ることも可能です。プログラムなんて書けないっていう方でも、開発からマクロの記録を選択すると、そこからの動作をマクロとして記録してくれます。
enter image description here
これを用いれば、単純な反復作業をボタンぽちっとすればエクセルがやってくれるようになるわけです。
開発タブから【Visual Basic】を押せば、行動記録ではなく、自分でプログラムしてマクロを組むことができる画面になります。ここで紹介するコードは、この画面で作ったものです。

指定列ごとに改行したデータを作成するマクロ

 前置きが長くなりましたが、本題に入ります。私が直面したのは、横長のデータを4列ごとで改行して縦長にして整理したい、という状況です。エクセルには、行から列へ、列から行へ変換する機能は元々備わっていますが、4列ごとに行へ変換することは(おそらく)できません。
 エクセルの関数だけを用いてやろうとすると、今の場合4列ごとと決まっているので、コピーしたいセルの指定をOFFSET関数やMOD関数などを組み合わせてやればできるかもしれませんが、ちょっと難しくて分かりません。
 前述の行動記録マクロを作ろうにも、対象とするセルが毎回変わってしまうので、どうにも上手くいきません。そこで、マクロに挑戦してみました。さっそくコードです。

Sub 複数列コピペ()

Dim ds As Worksheet, ad As Worksheet
Dim MaxRow As String, MaxRow_ad As String, MaxColumn As String
Dim i As Integer, INP As Integer

Set ds = Sheets("元データ")
Set ad = Sheets("整形後データ")

MaxRow = ds.Cells(Rows.Count, 1).End(xlUp).Row
MaxColumn = ds.Cells(1, Columns.Count).End(xlToLeft).Column

For i = 1 To MaxRow
    For INP = 1 To MaxColumn Step 4

        ds.Range(Cells(i, INP), Cells(i, INP + 3)).Copy
        MaxRow_ad = ad.Cells(Rows.Count, 1).End(xlUp).Row
        ad.Cells(MaxRow_ad + 1, 1).Insert
        Application.CutCopyMode = False    

     Next INP
Next i

End Sub

 プログラミング経験がほぼないので、見る人が見たら鼻で笑われるコードかもしれませんし、不要なことをしているかもしれませんが、求めていた動作はしてくれました。
 タイトルでは改行と書いてありますが、その実態は”元データ”シートから4列ずつコピーして”整形後データ”シートに貼り付けるということをしています。つまり、

元データ
このようなデータを別のシートに
整形後データ
という形にして貼り付けします。
 種々の関数の説明は省略しますが、僭越ながら軽く動作の説明をしたいと思います。
 最初に、使う変数の定義をし、そのあとにエクセルのシート名を変数に入れています。
次に、コピーしたいデータの最終行と最終列の情報を次の2文で持ってきています。

MaxRow = ds.Cells(Rows.Count, 1).End(xlUp).Row
MaxColumn = ds.Cells(1, Columns.Count).End(xlToLeft).Column

次に、For文で今持ってきた行数分だけ同じ動作を繰り返します。
その動作が、
・1列目から4列目までをコピー(INP=1のとき)

ds.Range(Cells(i, INP), Cells(i, INP + 3)).Copy

・貼り付け対象シートのデータが入っている最後の行を取得

MaxRow_ad = ad.Cells(Rows.Count, 1).End(xlUp).Row

・その次の行にコピーしたデータを貼り付ける

ad.Cells(MaxRow_ad + 1, 1).Insert

・コピペモードを終了し、次のINPに4を足して次のループへ
という流れになっています。

終わりに

 本職エンジニアの方々が難しそうな記事を投稿している中、初心者が簡単なコードを投稿するという恐れ多い記事ですが、お役に立てたら幸いです。マクロをまだまだ理解できていないので、他の方の環境でも動作するのか分かりませんが、ご了承ください。

※2017/06/13 記事をより分かりやすくするため、編集しました。

ActiveRecord::Relationとは一体なんなのか

0

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

ActiveRecord::Relation まわりのことを、ソースコードを読んで調べてまとめました。

はじめに

Ruby on Railsを使い始めて2ヶ月と少しの初心者ですが、最近はなんとなくわかったような気になってrailsを使っています。
つまりわかっていないということなので、これもいい機会だと思って ActiveRecord::Relation について調べてみました(ソースコードを読んでみました)。

この記事の内容はざっくりいうと ActiveRecord::Relation とは一体何で、何をしているのか…という話(の一部分)です。
特に、どういうタイミングでどういったSQLクエリが発行されるか、といったことに注目しています。

これを決める前に最初に持っていた疑問は

  • 関連付けで作られたメソッドは一体何を返しているの?
  • eachって一体何に対してeachを使っているの?
  • find はArrayのようにコードブロックもとれるらしいけれど、一体どうやってるの?
  • どのメソッドを使うとどういうクエリが何回発行されるの?

などでした(記事中で解決するものも、しないものもあります)。

以下では記事を書いた時点でのrailsソースコード(5.1.0 相当)を参考にしていますが、おそらく5系統の中ではそれほど違うことはないかと思います。

そもそもActiveRecord::Relationとは?

ActiveRecord::Relationは

  • クエリを生成するための条件を持っておいて、必要に応じて適切なSQLクエリを生成・発行してくれる
  • その結果からオブジェクトを作って返したり保持したりしてくれる

感じのものです。つい最近知ったのですが、こういったものをO/Rマッパーというそうです。

ActiveRecord::Relationがどこで出てくるかというと、たとえばUserなるモデルがあったとすると User.all や User.where(name: "hoge") などで返ってくるものが ActiveRecord::Relation のインスタンスです。
また、has_many関連で作られたメソッドは ActiveRecord::CollectionProxy のインスタンスを返しますが、この ActiveRecord::CollectionProxy は ActiveRecord::Relationを継承しています。

初心者が ActiveRecord::Relation について知るには、ひとまず以下のインスタンス変数に注目するのがよさそうです。

  • @values: SQLクエリを作るための条件
  • @loaded: SQLクエリを発行し、 オブジェクトを取得したことがあるかのフラグ
    • (ソースコード中では loaded? という名前で参照されています)
  • @records: 条件に合うオブジェクトの配列

@recordsにアクセスするための records というメソッドがあります。
これは、 loaded? がfalseのとき(=まだオブジェクトを取得していないとき)はクエリを発行して@records を取得して返し、loaded? が trueのとき(=すでにオブジェクトを取得しているとき)は @records をそのまま返します、

# activerecord/lib/active_record/relation.rb

def records # :nodoc:
  load
  @records
end

def load(&block)
  exec_queries(&block) unless loaded?

  self
end

loadする前は @records がないので、だいたい loaded?のtrue/false = @records の有無です。

各メソッドはこの records を使ったり使わなかったりします。知る限りではだいたい3種類です。

  • recordsを使うもの
  • recordsを使わないもの
  • その他

records を使うメソッド

recordsを使うメソッドは、loaded? がtrueならクエリを発行しないメソッドでもあります。

records に delegateされているメソッド

ActiveRecord::Relation のいくつかのメソッドは records に delegate されています。

# activerecord/lib/active_record/relation/delegation.rb

delegate :to_xml, :encode_with, :length, :each, :uniq, :to_ary, :join,
         :[], :&, :|, :+, :-, :sample, :reverse, :compact, :in_groups, :in_groups_of,
         :to_sentence, :to_formatted_s, :as_json,
         :shuffle, :split, :index, to: :records

つまり records(Array) に対してメソッドを呼び出すもので、すでに @records があればSQLクエリを発行しません。

また、ActiveRecord::Relation は Enumerableをインクルードしています。
delegateされていなくても、Enumerableのメソッド (例えば map, collect など)は each を使って定義されているので、他のメソッドと同様 @records のメソッドを呼び出すことになります。

5.0.3では map,collect などもdelegate されているようですが、recordsに対してArrayのメソッドが呼ばれることに変わりはありません。

pluck

pluck は各レコードを丸ごとオブジェクトとしてとってくるのではなく、引数で指定したカラムのみの配列で返すメソッドです。
pluck はdelegateされているわけではないですが、 @records があればそちらからとってきます。

# activerecord/lib/active_record/relation/calculations.rb

def pluck(*column_names)
  if loaded? && (column_names.map(&:to_s) - @klass.attribute_names - @klass.attribute_aliases.keys).empty?
    return records.pluck(*column_names)
  end

  if has_include?(column_names.first)
    construct_relation_for_association_calculations.pluck(*column_names)
  else
    relation = spawn
    relation.select_values = column_names.map { |cn|
      @klass.has_attribute?(cn) || @klass.attribute_alias?(cn) ? arel_attribute(cn) : cn
    }
    result = klass.connection.select_all(relation.arel, nil, bound_attributes)
    result.cast_values(klass.attribute_types)
  end
end

size

size は、 @records があればArray#lengthでサイズを取得します。
@records がない場合DB内でサイズを計算してしまうので、 @records は更新されません。

# activerecord/lib/active_record/relation.rb

def size
  loaded? ? @records.length : count(:all)
end

recordsを使わないメソッド

@records があってもそれを使わずに、SQLクエリを使って計算を行うメソッドがあります。
rails ガイドで列挙されているこのあたりがそうです。
https://railsguides.jp/active_record_querying.html#%E8%A8%88%E7%AE%97

  • count
  • sum
  • average
  • minimum
  • maximum

この5つは activerecord/lib/active_record/relation/calculations.rb で定義されています。

count, sum は Array にもあるメソッドなのでややこしいですが、 ActiveRecord::Relation のcountやsumはコードブロックを無視します。
Arrayのcountやsumを使いたい時はrecords、to_aなどでArrayに変換する必要があります。

その他のややこしいメソッド

教えてもらうまで全く知らなかったのですが、コードブロックを渡すかどうかで
SQLクエリとしてそれを処理するかArrayとして処理するかが変わるメソッドがあるそうです。

find

findは引数を渡すかコードブロックを渡すかで挙動が変わります。

引数を渡したときは引数を渡したときはSQLを発行し、idが引数と一致するものをひとつ探してきます。

コードブロックを渡したときはEnumerable(というかArray)のfindが呼ばれます。

# activerecord/lib/active_record/relation/finder_methods.rb

def find(*args)
  return super if block_given?
  find_with_ids(*args)
end

select

selectもfindと同じように、引数を渡すかコードブロックを渡すか挙動が変わります。

引数(カラム名)を渡した時のselectは、引数のカラムの値だけを取ってきて、その値のみが入ったオブジェクトを返すものです。
( SQLクエリは “SELECT `args` FROM table_name …” のようになります)。

コードブロックを渡した時のselectは、Arrayのselectです。コードブロックに渡したときtrueを返すものの配列を返します。

これはそもそも用途が違うので、どちらを使うか迷うことはなさそうですが…

# activerecord/lib/active_record/relation/finder_methods.rb

def select(*fields)
  if block_given?
    if fields.any?
      raise ArgumentError, "`select' with block doesn't take arguments."
    end

    return super()
  end

  raise ArgumentError, "Call `select' with at least one field" if fields.empty?
  spawn._select!(*fields)
end

まとめ

ここまで出てきたメソッドをもう一度おさらいしておきます。

  • 条件にあう配列 records がすでにあればそれを使うもの
    • each, Enumerableのメソッド
    • [], length, uniq, sample, reverse, compact などのdelegateされているメソッド
    • pluck
    • size
  • recordsがあってもなくてもクエリで計算を行うもの
    • count
    • sum
    • minimum
    • average
    • maximum
  • その他
    • find
    • select

ここで列挙したのは個人的に使うメソッドだけなのですが、同じように見ていけば他のメソッドもどうなっているかわかるはずです。

なんとなくで使えてしまうのでなんとなく使ってしまいがちなActiveRecordですが、なんとなくでも何が起こっているのか知っておくと後々役に立ちそうです。
findの使い分けやcount,size,lengthの使い分けはDBでやるのが速いか、rails上でやるのが速いかは場合によったりもしますし、違いを知って使い分けていければいいなと思っています。

参考:

ゲームデータをExcelで書くときに役立つ4つのこと

0

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

 ゲームを作るとき、利用するソフトや制作形態によって、データをどう入力するかは 大きく異なってくる。この記事ではExcelで入力する場合を取り上げるが、考え方などは 他のツールでも役に立つかもしれないので、参考にしていただけたらと思う。

データの入力例

 戦闘があるゲームを例として考えてみよう。
 ↓の画像はスキル設定シートにおける入力だ。データ的に必要なものだけを入力している。なお、実際のゲームでは消費MPとか威力とかその他色々な内容を設定する必要があるが、今回は例なのでこの程度にしている。
enter image description here
 どの数字が何に対応しているか分からないこのシート、入力する人は大変な思いをするだろう。何百、何千とスキルがあったとしたら・・・想像するだけでゾッとする。入力ミスは当然起きるだろうし、時間もかかる。だが、Excelならそんな悩みは吹き飛ばせる。

データの入力規則とVLOOKUP

まずは新たに↓のようなシートを用意しよう。
enter image description here
そして
 B5~B8をドラッグ→数式タブの中にある「名前の管理」→「新規作成」
と操作し、「スキル種類」と名付ける。次にB5~C8をドラッグして、今度は「スキル種類リスト」と名付ける。他の項目も同様にやっておく。
(余談:このようなシートを「reference(参照)」と呼んだりする)

一方、入力用のシートは↓のようにする。
enter image description here
そして
 名称のセルを選択→データタブの中にある「データの入力規則」
と操作し、↑画像のように入力する。これで先ほど設定した「スキル種類」と名付けた
内容がプルダウン式で選択できるようになる。
 次に数字が入っているほうのセルには、「=IF(D4=””,””,VLOOKUP(D4,スキル種類リスト,2,0))」と入力する。このように入力することでD4の値に合ったcodeが出るようになる。こういった自動で入力されるセルには分かりやすく色付けしておくといいだろう。↓こんな感じだ。
enter image description here
(余談:このようなIF文にすると名称欄が空欄だと何も出ないが、単にVLOOKUPのみにすると#N/Aエラーが出る。自分や開発環境のスタイルに合わせて使い分けてほしい。)

 このような入力方法にすることで、作業効率をアップし、ミスを減らすことができる。さらに、この方法は後で「ターゲットのcode2番をランダム1体にしたい」となったときに、referenceシートの方で、2番にランダム1体、3番に全体、とすれば自動的に入力シートに反映される。ただし、名前の管理から3番目が外れていたら直しておく必要があるので注意。

文字列を書き換える置換

(置換なんて知ってるよ!って人は飛ばしていい。これは完全に初歩だ。)
 先ほどのシートで、「ターゲットの単体を1体に書き変えたい」となったとしよう。referenceと合わせても「単体」と書いているセルは6個なのでこの程度なら直接書き換えても大したことはない。しかし、既にスキルを100個作っていたとしたら1つ1つ変えることほど馬鹿らしくて間違えやすいことはない。
 ここは置換機能を使おう。ホームタブの「検索と選択」の中にある。検索する文字列には「単体」、置換後の文字列には「1体」と入力する。次に「すべて置換」を押したいところだが、先に「すべて検索」を押そう。これを押すことでこれから置換しようとしている対象のセルが全て見れる。押してみると、説明文の方に「単体」の文字が入っていることが分かる。(↓の画像のようになったはず)
enter image description here
もし説明文はこのままにしておきたければ、「セル内容が完全に同一であるものを検索する」にチェックを入れよう。あとは「すべて置換」を押せば完了だ。

エラー値チェック

 Excelを使ったことがあれば誰でも「#N/A」「#REF!」などでイラッときた経験はあるだろう。こういったエラー値表示はいくつかの方法でチェックすることができる。ここでは3つほど紹介する。

1. 検索で「#N/A」などのありがちなエラー値を検索する
 これはそのまんま。自分がよく見かけるものを検索しよう。ただ、エラー値表示は7種あるため、毎回全て検索するのは結構面倒くさい。
 参考:エラー値は「#DIV/0!」「#N/A」「#NAME?」「#NULL!」「#NUM!」「#REF!」「#VALUE!」の7種。

2. 数式タブの「エラーチェック」
 編集中のシートのあらゆるエラー値を調べてくれる便利なボタンだ。ボタンを押すだけで編集中のシートのエラーを調べられるのだが、検索のように一覧で見ることはできないし、ブック全体を調べることはできないので、シートがたくさんある場合はあまり有効ではないので注意。

3. 条件付き書式
 これはエラーを探すというより、あらかじめエラーを探しやすくする方法だ。まずはエラーが起こりそうなセルを選択し、ホームタブの「条件付き書式」内のその他のルールを選択。「数式を使用して、書式設定するセルを決定」を選択し、「=ISERROR(A1)」と入力(A1は設定するセルの場所)。書式は気づきやすいようにセルを赤色にしたりすると良い。(↓こんな感じだ。)
enter image description here
 こうすることで、設定したセルがエラー文を表示しているときだけ、設定した書式に変化する。検索と組み合わせれば、設定した書式を検索するだけで全て調べ上げることもできる。条件付き書式はエラーチェックだけでなく、様々なことに利用できるので色々試してみると良いだろう。ただし、条件付き書式はExcel全体が重くなる原因になりやすいので、多用は禁物。

表示順を設定する

 設定したゲームデータが表示される順番は、勿論設定した順番になる。しかし、例えば「ポーションとメガポーションを作ったが、後からハイポーションを間に入れたくなった」となったときどうすればいいだろうか。
 論理的には2つの間に行を挿入してハイポーションを作れば事足りるが、これはあまりよろしくない制作方法だ。新しく何かを追加するとき、一番下の行に追加しないと、どれが新しく追加したものか後から分からなくなってしまうからだ。
(1人で作っているならどうしようと勝手ではあるが)
 さて、話を戻すと各行毎に数値で表示優先度を振ることになる。このとき、ある程度の桁数と一定のルールで振るといい。例えば回復系アイテムは「10001000~10009999」とか、強化系アイテムは「20001000~20009999」とかそういう感じだ。どう振るのが適切かはゲームによりけりなので自分で考えよう。

まとめ

 いかがだっただろうか。他にも当たり前のようにやっていることや考えていることはあるだろうが、今回はこういうお題で思いついたものを書かせていただいた。
 Excelは調べれば使い方はたくさん出てくるが、それをどう活かすかは自分で考えなければならない。いや、Excelに限らずどのツールでもそうだろう。自分はまだまだ未熟なのでもっと精進しなければならない。

pyenv + pyenv-virtualenv + Serverless Framework で AWS Lambda 向け開発環境を作る

0

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

先日の記事のとおり、Serverless Frmework で Python3.6 の環境を作成するのにちょっとハマったということを書きましたが、やっぱり pyenv が便利なので pyenv の場合の環境構築手順をまとめてみました。

環境

  • OS: CentOS Linux release 7.3.1611 (Core)
  • Serverless Frameowrk 1.14.0

あらかじめ Serverless Framework はインストールされている前提です。
インストールされていない場合は、以下のページなどを参考にインストールして下さい。

pyenv と pyenv-virtualenv をインストール

Pythonの環境を複数用意する方法として、 pyenv + pyenv-virtualenv で行う方法と、virtualenv(venv) で行う方法があり、デファクトスタンダードは virtualenv(venv) のようですが、
コマンドのインターフェースが rbenv に似てて個人的に使いやすい点、python-build によって気軽に複数バージョンをインストールできる点から pyenv を使う方法を選択しました。

pyenv

以下のように pyenv をインストールします。

# Github から clone 
$ git clone https://github.com/pyenv/pyenv.git ~/.pyenv

# .bash_profile に環境変数等の設定を追加しておく
$ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile
$ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile
$ echo 'eval "$(pyenv init -)"' >> ~/.bash_profile

シェルを再起動した後、試しに現在利用中の Python のバージョンを確認してみると

$ pyenv versions
system (set by /home/vagrant/.pyenv/version)

$ python --version
Python 2.7.5

system にインストールされている Python 2.7.5 が利用中ということがわかります。

pyenv-virtualenv

続いて pyenv-virtualenv をインストールします。

# Githubから ~/.pyenv/plugins 配下に clone
$ git clone https://github.com/pyenv/pyenv-virtualenv.git $(pyenv root)/plugins/pyenv-virtualenv


# プロンプトへのバージョン表示を無効化(個人的にうざいので止める)
$ echo 'export PYENV_VIRTUALENV_DISABLE_PROMPT=1' >> ~/.bash_profile
# .python-version ファイルによる自動的な環境切り替えを有効化するために以下を実行
$ echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.bash_profile

シェルを再起動して、インストール結果を確認

$ pyenv virtualenv --version
pyenv-virtualenv 1.0.0 (virtualenv unknown)

上記のようにバージョンが表示されてたらインストール完了。

pyenvでpython3.xをインストール

Lambda の実行環境として Python3.6 を使いたいので、pyenv でインストールします。

まずインストール可能なバージョンの一覧を確認

$ pyenv install --list

一覧から、現時点の最新のリリースと思われる 3.6.1 が見つかったので、以下のようにインストール

$ pyenv install 3.6.1

インストールが完了したら、以下のコマンドでインストールされているバージョンを確認

$ pyenv versions
* system (set by /home/vagrant/.pyenv/version)
  3.6.1

このように 3.6.1 がインストールされていることがわかります。

pyenv-virtualenv で環境作成

# 一旦、 shell で利用する python のバージョンを 3.6.1 に切り替え
$ pyenv shell 3.6.1

pyenv-virtualenv では Python3系に標準添付の venv モジュールがあればそれが利用されますが、それ以下のバージョンでは virtualenv パッケージが別途インストールされている前提で動作するような挙動となっているようです。
もし、ここでバージョンを切り替えずに system 環境に virtualenv パッケージがインストールされていない場合、 pyenv-virtualenv の実行中ににエラーとなります。

# 3.6.1 に python3.6 という名前で環境を作成
$ pyenv virtualenv --python ~/.pyenv/versions/3.6.1/bin/python3.6 python3.6

–python ~/.pyenv/versions/3.6.1/bin/python3.6 の箇所が重要。
これが無いと、仮想環境の bin ディレクトリに python3.6 コマンドが無いため、前回の記事のようなエラーとなります。

プロジェクトディレクトリで利用するバージョンを指定

プロジェクトのディレクトリに移動して、利用するバージョン(環境名)を指定します。

$ cd path/to/project
$ pyenv local python3.6

ここで指定する python3.6 は前述の pyenv virtualenv コマンドで指定した環境名です。

上記コマンドの結果、カレントディレクトリに.python-version が出来て、このディレクトリ配下で利用するバージョン(環境名)が保存されます。

試しに現在のバージョンを確認します

$ pyenv version
python3.6 (set by /home/vagrant/path/to/project/.python-version)

# 念のため python3.6 コマンドにPATHが通っていることも確認
$ type python3.6
python3.6 は /home/vagrant/.pyenv/shims/python3.6 です

必要なパッケージをインストール

環境ができたのでこの中に必要なパッケージをインストールしていきます。
今回、Lambdaへデプロイする前提のため、boto3をインストールします。

$ pip install boto3

以上で、Serverless Framework を使うための環境が用意できました。

試しに Python3.6 向けの Lambda 関数をローカルで実行してみる

以下のようにテスト用のサービスを作ります。

$ sls create --template aws-python3 --path hello36
$ cd hello36

handler.py を以下のように変更

import sys
import json

def hello(event, context):
    return {
        "message": "Go Serverless v1.0! Your function executed with %s" % sys.executable ,
        "event": event
    }

ローカルでこの Lambda 関数を実行してみます。

$ sls invoke local -f hello
{
    "message": "Go Serverless v1.0! Your function executed with /home/vagrant/.pyenv/versions/3.6.1/envs/python3.6/bin/python3.6",
    "event": {}
}

無事、ローカル環境でも Python3.6 上で Lambda関数が実行できるようになりました。

実際、この Lambda 関数を AWS 上にデプロイして実行するためには serverless.yml をいろいろ設定する必要がありますが、その点についてはこの記事では触れません。

詳しくは Serverless Framework – AWS Lambda Guide などを参考にしてください。

さいごに

自分自身、まだ python の環境構築系のツールの使い方に慣れていないので、もっと良いやり方があれば指摘もらえると幸いです。

最後に、環境を構築するにあたって参考としたサイトを記載しておきます。

リスティング広告の費用対効果が高い理由と効果が高い企業の特徴3選

0

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

リスティング広告は一般的に費用対効果が高いと言われている。そこで本記事ではリスティング広告の費用対効果が高い理由と、特に高い費用対効果が期待できる企業の特徴を3つ紹介する。

リスティング広告の費用対効果は高いと一般的に言われているが、あなたはその理由をご存知だろうか。本記事では、なぜリスティング広告は費用対効果が高いのかの理由について解説し、さらに特に高い費用対効果が期待できる企業が持つ3つの特徴について紹介していく。

リスティング広告の費用対効果が高い理由1:高精度のターゲティングが可能

リスティング広告はGoogleやYahoo!等の検索結果の上部にある広告枠に掲載される。検索結果はユーザーがキーワードで検索した際に表示されるものだが、リスティング広告では管理画面上で「どのキーワードに広告を出すか」「どの時間帯・曜日に広告を出すか」「どの地域に広告を出すか」など、さまざまな条件を絞り込むことができる。そのため、商品が売れやすかったり、問い合わせが得られやすい”条件にだけ”広告を出すことができるのだ。

例えば、ターゲティングの条件には以下のようなものがあるが、それぞれを高精度で絞り込むことができる。

  • キーワードターゲティング:どのキーワードに広告を出すかを設定できる
  • 時間帯・曜日ターゲティング:どの時間・曜日に広告を出すか設定できる
  • 地域ターゲティング:どの地域・都道府県・市区町村に広告を出すか設定できる
  • 性別・年齢ターゲティング:どの年齢層・性別に広告を出すか設定できる
  • リマーケティング:サイトに訪問したユーザー等特定条件を満たした場合に広告を出すか設定できる

上記のターゲティングを組み合わせて使うことによって、本当にコンバージョン(商品購入・問い合わせ)が発生するところ”だけ”を狙って広告を打つことができるため費用対効果が高い。

リスティング広告の費用対効果が高い理由2:少ない広告費用でも効果が期待できる

マス向けの広告では費用対効果を計測することが難しいことが多く、実際に見られているのか、効果に繋がっているのかを調査するにはある程度分析スキルや環境が必要になる。

一方で、リスティング広告をはじめとするネット広告では理由1の高精度なターゲティングもあり、効果の高い条件だけに絞り込むことができるため、少ない費用からでも効果が出やすい。

また、アナログではなくデジタルな世界のため「どれだけ広告が見られたのか」「広告を経由して発生した商品購入はどれぐらいあるのか」「収益にどれだけインパクトがあったのか」などをリアルタイムに数値で確認することが簡単にできる。そのため、高い費用対効果を維持しやすいのである。

リスティング広告の費用対効果が高い理由3:広告設定チューニングで費用対効果の改善が可能

リスティング広告は、広告を打てば打つほどデータが蓄積する。その蓄積したデータをもとに、どういった条件の時に商品が売れやすいのか。問い合わせが得られやすいのかを把握することができる。そのため、以下のような設定をチューニングすることによって、コンバージョン率(商品購入されやすい・問い合わせされやすい)条件にだけ広告を出稿することができる。

1.キーワードで絞り込む

リスティング広告はどんなキーワードで検索されたときの検索結果に広告を出したいかを選ぶことができる。そこで、各キーワード別にコンバージョン率やクリック率が高いキーワードに”だけ”広告を絞り込む設定をすることによって費用対効果が改善する。

2.時間帯・曜日を絞り込む

管理画面やレポートでどの時間帯・曜日のコンバージョン率・クリック率が高いかも知ることができる。こちらも同様に効果の高い時間帯や曜日に”だけ”広告を絞り込むことで費用対効果が高まる。

3.地域で絞り込む

リスティング広告では地域・都道府県・市区町村など、かなり細かに広告出稿対象のエリアを設定できる。そこで、特定の地域に根付いたビジネスをしていたり、特定のエリアにだけ広告を出したい場合なども可能だ。広告効果の高い地域に”だけ”広告を絞り込むと費用対効果が改善する。

4.リマーケティング(RLSA)で絞り込む

リマーケティング広告という言葉を聞いたことがないだろうか。この機能は上記の3つと比べると少し難易度が高いが、WebページにJavaScriptのソースコード(いわゆるタグ)を埋め込んでおくことで、サイトに訪問したことがあるユーザーをリストに登録することができる。これがリマーケティングリストだ。

リマーケティングリストを活用したリスティング広告をRLSA(リマーケティング・フォー・サーチ・アド)といい、つまりリスティング広告にリマーケティングリストを使うことができる。これはとても革新的なことで、自社のサイトに興味を持って、サイトを訪問したことがあるユーザーは商品購入率や問い合わせ率が高い傾向にある。そういったユーザーだけに絞り込んで費用対効果を高めることができる。

5.性別・年齢(DFSA)で絞り込む

デモグラフィックという言葉がある。これはユーザーの性別や年齢層のデータを示している。リスティング広告の中でこちらも新しめの機能だが、ユーザーの性別や年齢層を設定できるDFSA(デモグラフィック・フォー・サーチ・アド)がある。この機能を活用することによって、効果の高い特定の性別や年齢層に絞り込むことができるため費用対効果を高めることができる。

特に費用対効果が期待できる企業の3つの特徴

リスティング広告には相性が良い企業特徴というものが存在する。以下の3つの特徴を持っている企業の場合は、比較的高い費用対効果が期待できる。

企業ブランド・商品ブランドが既に認知されている

リスティング広告とブランド力はとても相性がいい。なぜならブランドが認知されていると”検索される”からである。ブランドが認知されていればされているほど検索ボリュームが多くなり、いわゆる”ブランドキーワード”つまりブランド名を名指しで検索しているワードに対してリスティング広告を打てば、高い費用対効果が期待できる。

SEO(自然検索経由)流入で既にコンバージョンしている

SEO、つまり広告枠ではなく、自然な検索でサイトへ訪問したユーザーが既にコンバージョンしているケース。この場合はSEOで効果が高いキーワードにリスティング広告を打つことで、最初から高い費用対効果が期待できる。

中には、SEOで既に1位や2位のキーワードに、リスティング広告を打つ意味があるのか?と思われるかもしれないが、実際にPCやスマホの検索結果を見てほしい。リスティング広告をはSEOの自然検索枠よりも”上”に表示される。これは特にスマホでは画面の大半を占めることになる。そのため競合よりも自社のサイトに訪問してもらうために、SEO・リスティング広告の両面で上位を獲得することには意味がある。

1コンバージョンあたりの売上・利益が高い

例えば転職・求人サービス等のように紹介単価の高いサービスであったり、ある程度単価の高い商品を取り扱っているECサイト等の場合は1つのコンバージョンを手に入れるために、使える広告予算が高い。そのため、データの蓄積が容易であるため、絞り込みの精度が高くなりやすく、リスティング広告と相性が良い。

最後に

リスティング広告は基本的には費用対効果が高い集客施策として知られている。しかし、上記の費用対効果を改善する体制やノウハウが無く、あまり良い効果を得られていないケースもあるかもしれない。そういった場合はまずこの記事に書かれている項目について設定の改善を試みてほしい。

また、効果が期待できる3つの特徴に当てはまっているが今現在リスティング広告を導入していない場合は、ぜひ費用対効果の高いチャネルの1つとして検討してみてほしい。


■アピリッツのリスティング広告出稿代行サービス

弊社アピリッツでは、上記をふまえたリスティング広告出稿代行サービスを行っています。

現在のリスティング広告の効果に満足できていない。もっと改善する余地があるのではないか。
また、これからリスティング広告を導入したいが、高い費用対効果に向けて取り込んでいきたいと考えている担当者様。

お問い合わせ・ご相談を承っております。
サービス詳細 | リスティング広告出稿(SEM)代行サービス | アピリッツ

`sls invoke local` すると`Error: spawn python3.6 ENOENT`と言われた

0

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

Serverless Framework 使ってみてますが便利ですね。Serverless Frameworkの「売り」のひとつである「ローカル環境での Lambda関数の実行」がなぜかエラーになっていたので少し突っ込んで調べてみました。

環境

以下のような環境で確認しました。

  • macOS 10.10.5 (Yosemite)
  • python 3.6.1 (pyenvでインストール)
  • Serverless Framework 1.14.0
  • node 4.4.5

原因

serverless コマンドのソースコードを覗いてみました。
どうやら、lib/plugins/aws/invokeLocal/index.js の ここ や ここ で process.env の 環境変数 PATH を書き換えているが、pyenvでインストールしている python3.6 にPATHが通っていないため発生している模様。

対処

最初、Serverless Framework側にプルリク投げる必要があるかと思ったが、いろいろ見たら、pyenv はいらないという結論になったのでpyenvをアンインストール。

あらためて pytho3.6 をインストール

$ brew install python3

boto3 も入れ直す

$ pip3 install boto3

以上で正常に動作するようになった。

さいごに

最近、rbenv で Ruby 環境を構築するのに慣れてて、同じ感覚で Python 環境も構築してましたが、ちょっと勝手が違ったようです。

今回は、Python 2.x 系と 3.x 系を共存させたいという要件はなかったため、Python 3.6 をそのままインストールしてしまいましたが、共存させる場合は前述の Qiita の記事にあるように virtualenv(venv) 使うのが良さそうです。

2017/6/4 追記

この記事では pyenv をやめて直接 Python3.6 をインストールしたという結果になりましたが、後日調査して pyenv でも Python3系でのローカル実行できる環境が作成できることがわかりました。
以下の記事をご覧ください。

method_missingを使ってみる

0

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

method_missing とは

何かメソッドを呼び出したとき、
rubyはそのクラスに該当メソッドがあるか捜索し、なければ継承元のクラスを捜索しに行きます。
これはごく普通の挙動ですよね。

ですが捜索した結果該当メソッドが存在しなかった場合呼び出されるのが method_missing です。
これをうまいこと使うとコードが一気に短縮できるのでそのパターンを紹介します。

実際以下のようなクラスがあった場合を想定します。

class User
  def update
     puts "更新されたよ"
  end

  def login
    puts "ログインしたよ"
  end
  # more methods...
end
class UsersManager
  def initialize
    @array_users = []
  end

  def add_user(user)
    @array_users << user
  end

  def update 
    @array_users.each do |user|
      user.update
    end
  end
end

UsersManagerが持っている配列内のUserのメソッドをまとめてコールするというだけのものです。

manager = UsersManager.new
manager.add_user(User.new)

manager.update
=> "更新されたよ"

問題なく使用できると思います。

ですが今の状態で下記のように実行すると

manager.login

NoMethodError: undefined method `login'

もちろんエラーになります。
単純に考えるとUserManagerにloginの処理を追加すれば実行できますが、Userが100や150のメソッドを持っていてそれらも実行しようとしたとき、その数分メソッドを作るのは現実的じゃないですよね。

method_missingを利用してみよう

上の実行ではNoMethodErrorが出力されています。
rubyでは呼び出されたメソッドが存在しなかった場合に

manager.send(:method_missing, :login)

が呼ばれ、その結果NoMethodErrorになります。

これがどうかしたのかというと、あくまでただのメソッドなので
継承先に method_missing を実装してやることで挙動を制御することができます。

先ほどのクラスに method_missing を下記のように実装して実行すると、

class UsersManager
  def initialize
    @array_users = []
  end

  def add_user(user)
    @array_users << user
  end

  def method_missing(name, *args)
    @array_users.each do |user|
      user.send( name, *args )
    end
  end
end
manager = UsersManager.new
manager.add_user(User.new)

manager.login
=>ログインしたよ

という形になります。
これは呼び出したメソッドがUserManagerクラスに存在しなかったので method_missing が呼び出されるのですが、その時UserManagerクラスに method_missing がないか捜索してくれるのでこのような実行結果になるわけです。

こうすればUserクラスにどれだけメソッドが増えたとしてもUserManagerには処理を追加する必要がなくなります。
とっても便利ですよね。

使用時の注意点

無限ループに陥りやすい?

method_missingを記述するとき、「継承元にあるmethod_missingは呼ばれない」ということに気をつけなければいけません。
例えば先ほどのUserManagerクラスを以下のように変更します

def method_missing(name, *args)
  self.hoge
  @array_users.each do |user|
    user.send( name, *args )
  end
end

この状態でmethod_missingが呼ばれると

manager.login
=>output error: SystemStackError: stack level too deep

どうやらStackOverFlowが起こっているようです。

self.hogeが存在しない -> method_missingが呼ばれる -> self.hogeが存在しない -> ……
といった風に無限ループを発生させてしまっています。

今回のパターンではかなり露骨な書き方をしているのでわかりやすいですが、NoMethodErrorは比較的やってしまいがちなエラーだと思います。それがいきなりStackOverFlowになってしまうので、この危険性は頭の隅に置いておかないと何のエラーだかわかりにくく危険です。

処理を追いにくい

method_missingを使用したコードを他の人が追おうとした時、クラスに呼んだメソッドが記述されていなかったらまず親クラスのメソッドなのかと疑うと思います。そもそもmethod_missingを知らない人はどうしようもないですよね。

まとめ

実際うまく使えたらとっても便利だと思いますが、
method_missingを知っておかないと何をしているのか分からないコードだなという印象を強く感じ、
こういった特殊な動きをするものは知識として入れておかないといけないなと再認識できました。
これから先難解なコードに手をつける機会が訪れた時に迷うことを極力減らせるように、rubyの知識を増やしていこうと思います。

systemd でデーモン化しつつ logrotate に対応したスクリプトを書く

0

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

ときどき、「rubyでちょっとしたデーモンを動かしたい」かつ、「出力されるログファイルもローテーションさせたい」ということたびたび発生します。 そんなとき、どのようにスクリプトを組むのが良いかという点についてまとめてみました。

TL;DR

ポイントは以下です。

  • ふつうにRubyのスクリプト書いて systemd でデーモン化する
  • logrotate の postscript の中で実行中のスクリプトに対してUSR1を送出する
  • スクリプト側ではUSR1シグナルを受け取ったらログファイルをreopenする

スクリプト本体

デーモンとなるスクリプト本体です。

log_file = File.expand_path("hoge.log", File.dirname(__FILE__))
pid_file = File.expand_path("hoge.pid", File.dirname(__FILE__))

# pid 作成
File.write(pid_file, Process.pid)

# logger 初期化
logger = Logger.new(log_file)
logger.level = Logger::INFO

$reopen_log = false

# USR1シグナルでログのreopenフラグを立てる
trap("USR1") do
  $reopen_log = true
end

# 終了時に pid ファイルを削除
END {
  File.delete(pid_file)
}

loop do
  # フラグ立ってたらログを reopen する
  if $reopen_log
    logger.reopen(log_file)
    $reopen_log = false
  end

  logger.info "hogehoge---"
  sleep 10
end

スクリプトのポイント

いくつかポイントとなる箇所があるので解説。

プロセスIDをファイルに出力する

理由は、logrotate の postrotate のスクリプトで kill -USR1 でプロセスに対してシグナルを送出するためです。

# pid 作成
File.write(pid_file, Process.pid)
# 終了時に pid ファイルを削除
END {
  File.delete(pid_file)
}

シグナルを受け取ってログを再オープンする

普通にログローテートすると、対象ファイルがローテーションした際、既存のファイルがリネームされて新しいファイルが作られるが、reopenしないとずっと古いファイル(リネームされた方)に出力されてしまう。
そこで、USR1(たいていUSR1が使われるらしい)のシグナルを捕捉して、シグナルを受け取ったらreopenする処理を組み込む。

# USR1シグナルでログのreopenフラグを立てる
trap("USR1") do
  $reopen_log = true
end
  # フラグ立ってたらログを reopen する
  if $reopen_log
    logger.reopen(log_file)
    $reopen_log = false
  end

ちなみに、trap()のブロック内(シグナルハンドラと呼ぶらしい)でreopenしてみましたが、Threadがなんちゃらというエラーが出て落ちてしまいました。
一般的にシグナルハンドラの中では入出力を伴う処理等を行うのはよくないらしく、フラグを立てるくらいしたほうが良いらしいです。

systemd に登録するやつ

特に解説はしません。
Systemdを使ってさくっと自作コマンドをサービス化してみる – Qiita などを参考に。

[Unit]
Description = xxxx daemon

[Service]
ExecStart = /home/xxxx/.rbenv/shims/ruby /path/to/hoge.rb
Restart = always
Type = simple
User = hoge
Group = hoge

[Install]
WantedBy = multi-user.target

このファイル名 hoge.service がサービス名(hogeと省略可能)となります。

logrotate

日毎のローテーションで、履歴を10件保持する設定です。
この設定のポイントは、postrotateのブロックに記述されたスクリプトで、kill -USR1 $(cat $pid) の部分です。
これによってローテーション完了後、スクリプトにUSR1シグナルが送出され、それによってスクリプト側ではログファイルがreopenされ、結果としてローテーション後の新しいログファイルにログが出力されるようになるという仕組みになっています。

/path/to/hoge.log {
    daily
    rotate 10
    missingok
    su hoge hoge
    create 0600 hoge hoge
    postrotate
        pid=/path/to/hoge.pid
        test -e $pid && kill -USR1 $(cat $pid) || true
    endscript
}

デーモンの起動/停止

以下のコマンドでデーモンが起動します。

$ sudo systemctl start hoge

停止は以下

$ sudo systemctl stop hoge

さいごに

こんな感じで、意外なほど簡単にRubyで気軽にデーモンが作れるのでbotとかいろいろ作ってみたらどうでしょうか。

ActiveRecord::Relationの落とし穴

0

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

モデルをwhere句等で取ってくると、ActiveRecord::Relationが返ってくる。それを利用した操作はとても遅くなる時があった。

近況+直面した問題

 おやつをこんにゃくゼリーにしたら便通が良くなったHelloWorld?です。
 Ruby, Railsを学び始めて合計2ヶ月程(事前研修+入社してから1か月半)が過ぎ、新機能の実装をちょっとずつやり始めるようになりました。
 その機能の実装の一つで、普通に実装したらそのメソッドを実行するのに5秒程掛かっていてこれじゃあ運用できないよ、となっていたのが、ボトルネックを調べてそこ辺りの解消を試みてみたら、実行に掛かる時間が0.5秒以下まで抑えられたと言う事がありました。
 その理由を突き詰めてみました。
(バージョンは以下の通りです。
Rails: 4.1.9
Ruby: 2.1.5p273
Mysql: 5.6.36
)

原因、解決

 実際の実装でボトルネックを調べると、
 実装する為に分割した一つの、以下のようなメソッドに問題がありました。

    find_suitables(w_models)
      ret_models = []
      w_models.each do |model|
        if ...
           ...
          ret_models << model
        end
      end
      ret_models
    end

 w_models: 別のモデルからwhere句で取ってきたActiveRecord::Relation

 この中で、each文の中身が一番のボトルネックかな、と思っていたのですが、一向に軽くならないので、each文の中身全て削除して時間を計測してみたら、そのeach文そのものに時間をかなり取られていた事が分かりました。
 さて、これはどう解決すれば良いんだろう、と思って。
 試しに一旦そのw_modelsの型をActiveRecord::Relationからハッシュや配列に変換してからeach文に入れてみたら、劇的な動作速度の向上が出ました。
 でも、ActiveRecord::Relationから変換するにも、取ってきてから変換しているんじゃ意味が無いよな、と思って色々調べてみたら、以下のページが見つかりました。
http://qiita.com/yut_h1979/items/4cb3d9a3b3fc87ca0435
 ActiveRecord::Base.connectionから生SQLでDBにアクセスできる! そして返り値を配列やらハッシュやらで取ってこれる!
 これは使うしか無いな、ともう、自分のコードの中でActiveRecordを使っていた部分の大半をこれで生SQLを打ち込んで、コードもActiveRecord::Relationからハッシュや配列を扱うようにしたら、実行に掛かる時間がとても短くなりました。

検証

 実装した後、(テストコードも実装して)ちょっとその理由を様々な観点から確かめてみる事にしました。
 以下のように十分なレコード数がある適当なモデルからデータをActiveRecord::Relation、hash、arrayの形式で取得してきて、

    def take
      ar = ModelX.where("id <= #{NUMBER}") #ActiveRecord::Relation...takeで取ってくると配列になる。
      hash = ActiveRecord::Base.connection.select_all("SELECT * FROM model_xes LIMIT #{NUMBER}").to_hash #hash
      array = ActiveRecord::Base.connection.select_rows("SELECT * FROM model_xes LIMIT #{NUMBER}") #array
    #NUMBER: 1000, 2000, 3000, 4000,idに抜け落ちはありません。
    end

 時間の計測に関しては、測りたいコードの前後に

    time = Time.now
    code
    time = Time.now - time
    print "#{time}"

 を書けば小数点以下の時間も計測出来るとのことです(後で、benchmarkの存在も知りました)。
 eachで回したり、findと線形探索を比較してみたり、他にも様々な検証をrailsのコンソール上でしてみました。

 以下のメソッドをtakeからそれぞれ呼び出してみて、時間を計測しました。

    def get_each_time(ar, hash, array)
      time = Time.now
      ar.each do |n|
        if n.id % 10 == 0
        end
      end
      time = Time.now - time
      print "#{time}"

      time = Time.now
      hash.each do |n|
        if n["id"] % 10 == 0
        end
      end
      time = Time.now - time
      print "#{time}"

      time = Time.now
      array.each do |n|
        if n[0] % 10 == 0
        end
      end
      time = Time.now - time
      print "#{time}"
    end

    def get_find_time(ar, hash, array)
      id = ar.count * 3/4 #どのような探索でもちょっと探索をするような値を。
      time = Time.now
      ar.find(id)
      time = Time.now - time
      print "#{time}"

      time = Time.now
      hash.each do |n|
        if n["id"] == id
        break
        end
      end
      time = Time.now - time
      print "#{time}"

      time = Time.now
      array.each do |n|
        if n[0] = id
        break
        end
      end
      time = Time.now - time
      print "#{time}"
    end

    def get_find_by_time(ar, hash, array)
    #絶対に条件に引っかからないものを検索
      time = Time.now
      ar.find_by(A: 'AAA', B: 'BBB', C: 'CCC')
      time = Time.now - time
      print "#{time}\n"

      time = Time.now
      hash.each do |n|
        if n["A"] == "AAA" && n["B"] = 'BBB' && n["C"] == "CCC"
          break
        end
      end
      time = Time.now - time
      print "#{time}\n"

      time = Time.now
      array.each do |n|
        if n[1] == "AAA" && n[2] == "BBB" && n[3] = "CCC"
          break
        end
      end
      time = Time.now - time
      print "#{time}\n"
    end

 その結果は以下です。



 eachにおいては時間の軸を対数にしないと、全部表示できないようなレベルの差が出ました。これは、 SQL呼び出しの時間を鑑みても、無視できないレベルです。
 また、それぞれをメソッドとして呼ばずに、takeのメソッドの中でそのまま呼び出した場合をグラフにしてみると。


 find_byで値に変化が生じました。これはキャッシュ関連の問題だと思います。

結論

 each, findの操作をする時は、ActiveRecord::Relationを使用するべきではない、というよりActiveRecord::Relationは極力使うべきでは無い、と自分は思いました。
 1000以上のレコード数のときではありますが、大抵の場合ハッシュや配列よりも時間が掛かっていますし、また、一気に処理時間が多くなるような事も容易に起こり得るようなものだと自分は危険視し始めています。

 実際の実装の時は、ハッシュを利用しました。
 配列の方が速いときもありますが、そう大きな差ではありませんし、また、可読性の面からも見て、ハッシュの方が良いと思ったからです。

展望

 現在配属されているプロジェクトにおいて、似たような事でちょっと遅くなっているメソッドとかがあれば、同様にして処理の時間を減らせたらな、と思っています。

後日

 なぜ、ActiveRecord::Relationが特にeach文では遅いかという理由を、この記事を読んだ同じ会社の同期や先輩からご教授頂きました。
 その理由は、ActiveRecord::Relationは、where句で取ってきたタイミングではなく、each文の中で初めてSQLを叩いているからだ、という結論で。
 なので、一番最初に行ったeachの比較でActiveRecord::Relationだけ、SQL文を叩いた時間を含める事となったようです。
 という訳で、どうやら、検証自体が余りフェアなものでは無かったのかもしれません。
 ただ、find, find_byに対しては多分大丈夫だと思います。後日、再度検討してみます。

https://doruby.jp/users/mkobayashi/entries/ActiveRecord–Relationとは一体なんなのか
 同期がそれに関して詳しく調べた記事を書いたので、そのリンクも載せておきます。

https://doruby.jp/users/hello_rails/entries/ActiveRecord–Relationの落とし穴-解決編
 後日、再度検討しました。

自社ブログで記事を書く人必見!無理せず読まれる記事を書く4つのポイント

0

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

この記事は、これからDoRubyに記事を投稿していくであろう新卒の皆様向けに「そんなに無理をしないでできるSEOライティング」について書いていく記事である。

この度、弊社アピリッツにも2017新卒の皆様が入社された。この場を借りて、入社おめでとうございます。

ところで、アピリッツには入社してしばらくすると、新卒合宿なるものがある。その合宿では毎年、会社をもっと良くするための課題が出される。今年は度重なる話し合いのすえ、この”DoRuby”に持ち回りで記事を投稿して盛り上げよう!という話になったらしい。対象は新卒全員。どうやら1ヶ月に1記事ずつ、おそらく今月あたりから、入社2年目の今頃まで投稿していくそうなので、人数を考えると、大まかに200から300記事くらいは投稿されそうな予感がしている。私も去年の5月頃、新卒にもかかわらずDorubyのサイトリニューアルに関わらせて頂いたので、新生DoRubyが少しずつ成長していくようで非常に嬉しい。

この記事は、これからDoRubyに記事を投稿していくであろう新卒の皆様向けに「そんなに無理をしないでできるSEOライティング」について書いていく記事である。

そもそもSEOとは何か?

おそらく、これを読んでくれている方の中には少なからず『SEOってなに?初めて聞いた。』と疑問を抱いている方もいるかと思う。一言でいうと「Googleなどの検索エンジンで検索にヒットして、かつ、できるだけ上部に掲載するための技術と考え方』だ。もしかすると、この記事に書いてあることを参考にして記事を書いてみると、検索したときに自分の書いた記事がでてくるかもしれない。

なぜ検索結果の上の方に掲載されたほうがいいか?

もう少し前提の話をすると、Dorubyに投稿した記事は、検索されたときに検索結果の上部にあった方がいい。例えば、検索結果の1位と2位では記事が読まれる確率に10%~20%くらいの差がある。なのでなるべく上にあったほうがいい。


仮に「読まれる記事 書き方」というキーワードが月に1,000回検索されているとする。そのときに1位に記事があると月に300回くらい誰かが読んでくれる。これは検索ユーザー全体の30%にクリックされたことになる。一方、2位では100回くらいしか読んでもらえなかったりする。これだと全体の10%程度にしかクリックされていないことになる。あくまで傾向なので、かなりざっくりしているが、とにかく差がある。詳しく知りたい人は、この記事が参考になる。検索順位別クリック率(CTR)と年代別推移まとめ

検索結果の1位になるのがゴール?

1位になってもその先がある。検索結果の中でクリックされるかどうか問題である。これも記事のタイトルのつけ方だったり、記事の説明文の内容だったりで結構変わってくる。例えば、nanapiというサイトは、記事タイトルや概要文を徹底的に書き直し、クリックされる確率を40%以上にあげたという話がある。同じ1位でも更にユーザーがきてくれる確率が上がっている。ちなみに40%はめちゃくちゃ高い。平均は30%前後。それについてはこの記事が参考になる。nanapiが人力でやっている細かすぎるが効果的なグロースハック

ちなみにnanapiのグロースハッカー(SEOの戦略とかを考えている人)はこの人。けんすう@kensuu
Webマーケティングに詳しくなりたい人はフォローしておくとよいかもしれない。

ここからは、そんなに無理をしないで、検索結果にヒットさせつつ1ページ目の上部に掲載されやすくなるSEOライティングの方法について書いていく。

誰かに読まれる記事ってどんな記事?

まずはこれを自分のことに置きかえて考えてみてほしい。どんなときにググるのか?おそらく、何かを知りたいときだったり、欲しいものを買うときだったりすると思う。つまり、何らかの悩みを解決したいときに検索する。知りたい。見たい。買いたい等。

なので、記事を書くときは、以下を考えてみてほしい。

  • そもそも誰がどんなことで悩んでいるときに役に立つ記事なのか?
  • その悩みを抱えている人は、どんなキーワードで検索をするか?
  • その悩みはどんな情報があれば解決するか?

    実は、これ最強のSEOであり、↑の3つを一番しっかり考えているページが1位になる。

1.記事タイトルの考え方

まずは、その記事がどんな検索キーワードで検索してきた人が読むのか?を考えたときに思いついたキーワードを記事に含めよう。例えば、この記事は、「読まれる記事 書き方」などで検索する人にとって有益かもしれないので、このキーワードを含めてみる。

それと同時に、検索結果には30文字前後が表示されること。はてなブックマークなどのトップページでは32文字程度が表示されることを考えると、だいたい記事のタイトルは30文字ピッタリかどうか、ぐらいを狙って考えると良い。(ちなみにDorubyはサイト名とカテゴリ名が自動的にタイトルに含まれる設定なので、それを考慮して20文字くらいにすると良い。)

例えば、『自社ブログで記事を書く人必見!無理せず読まれる記事を書く4つのポイント|SEO|Doruby』 とかである。だが、仮にこの記事をこのタイトルで投稿しても、このキーワードの競合は強いので1ページ目に入るかは難しい。(”|”以降は自動部分)

2.記事の説明文の考え方

これはその記事が、検索されたときに検索結果のタイトルの下にでてくるものである。そのため、検索結果に並んだ時に、この記事を読みたい!となるような情報を書くといい。PCの検索結果では120文字、スマホでは50文字くらいが表示されるので、50文字で読んでも意味が通じて、120文字で読んでも大丈夫な説明文にするといい。重要な部分は前半の50文字に含めよう。

3.文章を1500文字以上書く。

一概に、文章量が多ければ多いほど、SEOに強くて検索上位にあがりやすいか?と言われると、悩みによって違う。しかし、おおよそ無理なく、かつ品質の高い記事のラインは1500文字程度ではないかと思う。なので、これくらいの文章量を超えることをまず目指すと良い。

4.見出しをつける

文章はある程度見出しが付いていたほうが読みやすい。それに検索エンジンにとっても、ページに書かれている内容が理解しやすい。なので、見出しをつけよう。

わかりやすければ、基本的に見出しは何でもいい、文字数も特に決まりはない。区切り方にも決まりはないが、少なくとも500文字に1個は見出しがあったほうがいい。

まとめ

ここまでのことを実践するだけでも、なにも意識しないで記事を書くよりSEOに強くて役に立つ記事になりやすい。

おまけ

記事のアイキャッチ画像は付けたほうがいいか?

付けたほうが良い。SEO的にも、ソーシャル流入を狙う観点でもアイキャッチ画像があると効果が高いと言われている。アイキャッチなので、なるべく記事と関連性がある画像や写真を使うといい。特に解像度が高くてサイズが合っていると記事に馴染む。よくわからないという人は、商用利用可能で高品質なフリー素材を提供している「ぱくたそ」にアクセスし、気に入った写真を使ってみよう。

ちなみにこの記事の写真はこれ。
なかなか仕事が捗らない新卒の女の子 [モデル:河村友歌]

SEOについてもう少し知りたい

SEOについておすすめのサイトの中でも、わかりやすく、SEOに詳しい人が良く見ているサイトは以下。

SEO HACKS
バズ部

関連記事や参考記事のリンクをはる

SEO的にも、記事の読者的にも、その記事と一緒に読むと役に立つ記事であったり、その記事の参考や情報ソースとなる記事へのリンクは有益なため、できることなら付けたほうが良い。その際、テキストリンクの文字列は、URLをそのままにするのではなく、この記事のようにリンク先の記事のタイトルであったり、関係のある文章にしよう。

どんな記事を書くか決めるのが難しい。記事執筆に時間がかかる。

そんな人は、仕事で覚えたことをメモやアウトプットとして整理して記事にしてみよう。Dorubyに記事を投稿するためだけに何かを勉強するのではなく、仕事で覚えたことを投稿すれば、既に知っていることを書くだけなのでネタも尽きないし、執筆もはやくなる。

SEO等について質問や要望があれば追記していきます

この記事を読んでみて、聞いてみたい事、調べてもでてこないことなどあれば気軽に質問をください。内容によっては追記していきます。

HTTPS使用暗号スイートをサーバーログ記録

0

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

Webサーバー(Apache、Nginx)の利用者が、HTTPS通信時に使用するSSL(TLS)のバージョンプロトコルと暗号スイート(cipher)をサーバー側のログに記録します。

はじめに

Webサーバーのログに、SSL(TLS)のバージョンプロトコルと暗号スイートを記録しているでしょうか?
これらの情報を記録し、アクセス解析することによって、強度に問題のある暗号通信が、どの程度使われているか知ることができます。
また、クラウド環境など各種負荷が直接料金に反映されることもあるため、環境に適した暗号スイートを選択する際に必要な情報としても利用できます。

通信中については、スマホアプリのHTTPS通信をBurp Suiteで確認などを参考にしてください。

Webサーバーの設定方法

それぞれ設定ファイルの適切な箇所に下記を追加します。
ログのパス、ファイル名などは任意。同時に記録する内容は、公式マニュアル等を確認ください。
設定後、設定ファイルの再読み込みか、再起動を行ってください。

残念ながら、この方法でハンドシェイクの段階は記録できません。

Apache 2.4系

SSLOptions +StdEnvVars
CustomLog logs/ssl_cipher.log "%t %h %{SSL_PROTOCOL}e %{SSL_CIPHER}e %{SSL_SESSION_ID}e %{SSL_SESSION_RESUMED}e"

Nginx

log_format ssl "$time_local $remote_addr $ssl_protocol $ssl_cipher $ssl_session_id $ssl_session_reused";
access_log /var/log/nginx/ssl_cipher.log ssl;

ログ出力例

FirefoxとIE6で、記録したログは以下のような表示になりました。
バージョンプロトコルと暗号スイート(cipher)が記録されています。

SSL TLS cipher ログ

関係団体の公開情報など

BEAST、 Poodleなど、プロトコルに対する攻撃が有名です。

2015年06月、IETFがSSLv3の廃止を求めるRFC7568を公開しています。
またIPAも、SSL 3.0 の脆弱性対策について(CVE-2014-3566)を掲載しております。

ECサイトなどでも用いられるクレジット業界におけるグローバルセキュリティ基準 PCI DSSでは、
2016年11月からの審査基準 v3.2で、新規実装では、SSLプロトコル、初期のTLSが使用できません。
また、稼働環境でも2018年6月30日SSLプロトコル、初期のTLSの使用を停止する必要があり、それ以前にリスク軽減と移行計画が必要になります。
詳しくは、PCI Document Libraryを、確認してください。

参考サイト

SSL導入になどに、下記のサイトを参考にApacheの場合はSSLCipherSuite、Nginxの場合は ssl_ciphersを、設定ファイルに確実に記述しましょう。

SSL/TLS暗号設定ガイドライン~安全なウェブサイトのために(暗号設定対策編)~
IPAのサイトです。PDFで全93ページのSSL/TLS暗号設定ガイドラインや、チェックリストがダウンロードできます。

CRYPTREC
日本政府による暗号技術評価プロジェクト。

Mozilla SSL Configuration Generator
Mozillaが提供している、主要ミドルウェアのSSL設定ファイル生成サイト。

Symantec CryptoReport
Symantecが提供している、外部からのSSLチェックが実施できるサイトです。

SSL Server Test (Powered by Qualys SSL Labs)
Qualysが提供している、外部からのSSLチェックが実施できるサイトです。

おわりに

元々、ECサイトやマーケティングに重きを置いている組織では、機会損失になることを恐れ、対応端末の減るバージョンプロトコルや暗号スイートの見直しをなかなか行ってくれない傾向があります。
Googleアナリティクスなどを利用し、古いブラウザの使用状況や購入状況を確認して、見直すのがスマートです。
しかしながら、パフォーマンス測定など、実際使用された暗号スイートを記録したい事情がある場合などは、今回のようにログに記録してみてください。

関連記事など

脆弱性診断サービスなど

弊社、アピリッツではセキュリティ診断サービスを行っております。

下記リンクから内容の確認と、問い合わせが可能です。

http://security.appirits.com/

最近人気な記事