ここでは、「game01」というディレクトリ内で作業しているという前提で説明しています。
敵に弾幕を張らせる
自機は弾が撃てますが、敵が攻撃してこないので弾幕を張るようにします。
今の状態ではbox2dを使ったオブジェクトも同じ「enemy01」というクラスを使っているので、敵専用の新しいクラスを生成します。
ゲームのトップディレクトリ(game01直下)で下記コマンドを実行します。
$ enforce derive enemy02
「src/enemy02.coffee」が生成されているのを確認しましたら、生成されたファイルを開きインスタンスメソッド「destructor」に下記を追記します。
この処理は、弾幕を張っている間に敵に弾が当たった場合にそれ以上弾幕を生成しないようにしています。
if (@enemyBulletControlObj?)
removeObject(@enemyBulletControlObj)
次に、「behavior」を下記のように修正します。
behavior:->
super()
switch @_processnumber
when 0
@xs = 4
@nextjob()
when 1
if (@x > SCREEN_WIDTH)
@x = SCREEN_WIDTH
@xs *= -1
else if (@x < 0)
@x = 0
@xs *= -1
# ここから追加分
tmp = rand(50)
if (tmp == 0)
@xs = 0
@ys = 0
@enemyBulletControlObj = addObject
type: CONTROL
motionObj: enemyBulletControl
x: @x
y: @y
, @
@nextjob()
追加分までは「enemy01」と同じです。
0〜50までの乱数を取得し、0だったならばx方向・y方向どちらの移動量も0にし、「enemyBulletControl」オブジェクトを生成します。
パラメーターとして、オブジェクトタイプに「CONTROL」、動きが記述されたクラスとして「enemyBulletControl」、そしてx座標とy座標、親オブジェクトとして自分自身を渡しています。
弾幕オブジェクトを生成した後に、「@nextjob()」で待機させます。
次に、「enemy01」と同じように下記のインスタンスメソッドを追記します。
「enemy01」とは敵を再生成する時のクラス名が違います。
burn:->
@burnControlObj = addObject
type: CONTROL
x: @x
y: @y
motionObj: burnControl
removeObject(@)
enemyObj = addObject
motionObj: enemy02
type: SPRITE
x: rand(SCREEN_WIDTH)
y: rand(SCREEN_HEIGHT / 2)
image: 'bear'
width: 32
height: 32
animlist: [
[50, [0, 1, 0, 2]]
]
GLOBAL['enemy'] = enemyObj
次に、「gameControl.coffee」で敵を生成している箇所を修正します。
下記のように修正してください。
enemyObj = addObject
# 下記を修正
motionObj: enemy02
type: SPRITE
x: rand(SCREEN_WIDTH)
y: rand(SCREEN_HEIGHT / 2)
image: 'bear'
width: 32
height: 32
animlist: [
[50, [0, 1, 0, 2]]
]
敵を生成している処理で「motionObj」で指定するクラス名を、
enemy01 → enemy02
に変更します。
次に、敵の弾をコントロールする「enemyBulletControl.coffee」を生成します。
このクラスで行う処理は、敵キャラクターを中心に36発の弾を放射状に発射します。
発射する数は「360(度) ÷ 10」で36発です。
それでは、ゲームのトップディレクトリ(game01直下)で下記コマンドを実行してください。
$ enforce derive enemyBulletControl
「src/enemyBulletControl.coffee」が生成されているのを確認しましたら、生成されたファイルを開きインスタンスメソッド「constructor」に下記を追記します。
@bulletangle = 0
そして、「behavior」を下記のように修正します。
behavior:->
super()
switch @_processnumber
when 0
xv = Math.cos(@bulletangle * RAD)
yv = Math.sin(@bulletangle * RAD)
bullet = addObject
motionObj: enemyBullet
x: @x
y: @y
xs: xv * 8
ys: yv * 8
image: 'bear'
width: 32
height: 32
scaleX: 0.5
scaleY: 0.5
animlist: [
[100, [0]]
]
, @
@bulletangle += 10
if (@bulletangle > 360.0)
@parent.setProcessNumber(0)
removeObject(@)
処理を順番に説明します。
まず、x方向とy方向の移動量を「@bulletangle」から算出します。
角度に対するxとyそれぞれの移動量を、コサインとサインで算出します。
角度の指定には「@bulletangle」を使っていますが、この値は0〜360で変化します。
これを角度の指定に使うのですが、そのままでは使えないのでラジアンに変換するためにシステム定数「RAD」を掛けています。
これで角度に対する移動量が算出されます。
算出された値を使って敵の弾オブジェクトを生成します。
一発生成する毎に角度の値「@bulletangle」に10を足します。
それを360が越えるまで続けます。
360を越えていた場合は敵キャラを再び歩かせるために親オブジェクト(敵キャラ)の「@_processnumber」を0に設定し、自分自信(enemyBulletControlオブジェクト)を削除します。
最後に敵の弾クラスを生成しますので、ゲームのトップディレクトリ(game01直下)で下記コマンドを実行してください。
$ enforce derive enemyBullet
「src/enemyBullet.coffee」が生成されているのを確認しましたら、生成されたファイルを開きインスタンスメソッド「behavior」を下記のように修正してください。
behavior:->
super()
switch @_processnumber
when 0
if (@x < 0 || @x > SCREEN_WIDTH || @y < 0 || @y > SCREEN_HEIGHT)
removeObject(@)
これは、単純に画面外に消えたら自分自信を削除しています。
これで、敵が1/50の確率で弾幕を張るようになります。
(自機の当たり判定が無いのでなにも変わりませんが)
この弾幕に使っている弾の座標は、サイン/コサインを使っているので当然、返ってくる値は浮動小数点数になるのですが、座標値は整数になるため普通に計算しているとどんどん誤差が溜まってしまいます。
enforceでは、実際の座標値は浮動小数点数で保持しており座標値の計算は、浮動小数点数で行われます。
画面に表示する時に整数に変換しているので、自然な放物線状に弾が発射されます。
チュートリアル(14) <--- ⬛︎ ---> チュートリアル(16)