【Macでゲームを作ろう】ブロック崩しを作っていく第8回 [AppleScript辞書はつくれるか?]
そでは今回は最後の、ブロックを積んで崩して行きましょう。
今回新しく出てくるコードは、
NSMakeRect
initWithFrame_
addSubview
removeFromSuperview
です。
ブロック崩しといえば、たくさんのブロックが配置されていてそれをボールで崩していくものですよね?
じゃぁ、そのブロックを一つ一つ並べていく作業からして行くのだろうな。
・・・なんて考えていないでしょうね?
いままでは、一つ二つおいて、好きな位置へ画像へと変更して行きましたが、それが数十個、数百個と増えていったらやり切れると思いますか?
できませんよね?
そこは、まぁ、目の前にあるのはコンピューターですよ。自動で並べることをやらせましょう。
まず、ブロックを配置していくベースを作ります。
今あるView1(NSView)の上にView2(サブView)を写真の位置になるように重ねて配置します。
そのサブViewをコントロールするためのコードを書き、
property view2 : missing value
つないでいきましょう。
次に、そのサブViewの上にブロックを並べて行きましょう。
今回も(ダサい)ブロックを5個用意しました。
これを横に6つ、縦に4つ並べて行きます。
set ImageItem to thisApp's NSImageView's alloc()'s initWithFrame_(thisApp's NSMakeRect(blockX, blockY, blockSizeW, blockSizeH))
これで新しいImageViewの新しく構築して、
ImageItem's setEditable_(false)
ImageItem's setImageFrameStyle_(0)
ImageItem's setImageAlignment_(0)
ImageItem's setImageScaling_(1)
set imageItemName to "block01.png"
set aImage to NSImage's imageNamed_(imageItemName)
ImageItem's setImage_(aImage)
各種設定を行い
view2's addSubview_(ImageItem)
で設置します。
ちなみに上の
● setEditable
これは使えるようにするか否かの設定で、(設定しなくても)通常は『使えないことなない(false)』つまり『使える』状態になってます。使えないにすると半透明になってしまうので、その回避用。なんらかの仕様変更がこわいので設定しておきます。
● setImageFrameStyle
はImageViewのフレームの仕様です。
NSImageFrameNone フレーム無し (= 0)
NSImageFramePhoto 細い線で細い影付き (= 1)
NSImageFrameGrayBezel ベゼル付き (= 2)
NSImageGroove 溝付き (= 3)
NSImageFrameButton ボタンのようなべベル付き (= 4)
通常は、NSImageFrameNoneです。なにか、コードを入れてもうまくいかないので、後ろのインデックス番号を使用しています。
● setImageAlignment
はImageViewの中でどの位置に寄せるのかを設定しています。
NSImageAlignCenter 中心寄せ (=0)
NSImageAlignTop 上寄せ (=1)
NSImageAlignTopLeft2 左上寄せ (=2)
NSImageAlignTopRight 右上寄せ (=3)
NSImageAlignLeft 左寄せ (=4)
NSImageAlignBottom 下寄せ (=5)
NSImageAlignBottomLeft 左下寄せ (=6)
NSImageAlignBottomRight 右下寄せ (=7)
NSImageAlignRight 右寄せ (=8)
通常はNSImageAlignCenterです。
● setImageScaling
NSScaleProportionally 縦横比を維持してNSImageViewに合わせます (=0)
NSScaleToFit NSImageViewに比率は無視して縦横合わせる (=1)
NSScaleNone 拡大縮小せずそのまま (=2)
通常は、NSScaleProportionallyです。
※ちなみに、ここにはめ込む画像は、解像度(72dpiや300dpi)などにも大きく影響されるので画像を保存する際は72dpi等に統一しておいたほうが良いでしょうね。
そして最後に、ここまで構築していったイメージ情報をサブView貼り付けます。
view2's addSubview_(ImageItem)
これをブロック全てに繰り返し行えば良いのです。
ハンドラにしておけば結構楽ですよ。
さて、疑問になるのは何故サブViewを使って配置したのかです。
ただの差別化したかったから?
いいえ、ちゃんとした意味があります。ちなみに、サブViewを使わずに直接今あるメインのViewに出力した例を見てみましょう。
どうでしょう?
新しく作られたオブジェクト(つまりここではブロックたち)が全ての前面に来てみっともない配置になってしまいます。
それを回避するために、いろいろ考えずに新しいサブViewの上に配置していこうと思ったわけです。
そして、サブViewの配置するときにも特に注意して配置していたわけですが、
のように、上なら背面、下に行けば行くほど前面に出てくるというのが仕組みです。
さて次は、ブロックの当たり判定のコードをちょちょいっと入力します。
先ほどのブロックを表示したままでは、どこにあるのか?どうやって消したら良いのかに困ってしまいます。
ブロックを配置する際に、その情報をデータベース化?します。
set blockObject to blockObject & {ImageItem}
set blockPosition to blockPosition & {{blockX, blockY, blockSizeW, blockSizeH}}
この情報を使って、次から当たり判定やブロックの消去を行います。
set {tamaCenterX, tamaCenterY} to {tamaX + tamaW, tamaY + tamaH}
repeat with i from 1 to (count of blockPosition)
set {block_x,block_y,block_w,block_h} to item i of blockPosition
set {block_w,block_h} to {block_w + block_x,block_h + block_y}
if (block_x < tamaCenterX) and (block_w > tamaCenterX) then
if (block_y < tamaCenterY) and (block_H > tamaCenterY) then
set moveY to 0 - moveY
tell item i of blockObject to removeFromSuperview()
set item i of blockPosition to {0,0,0,0}
end if
end if
end repeat
これで判定のあったブロックのImageViewオブジェクトの存在を消し、
判定のために使った座標を、反応しない座標に変更しています。
全てのブロックが消えたかどうかの判定のために、ブロックが配置された直後に
set blockCount to count of blockObject
として数を覚えておき、
消えた際にカウンドをダウンしておきます。
それがゼロになったらクリアという判定も入れておきましょう。
if blockCount is 1 then
--|Clear !|
tell TimerObj to invalidate()
set blockObject to {}
set blockPosition to {}
textLabel1's setStringValue_("Clear !")
set flg to false
NSCursor's unhide()
else
set blockCount to blockCount - 1
exit repeat
end if
(判定が1だったら終わりになっていますが、すぐ次にカウンドがダウンしたら0になるでしょ?という意味で使用しています。)
あとここまで説明していませんが、恒例の
global blockSizeW, blockSizeH
global blockObject, blockPosition, blockCount
グローバル設定もお忘れなく
しかしこのままでは、画面が同じ色のブロックが並ぶだけですよね?
そして、ここからの広がりのために最初のステージというハンドラを作りますか。(いくつか重複してますが)
on stage01()
set blockObject to {}
set blockPosition to {}
set {blockSizeW, blockSizeH} to {80, 20}
set stageText to "332533" & return
set stageText to stageText & "343313" & return
set stageText to stageText & "313343" & return
set stageText to stageText & "335233"
repeat with y from 1 to (count of (every paragraph of stageText))
set oneLine to paragraph y of stageText
repeat with x from 1 to (count of oneLine)
set b_x to ((x - 1) * blockSizeW)
set b_Y to ((y - 1) * (blockSizeH + 10)) + 200
set blockColor to character x of oneLine
my blockMakeing(b_x, b_Y, blockSizeW, blockSizeH ,blockColor)
end repeat
end repeat
set blockCount to count of blockObject
end stage01
on blockMakeing(blockX, blockY, blockSizeW, blockSizeH, blockColor)
set ImageItem to thisApp's NSImageView's alloc()'s initWithFrame_(thisApp's NSMakeRect(blockX, blockY, blockSizeW, blockSizeH))
ImageItem's setEditable_(false)
ImageItem's setImageFrameStyle_(0)
ImageItem's setImageAlignment_(0)
ImageItem's setImageScaling_(1)
set imageItemName to "block0" & blockColor & ".png"
set aImage to NSImage's imageNamed_(imageItemName)
ImageItem's setImage_(aImage)
view2's addSubview_(ImageItem)
set blockObject to blockObject & {ImageItem}
set blockPosition to blockPosition & {{blockX, blockY, blockSizeW, blockSizeH}}
end blockMakeing
ちくっと、新しいハンドラを作って対応してみました。
ちょっと大雑把になってしまいましたが、必要となる部品の話はできたと思います。
最後に、全体のスクリプトを置いておきます。
これで、今回のブロック崩しを作ってみようは終わりになります。
2ヶ月もかけてしまいましたが、お付き合いありがとうございました。
script AppDelegate
property parent : class "NSObject"
property NSTimer : class "NSTimer"
global TimerObj
global flg
property NSEvent : current application's class "NSEvent"
property view1 : missing value
property image1 : missing value
property image2 : missing value
property textLabel1 : missing value
property Button1 : missing value
property NSImage : class "NSImage"
property thisApp : current application
property view2 : missing value
global blockSizeW, blockSizeH
global blockObject, blockPosition, blockCount
global viewX,viewY, tamaX,tamaY, moveX,moveY
global tamaW,tamaH, tamaSizeX,tamaSizeY
global flg
global padX,padY, padW, padH, padSizeX,padSizeY
global mouse0
global tamaReset
property NSCursor : class "NSCursor"
on action1_(sender)
if flg is false then
(* NSViewの範囲 *)
set origin1 to view1's frame()'s |size|
set {viewX,viewY} to {origin1's width, origin1's height}
(* tamaの情報をリセット *)
set {moveX,moveY} to {5,5}
set {tamaX,tamaY} to tamaReset
set BollPosition to {x:tamaX, y:tamaY}
image1's setFrameOrigin_(BollPosition)
(* Padの情報取得 *)
set {padSizeX, padSizeY} to {100, 24}
set {padSizeX, padSizeY} to {(padSizeX / 2), (padSizeY / 2)}
set origin3 to image2's frame()'s origin
set {padX,padY} to {origin3's x, origin3's y}
set size3 to image2's frame()'s |size|
set {padW, padH} to {size3's width, size3's height}
set {padW, padH} to {(padW / 2), (padH / 2)}
(* 現在のマウスの位置を取得 *)
set {mouseX,mouseY} to my mousePosition()
set mouse0 to mouseX
my timerStart()
set flg to true
button1's setAlphaValue_(0.0)
textLabel1's setStringValue_("")
NSCursor's hide()
end if
end action1_
on stage01()
set blockObject to {}
set blockPosition to {}
set {blockSizeW, blockSizeH} to {80, 20}
set stageText to "332533" & return
set stageText to stageText & "343313" & return
set stageText to stageText & "313343" & return
set stageText to stageText & "335233"
repeat with y from 1 to (count of (every paragraph of stageText))
set oneLine to paragraph y of stageText
repeat with x from 1 to (count of oneLine)
set b_x to ((x - 1) * blockSizeW)
set b_Y to ((y - 1) * (blockSizeH + 10)) + 200
set blockColor to character x of oneLine
my blockMakeing(b_x, b_Y, blockSizeW, blockSizeH ,blockColor)
end repeat
end repeat
set blockCount to count of blockObject
end stage01
on blockMakeing(blockX, blockY, blockSizeW, blockSizeH, blockColor)
set ImageItem to thisApp's NSImageView's alloc()'s initWithFrame_(thisApp's NSMakeRect(blockX, blockY, blockSizeW, blockSizeH))
ImageItem's setEditable_(false)
ImageItem's setImageFrameStyle_(0)
ImageItem's setImageAlignment_(0)
ImageItem's setImageScaling_(1)
set imageItemName to "block0" & blockColor & ".png"
set aImage to NSImage's imageNamed_(imageItemName)
ImageItem's setImage_(aImage)
view2's addSubview_(ImageItem)
set blockObject to blockObject & {ImageItem}
set blockPosition to blockPosition & {{blockX, blockY, blockSizeW, blockSizeH}}
end blockMakeing
on timerStart()
try
set TimerObj to NSTimer's scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(0.01, me, "mainLoop", missing value, true)
on error
set TimerObj to missing value
end try
end timerStart
on mainLoop()
my moveBall()
my movePad()
end mainLoop
on abs(n)
return (n ^ 2) ^ 0.5
end abs
on moveBall()
set {tamaX,tamaY} to {tamaX + moveX, tamaY + moveY}
set {tamaCenterX, tamaCenterY} to {tamaX + tamaW, tamaY + tamaH}
set {Xposition1, Yposition1} to {(tamaCenterX - tamaSizeX), (tamaCenterY - tamaSizeY)}
set {Xposition2, Yposition2} to {(tamaCenterX + tamaSizeX), (tamaCenterY + tamaSizeY)}
if (Xposition1 < 0) or (Xposition2 > viewX) then set moveX to 0 - moveX
if Yposition2 > viewY then set moveY to 0 - moveY
if Yposition1 < 0 then
tell TimerObj to invalidate()
textLabel1's setStringValue_("Game Over !")
set flg to false
button1's setAlphaValue_(1.0)
NSCursor's unhide()
end if
set decisionX to my abs((padX + padSizeX) - tamaCenterX)
set decisionY to my abs((padY + padSizeY) - (tamaCenterY - tamaSizeY))
if (decisionX < padSizeX) and (decisionY < padSizeY) then set moveY to my abs(moveY)
repeat with i from 1 to (count of blockPosition)
set {block_x,block_y,block_w,block_h} to item i of blockPosition
set {block_w,block_h} to {block_w + block_x,block_h + block_y}
if (block_x < tamaCenterX) and (block_w > tamaCenterX) then
if (block_y < tamaCenterY) and (block_H > tamaCenterY) then
set moveY to 0 - moveY
tell item i of blockObject to removeFromSuperview()
set item i of blockPosition to {0,0,0,0}
if blockCount is 1 then
--|Clear !|
tell TimerObj to invalidate()
set blockObject to {}
set blockPosition to {}
textLabel1's setStringValue_("Clear !")
set flg to false
NSCursor's unhide()
else
set blockCount to blockCount - 1
exit repeat
end if
end if
end if
end repeat
set BollPosition to {x:tamaX, y:tamaY}
image1's setFrameOrigin_(BollPosition)
end moveBall
on movePad()
set {mouseX,mouseY} to my mousePosition()
set padX to padX + (mouseX - mouse0)
set padLeft to padX + padW - padSizeX
set padRight to padX + padW + padSizeX
if padLeft < 0 then set padX to padSizeX - padW
if padRight > viewX then set padX to viewX - padW - padSizeX
set mouse0 to mouseX
set padPosition to {x:padX, y:padY}
image2's setFrameOrigin_(padPosition)
end movePad
on mousePosition()
set mouseLocate to NSEvent's mouseLocation()
set mouseX to (mouseLocate's x) as integer
set mouseY to (mouseLocate's y) as integer
return {mouseX,mouseY}
end mousePosition
-- IBOutlets
property theWindow : missing value
on awakeFromNib()
textLabel1's setStringValue_("")
end awakeFromNib
on applicationWillFinishLaunching_(aNotification)
set flg to false
(* tamaの情報取得 *)
set {tamaSizeX, tamaSizeY} to {16, 16}
set {tamaSizeX, tamaSizeY} to {(tamaSizeX / 2), (tamaSizeY / 2)}
set origin2 to image1's frame()'s origin
set {tamaX,tamaY} to {origin2's x, origin2's y}
set size2 to image1's frame()'s |size|
set {tamaW, tamaH} to {size2's width, size2's height}
set {tamaW, tamaH} to {(tamaW / 2), (tamaH / 2)}
(* tamaの位置の初期値を保存 *)
set tamaReset to {tamaX,tamaY}
my stage01()
end applicationWillFinishLaunching_
on applicationShouldTerminateAfterLastWindowClosed_(sender)
return true
end applicationShouldTerminateAfterLastWindowClosed_
on applicationShouldTerminate_(sender)
return current application's NSTerminateNow
end applicationShouldTerminate_
end script
コメント 0