2018年 6月9日
今回はオートエイム(自動照準)を実装します。
スマホゲームって、どうしても操作に馴染まないというか、画面を操作すると指で隠れますから鑑賞できないというか、だから自動ゲームというか放置ゲーム向きだと思います。
その点パズドラは良く考えられていると思うけど、精々そのくらいの操作が限界なのかとも思う。
この 1MAP-STGもスマホだとイマイチゲームしずらいですね。
そこで今回はスマホ用にオートエイムを作ってみる事にします。
オートエイムのやりかたですけど「標的とする敵の座標へ照準を動かす」だけです。
どの敵を狙うかは、始めと標的を倒した時だけ捜査します。
毎フレーム事だと計算が頻繁になるし、標的が都度変わるので照準がウロウロします。
どの敵が一番近いかは、照準と全敵との距離を測ります。
距離を測るプログラムは前回以前にやりました。
x2=ex(cnt)-cx : y2=ey(cnt)-cy
nd2=sqrt(x2*x2+y2*y2); 距離
毎フレーム事、標的にした敵の座標へ照準を動かし、もし照準と敵の距離が照準のスピード以下なら照準の座標を標的の座標に置き換えます。
autofでオートモードとマニュアルモードの切り替えをします。
autof=0でマニュアル、1でオートモードです。
切り替えは真ん中の自機をクリックする事で行います。
それと今回は #deffuncを使って、標的を特定するソースを、メインルーチンから呼び出して使ってます。
これは標的を特定する箇所がソース内で複数のため、同じソースを何回も書くと多くなるし見づらいからです。
合わせてこの方法だと、バグを潰しやすくなります。
実は #deffunc returnについては、ラベルの gosub returnと違いありません。
つまり *mainというラベルがあったとして #deffunc main と書かれるだけの事です。
一種のラベル定義命令だと思えば良いと思います。
ただ変数を渡せるのと、例えばソースが
n=10 : gosub *syori と書くのと
syori 10 と書くのとで
小さく書けるので、頻繁に使ったりするのだと書きやすいし見易いです。
HSPの場合の欠点は「呼び出される #deffuncソースが、呼び出しを行うソースより前に無いとエラー」になる事です。
そのため HSPの場合どうしても、メインルーチンがソースの一番最後に置かれる、という奇妙な事になります。
昔 BASIC何かのインタプリタ(逐次訳実行)言語では、メインルーチンは処理の早い前に置け、と言うのがセオリーでした(笑)(今のコンピュターの速度では大差無いんでしょうね)
今一度キャラを全部置いておきます。
![]()
![]()
![]()
![]()
![]()
![]()
![]()
右クリックして「画像を保存」で使って下さい。
それではソースを
; 企画第一弾 : 1画面防衛シューティングを作ろう
; var.0.006
#include "hsp3dish.as" ; HSPdishの宣言
randomize ; 乱数の初期化
pai=3.141593
pai2=pai*2
rad45=0.7853982 ; ラジアンで45度
rad90=1.5707965 ; ラジアンで90度
wx=640 ; 画面の大きさx
wy=360 ; 画面の大きさy
gmode 2
jw1=1 : jo=20 : jo2=jo/2 ; 自機、マニュアル
celload "jiki_3.png",jw1
celdiv jw1,jo,jo,jo2,jo2
jw2=2 ; 自機、オート
celload "jiki_2.png",jw2
celdiv jw2,jo,jo,jo2,jo2
sw=3 : so=6 : so2=so/2 ; ショット
celload "shot_1.png",sw
celdiv sw,so,so,so2,so2
ew=4 : eo=20 : eo2=eo/2 ; 敵
celload "ene_1.png",ew
celdiv ew,eo,eo,eo2,eo2
cw=5 : co=32 : co2=co/2 ; カーソル
celload "csr_2.png",cw
celdiv cw,co,co,co2,co2
hw=6 : ho=16 : ho2=ho/2 ; ヒット
celload "hit_4.png",hw
celdiv hw,ho,ho,ho2,ho2
vw=7 : vo=16 : vo2=vo/2 ; バルカン
celload "vulcan_1.png",vw
celdiv vw,vo,vo,vo2,vo2
; 自機 ----------
jx=wx/2 ; プレイヤーの座標x
jy=wy/2 ; プレイヤーの座標y
jx1=jx-jo2 ; 自機左座標
jx2=jx+jo2 ; 自機右座標
jy1=jy-jo2 ; 自機上座標
jy2=jy+jo2 ; 自機下座標
cx=double(jx) ; 照準座標x
cy=double(jy) ; 照準座標y
csp=15.0 ; 照準スピード
esn=0 ; 標的
autof=1 ; オートフラグ(0=マニュアル、1=オート)
; SHOT ----------
sb=20 ; ショット最大数
dim sf,sb ; ショット発射フラグ
ddim sx,sb ; ショット座標x
ddim sy,sb ; ショット座標y
ddim sdx,sb ; ショット移動量x
ddim sdy,sb ; ショット移動量y
ddim sr,sb ; 向き
dim sxb,sb ; 終点座標x
dim syb,sb ; 終点座標y
ddim skn,sb ; 現移動距離
ddim skb,sb ; 最終移動距離
ssp=20.0 ; スピード
skc=3 ; 1フレームで移動計算する回数
sc=0 ; 出現カウント
scb=0 ; 発射間隔(フレーム)
sp=6 ; パワー
su=32 ; 揺れ幅(集弾値)
; VULCAN ----------
vb=30 ; バルカン最大数
dim vf,vb ; バルカン発射フラグ
ddim vx,vb ; バルカン座標x
ddim vy,vb ; バルカン座標y
ddim vdx,vb ; バルカン移動量x
ddim vdy,vb ; バルカン移動量y
ddim vr,vb ; 向き
vkc=3 ; 1フレームに出現する数
vsp=20.0 ; スピード
vp=4 ; パワー
vu=30 ; 揺れ幅(集弾値)
vnb=140 ; 1クリックで発射される数
vc=0 ; 残弾数
vcb=20 ; 1つ敵を倒した時に増える数
; ENEMY ----------
eb=40 ; 敵最大数
dim ef,eb ; 出現フラグ
dim ehp,eb ; 耐久度
ddim ex,eb ; 敵座標x
ddim ey,eb ; 敵座標y
ddim edx,eb ; 敵移動量x
ddim edy,eb ; 敵移動量y
ddim er,eb ; 向き
ehpb=60 ; 耐久度
esp=0.8 ; 敵スピード
ec=0 ; 出現カウント
ecb=8 ; 出現間隔(フレーム)
scr=0 ; スコア
hiscr=0 ; ハイスコア
kf=0 ; キーを押してるかフラグ
kc=0 ; 何フレームキーを押してるか
gosub *main : end
#deffunc esn_set int esn_setf ; 標的設定
esn=0 ; 一番近い敵No.
nd1=1000.0 ; 一番近い距離
repeat eb
if ef(cnt)=0 : continue
if esn_setf=0 : x2=ex(cnt)-jx : y2=ey(cnt)-jy ; 自機に近い
if esn_setf=1 : x2=ex(cnt)-cx : y2=ey(cnt)-cy ; 照準に近い
nd2=sqrt(x2*x2+y2*y2); 距離
if nd2<nd1 : nd1=nd2 : esn=cnt
loop
return
*main
mainf=0 ; ゲームフラグ
repeat ; メインループ ----------------------------------
switch mainf
case 0 ; タイトル ----------
stick k : if k=0 : swbreak ; ボタンでゲーム再開
mainf=1
swbreak ; ----------
case 1 ; ゲーム ----------
; 入力 ----------
x=mousex : y=mousey ; マウス、スマホの座標
stick k,256 ; マウス、スマホクリック
repeat 1 ; バルカンの入力判定
if k=256 : kf=1 : kc+ : break ; 押している
if kf=0 : break ; 一度も押してない
kf=0 ; 放した、押しフラグリセット
if kc>2 : kc=0 : break ; 2フレーム以上押していた
kc=0
if jx1<x and x<jx2 and jy1<y and y<jy2 : autof=(autof+1)\2 : esn_set 0 : break ; 自機の位置の場合、オート切り替え
if vc=0 or vn>0 : break ; 弾切れ or バルカン発射中
vn+=vnb ; バルカン発射要求数
cx=double(x) : cy=double(y) ; 照準位置をカーソル位置にセット
esn_set 1 ; 次の標的を探す
loop
; 照準 ----------
x2=ex(esn)-cx : y2=ey(esn)-cy
nd=sqrt(x2*x2+y2*y2); 標的と照準の距離
repeat 1 ; 照準の移動
if autof=0 : cx=double(x) : cy=double(y) : break ; マニュアルの場合
if nd<csp : cx=ex(esn) : cy=ey(esn) : break
; ↑ 標的が照準スピード以下の距離なら、照準座標を標的の座標にする
; ↓ そうでないなら照準スピード分動かす
xn=double(ex(esn)-cx) ; 一番近い敵と照準の距離x
yn=double(ey(esn)-cy) ; 一番近い敵と照準の距離y
rad=atan(xn,yn) ; 距離xyから角度を求める
cx+=sin(rad)*csp ; 照準移動x
cy+=cos(rad)*csp ; 照準移動y
loop
; SHOT ----------
repeat 1 ; 発射
if sc>0 : sc- : break ; ウエイト中
repeat sb
if sf(cnt)!0 : continue ; 使用中
sf(cnt)=1 ; 使用フラグon
sx(cnt)=double(jx) : sy(cnt)=double(jy) ; 初期座標
xx=int(cx)-su/2+rnd(su) ; ショットの着弾点x
yy=int(cy)-su/2+rnd(su) ; ショットの着弾点y
xn=double(xx-jx) ; 自機とマウスの距離x
yn=double(yy-jy) ; 自機とマウスの距離y
rad=atan(xn,yn) ; 距離xyから角度を求める
sr(cnt)=-rad+pai ; 向き
sdx(cnt)=sin(rad)*ssp ; 角度とスピードから移動量xを求める
sdy(cnt)=cos(rad)*ssp ; 角度とスピードから移動量yを求める
sxb(cnt)=xx : syb(cnt)=yy ; 終点座標
skn(cnt)=0.0 ; 現移動距離をリセット
x2=xx-jx : y2=yy-jy
skb(cnt)=sqrt(x2*x2+y2*y2); 最終移動距離
break
loop
sc=scb ; 発射ウエイト
loop
repeat sb : scn=cnt ; ショット移動処理
if sf(scn)=0 : continue ; 無し
repeat skc : cnt2=cnt ; 1ショットの移動繰り返し ====================
sx(scn)+=sdx(scn) ; xの移動
sy(scn)+=sdy(scn) ; yの移動
skn(scn)+=ssp ; どれだけ距離を進んだか
if skn(scn)>=skb(scn) { ; 終点判定
sx(scn)=double(sxb(scn))
sy(scn)=double(syb(scn))
sf(scn)=-1 ; 着弾フラグ
}
sx1=int(sx(scn))-so2 ; ショット左座標
sx2=int(sx(scn))+so2 ; ショット右座標
sy1=int(sy(scn))-so2 ; ショット上座標
sy2=int(sy(scn))+so2 ; ショット下座標
repeat eb ; 敵との当たり判定
if ef(cnt)=0 : continue
ex1=int(ex(cnt))-eo2 ; 敵左座標
ex2=int(ex(cnt))+eo2 ; 敵右座標
ey1=int(ey(cnt))-eo2 ; 敵上座標
ey2=int(ey(cnt))+eo2 ; 敵下座標
if sx1<ex2 and ex1<sx2 and sy1<ey2 and ey1<sy2 {
sf(scn)=-1
ehp(cnt)-=sp ; 耐久度-
if ehp(cnt)<=0 { ; 倒したか
ef(cnt)=0 ; 当たった敵とショットを消す
esn_set 0 ; 次の標的を探す
vc+=vcb ; バルカンの残弾数を増やす
scr+ ; スコア
if hiscr<scr : hiscr=scr ; ハイスコアの更新
break
}
}
loop
if sf(scn)=1 : if sx2<0 or wx<sx1 or sy2<0 or wy<sy1 : sf(scn)=0 : break ; 場外でフラグ off
if sf(scn)=-1 : break ; 着弾してる
loop ; ====================
loop
; VULCAN ----------
repeat vkc : cnt2=cnt ; 1フレームに発射される数
if vn=0 or vc=0 : break ; 発射要求数、または残弾が0
repeat vb : vcn=cnt
if vf(vcn)!0 : continue ; 使用中
vn- ; 発射要求数-
vc- : if vc=0 : vn=0 ; 残弾-
vf(vcn)=1 ; 使用フラグon
vx(vcn)=double(jx) : vy(vcn)=double(jy) ; 初期座標
xn=cx-double(jx) ; 自機とマウスの距離x
yn=cy-double(jy) ; 自機とマウスの距離y
rd=double(-(vu/2)+rnd(vu+1))/100.0 ; 揺れ幅(ラジアン)
rad=atan(xn,yn)+rd ; 距離xyから角度を求める
vr(vcn)=-rad+pai ; 向き
vdx(vcn)=sin(rad)*vsp ; 角度とスピードから移動量xを求める
vdy(vcn)=cos(rad)*vsp ; 角度とスピードから移動量yを求める
repeat cnt2
vx(vcn)-=vdx(vcn) ; 始点座標をバックさせておくx
vy(vcn)-=vdy(vcn) ; 始点座標をバックさせておくy
loop
break
loop
loop
repeat vkc ; vkc回移動
repeat vb : vcn=cnt ; バルカン移動処理
if vf(vcn)=0 : continue ; 無し
vx(vcn)+=vdx(vcn) ; xの移動
vy(vcn)+=vdy(vcn) ; yの移動
vx1=int(vx(vcn))-vo2 ; バルカン左座標
vx2=int(vx(vcn))+vo2 ; バルカン右座標
vy1=int(vy(vcn))-vo2 ; バルカン上座標
vy2=int(vy(vcn))+vo2 ; バルカン下座標
repeat eb ; 敵との当たり判定
if ef(cnt)=0 : continue
ex1=int(ex(cnt))-eo2 ; 敵左座標
ex2=int(ex(cnt))+eo2 ; 敵右座標
ey1=int(ey(cnt))-eo2 ; 敵上座標
ey2=int(ey(cnt))+eo2 ; 敵下座標
if vx1<ex2 and ex1<vx2 and vy1<ey2 and ey1<vy2 {
vf(vcn)=-1
ehp(cnt)-=vp ; 耐久度-
if ehp(cnt)<=0 { ; 倒したか
ef(cnt)=0 ; 敵を消す
esn_set 1 ; 次の標的を探す
vcr+ ; スコア
if hiscr<scr : hiscr=scr ; ハイスコアの更新
break
}
}
loop
if vf(vcn)=1 : if vx2<0 or wx<vx1 or vy2<0 or wy<vy1 : vf(vcn)=0 : continue ; 場外でフラグ off
if vf(vcn)=-1 : continue ; 着弾してる
loop
loop
; ENEMY ----------
repeat 1 ; 敵出現
if ec>0 : ec- : break ; ウエイト中
repeat eb ; フラグ確認
if ef(cnt)>0 : continue ; 使用中
ef(cnt)=1 ; 使用フラグon
ehp(cnt)=ehpb
r=rnd(4) ; 出現方向
if r=0 : exs=rnd(wx)-eo2 : eys=0-eo2 ; 上
if r=1 : exs=rnd(wx)-eo2 : eys=wy+eo2 ; 下
if r=2 : exs=-eo2 : eys=rnd(wy)-eo2 ; 左
if r=3 : exs=wx+eo2 : eys=rnd(wy)-eo2 ; 右
ex(cnt)=double(exs) ; 初期位置x
ey(cnt)=double(eys) ; 初期位置y
xn=double(jx-exs) ; 敵と自機の距離x
yn=double(jy-eys) ; 敵と自機の距離y
rad=atan(xn,yn) ; 距離xyから角度を求める
edx(cnt)=sin(rad)*esp ; 角度とスピードから移動量xを求める
edy(cnt)=cos(rad)*esp ; 角度とスピードから移動量yを求める
er(cnt)=-rad+pai ; 向き
ec=ecb ; 出現ウエイト
break ; 登録終わり
loop
loop
repeat eb ; 敵移動処理
if ef(cnt)=0 : continue ; 無し
ex(cnt)+=edx(cnt) : ey(cnt)+=edy(cnt) ; 移動
ex1=int(ex(cnt))-eo2 ; 敵左座標
ex2=int(ex(cnt))+eo2 ; 敵右座標
ey1=int(ey(cnt))-eo2 ; 敵上座標
ey2=int(ey(cnt))+eo2 ; 敵下座標
if jx1<ex2 and ex1<jx2 and jy1<ey2 and ey1<jy2 {
mainf=2 : break ; 自機と当たりで GAME OVER
}
if ex2<0 or wx<ex1 or ey2<0 or wy<ey1 : ef(cnt)=0 ; 枠外でフラグ off
loop
swbreak ; ----------
case 2 ; gameover ----------
stick k : if k=0 : swbreak ; ボタンでゲーム再開
cx=double(jx) ; 照準座標xリセット
cy=double(jy) ; 照準座標yリセット
repeat sb
sf(cnt)=0 ; ショットリセット
loop
repeat vb
vf(cnt)=0 ; バルカンリセット
loop
vn=0 : vc=0 ; 残弾リセット
repeat eb
ef(cnt)=0 ; 敵リセット
loop
scr=0 ; スコアリセット
mainf=0 ; ゲーム再開
swbreak ; ----------
swend
redraw 0 ; 表示 ====================
color 1,1,1 : boxf : color 255,255,255 ; 画面クリア
pos jx,jy
if autof=0 : celput jw1 ; 自機、マニュアル
if autof=1 : celput jw2 ; 自機、オート
repeat eb ; 敵
if ef(cnt)=0 : continue ; 無い
pos ex(cnt),ey(cnt) : celput ew,,,,er(cnt) ; 敵
loop
repeat sb ; ショット
if sf(cnt)=0 : continue ; 無い
pos sx(cnt),sy(cnt)
if sf(cnt)=-1 : celput hw,,,,sr(cnt) : sf(cnt)=0 : continue ; フラグリセット
celput sw ; ショット
loop
repeat vb ; バルカン
if vf(cnt)=0 : continue ; 無い
pos vx(cnt),vy(cnt)
if vf(cnt)=-1 : celput hw,,,,vr(cnt) : vf(cnt)=0 : continue ; フラグリセット
celput vw,,,,vr(cnt) ; バルカン
loop
pos 10,wy-20 : mes vc ; バルカンの残弾表示
repeat vc/10
pos 50+cnt*6,wy-12 : celput vw
if cnt=100 : break ; 最大
loop
pos cx,cy : celput cw ; 照準
switch mainf
case 0 ; タイトル
pos wx/2-120,wy/2-50 : mes "1画面防衛シューティング"
pos wx/2-43,wy/2+30 : mes "var.0.006"
swbreak
case 1 ; ゲーム
swbreak
case 2 ; gameover
pos wx/2-43,wy/2+50 : mes "GAME OVER"
swbreak
swend
pos wx-300,5 : mes "SCORE "+scr
pos wx-150,5 : mes "HI SCORE "+hiscr
redraw 1 ; ====================
await 1000/15 ; ウエイト、FPS 15
loop ; --------------------------------------------------
return ; 終わり
プログラム解説
標的の選定
|
#deffunc esn_set int esn_setf ; 標的設定 esn=0 ; 一番近い敵No. nd1=1000.0 ; 一番近い距離 repeat eb if ef(cnt)=0 : continue if esn_setf=0 : x2=ex(cnt)-jx : y2=ey(cnt)-jy ; 自機に近い if esn_setf=1 : x2=ex(cnt)-cx : y2=ey(cnt)-cy ; 照準に近い nd2=sqrt(x2*x2+y2*y2); 距離 if nd2<nd1 : nd1=nd2 : esn=cnt loop return #deffunc esn_set int esn_setf 呼び出しが複数個所で行われていて、ショットで敵を倒した場合は 0、つまり自機に近い敵、バルカンで倒した場合は 1、つまり照準のその場所から近い敵となってる。 return |
オートモード
|
if jx1<x and x<jx2 and jy1<y and y<jy2 : autof=(autof+1)\2 : break クリック発動の時に、座標が自機の位置ならオートモードの on off切り替え。 autof=(autof+1)\2の\(HSPエディタのソース表示では ¥ の表示)は、割った数の余りを求める記号で、この場合実効事に autofの値は 0 1 0 1 0 ・・・となる。 if autof=0 : cx=double(x) : cy=double(y) : break ; マニュアルの場合 if nd<csp : cx=ex(esn) : cy=ey(esn) : break |
