kazasiki's blog

プログラミングとかVRゲームとか

私がスタンディングワークをやってる理由

私は普段立って仕事をしてます。かれこれ2年以上は立って仕事をしてます。念のために書いておくと私の職業はプログラマーです。

初対面の同僚の5人に1人くらいに「なんで立ってるんですか?」と聞かれます。最近転職したこともあって短期間に何回も同じ説明をするうちに「あ、これは文章にしたほうが良いな」と思って今キーボードを叩いております。

結論から書くと、私が立って仕事をしてる理由は座るのが面倒になったからです。

スタンディングワークを始めた理由

スタンディングワークを始める理由は大体以下のどれかだと思います。

  • 長時間イスに座っていて腰が痛くなるのが嫌
  • 立ってるほうが集中できる(眠くならない)と聞いた
  • なんかかっこいい

で、私がスタンディングワークを始めた理由は、(当時の)同僚がスタンディングワークを始めたが3日坊主になったので、それ用の台(?)をノリで譲り受けたからです。ちなみにその台は以下です。

isuta.jp

同僚はきっと上記の理由のどれかで始めたんだと思いますが、足が痛くなるとのことですぐやめてしまいました。自分は特になにかメリットを期待してた訳ではないのですが、くれるというのでもらいました。(1000円くらいは払った気がします)

スタンディングワークを続けてる理由

これは冒頭にも書いたとおり座るのが面倒くさくなったからです。

スタンディングワークを始めて、日常の座るという動作は案外面倒くさいものだということに気づきました。足に負荷がかかります。座ってる状態からウォーターサーバーに水を汲みに行く場合、立ち上がって、歩いて、もう一度座る必要がありますが、もとから立ってればただ歩くだけです。

私は自宅でも基本は立ってゲームをしたり本を読んだりしてますが、ちょっと離れたところの物をとったりするのもあまり苦になりません。もとから立ってるので。

立ってること自体が苦になるかどうかは恐らく体格や筋肉量によると思います。自分は痩せ型なのでたまたま問題なかったというだけかと。

スタンディングワークのために買ったもの

貰い物から始まったスタンディングワークとはいえ、色々入り用になるものがあります。

自宅用に買ったデスクです。デスクの下の空間が広いのでデスクトップPCなどを詰め込んだメタルラックを格納できたりします。VRHMDやコントローラもそこに収まってます。

サンコー クランプ式キーボードトレイ KEYBTRAY

サンコー クランプ式キーボードトレイ KEYBTRAY

  • 発売日: 2008/06/17
  • メディア: Personal Computers

↑のデスクにつけたキーボードトレイです。デスクの上に直接キーボードを置くとちょっと高かったので調整のために買いました。デスクも広く使えるので便利です。

[アモジ] サンダル AM1702 ゴールド 25.0m

[アモジ] サンダル AM1702 ゴールド 25.0m

  • メディア: ウェア&シューズ

自宅用のスリッパです。流石に裸足だと足の裏に負担がかかりすぎてかかとが痛くなります。足底腱膜炎というらしいです。ある程度底の厚いものを選びましょう。オフィスでも同様の考慮は必要ですが、ある程度底がちゃんとしてれば普通の靴でも大丈夫だと思います。私は革靴でも問題ないです。(人によるかもしれません)

オフィスで外付けマウスとか置くように使ってた台です。今はMacbook備え付けのTrackpadで満足してるので使ってません。実はこれもスタンディングワークに挫折した同僚からの貰い物です。

昇降機とか台とか

ディスプレイをデスクに置いてデュアルディスプレイなどをしてる場合、ディスプレイの高さを調整する必要があります。Webで探せばいくらでも昇降台が見つかると思いますが、私はオフィスでは余ってたMacbookの空箱を積み上げてディスプレイの高さを調整しています。自宅はちゃんとスタンディングデスクを買ったので不要です。

おまけ

実は以下の記事で私のスタンディングデスク環境が見れます。

techblog.kayac.com

皆様も良いスタンディングライフを。

git-flowとgithub-flowの使い分け

tl;dr

  • 1~2日以上を要するQAやリリース作業を含む開発フローを想定するならgit-flowを使う。
  • CIをパスさせて速やかにデプロイするフローを想定するならgithub-flowを使う。
  • 初期バージョンのリリース作業(一般的にはQA)が始まるまでは、masterブランチをベースにgithub-flowで十分。
  • 自分たちのアプリケーションのリリースフローや品質要求について予め考えておこう。

前置き

世の中にはアプリケーション開発のブランチ運用方法としてgit-flowとgithub-flowというものがあります。 どっちを使うか、もしくは自分たちにどっちが適しているかというのはよくある悩みだと思います。 この記事では、それについてざっくりまとめてみます。

この記事ではgit-flowおよびgithub-flow自体の解説はしません。

git-flow

git-flowはリリースにあたって1~2日もしくはそれ以上の作業が必要になる場合に適したなフローです。

いくつかの機能をまとめてリリース作業に引き渡すときにreleaseブランチを作ってソースをfixします。リリース作業にはQAや(もしあるなら)リリース判定会議、iOSアプリの場合はアプリの審査などが含まれます。

リリース作業中にも次期バージョンの開発は止まらないのであれば、それはdevelopブランチで行われます。

github-flow

github-flowの大前提は機能ごとに開発、masterブランチにmergeして、即デプロイすることです。github-flowの方がgit-flowよりも簡単ですが、この前提を満たせるかどうかを考える必要があります。

例えばA,Bをばらばらに開発した場合に、両方ともを含めたQAが必要となるとgit-flowは成立しません。基本的にはCIのテストや開発者のチェックでOKというフローになってる必要があります。

もし、少人数の開発などで並列して機能開発するケースが稀であるならgithub-flowで構いません。

その他いろいろ

例えば、iOSアプリとそこから叩かれるAPIサーバを開発している場合を考えます。

iOSアプリの更新には審査期間があるため、APIサーバのiOSアプリと連携する部分(API定義など)はgit-flowに従う必要があります。逆に、サーバに閉じてる修正(バグ修正や細かいチューニングなど)はgithub-flowで作業できます。この場合、サーバに閉じている修正はgit-flowで言うところのhotfixと同等に扱っているとも言えます。

また、どっちのフローを使うにしても初期バージョンがリリース準備に入るまではmasterブランチをベースにしてgithub-flow的な運用をすれば十分だと思います。ただ、例えば masterブランチへのpushをトリガにしてtagをうつ みたいなリリース関わる処理をCIに組み込む場合、ちょいちょい余計な処理が走る気持ち悪さがあるかもしれません。許容できるなら許容してしまいましょう。

修正をリリースするにあたってどのようなQAをして、どのような作業が必要になるのか、自分たちのアプリケーションのリリースフローや品質要求について予め考えてみましょう。

なんだか当たり前のことを膨らませて書いたような気がしますが、皆様の参考になれば幸いです。

VRゲームのおすすめの情報源一覧

VRゲーム系のニュースサイト結構増えてきたしおすすめの情報源をまとめておきます。(2019年9月時点)

MoguLive

www.moguravr.com

VRエンタメ全体を取り扱うニュースサイトです。実は貴重な日本語の情報源。普通に最新記事一覧をみるとVtuberの記事が多いので、ゲームカテゴリだけ見るのをおすすめします。

2chVR dicordサーバ

discord.gg

VRゲーム関連で日本語で運営されているdiscordサーバは私の知ってる限りここだけです。適当にゲームをカテゴリ分けしたチャンネルがあるくらいで基本はゲーマー同士で適当に話す場です。ボイスチャットだけでなくテキストチャットも盛んです。(私が管理してるわけではありません)

SteamのVRカテゴリ

store.steampowered.com

ほとんどのVRゲームはここに並ぶので。

RoadToVR

www.roadtovr.com

ゲーム系の情報が多めのサイト。英語。話題のゲームやハードウェアのニュースやレビューが多いのでまず見ておくのがおすすめします。

Nathie Youtubeチャンネル

www.youtube.com

VRゲームの実況動画をメインに取り扱うYoutubeチャンネル。英語。動画の構成はわかりやすいので英語わからなくてもゲームの雰囲気を掴むにはちょうど良いです。

Oculus Questのおすすめゲーム5選

さてOculus Questが発売しましたね。日本各地でやっと届いたとの声が上がっています。私はRiftを持ってるのでQuestは買ってないのですが、当然ながらゲームのラインナップはかなり被ってるので先輩風吹かせておすすめ記事でも書こうと思います。

Questのアプリストアを上から見ていった順で、自分がRiftで遊んで面白かったやつにコメントしていきます。

Moss

www.youtube.com

VRでは珍しい3人称視点のアクションパズルゲームです。3人称視点なので酔いの心配がありません。ゲーム性はゼルダっぽい感じです。キャラクターが可愛く、物語もよくできてます。アクション部分は若干難易度は低めで、パズルの難易度は丁度良いくらいです。

Quest発売に合わせてコンテンツが追加されたようですが、追加される前は5時間程度エンディングにたどり着けるボリュームでした。

Angry Birds VR: Isle of Pigs

www.youtube.com

ゲーム性はアングリーバードを3Dにしたそのまんまな感じです。パチンコで鳥を飛ばす操作はVRのコントローラにもよく馴染んでいます。グラフィック的にも質がよく、物理演算にも違和感&バグがないのが素晴らしいです。

レベルデザインも良好で、クリアするだけなら2~3回リトライすれば行ける程度で、星3つを狙うならやり込む必要があります。立体なのでスマホの平面なものに比べると若干難しい気がします。

ROBO RECALL unplugged

www.youtube.com

人類に反旗を翻したロボットをぶっ壊していくゲーム。グラフィックのクオリティが高く、1人称で自由に動けるVRらしいゲームです。日本語非対応ですが、ストーリーはあんまりゲームに関係してこないので気にする必要はありません。

実は銃を撃つよりワープで近づいて首をねじ切るほうが強いです。

SUPERHOT VR

www.youtube.com

自分が動いている時だけ時間が進むというルールで進むガンアクションゲーム。映画のワンシーンのような動きができて楽しいゲームです。界隈ではマトリックスシミュレータと呼ばれています。

個人的には簡単すぎな印象で、ゲームとして楽しむというよりは映画のワンシーンを疑似体験することを楽しむものというイメージです。

Virtual Virtual Reality

www.youtube.com

VRの世界の中でVRの世界に入るアドベンチャーゲーム。日本語対応してます。AIが支配する世界で、人間はAIのご機嫌を取るための奉仕活動を仕事にしている、みたいな世界観です。自分はもちろん人間です。

シュールな雰囲気や徐々に謎を解き明かしていく感じが楽しいゲームなので、そういうアドベンチャーゲームが好きな人にはお勧めです。ホラー要素はありません。

その他おすすめ

自分はプレイしてませんが、以下のゲームは評判も良く、いつかプレイしたと思いながらやれてないものです。

  1. Beat Saber | Oculus ライトセーバー振るゲーム
  2. Racket Fury: Table Tennis VR | Oculus 卓球するゲーム
  3. RUSH | Oculus スカイダイビングするゲーム
  4. Space Pirate Trainer | Oculus 宇宙海賊になってロボット撃ち落とすゲーム

では皆さん楽しいVRライフを

enum値を引数に持つメソッドを持つstructのInterfaceを作るコツ

golangではライブラリ自体はinterfaceを提供しておらず、使う側で必要になったら作れという風潮があると思ってます。golangのinterfaceはダックタイプなので、概ねはそれで上手く行きます。
ただ、引数にenum値がある場合、素直にメソッドのシグネチャをコピペするとinterface自体が実装に依存してしまうので一工夫が必要です。
やってることはinterfaceにも同じenumを作りましょうというだけなのでなんということはないのですが、DIとかに慣れてない人はちょっと嵌りそうな気がします。

実装例は以下です。参考になれば幸いです。

package main

import "fmt"

// 例えば、以下のようなinterface化したいライブラリ(?)があったとする。

type Hoge struct {
    a int
}

type Mode int

const (
    ModeOne = iota
    ModeTwo
)

func NewHoge(a int) *Hoge {
    return &Hoge{a: a}
}

func (h Hoge) Mul(m Mode) int {
    return h.a * int(m)
}

// ここから下はインターフェース

type MulerMode int

const (
    MulerModeOne = iota
    MulerModeTwo
)

// 普通にやるとMulの引数の型にModeを指定してしまうが、そうするとInterfaceが実装に依存してしまう
// ここでは同じ値域のenum、MulerModeをinterfaceとセットで用意して回避する
type Muler interface {
    Mul(m MulerMode) int
}

// ここから下はインターフェースを通じてHogeを使いたい場合

type MulerImpl struct {
    Hoge
}

func NewMulerImpl(a int) *MulerImpl {
    return &MulerImpl{Hoge: Hoge{a: a}}
}

func (m *MulerImpl) Mul(mm MulerMode) int {
    var md Mode // ここでenum同士のマッピングをする
    switch mm {
    case MulerModeOne:
        md = ModeOne
    case MulerModeTwo:
        md = ModeTwo
    default:
        return 0 // 本当はエラーとかだと思う
    }
    return m.Hoge.Mul(md)
}

// 実際に呼び出す

func main() {
    var m Muler
    m = NewMulerImpl(1)
    fmt.Print(m.Mul(MulerModeOne))
}

Slidesファイルをpdfに変換するGoogleAppsScript

仕様書やワイヤーフレームをGoogleSlidesでよく作るor作ってもらうんだけど、地味に開くのに時間がかかるのでpdfかするgasを書きました。 destDir.createFile(file.getBlob()) だけで変換自体はできるんだけど、同名のファイルが既にあれば消したり、前回変換したときから更新されてるやつだけ対象にしたら結構なスクリプトになったので公開します。

function myFunction() {
  var srcDir = DriveApp.getFolderById("slides folder");
  var destDir = DriveApp.getFolderById("pdf folder");
  var files = srcDir.getFiles();
  while (files.hasNext()) {
    var file = files.next();
    if (file.getMimeType() != "application/vnd.google-apps.presentation") {
      continue
    }
    destName = file.getName() + ".pdf";
    var exist = false;
    var dFile;
    destFiles = destDir.getFilesByName(destName); // 毎回getFilesByNameをするの効率悪い...
    while (destFiles.hasNext()) {
      var destFile = destFiles.next()
      if (destFile.getName() == destName) {
        exist = true;
        dFile = destFile
        break;
      }
    }
    if (!exist){
      destDir.createFile(file.getBlob())
    }
    if (exist && dFile.getLastUpdated() < file.getLastUpdated()) {
      dFile.setTrashed(true)
      destDir.createFile(file.getBlob())
    }
  }
}

自分が重宝してるGolangのtesthelper

golangに限らず、テストを書いてると同じようなパターンを何回も書くことが多いです。 そこで私が重宝してるヘルパー関数を紹介します。

golangの関数を書く場合、戻り値にerrorと何かを返すパターンが多いですね。 そこで以下のヘルパー関数を用意します。

// CheckErr エラーの存在チェック
func CheckErr(t *testing.T, i int, ok bool, err error) bool {
    t.Helper()
    if ok {
        if err != nil {
            t.Errorf("[%d] err should be nil, but got:%s", i, err)
            return true
        }
        return false
    }
    if err == nil {
        t.Errorf("[%d] err should not be nil, but got:%s", i, err)
        return true
    }
    return true
}

err以外のレスポンスをチェックする場合はfalseを返します。
iはTDDのループのindexを入れます。TDDしてるとどこででたエラーが出たかわからなくなることがあるので...
okはerrが出るテストケースかどうかをboolで入れます(trueならエラーがない)
errはテスト対象の関数のerror戻り値を入れます

例えばこんな感じで使います。
errorを返す場合は、他の戻り値はどうせゼロ値なのでチェックしないでしょう。

func Something(x int) (string, error){
    if x < 0 {
        return "", errors.New("")
    } else {
        return strconv.Itoa(x), nil
    }
}

func TestSomething(t *testing.T){
    testTable := []struct{
        x int
        res string
        isErr bool
    }{
        {x: 1, res: "1", isErr: false},
        {x: -1, isErr: true},
    }
    for i, tt := range testTable {
        res, err := Something(i)
        if !CheckErr(t, i, tt.isErr, err) {
            if w, g := tt.x, res; w != g {
                t.Errof("unmatch w: %s, g: %s", w, g)
            }
        }
    }
}

さらに例えばerrorがcodeを返すように拡張(?)してる場合もあるでしょう。

// ErrCodeを返すMyErrorがあるとする
type MyError interface{
    Error() string
    ErrCode() uint32
}

type HogeError struct{}

func (e HogeError) Error() string {
    return "error"
}

func (e HogeError) ErrCode() uint32 {
    return 1001
}

その場合はCheckErrも以下のように拡張します。

// CheckErrCode 想定されるエラーコードがあればそれを確認する
func CheckErrCode(t *testing.T, i int, code uint32, err error) bool {
    t.Helper()
    noErr := code == 0
    if !noErr && err != nil {
        if terr, ok := errors.Cause(err).(MyError); ok {
            if w, g := terr.ErrCode(), code; w != g {
                t.Errorf("[%d] err_code unmached. want:%d, but got %d", i, w, g)
            }
        } else {
            t.Errorf("[%d] err type unmached. %T", i, terr)
        }
    }
    return CheckErr(t, i, noErr, err)
}

func渡したり構造体作ったりもせずシンプルな関数なので適当に拡張できて便利です。