はじめに
Scratch3 の『もし端に着いたら跳ね返る』の角度について、しつこく・しつこく調べてみました。自分でもくどいくらいに調べてみた。
よくありがちな説明
ネットとかScratch本をみると、反射角だよとの説明がありました。
こんな感じで 入射角=反射角 になるよ と説明されています。
しつこく調べてみるのが面白い
そうですか?本当ですか?としつこく調べてみるのが面白いと思いませんか?
45度の角度で
では 45度の角度(向き)で端にぶつけてみましょう。スプライトの軌跡をわかりやすくするために「ペン機能」を使います。
コード
結果
45度で跳ね返るようですね。「入射角=反射角」でした。(当たり前か)
次は85度だ
コード
結果
85度で上の端に着いたら101度で跳ね返りました。
いつでも「入射角=反射角」ではないようですね
いろんな角度で試してみよう
入射角をいろいろと変えてみて、反射するときの「向き」がどうなるかを調べてみましょう。
調べた結果
前提:「上の端に着いたとき」の反射です。
入射 | 反射 | |
---|---|---|
0~78 | 入射角と同じ | |
79 | 78 | |
80~90 | 79 | 90 - 79 = 11 |
「入射角=反射角」とはならない入射角がありました。
入射角が 90度に近いときは 反射角 = 入射角 + α
というか、80度≦入射角 のときは、反射角=79度(固定) です。
わざと 少し大きな角度で反射させているようです(Scratch3 の仕様かな)
入射角が小さいときに 反射を強くさせることで「動き」を強調しているのかも。
なぜなのかはScratch3 の実装者にきかないとわからないですが、そんな反射をすると覚えておくと、いいことがあるかもね。
もっと追及してみる
『もし端に着いたら跳ね返る』ブロックが実行されると何が起こるのでしょうか?
反射して向きが変わるだけでしょうか。
前後で少し止めて動きを観察する
経過
(1)開始
(2)右上の端に触った
(3)向きが変わる
(4)移動が起こる
(2)で変わった「向き」の方向へ移動していないことに注目してください。
「向き」は維持したまま、スプライトが端に(上下左右)触れないぎりぎりのところをみつけ、そこに移動しています。
仮説(推論)
端の隅から跳ね返るときは向きの方向に跳ね返るわけではなくて、隅に触れるぎりぎりの場所へ移動する!
仮説の検証
(1)開始
(2)右上の端に触った
(3)向きが変わる
(4)移動が起こる
ここが重要!ひげが伸びているわけだが。
隅っこに触ったときの整理
右上の隅なら?
直前で調べているとおり、右上の隅に触ったときは、上の端で反射するように角度が変わる。
右下の隅なら?
角度はどうなった?
右下の隅に触ったときは、右側の端で反射しているように角度が変わる
左上の隅なら?
角度はどうなった?
左上の隅に触ったときは、左側の端で反射しているように角度が変わる
左下の隅なら?
角度はどうなった?
- 右上の隅⇒上の端で反射するように角度が変わる
- 右下の隅⇒右の端で反射するように角度が変わる
- 左下の隅⇒???(下の端で反射するのかな?)
- 左上の隅⇒左の端で反射するように角度が変わる
左下の隅に触ったときは、下の端で反射しているように角度が変わる
と思いきや、「左の端で反射しているように角度が変わる」のでした。
まとめ(隅に触ったときの反射)
左上の隅、左下の隅、ともに「左端で反射!」です。変じゃない?
どういうこと?
どういうこと?(美しくないな)、実験のミスか?と何度もやり直したけど、結果は変わらず。しょうがないので、Scratch-vm 実装をみてみる。
ifOnEdgeBounce (args, util) {
~
if (nearestEdge === 'left') {
dx = Math.max(0.2, Math.abs(dx));//x
} else if (nearestEdge === 'top') {
dy = Math.max(0.2, Math.abs(dy));
} else if (nearestEdge === 'right') {
dx = 0 - Math.max(0.2, Math.abs(dx));
} else if (nearestEdge === 'bottom') {
dy = 0 - Math.max(0.2, Math.abs(dy));
}
const newDirection = MathUtil.radToDeg(Math.atan2(dy, dx)) + 90;
util.target.setDirection(newDirection);
~
}
// 【解説】
// もし 左端についている ならば
// 左端で反射する
// でなければ
// もし 上端についている ならば
// 上端で反射する
// でなければ
// もし 右端についている ならば
// 右端で反射する
// でなければ
// もし 下端についている ならば
// 下端で反射する
// でなければ
// 反射しない
下端に触れていてもいなくても、「左端に触れている」ならば、左端で反射することになる。
なるほど。こんな実装だったら左上隅も左下隅も「左端で反射」するね。納得した。
次の確認
次は「ひとつの端」に触れたときの「跳ね返り」を確認する
まとめ:「もし端に着いたら跳ね返る」ブロックの動きとは?
- 向きを反転させる(向きは 80度 ≦入射角 のときは 反射角= 79度)
- 触れている端に(上下左右で)触らないぎりぎりのところに、スプライトを移動させる
の2つを実行するブロックでした。
追加ルール
複数の端にふれているときの反射角は どの端で反射した角度なのか?についてはScratchなりのルールがある。でした。