Smalltalk のプログラミングの最終回では,流れ星を作ってみよう.主なオ ブジェクトは次のとおりである.
下の図のように,Squeak の Window 上に ClippingBox という長方形の領 域を設定する.長方形は,左上の点と右下の点により定義する.
インスタンスメソッドとして,長方形の定義をするメソッド (lx:ly:rx:ry:),位置(Point のインスタンス)がClippingBox の内部に位置 する場合に true を返すメソッド(isIn:)を作る.クラスメソッドとして,イ ンスタンスの生成と長方形の定義を同時に行うメソッド(lx:ly:rx:ry:)を作る.
インスタンス変数としては,長方形の2点の座標(lx,ly,rx,ry)が必要で ある.
(クラス)
Object subclass: #ClippingBox
instanceVariableNames: 'lx ly rx ry '
(インスタンスメソッド1)
lx: x1 ly: y1 rx: x2 ry: y2
x1 < x2
ifTrue: [lx := x1. rx := x2]
ifFalse: [lx := x2. rx := x1].
y1 < y2
ifTrue: [ly := y1. ry := y2]
ifFalse: [ly := y2. ry := y1]
(インスタンスメソッド2)
isIn: pnt
(lx > pnt x or: rx < pnt x or: ly > pnt y or: ry < pnt y)
ifTrue: [^ false]
^ true
(クラスメソッド1)
lx: x1 ly: y1 rx: x2 ry: y2
^ self new lx: x1 ly: y1 rx: x2 ry: y2
|
(Workspace上での動作確認) c1 := ClippingBox new lx: 100 ly: 100 rx: 200 ry: 200 (Alt-dを押す) c1 isIn: 110@120 (Alt-p を押すと true が表示される) c2 := ClippingBox lx: 100 ly: 100 rx: 200 ry: 200 (Alt-dを押す) c2 isIn: 90@120 (Alt-p を押すと false が表示される) |
PointGenerator は,要求を受けるたびに,位置を返すオブジェクトである.
PointGenerator として必須の機能は,next メッセージを受け付けて, Point のインスタンスを返すことである.この性質を定義するために, PointGeneratorには,抽象メソッドとして next を定義する.また,初期位置 から提供するメソッド reset も同様である.
(クラス) Object subclass: #PointGenerator (インスタンスメソッド1) next self subclassResponsibility (インスタンスメソッド2) reset self subclassResponsibility |
具体的に,位置の系列を返すために,配列を使用する方法がある.next の要求のたびに,配列から1つずつ位置をとりだして,答えるという方法をと る.配列を使用する PointGenerator のことを,ArrayedPointGenerator とし て実装する.
ArrayedPointGenerator は,インスタンス変数として,配列変数 pnts, 参照位置 t を使用する.
(クラス)
PointGenerator subclass: #ArrayedPointGenerator
instanceVariableNames: 't pnts'
(インスタンスメソッド1)
next
| pre |
pre := t.
t := t + 1.
t > pnts size
ifTrue: [t := 1].
^ pnts at: pre
(インスタンスメソッド2)
reset
t := 1
(インスタンスメソッド3)
setArray: a
t := 1.
pnts := a
|
(Workspace上での動作確認) a := Array new: 3. a at: 1 put: 100@100. a at: 2 put: 110@150. a at: 3 put: 120@200. g := ArrayedPointGenerator new. g setArray: a. g next (Alt-p を押すごとに座標が得られる) |
新たに次の機能を追加する.
以上の機能を実現するために,以下のインスタンス変数を追加する.
(クラス)
StarMorph subclass: #MyStarMorph
instanceVariableNames: 'fout clipping meteorFlag returnFlag '
(インスタンスメソッド1)
init
returnFlag := true
(インスタンスメソッド2)
setClipping: c
clipping := c
(インスタンスメソッド3)
exmode: b
returnFlag := b
(インスタンスメソッド4)
stop
fout = nil
ifFalse: [fout close. fout := nil]
ifTrue: [ meteorFlag := false ]
(インスタンスメソッド5)
position: pos
?
? 前回までのとおり
?
?
?
clipping = nil
ifFalse: [(clipping isIn: pos)
ifFalse: [Exception new signal: 'OutOfClipping']].
super position: pos
(インスタンスメソッド5)
meteor: gen
meteorFlag := true.
[[ meteorFlag ]
whileTrue: [(Delay forMilliseconds: 100) wait.
[self position: gen next]
on: Exception
do: [: ex | ex messageText = 'OutOfClipping'
ifTrue: [returnFlag
ifTrue: [gen reset.
ex resume]
ifFalse: [meteorFlag := false]]]].
nil] fork
|
以上を入力したら,実行する前に,ワールドを保存し ておこう. fork を使用しているので,思わぬ不具合が発生すること がよくある.復帰が難しいので,ウインドウを閉じるしかなくなることがある.
以下の手順で動作する.
(ワークスペース) a := Array new: 10 a at: 1 put: 100@100. a at: 2 put: 103@112. a at: 3 put: 106@124. a at: 4 put: 109@136. a at: 5 put: 112@148. a at: 6 put: 115@160. a at: 7 put: 118@172. a at: 8 put: 121@184. a at: 9 put: 124@196. a at: 10 put: 127@208. g := ArrayedPointGenerator new. g setArray: a. c := ClippingBox lx: 90 ly: 90 rx: 200: ry: 250. s1 := MyStarMorph new. s1 init; openInWorld; setClipping: c; meteor: g. s2 := MyStarMorph new. s2 init; openInWorld; setClipping: c; meteor: g. (止めるとき) s1 stop. s2 stop. (再開するとき) s1 meteor: g. s2 meteor: g. |
次のような改良をしてみよう.
ArrayedPointGenerator のサブクラスとして,FiledPointGenerator を作 る.FiledPointGenerator は,前回の宿題で作成した位置ファイルを読み込み, Array に格納し,ArrayedPointGenerator と同一の動作をする.
MyStarMorph の meteor メソッドに、Delay forMilliseconds: 100 とい う部分がある.ここの 100 という数字を大きくすると,星の動きはゆっくり になる.そこで,インスタンス変数で,ここの数字を表すことにして,その変 数値をあるメソッドから操作できるようにする.
たとえば,instanceVariableNames に wtime を追加し,Delay forMilliseconds: wtime とする.新たなメソッド setWait: t において, wtime := t を実行するように定義する.また,メソッド init において, wtime := 100 という初期化を追加する.実行時に s1 setWait: 300 とすると, 1つだけゆっくりと動くことになる(インスタンス変数を操作するためのメソッ ドをアクセサと言う).