LoginSignup
1
3

More than 5 years have passed since last update.

ループの考え方

Last updated at Posted at 2017-02-22

なぜ ここの数字は 2 なのか、とか コメント書くのが めんどくさいので
Qiita に書いて プログラムのコメントには 参照:参照:、ばっかり書くことにする。

画像が16枚ならんでいるとしよう。
Gazo

わたしの感覚では 左上から 0、1、2 …… と番号が振ってあると分かりやすいんだが、UnityのTexture2D は 左下が原点と言って聞かん坊なので それに合わせることにする。ループ・カウンターは

Yが 3から -1の手前、
Xが 0から 4の手前、

まで回せばいい。C#風に書くなら

// オヌヌメの書き方
for( int y = 3; -1 < y; y-- ){
    for( int x = 0; x < 4; x++ ){
    }
}

だろうか。ここで、 次のようには書かない という考え方がある。

// わたしが避ける書き方
for( int y = 3; 0 <= y; y-- ){
    for( int x = 0; x <= 3; x++ ){
    }
}

違い

書き方A 書き方B
3から-1の手前まで 3から0まで
0から4の手前まで 0から3まで

これは、数字をなんだと思ってるかによって 違ってくる。

指を使って 1、2、3 と数えるときは 書き方B が分かりやすい。
Gazo

これで十分じゃないか、と考える人は
Gazo
3時に待ち合わせしましょう、という時に

Gazo
3時29分にやってくる。

3時に待ち合わせといったら 3時00分だろう! というのは 解釈の1つだと思うんだが 口に出すと怒られるので言わない。

一応もうひとつ例を出しておこう。
Gazo
ここで 青い日と 赤い日 は、休日ではなく遊ぶ日 だ。ここで仕事の締め切りが17日なら、仕事はいつまでに終わらせればいいだろうか。

17日に締め切りといったら 17日00時00分だろう!

20日の朝6時30分 とか答えていいのは インディーズ・ゲーム開発者だけであって わたしの感覚的には 17日いっぱいの17時30分ぐらいかな、と思うんだが これも 解釈の1つだろう。

つまり 指には、
Gazo
3.99 とか無いのだ。かっこよく言うと 離散的だ。

むしろ
Gazo
くそ小学生から見れば 数字に途切れなんか見えない。

りんご1つと りんご1つを足すと りんご2つというのは りんごのサイズはみなだいたい同じという 算数を教えたい教師の都合のようなもので、
Gazo
くそ小学生には りんごが2つであることに どういう意味があるのか分からない。

むしろ
Gazo
りんごは 沼の下で1本の根につながっているかもしれない。ワンチャンある。算数が大嫌いだ。

つまり これらの方法は 1点を指そう、としているところに問題がある。
Gazo

オヌヌメの方法は 閾値(しきいち)を使うことだ。

Gazo
この図が言わんとしていることは、0時から、3時は含まないその手前だ。

日本語にすると 3時より手前で待ち合わせしよう、ということになる。

じゃあ、待ち合わせ場所に 0時に来てもいいし、2時59分59秒に来てもいいことが分かる。

あのカレンダーで言うなら、
Gazo

日本語で言うなら 18日より手前が締め切りだ、 ということになる。

17日の23時59分59秒までオッケーだろう。実際 イベントの締め切り日の23時45分から Webサイトを開いてフォームにキータイピングを開始する時間ぎりぎり平気症の人もいるだろう。

じゃあ、23時59分59秒までオッケー でいいじゃないか、という人もいるが 23時59分59秒999ナノ秒はオッケーなのか?ダメなのか? という疑問が晴れない。解釈の1つだ。

数字は どんなにピンポイントで示しても 小さな端数 が気になってしまう。
18日の手前まで という閾値を用いた方法は 小さな端数 を気にしなくてよくなる 良い方法だ。
Gazo
くそ小学生も 一時的におとなしくなるだろう。

これからは もう こう書きたくなるだろう。

// オヌヌメの書き方
for( int y = 3; -1 < y; y-- ){
    for( int x = 0; x < 4; x++ ){
    }
}

この症状が ひどくなってくると、こんな書き方をしたりもする。

// 我流
for( int tickY = THIS_Y; NEXT_Y < tickY; tickY-- ){
    for( int tickX = THIS_X; tickX < NEXT_X; tickX++ ){
    }
}

tick というのは チックタック時計が進んでいくことだが、刻んでいる。図で示すと……。

Gazo
例えば THIS_X が 10 で、NEXT_X が 18 ということになる。tickX は 10から17だ。

この方法の利点の1つとして、NEXT_X を THIS_X に移せば、そのまま次のディビジョンが始められることだ。ディビジョンという言葉が難しければ、セカンド・シーズンとか 次の区画とか そんな意味だ。

Gazo
ホリデーから始まるディビジョンとか 嫌なものを見た。

で、このことの何が利点なのかというと、
Gazo
開始地点だけ示せば いいわけだ。これが閾値を使う利点。

閾値を使わない場合、
Gazo
開始地点と、終了地点の2つを毎回 用意することになる。 これが 煩わしい。 開始地点があれば、終了地点も兼ねるのだから。

再掲しておこう。

// オヌヌメの書き方
for( int y = 3; -1 < y; y-- ){
    for( int x = 0; x < 4; x++ ){
    }
}

// わたしが避ける書き方
for( int y = 3; 0 <= y; y-- ){
    for( int x = 0; x <= 3; x++ ){
    }
}

お絵描きに疲れたので いったん休憩して またこの続きに記事を続けたい。

RGBは 255,255,255 とは限らない

ところで 月面宙返り3回転ひねりをしていいだろうか。
Gazo

バグが取れんなぁ、と思って たんたんとログ出力する内容を広げていたんだが、RGB値を見てみると 0.5019608 とか出ていた。

128 を 255 で割ると 0.50196078431…… なので、何が起こったのが想像に難くない。

どうも、Unityは何でもかんでも数字を 0.0~1.0 に normalization したいのかもしれない。おっちゃん、泣けてくるほっほ。

コメントに一言 書いていてくれれば こんな記事 書かずにさっさと 255 掛けてたんだが……。

一応 あとで 記事は続ける。

バグを取ること

Visual Studio 2015 のC#のライブラリで書いたコードを UnityのC#ライブラリで書いたコードに移植したつもりなんだが、
Gazo
生成物を比較してみると 部分的に同じで、部分的に異なっている。

処理の途中の生成物を出力してみるのもいいかもしれない。

コピペ・プログラマーもこんな気分だろう。おっ、いけるいける、と思って Rectangle を Rect、Bitmap を Texture2D に置き換えたら コードは張り替えられてコンパイルも通るのに 処理内容の詳細が違うわけだ。

ぶっちゃけ Y座標 に関わるところが ごっそりおかしく、その他に ちょいちょい違うところがある程度なので 見た目の相違ほどのひどさもないと思うが。

あとで 記事は続ける。

ループのことなんかどうでもよくなってきた

不具合の探し方なんだが、
Gazo
左がバグった出力結果、右が正常な出力結果。

数字を見てみると -1.04 ずれているようだ。

そもそも この数字が何かというと、Unity の Offset Y に入れる値なんだが、元の画像では1ピクセルであっても、Unityの画面上では グリッド1つ 100ピクセルだったり、ゲーム・オブジェクトのサイズや、拡縮のスケールが加味されていたりして なんだか分からない計算結果になっている。

まあ、ここでは 100倍して -104ピクセルずれている、と見ていい。

で、エラーが起こっている個所は 4つごとに1つ飛ばしになっているが、
立ち、跳ね、走り、屈み、その他 を1セットで 4キャラ分あるんだが、立ち、跳ね、走り、屈み が全部間違ってますよ、ということだ。

Gazo
17 というのは 左上を0として17枚目、ちょうど赤い枠が描かれている画像だが、ここで 赤い枠の場所を読み間違えているというものだ。

で、1スライス 128x128ピクセルサイズで、グリッドは 8ピクセル単位。
出力された数字は 128x128ピクセルの枠の中心から相対的に全部 縦-104ピクセルずれているというのを示していて、104ピクセルも上にずれているのだろうか。

このタイル画像は 1024x1024 サイズで、Texture2Dは左下原点ということで、Yは1023~-1の手前までループするようにしている。

あ、違う

Yは上に行くほど数字が増えるんだから、-104ずれているんだったら 下に104ずれているんだった。

例えば -128 ずれてるんだったら 分かりやすかった。1スライス分、1段下を見ているということだ。

128 - 104 = 24

この 24 は何か。確かに、赤い箱を見ると グリッド3セル分、24 ピクセルの厚みがあるが……、ん?

Gazo

24 下にずれているって?

Gazo

正しい値の -4 ピクセルというのは、画像の中心から上に 4ピクセル上がったところに 赤枠の中心Y があるということなんだが、
(注・この記事で全部間違っているが、正しい値は 4 ピクセルであって、画像の中心から上に 4ピクセル上がったところに赤枠があるというのは合っている。 -4 ピクセルという数字もあるがこれは別の画像の赤枠だ。この記事を読む上で特に困らないだろう)

例えば

Gazo

そもそも 8 ピクセル上にずれていて、-1段目から検知を開始していて、3段目を調べると その1段下に赤い枠があって検出してしまっていて、その赤枠の中心位置を求めるときに Rect の center.y プロパティーを拾っているんだが 縦幅の半分が中心位置とすると プラス値になっていて 座標は 折り紙を半分折り返したように上に上がってしまう、

と考えてみる。うーん、苦しい。

あ、直った

Gazo

center プロパティーは、プラス、マイナスが逆なんじゃないか、と思って自分で書き直したんだが、

Gazo

直った。

あっ、そう……。

よく分からんバグだった。

プログラムのコメントを楽しようと思ってQiitaに書いているんだが

Texture2Dは左下原点の座標で、Rectは左上原点の座標なんで、あとはよろしく、みたいなコメントでいいのだろうか。
Gazo
上図赤い枠が Texture2D。いわゆる画像。左下が原点でYは上にいくほど増える。緑色の枠が Rect。いわゆる矩形サイズ。左上が原点でYは下にいくほど増える。

例えば こんな座標があるとき、
Gazo

Texture2D では Yは上にいくほど増えていくので、
Gazo
そうか、上に行くほど増えていくんだな、と思うわけなんだが

Rect では Yは下にいくほど増えていくので、
Gazo
縦幅の半分を足せば ちょうど これぐらい。

そうですか。

じゃあ、まあ これで。

Yをループで回そう。

で、Yをループで回していくんだが、例えば 1024x1024 の画像なら、1023から-1の手前まで読み取っていけばいい。

ソースコードの中では、2つの -1 が出てくる。これが何かなんだが……。
Gazo

1024 から 1 引いて 1023 にして、
0 から 1 引いて -1 にしている。

3時59分59秒999ナノ秒 の理屈から言えば 1023.99999 にしなくていいのか という発想にもなるんだが、画像は 1ピクセルより小さな端数は出てこないのでこれでいい。

最後に画像を 貼っ付けておく。
Gazo

これで記事を終わる。

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3