今回はモーフに状態を持たせることについて学ぶ。
もちろん今までもモーフは大きさ、位置、色などさまざまな状態を持っていたが、それとは異なる内部的な状態を持たせたい。
例として今まで右に移動するだけだったMyMorphを、クリックするたびに90度ずつ向きを変えて移動するようにしよう。
さて、MyMorphで「移動」を実現しているのはstepメソッドである。
step self color: Color random. self topLeft: (self topLeft + (2@0))
MyMorphの左上のX座標を2増やすことで右に2ピクセル移動する。
例えばモーフが右に90度向きを変える(つまり下向きに移動する)とすれば、3行目は以下のようになるだろう。
self topLeft: (self topLeft + (0@2))
さらに90度向きを変えると左向きに進み、コードは以下のようになる。
self topLeft: (self topLeft - (2@0))
モーフの左上座標に対して、一定数の座標値を加減することで向きが変わると考えられるので、以下のようにコードを変更できる。
self topLeft: (self topLeft + vec)
つまり、座標値に加えるvecが2@0であれば右に進み、0@2であれば下に進むので、(vecは)モーフが進む方向を表す状態とみなせる。
このvecは動作しているモーフ固有の情報である。他にMyMorphモーフがあったとしても、それぞれの値は独立している。
このような値を表すのにSmalltalkではインスタンス変数を用いる。インスタンス変数はクラスを定義する際に宣言し、そのクラスに属する全てのモーフが独立した値を持つようになる。
MyMorphにインスタンス変数vecを加えるには、以下のようにクラス定義を変更する。
Morph subclass: #MyMorph instanceVariableNames: 'vec' classVariableNames: '' category: 'Hajimeteno Morphic Tutorial'
文字通りinstanceVariableNames:の後の文字列が、インスタンス変数の並びを表す。複数のインスタンス変数が必要ならば、スペースで区切り両端をシングルクオートで囲んで(つまり文字列として)指定する。
第4回のクラス定義と同じ方法で、クラス定義のテンプレートを上のように修正してAcceptすれば、既存のMyMorphの定義は上書きされる。上書きをしてもメソッド定義は消えないので心配しなくて良いが、動作中のモーフがあるとクラス定義の変更の影響を受ける場合があるので、あらかじめ削除しておいた方が無難である。
インスタンス変数vecを加えたMyMorphをAcceptしたら、stepの内容を以下のように変更する。
step self color: Color random. self topLeft: (self topLeft + vec)
この状態でMyMorphを動かすと、vecが未定義なためにエラーが発生する。モーフの生成と同時にvecの初期値を設定する必要がある。
そのため第6回で説明したようにinitializeメソッドを以下のように変更し、vecの初期値を2@0とする。(モーフは右に進む)
initialize super initialize. vec := 2@0. self extent: 16@16
クリックするたびにモーフが90度向きを変えるようにするため、mouseDown:メソッドを修正する。
mouseDown: anEvent self extent: self extent + (10@10). vec := vec y negated @ vec x
インスタンス変数vecの値を変更する部分で説明が必要なのはnegatedメッセージの意味である。negatedメッセージはメッセージを送った数値の正負を反転した値を返すメッセージである。
上記までの変更を行えば、MyMorphはクリックするたびに右へ90度向きを変えながら移動するようになる。
(第11回おわり)