gomobile で sprite を回転させる方法

# この記事は

この記事は [ACCESS Advent Calendar](http://qiita.com/advent-calendar/2015/access) 9日目の記事です。
ACCESS の @pankona です。

この記事は、 gomobile を触ってみようと思い、とりあえず手始めにサンプルをいじってスプライトをぐるぐる回してみよう！等と思ったが、その程度で割と苦戦してしまった人（私）の戦いの記録です。
gomobile でスプライトを回転させたい方がいて、やり方に困っていらっしゃるようでしたら、きっと役に立つと信じます。

# はじめに

* golang、gomobile の導入に関しては、本記事では割愛します（導入に関する記事は他にいっぱいあるからね！）。
* とはいえ、gomobile 公式はこちらです → https://github.com/golang/go/wiki/Mobile
* いきなりですが、スプライト回転させるソースコードの出来上がったものは https://github.com/pankona/gomobile_sprite_rotate_test です。私作です。
* 本記事にはソースの抜粋を載せていますが、断片だと意味わからないかもしれないので、ソース全体が気になる方はチェックしてみてね！
* スプライトをぐるぐる回した結果はこちら（形が分かりやすいように黒背景にしています）

![gopher_rotate.gif](https://qiita-image-store.s3.amazonaws.com/0/36552/693993a1-117f-4f4f-23a0-b82118a51fad.gif)

# 回し方の簡単な解説

## gomobile でのスプライト制御はアフィン変換で行う

* gomobile の スプライトをつかったサンプルコード [example/sprite/main.go](https://github.com/golang/mobile/blob/master/example/sprite/main.go) を見ると、sprite の配置はアフィン変換でやってることが見て取れます（一部抜粋）。`f32.Affine` とやってるのがそれです。

```go
	// 座標 (0, 0) 、サイズ (1, 1) に変換
	eng.SetTransform(scene, f32.Affine{
		{1, 0, 0},
		{0, 1, 0},
	})
	// （親ノードを基準に）
	// 座標 (0, 0)、サイズ (36, 36) に変換
	eng.SetTransform(n, f32.Affine{
		{36, 0, 0},
		{0, 36, 0},
	})
```

アフィン変換で行列の掛け算をしまくれば割と自由に操作できる感じではありますが、
そういうのが苦手な人（私）のような人は、すでに用意されているAPIを駆使していけばいいと思います。
例えば以下のようなAPIがあります（詳しくは [f32 パッケージの GoDoc](https://godoc.org/golang.org/x/mobile/exp/f32) を参照のこと）

* `func (m *Affine) Rotate(p *Affine, radians float32)` ← 回転
* `func (m *Affine) Scale(p *Affine, x, y float32)` ← 拡大縮小
* `func (m *Affine) Translate(p *Affine, x, y float32)`  ← 平行移動

## 回転させてみる

こんな感じにすると、回転させるための行列が取得できます。
本記事のGIF画像のように、その場でぐるぐる回転します。

```go
	// その場で回転させるための f32.Affine を計算する function
	// affine ... 計算のベースとなる affine 行列
	// radian ... どんだけ回転させるか（ラジアン）
	// sprite_w ... スプライトの横幅
	// sprite_h ... スプライトの縦幅
	func myRotateGood(affine *f32.Affine, radian, sprite_w, sprite_h float32) {
		affine.Translate(affine, 0.5, 0.5)       // 回転の軸をスプライトの中心にするためにいったん移動
		affine.Rotate(affine, radian)            // ラジアン指定で回転
		affine.Scale(affine, sprite_w, sprite_h) // スプライトの大きさに拡大
		affine.Translate(affine, -0.5, -0.5)     // 回転の軸をスプライトの中心にするためにいったん移動したのを元に戻す
	}
```

ちなみに、上記の function は以下のような感じで呼び出します。

```go
	// 以下は上記の function を呼び出す側

	// f32.Affine の初期化
	affine = &f32.Affine{
		{1, 0, 0},
		{0, 1, 0},
	}

	// 回転させる affine を計算
	myRotateGood(affine, radian, sprite_w, sprite_h)

	// 計算結果を適用
	eng.SetTransform(n, *affine)
```

## 回転させる function 以下は間違い

アフィン変換の素人（私）は以下のようなコードを書くかもしれませんが、上記のとは似て非なる以下のような function で回転させると微妙におかしいことになります。

```
	func myRotateBad(affine *f32.Affine, radian, sprite_w, sprite_h float32) {
		affine.Translate(affine, 0.5, 0.5)       // 回転の軸をスプライトの中心にするためにいったん移動
		affine.Scale(affine, sprite_w, sprite_h) // スプライトの大きさに拡大
		affine.Rotate(affine, radian)            // ラジアン指定で回転
		affine.Translate(affine, -0.5, -0.5)     // 回転の軸をスプライトの中心にするためにいったん移動したのを元に戻す
	}
```

* `func myRotateGood()` との違いは、`affine.Scale()` と `affine.Rotate()` の順番。
* この書き方だと、「回転させてから」→「拡大する」という意味になってしまって、正方形ならいいんですが長方形だと回転するにしたがって歪みます。
* そんなのアフィン変換界隈では常識のようですが、不勉強な輩（私）が数日悩むといけないので、一応、ゆがみねえ回転ができない方は掛け算の順番を気にしてみたらいいかもしれません。

以上です！

# 終わりに

gomobile、いまのところ Experimental な側面が強いようですが、なかなかトライしていて楽しい、トライし甲斐のあるお題ではないかと思います。
Android版、iOS版のみならず、Linux版、Mac版も作れちゃうすごいやつです。興味のある方はトライしてみてはいかがかな！

明日、12/10 は @gm_kou さんの出番です。そちらもぜひよろしくお願いします！