2012年7月14日土曜日

[ドリトル] 回転するカメ その2


前回の続き。 自転しながら動くタートルを作る。

前回は独自の「進行方向」プロパティを用意して、forward する際に、角度と移動距離から三角関数で移動後の位置を求めた。
いまいちタートルグラフィックスぽくないので別のやり方でやってみる。三角関数禁止。

基本戦略。
  • 進行方向を向くタートル(非可視)と回転するタートルの2匹組にする。
  • 片方のタートルが外から使える外部タートルでもう一匹は内部タートル
いくつかのオプションがあるので比較検討する。
多きくわけて、「外部/内部どちらが回転するか」、「外部/内部どちらが描画するか」の組み合わせて設計が分かれそう。


外部タートルを回転タートルにする。

この場合は内部タートルが進行方向を保持することになる。 forward の移動処理は内部タートルが行い、移動後に内部タートルの位置情報を外部タートルにコピーして移動させる。

内部タートル(進行方向保持)が描画

linewidth など描画関係の処理をすべからく内部タートルに渡す必要がある。
必然的に内部タートルを hide にすることはできないので、それ以外の方法で非可視にする必要がある。setScale で極端に小さくするか、setShape で存在しないファイル名を指定してやるか(エラーにはならず表示されないタートルになる)でできそう。

外部タートル(自転)が描画

linewidth など描画関係の処理はオーバーライドしないでも普通に効果が出るのがメリット。内部タートルは hide できる。 一方 forward等の移動系の処理はすべて内部タートルに渡してあげないといけない。circle や polygon などの複合移動処理は内部タートルを動かしても描画されないのでいかんともしがたい。


内部タートルを回転タートルにする。

外部タートルは進行方向を保持し、上に回転タートルをかぶせてしまう。 forward 処理は外部タートルが受け持ち、移動後に内部タートルを moveto で引き連れていく。

内部タートル(自転)が描画

外部タートルは hide しておけばよいのがメリット。
ただし、外部タートルを回転タートルにした場合と同様、circle や polygon などの処理が難しそう。

外部タートル(進行方向保持)が描画

hide できないので、hide 以外の方法で不可視にする必要がある。lineWidth や lineColor、makeFigure などがオーバーライドしないですむのがポイント。



いずれにしても、circle、polygon などのことを考えると「進行方向を保持する側のタートルが描画を受け持つ」ことになる。circle、poygon は最終的な位置の変化がないのでforward系と描画担当を分ける手もあるが makeFigure した際に訳が分からなくなってしまう。

width、linewidthなどのことを考えると外部タートルを「進行方向保持タートル」にして、
内部タートルを自転させることにする。

やってみよう。

SpinTurtle = Turtle!create.
SpinTurtle!hide.

// speed: rotate / sec. 
SpinTurtle:create = [ | speed  ; create | 
    obj = ! create.
    obj ! (speed) init.
    obj.
].

SpinTurtle:init = [ | speed | 
    self! "hogehoge" setShape.   // hide self.
    self:innerTurtle = Turtle ! create.
    self:innerTurtle ! penup.

    self:rotateTimer = Timer ! create. 
    self:rotateTimer ! 0.05 interval ((1 / 0.05) * 300) times.
    self:rotateTimer ! [
        // use parent "leftturn" 
        self:innerTurtle ! (360 * speed / 20) leftturn.] execute.
].

SpinTurtle:forward = [ | x ; forward |
    ! (x) forward.
    self:innerTurtle ! (self!xPosition?) (self!yPosition?) moveto.
    self.
].

// -------- End of SpinTurtle definition. -----------

t1 = SpinTurtle! 1 create.
t1 ! 2 lineWidth (blue) lineColor.
timer1 = Timer!create .
timer1 ! 0.05 interval 360 times.
timer1 ! [
  t1 ! 10 forward.
  t1 ! 5 leftturn.
] execute.
いい感じ。
この方法だと前の方法と違って、circle 命令や polygon 命令も期待通りに動く。

...
// -------- End of SpinTurtle definition. -----------

t1 = SpinTurtle! 1 create.
timer1 = Timer!create .
timer1 ! 0.7 interval 5 times.
timer1 ! [ | x |
  t1 ! (100 - x*10) 4 polygon.
] execute.
その時の自身の向きに関係なく描画方向にきちんと図形が描かれていることがわかる。



moveto、movecenterto の両命令で内部タートルを追随させる必要があるが省略。 setShape、scale 命令も内部タートルに渡さないといけない。
あとこのままだと衝突判定の際に、見た目と違ってサイズが0になっているので、本来期待されるマージンがない状態になっている。考えられる対策は2つ。

  •  外部タートルの画像としてダミーファイル名ではなく、本来のカメと同じサイズの画像をロードする。 
  • 内部タートルの衝突イベントハンドら(collision命令)から外部タイトルのハンドラを実行する。 

前者の場合は、背景画像上を通過時に白抜けになる可能性があることと、setShapeやscale時にマージン値が追従しない。
後者は大体うまくいくと思うが、跳ね返らず突き抜ける際に内部/外部両ハンドラがそれぞれ実行される可能性がある。

0 件のコメント:

コメントを投稿