勉強不足で至らんブログ

勉強不足ですが、何か発信できればと思っています。

UnityのスマホネイティブプラグインをKotlin/Nativeで共通化する

毎度の自己Qiita転載です

qiita.com

Unityのスマホネイティブプラグインを言語統一できないか?

昨今のスマホネイティブプラグインの言語の選択肢としてはJava, Kotlin, Objective-c, Swiftがあげられるかと思います。 たまに自分も趣味などでプラグインを使うことがあるのですが同じような処理を別で書かないといけないのが手間だと思っていました。 そこで昨年くらいからスマホのネイティブ界隈で話題になっていたKotlin/Nativeに目をつけてUnityで実行させてみたという記事です。

そもそもKotlin/Nativeとは

まとめている記事もありましたので参照させて頂きます。

基本的にはスマホネイティブの共通化できるロジックをKotlinで書いて共通化させようぜ!ってことなのですがAndroidでは.jarとしても吐き出せますし、iOSは.frameworkとして吐き出せるのでネイティブにとって扱いやすいものになっています。

さて、今回検証に使用したリポジトリです。動かして見たい人は是非ご活用ください。

実行した結果

Android iOS
Screenshot_20190502-040341.jpg Simulator Screen Shot - iPhone Xʀ - 2019-05-02 at 10.31.23.png

文字列をOS毎に変えるという処理ですが呼び出すメソッドは1つにしてあります。

準備

Kotlin/Native自体の作成方法は既にわかりやすい記事がありますのでそちらを参照させていただきます。 Kotlin/Nativeチュートリアル Android, iOS編

自分はここを参考にさせて頂きました。

Androidのネイティブが自分はよくわからなかったので、最初は基本的にコピペで作って必要な箇所を変えていきました。

今回、上記記事の「Common moduleの解説」の章まで出来たら一旦は大丈夫です。 上記の記事ほぼそのままですがソースコードを載せておきます。

共通

package com.sample.mizotake.kotlinnativeforunity

expect fun platformName(): String

public class common {
    public fun createApplicationScreenMessage(): String {
        return "Call Kotlin Native on ${platformName()}"
    }
}

Android

package com.sample.mizotake.kotlinnativeforunity

actual fun platformName(): String {
    return "Android"
}

iOS

package com.sample.mizotake.kotlinnativeforunity

import platform.UIKit.UIDevice

actual fun platformName(): String {
    return UIDevice.currentDevice.systemName() +
            " " +
            UIDevice.currentDevice.systemVersion
}

共通処理にUnityで呼び出すクラスとメソッドを定義します。OS毎に変える処理はexpect actual処理でinterfaceのように切り出して呼べるようです。

Unityへの導入

Android

自分もよく把握できていませんがGradle Syncをするとbuildというディレクトリができて スクリーンショット 2019-05-02 11.06.40.png

プロジェクト名 + android.jarができていました。 もし出来ていない場合は スクリーンショット 2019-05-02 11.04.44.png 右端にGradleというタブがあるのでそこからbuildの項目を見るとbuildの詳細一覧があるのでandroidJarをダブルクリックすれば走り出してjarができるかと思います。

吐き出されたjarをUnityのPlugin/Androidに放り入れるだけです。 これでUnityへの導入は完了です。

iOS

こちらは先ほどのKotlin/Nativeチュートリアル Android, iOS編の「iOSアプリ」の章にあるbuild.gradleの追記だけ行いましょう。

...

task packForXCode(type: Sync) {
    final File frameworkDir = new File(buildDir, "xcode-frameworks")
    final String mode = project.findProperty("XCODE_CONFIGURATION")?.toUpperCase() ?: 'DEBUG'

    inputs.property "mode", mode
    dependsOn kotlin.targets.iOS.compilations.main.linkTaskName("FRAMEWORK", mode)

    from { kotlin.targets.iOS.compilations.main.getBinary("FRAMEWORK", mode).parentFile }
    into frameworkDir

    doLast {
        new File(frameworkDir, 'gradlew').with {
            text = "#!/bin/bash\nexport 'JAVA_HOME=${System.getProperty("java.home")}'\ncd '${rootProject.rootDir}'\n./gradlew \$@\n"
            setExecutable(true)
        }
    }
}

tasks.build.dependsOn packForXCode

ここの部分ですね。これを追記して./gradlew buildすることで スクリーンショット 2019-05-02 11.17.06.png

main.frameworkができます。これをUnityのPlugin/iOSに放り込めばframeworkの導入は大丈夫ですが、iOSの場合もう一手間必要です。 externの実装がないとC#では呼び出せませんそのためPlugin/iOSフォルダに

#import <main/main.h>

extern "C" {
    const char* createApplicationScreenMessage() {
        NSString *message = [[Maincommon alloc] init].createApplicationScreenMessage;
        return strdup([message UTF8String]);
    }
}

を追加しましょう。これを追加することで先ほど作ったKotlinで書いたコードのframeworkを参照できます。 ここでcommonというkotlinファイルを作ったがMaincommonって何だろう?ってなると思います。どうやらframeworkに吐き出す時に変換されているようです。 それを確認するにはAndroidStudioでframeworkのHeaderを見ると一番下の行に自分で実装した処理が追記されていると思います。 スクリーンショット 2019-05-02 11.22.16.png

これを参考にしてObjective-c++でインターフェースを定義する必要があります。

ちなみにSwiftだとMaincommonという変換名ではなくcommonで呼び出せそうですがSwiftを使うために手間をかけるよりObjective-c++を書いた方が早いと自分は思うのでこのまま進めます。

C#で呼び出す

事前準備は終わりました。 UnityではuGUIのTextにネイティブで呼び出した文字列を表示させます。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Runtime.InteropServices;

public class CallKotlinNative : MonoBehaviour
{

#if UNITY_IOS
    [DllImport("__Internal")]
    private static extern string createApplicationScreenMessage();
#endif

    private Text viewableText;

    void Start()
    {
        var pluginMessage = "";
#if UNITY_ANDROID
        using (var plugin = new AndroidJavaObject("com.sample.mizotake.kotlinnativeforunity.common"))
        {
            pluginMessage = plugin.Call<string>("createApplicationScreenMessage");
            Debug.Log(pluginMessage);
        }
#elif UNITY_IOS
        pluginMessage = createApplicationScreenMessage();
#endif
        viewableText = GetComponent<Text>();
        viewableText.text = pluginMessage;
    }
}

C#側は普通にネイティブプラグインを呼び出すだけですね。 これを実機ビルドまたはシミュレータービルドすることで動作の確認ができると思います。

終わりに

Kotlin/NativeのUnityProjectへの導入は手間が必要かと思っていましたが思った以上に簡単でした。ただ、iOSの導入のexternだけどうにかならなかいとAndroidProject内に.mm入れてみて.frameworkだけ吐き出して更新させるなどをしようと思いましたがうまく行きませんでした…Androidネイティブのディレクトリ構成やtaskのカスタムに詳しければどうにかできるのかな?と思っています。 Kotlin/Nativeを使えば基本的に言語はKotlinひとつに統一できますし、共通処理やOS依存処理も問題ないのではない気がしています。個人的にKotlinでUIKitなどもimportして使えることに驚きました。 何にせよ扱う言語は少ないに限ると思っていますのでKotlin/Nativeは良いものだと思います。ただ現在betaなので書き方や吐き出し方が変わる可能性は高いです。

チームラボを退職しました

新卒で入って2年勤めたチームラボを退職しました。

4月から新しいところで働きます。4月1日から勤務なのでエイプリルフール直撃のため勤務が嘘でないことを祈ってます。

 

え?展示とかしてるチームラボ…?と思うかもしれません…

 

そうだけど…自分の業務はそうじゃない…!(ことが多い)

自分はスマートフォンチームに所属していたので展示系ではなくスマートフォンアプリの開発を行っていました。

所謂受注開発です。なので色々な企業さんのサービスに関らせて頂いてました。

 

ですが転職を決めて、退職エントリーを書こうと思い記事にします。

転職の経緯はこちら

maketake.hatenablog.com

 

入社の経緯

大八耐というハッカソンでチームラボ賞をもらってから会社見学に招待され「展示以外にもWebやアプリのサービスをやってる会社なんだ…幅広いから面白そう」と思ってインターンに応募したら通ったところから始まり、インターンの最終日に入社選考受けたい旨を直接伝えてみると面接や試験を設定してもらい内定を頂くことができました。

実際インターンでも当時やったことがなかったUnityのEditor拡張をやらせてもらったりと勉強になることが多かったです。

 

しかし、スマートフォンチームは基本的にネイティブ開発なのです。自分はUnityしか触ってなかったのでネイティブで、iOSの開発をするのかAndoirdの開発をするのか選ばなくてはいけません。昔からずっとAndroidユーザーな自分は

iOSを持ったことがないのでiOS開発したいです!」

と言って、あほを丸出しした自分を受け入れてくれてiOSのネイティブ開発をすることになりました。

 

やってきたこと

新規開発から保守開発まで様々な案件に関りました。開発者が自分1人のみや3人以上の多いものもあったり、コードレビューだけやったりと色々な関わり方をしていました。まだまだペーペーな自分ですがお客さん先に行ってミーティングをして、上流工程にも関わったりと実装以外の部分もやらせていただいてました。すごくよい経験になりました。

Wantedlyの記事書かせてもらったりなど開発以外のところも触れさせて頂いて良かったと思っています。

 

やりたいことがあれば言っとくだけお得な雰囲気だったので言うだけ言ってるとやらせてもらいました。もちろん案件のタイミングなどもあるので難しいこともありますが…

 

また、「八時間耐久作品制作会(仮) in 東京」というイベントを運営していたのでオフィス借りて行ったりもしていました。最初に行ったのは新卒で入って半年とかだったのですがオフィスでイベント行うことにも寛容でした。 

 

なぜ辞めるのか

結論から言うとゲームを作りたいからです。元々ゲーム作る人を目指していたのもあり趣味でゲームやコンテンツ開発はしていたのですが、本業にしたい思いが働いているうちに大きくなりました。

スマートフォンチームもデジタルコンテンツを作ることはあります。ですが、自分がタイミング的に関わることが少なくて自分の中で「もっとゲームのような楽しめるものを常に開発したいな」と思うようになったので転職を決意しました。

 

最後に

チームラボの人は良い人ばかりで人間関係がストレスになったことはありませんでした。PCも要望すれば大体どんなスペックでもOKだったのは嬉しかったです。社内でも新しい技術にも明るい人が多くてやりたいようにやれていたと思います。同期も楽しい人ばかりだし、新卒で入って良かったと思っています。

今後は新宿でゲームを作る会社に行くので楽しんで行こうと思います。

 

約束された欲望の干し芋リスト。 

amzn.asia


 

失敗LT祭! 〜俺の話を聞いてくれ〜 登壇してきました。

【ついに今日!】失敗LT祭! 〜俺の話を聞いてくれ〜 - connpass

にてUnityのCI/CD周りについて発表してきました。

初LTだったのでドチャクソ緊張して早口になってました。

 

www.slideshare.net

 

幅広くエンジニアが集まっている場所だったので詳細は省かせてもらってます。Cloud CI/CDいいっすね!ってふわっと話せたかなって思ってます。

詳細は以下記事

qiita.com

 

UnityでCI/CDの選択肢をどう増やすか、増やす方法についてLT & 記事書いてます。何か参考になれば幸いです。

 

スライドでサラッと触れていますが

can build android with fastlane on CircleCI by MizoTake · Pull Request #8 · GabLeRoux/unity3d-ci-example · GitHub

 

Unity + fastlaneを使ったCircleCI上のandroidビルドが可能になりましたのでプルリクエストでの公開状態となっています。(2019/02/21)

 

fastlaneであれば配信周りも自由度が広がるので是非とも参考にしていただけたらと思います。

 

iOSは頭の中にフローがあるものの手元に環境がないので…そのうち…です…

CircleCIでUnityのTest&Buildを雰囲気理解で走らせた

毎度毎度の自己Qiita転載でございます。 CircleCIでUnityのTest&Buildを雰囲気理解で走らせた

CircleCIを使ってみたかった

CircleCIなら制限時間はありますが無料でCI環境が使えて、GitHubから直接連携ができるので楽そう! またSlack連携なども情報はたくさんあるので応用は色々とできそう!

…Unityで動かす情報すくないな?ってことで触ってみました。

さて今回参考にしたRepositoryは2つです

上記からunity3d-ci-exampleをforkして

を作ってCircleCI連携を試してみたものの、そのままではうまく行かなかったです。 なので、どう解決したのかをunity3d-ci-exampleをベースに話します。

セットアップ

CircleCIやDockerの登録やらなんやらの説明は省きます。 https://circleci.com/

基本的には公式サイトに行ってGitHubやBitBucketの連携登録をすれば簡単にできるかと思います。

https://www.docker.com/

MacLinuxの方はすんなりセットアップできると思います、Windowsの方はセットアップして起動できてなさそうであればhyper-vという項目をONにしないといけないのでこちらを参考にしてみてください。

unity3d-ci-exampleで最初わからなかったのがREADMEにあるHow to activateという項目… 要するにUnityのLicense認証なのですが少し手間でした。

またこの段階からDockerが必要となります。Docker初心者ですが調べつつ何とかなったのでコマンドも覚えているものは残しておきます。 今回使用するContainerはgableroux/unity3dです。

DockerHubからimageを落として来ないといけないので落としてきます

docker pull gableroux/unity3d

落とせたかどうかは

docker images

で確認できます。

落とせていたら

UNITY_VERSION=2018.2.3f1
docker run -it --rm \
-e "UNITY_USERNAME=username@example.com" \
-e "UNITY_PASSWORD=example_password" \
-e "TEST_PLATFORM=linux" \
-e "WORKDIR=/root/project" \
-v "$(pwd):/root/project" \
gableroux/unity3d:$UNITY_VERSION \
bash

UNITY_VERSIONやUNITY_USERNAME、UNITY_PASSWORDは適宜変えましょう。 何をしているかというとgableroux/unity3dを起動させるときに環境変数として値を渡しているみたいです。 コマンドの処理が終わるとDockerが起動してDocker上のbashが操作できるようになります。 一回Dockerから出たい時は

exit

だけ打てば出れます。再度入る時は入った時と同じコマンドでいいと思います。 次にDocker上で

xvfb-run --auto-servernum --server-args='-screen 0 640x480x24' \
/opt/Unity/Editor/Unity \
-logFile \
-batchmode \
-username "$UNITY_USERNAME" -password "$UNITY_PASSWORD"

Unityの起動を行いLicense認証をしようと動きます。 うまくいくと

LICENSE SYSTEM [2017723 8:6:38] Posting <?xml version="1.0" encoding="UTF-8"?><root><SystemInfo><IsoCode>en</IsoCode><UserName>[...]

というxmlがログに現れるのでunity3d.alfという名前でホストのOSに保存しましょう。 次に https://license.unity3d.com/manual をホストのOSで開いてunity3d.alfを選択して送信します。 するとUnity_v2018.x.ulfというファイルがダウンロードされます。 How to activate的には、ここまでで区切りですがCircleCIの場合はもう少しセットアップが必要です。

Unity_v2018.x.ulfというのがUnityのLicenseあるよ!という証明するファイルになるのですが、そのままgit管理してしまうのは危険かもしれません。PlusライセンスやProライセンスだと他の人がアクティベートできてしまうからです。(たぶん) そこで自分でKEYを決めて暗号化をかけてCircleCI上で復号化して使おうじゃないか!という流れです。

次もホストOSでの処理になります。

export KEY=insert-your-strong-generated-key-here
openssl aes-256-cbc -e -in ci/Unity_v2018.x.ulf -out ci/Unity_v2018.x.ulf-cipher -k $KEY
git add ci/Unity_v2018.x.ulf-cipher
git commit -m "Add encrypted Unity_v2018.x.ulf using openssl aes-256-cbc KEY"

insert-your-strong-generated-key-hereを好きな文字列に置き換えて覚えておいてください。後々CircleCIの環境変数として保存します。 そして暗号化されたファイルがUnity_v2018.x.ulf-cipherとなります。間違ってもUnity_v2018.x.ulfを追加しないように気をつけてください。 そのためリポジトリとは別のディレクトリで作業してUnity_v2018.x.ulf-cipherのみリポジトリに追加する方が安全かもしれません。

CircleCIに環境変数を登録する

先ほど暗号化に使ったKEYをCircleCIに登録します。 CircleCIとリポジトリを連携するとリポジトリに対して設定が行えるようになります。設定画面にある 名称未設定.png

Enviroment Variablesの項目があり選択すると画像にはないですが右上にAdd VariablesというボタンがあるのでKEYという名前で環境変数を登録しましょう。

セットアップは終わりです。

config.ymlを書くぞ

CicleCIで処理して欲しい項目をyml形式で書くことになります。 保存場所はリポジトリのルートに.circlciフォルダを作って配下にconfig.ymlを追加してください。 今回使用したのは以下のymlです。(unity3d-ci-sampleのものをベースにしました)

version: 2
references:
  docker_image: &docker_image
    docker:
      - image: gableroux/unity3d:2018.2.6f1
  setup_unity_license_env_var: &setup_unity_license_env_var
    command: |
      mkdir -p /root/.cache/unity3d
      mkdir -p /root/.local/share/unity3d/Unity/
      openssl version
      openssl aes-256-cbc -md md5 -d -in ./ci/Unity_v2018.ulf-cipher -out /Unity_v2018.ulf -k $KEY
      export UNITY_LICENSE_CONTENT=`cat /Unity_v2018.ulf`
      echo "$UNITY_LICENSE_CONTENT" | tr -d '\r' > "/root/.local/share/unity3d/Unity/Unity_lic.ulf"
  remove_license_file: &remove_license_file
    command: |
      rm /Unity_v2018.ulf
      rm /root/.local/share/unity3d/Unity/Unity_lic.ulf
jobs:
  test_editmode:
    <<: *docker_image
    steps:
      # TODO: Add git to unity image so this is not required anymore
      # this will prevent following error on 'checkout' step:
      # Either git or ssh (required by git to clone through SSH) is not installed in the image. Falling back to CircleCI's native git client but the behavior may be different from official git. If this is an issue, please use an image that has official git and ssh installed.
      - run:
          command: apt-get update && apt-get install -y git && git --version
      - checkout
      - run:
          <<: *setup_unity_license_env_var
      - run:
          environment:
            TEST_PLATFORM: editmode
          command: |
            chmod -R 755 ./ci/test.sh
            ./ci/test.sh
      - run:
          <<: *remove_license_file
      - store_artifacts:
          path: '$(pwd)/$TEST_PLATFORM-results.xml'
          destination: '$TEST_PLATFORM-results.xml'
  test_playmode:
    <<: *docker_image
    steps:
      - run:
          command: apt-get update && apt-get install -y git && git --version
      - checkout
      - run:
          <<: *setup_unity_license_env_var
      - run:
          environment:
            TEST_PLATFORM: playmode
          command: |
            chmod -R 755 ./ci/test.sh
            ./ci/test.sh
      - run:
          <<: *remove_license_file
      - store_artifacts:
          path: '$(pwd)/$TEST_PLATFORM-results.xml'
          destination: '$TEST_PLATFORM-results.xml'
  build_StandaloneWindows:
    <<: *docker_image
    steps:
      - run:
          command: |
            apt-get update && apt-get install -y git zip && git --version
      - checkout
      - run:
          <<: *setup_unity_license_env_var
      - run:
          environment:
            BUILD_TARGET: StandaloneWindows
          command: |
            chmod -R 755 ./ci/build.sh
            ./ci/build.sh
      - run:
          <<: *remove_license_file
      - store_artifacts:
          path: './Builds/'
  build_StandaloneOSX:
    <<: *docker_image
    steps:
      - run:
          command: |
            apt-get update && apt-get install -y git zip && git --version
      - checkout
      - run:
          <<: *setup_unity_license_env_var
      - run:
          environment:
            BUILD_TARGET: StandaloneOSX
          command: |
            chmod -R 755 ./ci/build.sh
            ./ci/build.sh
      - run:
          <<: *remove_license_file
      - store_artifacts:
          path: './Builds/'
workflows:
  version: 2
  test_and_build:
    jobs:
      - build_StandaloneWindows
      - build_StandaloneOSX
      - test_editmode

これが コメント 2019-02-07 233248.jpg こうなります。

少し分解して解説を入れます。 setup_unity_license_env_varだけを見ます。

# なんでこれ追加したんだったか…無くて怒られたような…
mkdir -p /root/.cache/unity3d
mkdir -p /root/.local/share/unity3d/Unity/
openssl version
# 暗号化したファイルを復号化する処理
openssl aes-256-cbc -md md5 -d -in ./ci/Unity_v2018.ulf-cipher -out /Unity_v2018.ulf -k $KEY
export UNITY_LICENSE_CONTENT=`cat /Unity_v2018.ulf`
# 復号化した中身をファイルとしてUnity配下に保存
echo "$UNITY_LICENSE_CONTENT" | tr -d '\r' > "/root/.local/share/unity3d/Unity/Unity_lic.ulf"

復号化した中身をファイルとしてUnity配下に保存をしないと自分の場合動きませんでした。unity3d-ci-exampleにはなかったのですがwtanaka/docker-unity3dにはその記述があり書いてみたらライセンスが通りました。。。先駆者たちに感謝っ・・・・!圧倒的感謝っ・・・・!

あとはunity3d-ci-exampleのciフォルダ配下の.shに権限与えて動かしているだけです。 .shの中身はunityをコマンドで動かしているだけなので色々といじれるとは思います。 編集したり調べる際にはコマンド引数のリファレンスにだいたい乗っているので確認してみてください。

Testは基本的に成功知れていればいいと思いますがBuildは成果物がダウンロードできないと意味がないのでダウンロードする用意をします。 CircleCIにはArtifactsという機能がありArtifactsに保存したい成果物を登録しておけば長期的に保存ができるものになります。

unity3d-ci-sampleもBuildフォルダというものを作っておりフォルダごと保存するようになっています。 jobの詳細画面?でArtifactsが確認できます。 コメント 2019-02-07 231417.jpg

フォルダを成果物としているのでかなりバラバラになっています。(-customBuildPathの環境変数登録してなくてそのままなのはスルー) 1つ1つダウンロードしてられないのでzipにまとめます。

build_StandaloneWindows:
    <<: *docker_image
    steps:
      - run:
          command: |
            apt-get update && apt-get install -y git zip && git --version
      - checkout
      - run:
          <<: *setup_unity_license_env_var
      - run:
          environment:
            BUILD_TARGET: StandaloneWindows
          command: |
            chmod -R 755 ./ci/build.sh
            ./ci/build.sh
      - run:
          <<: *remove_license_file
      - store_artifacts:
          path: './Builds/'

一部抜粋ですが、apt-getでgitのついでにzipも一緒にインストールしておきます。最後の方にartifactsに登録しているのがわかると思います。 そしてbuild.shの一部を編集します。

if [ $UNITY_EXIT_CODE -eq 0 ]; then
  echo "Run succeeded, no failures occurred";
  cd $BUILD_PATH
  zip archive -r .
  cd /root/project
elif [ $UNITY_EXIT_CODE -eq 2 ]; then
  echo "Run succeeded, some tests failed";
elif [ $UNITY_EXIT_CODE -eq 3 ]; then
  echo "Run failure (other failure)";
else
  echo "Unexpected exit code $UNITY_EXIT_CODE";
fi

成功したときにartifactsに登録するディレクトリに移動してzip処理をします。そして後処理があるのでディレクトリをもとの位置に戻ります。 上記の編集を加えると

コメント 2019-02-07 231229.jpg

archive.zipになっているのがわかります。archive.zipを落として解答すればビルド結果が確認できるかと思います。

まとめ

CircleCIで一旦UnityのTestとBuildを動かす話はここまでです。Sampleをあげてくれている方がおりますがそのまま動かない場合があったので今回記事に残しておきます。 そのうちAndroidiOSのビルドもCircleCIでやりたいと思ってはいます…ネイティブのビルド自体はfastlaneとかでやっている人をみるので応用すればいけるかな?と思ってますがDocker知識が地味に必要そうなのでやる気次第ですぐにやるかもしれない…しないかもしれない… CircleCIやDockerは完全に初心者でやっているのでもっと効率のいい書き方とかあるよって話があれば教えて欲しいです!

転職ドラフトを使って転職を決めた

 

転職ドラフトを使って転職が決まりました。

Amazonギフトカードが欲しいので、こちらの記事は転職ドラフト体験談投稿キャンペーンに参加しています。

job-draft.jp

 

転職ドラフト以外も転職サービスは使ってはいましたのでそれを含めつつ記事に残そうかと…

転職のきっかけ諸々は別途退職エントリーを書いてみたいので書いてそのうち公開しようかと計画中

転職ドラフトに登録したきっかけ

最初は自分の市場価値は今の会社からの給与で正当なのだろうか?と疑問になったところから始まった。でも市場価値なんてわからないし…という時に大学の先輩が転職を決めていたので相談をさせて頂いた。

そこで、自分で職務経歴などを書いてオファーが貰える「転職ドラフト」に登録してみて考えてみたらいいんじゃないかとアドバイスをもらい転職する思いは強くなかったが登録をしてみるか…くらいの気持ちでいたと思う。

その当時、別に自分で探して

www.green-japan.com

というのも登録していた。こっちも職務経歴のようなものを書いて登録するもの。転職ドラフト違ってオファー額は出ず、企業の方が「スカウト」「面談確約スカウト」などで声がかかって直接話をするというもの。ここでの面談も何回かしたが自分はうまくマッチングできずに終わってしまった。

 

ドラフト期間

自分は転職期間中2回ドラフトに参加した。

1回目は1件指名を頂けた。当時、希望年収をあえて書かずに登録していた。市場価値を知るため。そこで思った以上の額を提示されて面談をしたが、自分がやりたいこととは少し違ったのもありお断りをさせて頂いた。

2回目は3件指名を頂けた。1回目を基準に希望年収を書いていたら、その通りいただけたり、それ以上の額の提示があり驚いた。しかし、自分が行きたいのはゲーム業界だったのもありゲーム業界でない2件の企業にはお断りをさせて頂いた。そして、ゲーム業界の企業と面談をすることに

 

転職を決めた企業との面談

かなり話が合っていたと思う。自分のやりたいことや出来るスキルも活かせそうだと思えたし、自分が挑戦したいことにも「いいね!」という具合に話が弾んでいた気がする。特に個人制作を注目して話を振ってもらえたりして、話せたのはうれしかった。さらに面談をした方とTwitterをその場で交換することになったのは自分の中で面白かった。正直内心今後のツイート気にしなきゃいけないかな?とか思っていたが、僕は気にせず今も元気にツイートをしています。

 

転職に使ったツール

基本的にはGoogle Driveで管理をしてGoogle Documentで履歴書と職務経歴書を作って管理していた。

f:id:MakeTake:20190211105910j:plain

みたいな感じです。GitHubなどはリンクを貼って、企業にURLを送ったりpdfに変換して送信するだけの状態にしていた。あとは志望動機云々は書かなかった。企業によって変えるのが手間だったのもあり汎用的なものにしていた。

印刷を求められたときは普通の印刷用紙で特段問題はなかった。(実は問題があったとかだと申し訳ないです。すみません。。。)

ちなみにGoogle Documentに履歴書テンプレートがあって、編集したものなので誰でも簡単に作れる気がする。

職務経歴書Google Documentで作っていた。どこかから拾ったテンプレートを編集したものだったので割愛。

 

そしてGoogle Slideで過去の制作物をまとめていた。

f:id:MakeTake:20190209135232j:plain

動画があるものは動画のリンクを貼ってみれるようにして、学生時代のものも含めて作っていた。

 

基本的にGoogle Driveで管理することでPCがなくてもスマホでプレゼンできるのは強みだった。さらにGoogleのoffice系は編集履歴が追えるので何かあったときも対応が楽…

 

転職ドラフトを使ってみて

レジュメを書かないとドラフトに参加できないが、きちんとレジュメのレビューが入って自分がやってきたことを振り返る機会にもなった。

そして、自分がやってきたことに具体的な数字で価値が見えるのは明快でうれしい。

個人的にはアプリかPWAとかでスマホに最適化されると良いなぁと思ったり…

 

最後に

転職活動中に色々な人に相談に乗ってもらいました。ありがとうございました!!志望通りの業界に行けます。3月は丸々有給消化で暇を持て余して過ごします!

GASから.assetを作ってプルリクを投げる

たまにやってる自分のQiita転載です。

GASから.assetを作ってプルリクを投げる - Qiita

Google Spread Sheetで文言とか持ってくるの…めんどいな

Google Spread Sheetで文言を管理することがあると思います。もちろん、Excel等もあると思いますが今回はSpread SheetとGASの連携による話になります。

Google Spread Sheetで文言管理するのはいいけど文字を打って写したり、GASを使ってファイルにしてDriveに保存してダウンロードする。などの手順を踏むかと思います。今回はそれすらもめんどくさくなったエンジニアの話です。

そんなこんなで、GASもapiを叩くことができるならGitHubapi叩けばよくね!?って思ったら最高に用途にあったライブラリを作っている方がいらっしゃいました。 GitHub API を GAS でいい感じで叩くためのライブラリを作った (未完)

最高です。GAS用のライブラリなのでSpread Sheetと直結です。

先に.gsが見たい方はこちら https://github.com/MizoTake/CreateAssetsFromGAS

UnityのSampleリポジトリはこちら https://github.com/MizoTake/CreateAssetsFromGASUnitySample

以降は解説に入りますが、GASのファイル作成やUnityのファイル作成など基礎的なところには触れていませんのでご了承ください。

導入する

GASのライブラリ何それおいしいの?ってところから自分は始まりました。おいしかったです(楽さ加減が)

キャプチャ.PNG

GASを開いたらリソースという項目があるのでクリックするとライブラリという項目が出るのでクリックします。

すると

キャプチャ.PNG

テキストフィールドに今回使用するGitHubのライブラリキーとなる1F4yn329GjHKdcXu9nm0uBZHFo40NGRUF8dfZCTHM1KjXpOXYr2BzIIcJを入力します。(GitHubのREADME.mdにもあります)

あとは、バージョンを最新に設定すれば終わりです。

Unityで.assetを作る

要するにScriptableObjectです。

第4章 ScriptableObject

等を参考に作ってみてください。 今回はSampleのプロジェクトを作成したので、それをベースにお話しします。 https://github.com/MizoTake/CreateAssetsFromGASUnitySample/blob/master/Assets/Message.cs

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

public class Message : ScriptableObject
{
    public Talk[] flow;

    [System.Serializable]
    public class Talk
    {
        public int id;
        public string name;
        public string message;
    }
}

キャプチャ.PNG

Signboardということで看板に話しかけた時に表示するメッセージだと仮定しておきます。

Inspectorには上図のように表示されます。 IdとかMessageは適当に入れています。

これで、.assetにプルリクを送る準備ができました。 何故先に作っておくかと言うと.metaを作っておきたいからです。

GASから.assetを作る

.assetを作るためにはScriptableObjectのテキスト情報を知らねばならないのでテキストエディタで開きます。 https://github.com/MizoTake/sLowZoo/blob/master/Assets/sLowZoo/Data/MessageSection/Signboard.asset

%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInternal: {fileID: 0}
  m_GameObject: {fileID: 0}
  m_Enabled: 1
  m_EditorHideFlags: 0
  m_Script: {fileID: 11500000, guid: 3db36c1731914ec4496d0d83b3360bc7, type: 3}
  m_Name: Signboard
  m_EditorClassIdentifier: 
  flow:
  - id: 0
    name: 
    message: "\u770B\u677F\u3060\u3088"
  - id: 0
    name: 
    message: "\u770B\u677F\u304B\u3082\u3057\u308C\u306A\u3044"
  - id: 0
    name: 
    message: "\u304A\u3084\u2026\u770B\u677F\u306E\u69D8\u5B50\u304C\u2026"

Sceneとかの構成と同じくyamlのようです。ここから編集が必要な情報はflow:以降の文字列になると思います。 ただし、日本語はutf16に変換されているようなのでgasでも変換処理が必要そうです。

処理の一部抜粋をしつつ少し解説します。

まず、.assetのflow:前の文字列を取得します。gasの文字列操作で頑張るとなんとかなります。

var githubUrl = 'https://raw.githubusercontent.com/MizoTake/CreateAssetsFromGASUnitySample/master/Assets/Signboard.asset'
// プルリク対象のテキスト情報をgithubからrawで持ってくる
var txtBlob = UrlFetchApp.fetch(githubUrl)
// "flow:"という文字列を検索し、indexを取得
var index = txtBlob.getContentText().lastIndexOf('flow:')
// flow: + 改行コード = 6とし、flow:以降の文字列を取得
var deleteTarget = txtBlob.getContentText().slice(index + 6)
// ScriptableObjectの参照情報である部分をheaderとして取得
var header = txtBlob.getContentText().replace(deleteTarget, '')

上記のコードでScriptableObjectのflow:までの文字列を取得できたので、中身となるidなどをSpread Sheetから取得し作成します。

まず使用するSpreadSheetはこちらです。 会話 キャプチャ.PNG

これを踏まえて以下の処理を使用します。

var targetName = 'Signboard'
var path = 'Assets/'

var sheet = SpreadsheetApp.openById('1KsmJTKSRGXMd1YdtJDLPzkp6xvDskaXRezc7pyq2WVQ').getSheetByName(targetName)
var fileName = targetName + '.asset'

var result = ""
// idの列を取得
var idRange = sheet.getRange(2, index('id'), sheet.getLastRow(), 1).getValues()
// nameの列を取得
var nameRange = sheet.getRange(2, index('name'), sheet.getLastRow(), 1).getValues()
// messageの列を取得
var messageRange = sheet.getRange(2, index('message'), sheet.getLastRow(), 1).getValues()
idRange.map(function(id, idIndex) {
  id.map(function(item, index) {
    if (item != "" && typeof item !== undefined) {
      // yamlのformatに合わせる
      var name = ''
      if(nameRange[idIndex] != '') {
        // escape関数でutf16に変換
        name = "\"" + escape(nameRange[idIndex]).split('%').join('\\') + "\""
      } 
      result += "    name: " + name + "\n"
      result += "    message: \"" + escape(messageRange[idIndex]).split('%').join('\\') + "\"\n"
    }
  });
});

function index(target) {
  var contentTitles = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0]
  return contentTitles.indexOf(target) + 1
}

以上までやるとheaderとresultを結合すれば、.assetの中身がgasで作れちゃいます。

%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInternal: {fileID: 0}
  m_GameObject: {fileID: 0}
  m_Enabled: 1
  m_EditorHideFlags: 0
  m_Script: {fileID: 11500000, guid: 3db36c1731914ec4496d0d83b3360bc7, type: 3}
  m_Name: Signboard
  m_EditorClassIdentifier: 
  flow:
  - id: 1
    name: 
    message: "\u770B\u677F\u3060\u3088"
  - id: 2
    name: 
    message: "\u770B\u677F\u304B\u3082\u3057\u308C\u306A\u3044"
  - id: 3
    name: 
    message: "\u304A\u3084\u2026\u770B\u677F\u306E\u69D8\u5B50\u304C\u2026"
  - id: 4
    name: 
    message: "\u770B\u677F\u3067\u3059\u3088"

結合結果です。

あとは、ライブラリさんに頼ってプルリクを出すだけです。

Githubにプルリクを出す

冒頭で紹介したライブラリを使用するには

  • UserName
  • MailAddress
  • Personal access tokens

が必要となります。

tokenは調べると情報は出てくるので割愛します。

そして、tokenを扱うことを前提としているので.gsに貼り付けることは基本的にしないようにしたいのでGASの機能であるユーザープロパティを使用します。

プロジェクトのプロパティから設定できるように見えるのですが、自分は何故か反映されずソースコードからであれば反映されたのでメソッドを用意しています。

// add user property from code
function setup() {
  var prop = PropertiesService.getUserProperties();
  prop.setProperty('NAME', '');
  prop.setProperty('EMAIL', '');
  prop.setProperty('GITHUB_TOKEN', '');
}

これに必要な情報を張り付けて1度実行すれば以降は入力する必要はありません。

次にpush対象とするリポジトリを設定します。

function setupGitHub() {
  var prop = PropertiesService.getUserProperties()
  var option = { name:prop.getProperty('NAME') , email:prop.getProperty('EMAIL') }
  return new GitHubAPI.GitHubAPI('MizoTake', 'CreateAssetsFromGASUnitySample', prop.getProperty('GITHUB_TOKEN'), option)
}

のように設定すれば大丈夫です。MizoTakeやCreateAssetsFromGASUnitySample部分はご自身の設定に置き換えてください。 ちなみにMizoTakeのところはOrganization名でも大丈夫です。

さて、コミットを作っていきましょう。 以降は、基本的にGitHubAPI情報を参考に見ていると「あーね」ってなる気がします。 https://developer.github.com/v3/

// 対象となるfileのpath, fileのテキスト情報, githubライブラリで使用しているクラスのインスタンス
function addCommitData(filePath, blobData, github) {
  var txtBlob = UrlFetchApp.fetch(githubUrl + filePath)
  var index = txtBlob.getContentText().lastIndexOf('flow:')
  // flow: + 改行コード = 6
  var deleteTarget = txtBlob.getContentText().slice(index + 6)
  var header = txtBlob.getContentText().replace(deleteTarget, '')

  // .assetのテキスト情報
  var blob = github.createBlob(header + blobData)
  // GitHub apiに沿った配列を作成
  commitData.push({
      'path': filePath,
      'mode': '100644',
      'type': 'blob',
      'sha': blob['sha']
    })
}

これでcommitDataという配列にcommitする情報を与えていきます。

function createCommit(github) {  
  var branch = github.getBranch(branchName)
  var pTree = github.getTree(branch['commit']['commit']['tree']['sha'])
  var origin = pTree['tree']  
  // 現在のtree情報をbaseとして新しいtree情報を与えます
  var data = {
    'base_tree': pTree['sha'],
    'tree': commitData
  }
  var tree = github.createTree(data)
  // commitのメッセージを決めれます。
  var commit = github.createCommit('new GSS Data', tree['sha'], branch['commit']['sha'])
  var result = github.updateReference(branchName, commit['sha'])
}

コミットを作ったのでプルリクに紐づけます。

// プルリクが既にあるとエラーのためめんどくさいからtry catch
function createPullRequest(date, github) {
  try {
    const previousDay = new Date(date.getTime() + 24 * 60 * 60 * 1000)
    // プルリクのタイトルを日付に設定
    var title = Utilities.formatDate(previousDay, Session.getScriptTimeZone(), "yyyy/M/d")
    // masterをマージ対象とする
    return github.createPullRequest(title, branchName, 'master')
  } catch (error) {
    Logger.log(error)
  }
}

これで成功するとプルリクが作れちゃいます!

キャプチャ.PNG

注意点としては

  • プッシュするbranchは先に作っておくこと(branchを作るapiまでカバーしていないため)
  • treeの情報を間違えると色んなデータが消えたりしたことになるので配列は確認をして扱っていくこと

UnityEditorで確認する

gitでbranchを自動プルリクのbranchに切り替えてpullをします。 そして、UnityEditorに戻ると

キャプチャ.PNG

4つ目の項目まで表示されています!

これでファイルを作ってプルリクを投げる自動化は、一区切りです。

おまけ: 定期的に自動でコミットをさせていく

キャプチャ.PNG

編集を押すと現在のプロジェクトのトリガーという項目があるので選択します。

キャプチャ.PNG

トリガーを追加とあるので選択します。

キャプチャ.PNG

上図のように設定すると毎日午前0時~1時の間に自動的にpushToGitHubという関数を実行します。 これをすることで、毎日自動的にコミットがされることになります。何もしなくてもプルリクなりコミットが作られます。便利

所感

ここまで自動化すると結構便利な気がしています。 これはUnityに限らず色んな静的なファイルに有効だと思うので色々応用するのは良い気がしています。

UnityのWebGLをVue.js使ってPWAに対応した表示してみた

自分のQiita転載でございます。

qiita.com

PWAを触ってみたかった

始めの動機はそんなもので、PWAというのがあるすごい!UnityはWebGLで吐ける!…いけるんじゃね? ってことでやってみました。

PWA for Unity Sample ezgif-5-edf42d3870eb.gif

はい結構適当ですが、UI Buttonを押してカウントアップするものです。

環境

UnityやVueのセットアップは他の記事を参考に行った方が良いと思うので今回は省きます。 Vueに関してはこちらを参考にさせて頂きました。 - Vue.jsで始めるPWA - VueコンポーネントでWindowサイズ変更検知&値取得

最初に言うとWeb畑の人間でないのでVue.jsとかの記述、設定諸々はよくわかってないです行き当たりばったりでやってるのでお手柔らかにお願いします。 Unity WebGLCSSの適応の仕方がわからなくて詰んで放り投げました。

以下が今回のプロジェクトリポジトリになります。 GitHub

一応、GitHub Pagesも試しましたが何故かAndroidで動かしたときにショートカットを追加とかの機能が動いてなかったぽいので今回はFirebase Hostingを使ってみました。

Unityの設定

設定もなにもWebGLでビルドできるようにコンポーネント追加してビルドするだけです。

ただ、ビルドする場所はVue.jsのstaticフォルダ配下にビルドするようにします。Vue.jsの静的なファイルを置くところらしいです。 個人的にはstaticフォルダ配下にUnityビルド用のフォルダを作ってまとめておくとよいと思います。

今回だとunityBuildというフォルダを作っています。

Vueの設定

UnityのWebGLの読み込みを自分で書いても良かったのですが既にライブラリがあったため使わせていただきました。 vue-unity-webgl

上記をインストールしてまずはindex.htmlのヘッダーに

<link rel="shortcut icon" href="<%= htmlWebpackPlugin.files.publicPath %>static/unitybuild/TemplateData/favicon.ico">
  <link rel="stylesheet" href="<%= htmlWebpackPlugin.files.publicPath %>static/unitybuild/TemplateData/style.css">
  <script src="<%= htmlWebpackPlugin.files.publicPath %>static/unitybuild/TemplateData/UnityProgress.js"></script>
  <script src="<%= htmlWebpackPlugin.files.publicPath %>static/unitybuild/Build/UnityLoader.js"></script>

を追加します。static配下のunitybuildフォルダ名は各自が命名したフォルダ名にしてください。

そして、App.vueを

<template>
  <div id="app">
    <header>
      <span>Vue.js PWA</span>
    </header>
    <main>
      <unity src="static/unityBuild/Build/unityBuild.json"
        v-bind="{ width: gameWidth, height: gameHeight }"
       unityLoader="static/unityBuild/Build/UnityLoader.js"></unity>
    </main>
  </div>
</template>

<script>
import unity from 'vue-unity-webgl'

export default {
  name: 'app',
  data: function () {
    return {
      gameWidth: window.innerWidth,
      gameHeight: window.innerHeight * 0.96 - 134 // 134はヘッダー等の値
    }
  },
  methods: {
    handleResize: function () {
      this.gameWidth = window.innerWidth
      this.gameHeight = window.innerHeight
    }
  },
  ready: function () {
    window.addEventListener('resize', this.handleResize)
  },
  beforeDestroy: function () {
    window.removeEventListener('resize', this.handleResize)
  },
  components: { unity }
}
</script>

/// <style>は初期状態のまま編集してないので略 ///

と編集することで、とりあえずはそれっぽくUnityのWebGLを表示できるかと思います。

PWAなのでモバイルでも動かすことを想定していますが、最初に出てくるアラートが鬱陶しい場合はテラシュールブログさんの 【Unity】WebGLとWebAssembly にあるモバイルで動作させるを参照してください。

Firebase

今回は静的ファイルばかりなのでhosting機能を使っています。 セットアップとデプロイに関しては公式のドキュメントで充分だと思います。 Firebase Hosting

所感

Vue.jsが楽… PWAなのでちゃんとホーム画面でインストールしたかのように扱えますし、オフライン動作部分を調整すればストアに出さなくてもゲームらしく出来るんじゃないか?と思います。 cssをきちんと適応させることはもちろんですが横画面にすることも考慮してできれば結構気楽にゲームなどをデプロイできる気がします あと、最近はPWAをWindows等の上でも動かせるのでデスクトップゲーム風とかもできるかもしれません。 本当はUnityでWebGLビルド時に勝手にVueProject作ってPWA環境にする!みたいなフレームワーク的なところまで妄想してましたが妄想で止まってしまいました。。

何かの参考になればと思います。

(GitHubのアラートはいつかなおすんだ…いつか…)