ひびのログ

日々ではないけどログを出力していくブログ

Unity に(再)入門しました

f:id:tee-talog:20180218173746p:plain

以前、Unity に少し触れていたのですが(確か Unity4 くらいの時)、最近のVRブームを受けて(1年遅れくらいで)Unity から VR に触れたらいいなーということで、もう一回やり直してみることにしました。

Unity、すごい。

やっぱすげぇわ…… そりゃすげぇでしょ……

プロユースだもん

成果物

ブロック崩しを作成しました。 クリックでボールが飛んでブロックが消せるだけだけの超絶手抜き実装ですが、そこはご愛嬌ということで。

Unity WebGL Player | BlockEraser

調べてわかったこととかハマったところとか

ビューとか

ビューの役割とかは前にやってたときから変わってなかったので問題なし。

マウス

Input.GetMouseButtonXxxxでマウス入力を取得する。 マウス入力を専用のクラスを作ってそこからオブジェクトを操作したかったが、Updateの中からしか操作する方法がわからず断念。

ボールを動かす

AddForceの第4引数が未だにどういう動きをするのか理解していない。 なんとなく変数名から想像はできるが、必要になったら調べるくらいでよさそう。 ちなみに今回は最初しか使用していないので、ForceMode.Impulseを使用。

摩擦を0、反発を1にすることで、簡単に実装できた。

変なふうにブロックとか動かすやつとかに当たると、ほぼ真横に飛んで詰んでしまうのはいただけない。縦にほぼ進まなくなったら、壁に当たった時点で若干上にAddForceしてあげるといいかもしれない。

プレイヤーを動かす

Input.mousePositionでマウスの位置を取ってCamera.main.ScreenToWorldPoint(position)でゲーム内座標に変換してあげる。 ……だけだったはずなのに、なぜかx座標の値がずっと0のまま。 理由は、マウスの位置を取った後、z座標が設定されていないので、それを設定してからでないとゲーム内座標に変換できないらしい。

ということで最初は1fを与えて実行してみたが、マウスに全然付いてこない。移動量が足りない。 サイトで上記のz座標に与える値が10fだったのでそれにしてみたら移動量が上がった。 最終的に20fにしたらいい感じについてきたが、理由はわからない。

上下とかを固定するのは、ゲーム座標に変換した後、y・z座標に固定値を入れればOK。

ブロックもろもろ

ブロックを消すのはそんなに難しくなかった。

OnCollisionEnterはボールが跳ね返るが、OnTriggerEnterは跳ね返らないので要注意。

ボールとの接触はタグで判定。引数のgameObject.tagでタグが参照できるので、それをifで切り分けて処理。

複数回当たらないと消えないブロックを作ろうとして、現在あるブロックをコピー→同じ場所に配置したら、一回の接触で両方とも消えてしまった。 ちゃんと小クラスとか作って衝突処理をオーバーライドしてあげないとダメだった(今回実装していないのでできるかはわからない)。

段数によって色を変えようと思ったが、親の GameObject に Material を設定してあげることで一気に子の GameObject に適用されるなんてことはなかった。 ソース内からforeachして設定しないとダメみたい? できれば Material は Material でまとめておきたいので却下。 プレハブ化して配置するのがいいのかもしれない。

その他わかったこと・気付いたこと

  • やり直しとかセーブとかゲームオーバーとかを作りたかった人生だった。→次回以降の課題
  • gameObjectは自分のオブジェクトを参照している。
  • UpdateFixedUpdate関数があり、後者は Rigidbody のときに使うらしい?
  • GameObject.Find()GetComponent<T>()さえあればどんなオブジェクトでも取れそう。使いすぎには注意。
  • Analytics の使い方がさっぱりわからん(必要ないので調べてない)。
  • 物理演算はやっぱり荒ぶる。

わかモナ ~わかる!数式が出てこないモナドへの入り口~

プログラミングをしている画面です。

数年前から関数型プログラミングが流行り始めて、やってみよう! という人もいらっしゃるかと思います。なにを隠そう、私がその口です。

そして Lisp や Haskell なんかに手を出した時、「モナド」という言葉が出てくるかもしれません。

知らないことは調べ尽くすのがプログラマの性。

しかし! モナドの意味を調べてみると、「計算を表現する構造」だったり「函手(かんしゅ)」だったり「ポケモン」だったりと、なんだかよくわからないことばかり書いてあるかと思います。

そこで、オブジェクト指向から関数型に入門し、モナドがなんとなーくわかった気になれるような説明をしたいと思います!

注意事項

自分の理解や説明の関係上、モナドを知っている人から見ると「間違ってる!」と思われるかもしれません(モナド則とか)。 全く知らない人が理解する取っ掛かりとして書きましたので、ある程度は見逃していただけるとうれしいです。 ただ、思いっきり間違っている場合は指摘していただけると嬉しいです。

なので、この内容を読んだだけで完全にわかった気にならないでください。 あくまで理解のとっかかりです。 間違っていても責任は取りません。

例は基本的に JavaScript で記述しています。

前提:モナドは理解できなくてもいい

理解していなくても、いつの間にか使っているので。 実際に使わなきゃいけない言語って少ないですし。必須なのはHaskelとかですかね? 書いたことないですけど。

関数型プログラミングを簡単に説明

そもそも関数とは

関数型プログラミングの本を見ると、大体見開き1ページ目に f(x) = x + aとか書いてありますけど、わからなくていいです。 私たちはプログラマであって、数学者じゃないんです。

ひとまず、プログラミングの文脈における「関数」という理解でいいです。 引数をとったりとらなかったりして、値を返したり返さなかったりするアレです。

ただし、オブジェクト指向ではないので、オブジェクトとかクラスとかはありません。 そのため、「メソッド」は存在しません。

関数型言語の重要な性質(前提知識)

  • First class function(第一級関数・高階関数)
  • 参照透過性
  • 副作用がない

難しい単語が出てきましたが、説明するのでチャンネルはそのままで!

First class function(第一級関数・高階関数)

今回の主旨とは関係ありませーん。

オブジェクト指向ではオブジェクトが変数に代入できるように、 関数型言語では関数が変数に代入できます。

// 変数に関数を代入する例
const func = () => console.log("呼ばれたよ");
func() // 出力:呼ばれたよ

参照透過性

関数を同じ引数で呼び出したら必ず同じ値が返ってくることです。 例えば、関数の中で引数同士の足し算をするだけなら参照透過ですが、 受け取った値にランダムな数を足すのは参照透過ではありません。

// 参照透過な関数
const func = (a, b) => a + b;
// 参照透過ではない関数
// 呼び出しごとにランダムな値が使われるため、結果が毎回変わる
const func = (a) => a + Math.random();

// 変数bの値が変わると、func2の結果も変わる
const b = 1;
const func2 = (a) => a + b;

副作用

大雑把に言えば、関数の中で戻り値以外に影響をあたえることです。 例えば、画面出力をしたり、関数の外にある変数を書き換えたりです。

// 副作用がない関数
const func = (a) => a + 1;

const func2 = (num) => num + 1;
const num1 = 1;
const num2 = func2(num1);
// 副作用がある関数
// 副作用:コンソールに文字を出力している
const func = (a) => {
    console.log("呼ばれたよ");
    return a + 1;
}

// 副作用:関数の外で定義されている変数を変更している
let num = 1;
const func2 = () => num++;

関数型言語は、手続き型言語ではない

見た感じだと手続き型と変わらないですが、実はまったく違うものです。 関数型の世界は、参照透過であり副作用がないという、手続き型とは違う世界なのです。

手続き型の常識は、PCと一緒に窓から投げ捨ててください。その後、PCを回収してきてください。


モナドとは?

いよいよモナドの説明です。

ここでいう「モナド」は、「とある関数」と読み替えて構いません。 あとで詳しく触れます。

関数型の世界は、「参照透過」であり「副作用がない」と説明しました。 そして、副作用の代表的な例に、「画面出力」が挙げられます。 画面出力は関数の中で戻り値以外に影響をあたえることなので副作用です。

では、関数型言語では画面に文字を出力できないのか?


答えはNoです。 なぜなら、モナドがあるからです。

モナドは、副作用を起こすときに使用します。


逆に言うと、モナドがあると、関数型の世界で副作用が起こせます。

ついでに、モナドは参照透過でなくてもかまわないです。 参照透過で副作用がない世界の中で、唯一参照透過でなくても、副作用があってもいい、それがモナドです。

ちなみに手続き型(オブジェクト指向)では副作用を起こせるので、モナドは必要ありません。

モナドの種類

上述の通り、モナドは参照透過ではなかったり副作用を起こしたりする際に使用します。

ですので、副作用の種類などによって、モナド自体の種類も変わります。

Maybe モナド

例外処理です。

例外というのは、引数でも戻り値でもありません。 ですから、関数型言語では扱えません。

そこで、「処理に失敗するかもしれない処理を行う」ために、Maybe モナドを使用します。

const maybeMonad = () => {
    try {
        const ret1 = mayFailToMaybe1(); // 失敗するかもしれない処理
        mayFailToMaybe2(ret1); // 失敗するかもしれない処理
        return "success";
    } catch (e) {
        return "error";
    }
};

処理に成功したら"success"が、処理に失敗したら"error"がそれぞれ返ってきます。 この関数は、引数が同じなのに戻り値が違うため、参照透過ではない関数です。

ですが、モナドという大義名分で、関数型の世界でも使用できます。

Java とか Scala とか Kotlin とかでは、OptionalとかOptionクラスとしておなじみです。あれです。

IO モナド

「画面に文字を出力する」という副作用を起こすためのモナド(関数)です。

const outputMonad = (str) => console.log(str) || str;

まとめ

  • 関数型言語は、参照透過で、副作用がない
  • そのため、コンソールに文字を出力したりできない
  • モナドを使用すると副作用が起こせるので、コンソールに文字を出力したりできる
  • モナドは、関数型言語において副作用を起こすために使う
  • 同様に、参照透過でない関数がモナドとして利用できる

一言で

参照透過で副作用がない関数型という世界の中で、参照透過でない・副作用がある処理を記述するときに使う(大事なことなので)。

おまけ

関数型の世界は、「参照透過」であり「副作用がない」ので、 コンパイル時にすべての値が計算できます。 「定数畳み込み」という最適化技法です。

もちろん手続き型のコンパイラでも行なっているとは思いますが、 関数型では前述した理由により定数とみなせる場合が多いので、 最適化が高いレベルで適用できます。

おまけ2

個人の感想ですが、最近流行りの Flux は、 「状態を保持する」という副作用をまとめた、状態モナドの一種とも考えられると思います。

Redux で言うと、returndispatch>>=は Reducer(state から値を取ってきて、新しい state を返す)

ついでに、Middleware というモナドを挟むことで副作用も実現しています。 (個人的には ActionCreator でやりたいところではありますが)

TypeScript で動的にメソッドを追加する

JavaScriptでは、prototypeに適当に代入してあげればメソッドやプロパティの追加ができますが、 型という楽園を手に入れた TypeScript では簡単にはできません。

でもどうにかこうにかやる方法があったので、ググって色々試した結果を載せておきます。

ちなみに、現在作成中のこちらのソースで使っています。

github.com

やってみる

Stringにメソッドを生やしたいと思います。 const methodName = "methodName"が定義されている前提です。

ひとまずJSと同様に

String.prototype[methodName] = function (): void {}
// => error TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'.

当たり前にエラーです。 string型でインデックスアクセスできませんよーとのことです。

インターフェースに定義してみる

interface String {
    [index: string]: Function;
}
String.prototype[methodName] = function (): void {}
// => error TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'.

インターフェースを定義してみましたが同じエラーが。 ちゃんと型定義ができていないみたいです。

ちなみに、indexkeyとかmethodNameとかにしてもダメでした。

ならばprototypeに直接

interface String.prototype {
    [index: string]: Function;
}
String.prototype[methodName] = function (): void {}
// => error TS1005: '{' expected.

そもそもクラスじゃないのでダメですね。

型に直接いれよう

interface string {
    [index: string]: Function;
}
String.prototype[methodName] = function (): void {}
// => error TS2427: Interface name cannot be 'string'.
//    error TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'.

インターフェースにはプリミティブ型は直接定義できないみたいです。

prototypeobject型なので

interface Object {
    [index: string]: Function;
}
String.prototype[methodName] = function (): void {}
// => error TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'.

こちらもinterface Stringと同じエラー。

とりあえず型のほうも試してみる

interface object {
    [index: string]: Function;
}
String.prototype[methodName] = function (): void {}
// => error TS2427: Interface name cannot be 'object'.
//    error TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'.

やっぱりだめでした。

そして伝説へ……

(String.prototype as any)[methodName] = function (): void {}

これでコンパイルが通りました!

要するに、anyにキャストしてからメソッドを追加しています。

アディショナルタイム

interface String {
    [index: string]: Function;
}
(String.prototype as String)[methodName] = function (): void {}
// => error TS2352: Type 'String' cannot be converted to type 'String'.
//    error TS2352: Type 'String' cannot be converted to type 'String'. Index signature is missing in type 'String'.

Stringにしてみてもいけるかなーって思ったんですがダメでした……

所感

型を守りたかったんですが、最後はキャストで無理やりという結果に。

他にいいやり方があれば教えていただければ幸いです。

たった3分で、初心者が Travis CI を導入する!

最近(前から?)「テストが重要」とか「CI」とかよく聞くけど、導入するの大変そう……

と思っている、そこのあなた!


いえ、たった「3分」で導入できちゃいます。


モダンな開発環境には欠かせない CI を、簡単にサクッと導入してみましょう!

3分でできる対象

  • Node.js プロジェクト ※
  • テストが書かれていて、npm testでテストが実行できる ※
  • GitHub にテスト対象のリポジトリがある

※ここで紹介している内容が JavaScript 向けなのでこのように書いていますが、 設定ファイルの内容を修正するだけで、他の言語でも簡単にテストが実行出来ます。

手順

1分目:ログインとCIの有効化

まずは Travis CI の公式サイトにアクセスします。

Travis CI - Test and Deploy Your Code with Confidence

右上の「Sign in with GitHub」をクリックして、GitHub アカウントでログインします。

f:id:tee-talog:20180120181940p:plain

するとプロフィール画面が開くので、下のプロジェクト一覧から CI したいプロジェクトを選択してください。 チェックマークが表示されれば準備OKです。

f:id:tee-talog:20180120182015p:plain

なお、スイッチの隣にある歯車マークを押すと設定画面が開きます。 cron 等はここから設定できます。 すべてここで設定できるわけではなく、次項の設定ファイルで設定する項目もありますので注意してください。

2分目:設定ファイル作成

Git のリポジトリルートに下記の内容で「.travis.yml」を作成してください。

language: node_js
node_js:
  - node
  - lts/*

これは、「Node.js」の「最新版」と「LST」でテストを行う、という設定内容になっています。

バージョンは、もちろん別にひとつだけでもいいですし、最新版でなくても構いません。 Travis CI が nvm を使用して Node.js をインストールしているため、 バージョンの指定方法は、そちらのドキュメントを見てください。

3分目:コミット・プッシュ

後は上で追加した .travis.yml を add, commit 、そして GitHub に Push すれば、勝手にテストが実施されます!

f:id:tee-talog:20180120182028p:plain

実行には少し時間がかかります(npm install等を行うため、最低でも1分くらいかかります)。

テストに成功するとこのように表示されます。

f:id:tee-talog:20180120182036p:plain

「Build Jobs」のそれぞれのタスクをクリックするとコンソールログが見られるので、 エラーになった場合はこちらから内容を確認してください。

これで Travis CI の導入は終わりです! ね? 簡単でしょ?


放課後:バッジを貼る

ここまでで基本は終了なのですが、せっかくなので「テストをしている」と証明できるものが欲しいですよね。

そこで、バッジというものがあります。 GitHub を見ているとよく見る、README.md に貼ってあるこれです。

f:id:tee-talog:20180120182044p:plain

これで、 Travis CI でテストがちゃんと通っていること(もしくは失敗していること)がわかるようになります。

肝心の貼り方です。

Travis CI のページをよく見ると、プロジェクト名の右にそのバッジがあります。 そのバッジをクリックすると、編集画面が出てきます。

f:id:tee-talog:20180120182053p:plain

こちらでは、「参照するブランチ」「形式」が選択でき、一番下の欄にはその結果が表示されます。 ブランチを「master」、形式を「Markdown」にして、一番下の欄に表示される文字列をコピーしましょう。 そして README.md のお好きな場所に記入してください。 おすすめは、一番上に書いてあるプロジェクト名の真下です。

その後 master に commmit & Push するとテストが実行され、結果がバッジに反映されます。

JavaScript テストフレームワークのおすすめ

JSのテストをするときには、Jest を使用することをおすすめします。 理由は、

  • 必要最低限のものが最初から揃っている
  • 設定しなくていい
  • Reactのテストが簡単にできる

からです。

まとめ

意外と簡単に CI ができたので、衝動的に記事にしてみました。

今まで敬遠してた方も、これを気にぜひ導入してみてください!

ちょっとした宣伝

この説明で使用したリポジトリ(https://github.com/tee-talog/trim-margin)は、 JavaScript で Kotlin のtrimMargin・Scala や Groovy のstripMarginを再現・拡張したものです。 テンプレートリテラル等の文字列内で、インデントを削除します。 使用方法や説明文を記述するときに使えます。

もしよろしければ使ってみてください!

Android チュートリアル写経

ふと Android 開発をしたいなーと思ったので、ひとまずチュートリアルをやってみよう! と思い立ってやってみました。

参考

こちらのサイトを参考にいたしました。

employment.en-japan.com

ソースコード

ちょっとずつ参考サイトから変更しています(一部後述)。

画面レイアウト

Android 画面レイアウト

工夫したところ

  • Start ボタンを連続して押したときに、重複してタイマーが作動しないようにした
  • 状態(State)を別クラスに切り分け、Flux らしきものにした
  • if 文を when 式で記述した

わからなかったところ

  • どのファイルを公開してどのファイルを公開してはいけないのか
  • 一般的なディレクトリ構成
  • 一般的な状態管理方法
  • Singleton or object
    • 今回は実装が楽な object で作りました
  • Activity

感想

わからないことは多々あれど、Android 開発の基礎の基礎がわかったのでよかったなと。

最近は ReactNative とか PWA とかあるので、そちらにも突っ込んでいこうかなと思います。