カテゴリー「3.改造記」の13件の記事

[改造記]インデックス

01:はじめに
02:まずは初期設定
03:画像の準備
04:使えなくなる命令
05:メインルーチン
06:画面描写(es_copy)
07:解剖記のスクリプトを改造しよう
08:スプライトを使う準備
09:スプライトの登録
10:真っ直ぐ進むスプライト
11:スプライトの任意移動制御
12:スプライトの当たり判定

これらの元記事は、ファイルの更新日時を確認する限り、恐らく2000年11月12日頃書いたものです。現在の状況(既にHSP3.0がリリースされてい る)とは整合性が取れていない内容もあると思いますが、当時の記事のまま転載しております。間違っている記述があるかもしれませんが、あらかじめご了承下 さい。

改造記の記述はHSP_version2.61を前提としております。現在でも以下のリンク先から入手可能です。
Get!HSP2.61

[改造記]スプライトの当たり判定

いよいよ問題の当たり判定処理です。今までとはちょっと違ったやり方になります。「キャラクタ一つ一つに対し、一つ一つ当たり判定をする」という基本は変わりないのですが、その「当たり判定」のやり方が根本的に違います。es_typeで設定したスプライトの種類を使い、es_findで検索、es_checkで判定する、という流れになります。

;■ 敵移動と当たり判定
*enemymove

enemy=0
repeat
es_find enemy,8,enemy ; 敵(8)スプライト検索
if enemy=-1 : break
es_get ex,enemy,3 ; x座標取得
es_get ey,enemy,5 ; y座標取得
es_get en,enemy,12 ; キャラクタ番号取得
if en=31 : gosub *enemy1
if en=32 : gosub *enemy2
if en=33 : gosub *enemy3
es_apos enemy,exv.enemy,eyv.enemy,100+level
et.enemy++ ; タイマー


es_check temp,enemy,4
; ↑自機弾(4)との当たり判定
if temp>0 {
ef.enemy--
es_get wx,temp,3
es_get wy,temp,5
es_kill temp ; 自機弾削除
if ef.enemy>0 {
es_new temp,505
es_set temp,wx,wy+5,7
; 自機弾爆風設置
es_type temp,16
; 効果type値(16)
es_adir temp,32,200
; 自機弾爆風移動指示
es_flag temp,$8306
; 消失タイマー
snd 3
score++
}
else {
es_kill enemy ; 敵削除
repeat 8
es_new temp,600
es_set temp,ex+5,ey+5,21
; 爆風設置
es_type temp,16
; 効果type値(16)
es_adir temp,cnt*8,400
; 爆風移動指示
es_flag temp,$8314
; 消失タイマー
loop
snd 2
gosub *messageborn
}
}

enemy++
loop
return

repeat~loopの回数指定なし無限ループを使うので、非常に注意が必要です。対象スプライトが見つからなかったらループから抜ける処理は必須です。もしこれを忘れてスクリプトの実行をしてしまうと、CTRL+ALT+DELも効かない、ALT+F4も効かない、電源コンセントを引っこ抜くしかないという悲惨な状況になります。もしテストするのが怖かったら「await 0」をrepeat~loopのどこかに入れましょう。これさえやっておけば、ALT+F4での強制終了だけは可能になります^^;

何故こんなことをしなければならないかというと、es_findはスプライト番号の若い方から順番に検索し、該当スプライトが見つかるとそこで処理が終わってしまう為です。そこで、処理の終わったスプライトの次の番号から再度検索をする必要があるのです。この一連の処理を行うには、repeat~loopの無限ループを使うのが一番効率がいいものと思われます。もっといい方法がある人は教えてください^^;

スクリプトを順番に見ていきましょう。まずes_findで存在する敵スプライトの検索をします。ここで活躍するのがes_typeで設定したtype値です。これは、当たり判定処理では必ず使うのにhtmlの命令別マニュアルには記載されていない(リファレンスを読みなさい、と書いてあります^^;)ちょっと都合の悪い奴です。ではまずリファレンスの説明から抜粋します。この説明文は非常にわかりやすいです。

・ スプライトtype値の設定について

スプライトのtype値は、ゲームなどで物体の識別をする時に有効に使うことができます。type値は、es_type命令で設定することができます。この値は、ユーザーの好きに設定することができる識別用の値となります。
設定できる値は、

1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768

の計16種類です。

es_set命令でスプライトを設定した直後は、type値は1になっています。この値は、衝突判定やスプライトの検索対象を指定するのに使用されます。スプライトが示す物体の種別を登録しておくのが主な利用法です。

たとえば、シューティングゲームで自機はtype1、敵はtype2、敵のミサイル
はtype4、自分のミサイルはtype8、といった感じでtype値をスプライトを出す時に設定しておきます。こうしておけば、自機が敵か、または敵ミサイルに衝突しているかを判定する場合にチェックがしやすくなります。

es_check命令では、複数のtype値を同時に判定できるので、この例で言うと自機のスプライトに対して、敵(type2)と敵ミサイル(type4)が衝突しているかを調べて自分の生死を決めることができます。

また、自分のミサイルが敵に衝突しているかを判定する場合には、まずes_find命令でtype8のスプライトだけを検索して、その1つ1つが、敵(type2)と衝突しているかを判定すればいいわけです。

このようにtype値をあらかじめ割り振っておくことで、スプライトの管理をスマートに行なうことが可能です。

いかがでしょうか?まさにtype値さまさまといった感じです(笑)。ところで、な~んでtype値ってこんな飛び飛びの数字なんだろうな~と思った人もいるんじゃないでしょうか?思わなかった人は読み飛ばしてください。これは2進法に直すとよくわかります。要するにコンピュータ的な処理で、なるべく容量を少なく識別をさせるには2進法が一番だからです。

                                                                                                                                     
10進法 2進法 16進法
1 %0000000000000001 $0001
2 %0000000000000010 $0002
4 %0000000000000100 $0004
8 %0000000000001000 $0008
16 %0000000000010000 $0010
32 %0000000000100000 $0020
64 %0000000001000000 $0040
128 %0000000010000000 $0080
256 %0000000100000000 $0100
512 %0000001000000000 $0200
1024 %0000010000000000 $0400
2048 %0000100000000000 $0800
4096 %0001000000000000 $1000
8192 %0010000000000000 $2000
16384 %0100000000000000 $4000
32768 %1000000000000000 $8000

type値がこのように割り振られているので、複数のtype値の判定や検索も可能なのです。例えばtype値8のスプライトとtype値8192のスプライトを検索する場合、こういった形になります。

                               
10進法 2進法 16進法
8 %0000000000001000 $0008
8192 %0010000000000000 $2000
8200 %0010000000001000 $2008

2進法の部分を見ると一番良くわかりますが、別々の位置がスイッチになってるのがよくわかると思います。要するにこうやってコンピュータは識別をしてるのです。

余談はこのくらいにして。

要するに、例えば自機のtype値を(1)、自機弾を(2)、敵機を(4)、敵弾を(8)といった具合に設定しておけば、es_findで検索 →es_checkで判定という処理ができる、ということです。原理はややこしいですが、処理は格段に楽になりますし、負荷も少なくできます。標準命令だ けでの当たり判定ルーチンを列記してみましょう。

;■ 敵移動
*enemymove
repeat emax
et.cnt++
if ef.cnt<1 : continue
if en.cnt=1 : gosub *enemy1
if en.cnt=2 : gosub *enemy2
if en.cnt=3 : gosub *enemy3
if ef.cnt<1 : continue
ent=cnt
repeat wmax
if wf.cnt<1 : continue
ddx=(ex.ent+25)-(wx.cnt+12) : if ddx<0 : ddx=-ddx
ddy=(ey.ent+25)-(wy.cnt+25) : if ddy<0 : ddy=-ddy
if (ddx<20)&(ddy<40) {
ef.ent-- : wf.cnt=0
if ef.ent>0 {
pos wx.cnt,wy.cnt
gcopy 2,175,25,25,25
snd 4
score++
}
else {
pos ex.ent,ey.ent
gcopy 2,200,0,50,50
snd 2
gosub *messageborn
}
}
loop
loop
return

このように二重ループを使って処理してましたね。仮に「emax=200」で「wmax=10」だとすると、ここだけで2000回のループを行うことにな ります。実際にはcontinueを使って処理飛ばしをしているので、全ての処理を2000回行っているわけではないのですが、やはり負荷は大きいです。 処理速度の減退の大きな理由になります。ところがスプライトのes_checkを使った処理の場合、確実に画面上に表示されてる対象スプライト数だけしか 処理を行いません。それどころか対象スプライトが無い場合は全く処理を行わないですむのです。せっかくDirectXを使って画面描写が速くなっても、そ れ以外の部分で処理が遅くなってては意味がありません。だからこのes_checkは非常に大きな意味があるし、使い応えもあるのです。

[改造記]スプライトの任意移動制御

さて、問題になってくるのがアルゴリズムを持って動くスプライトの制御です。まぁ問題、と言ってもそんなに大したことはありません。解剖記の敵の動かし方と基本は同じです。

	es_ini 1000, : es_screen 640,480,8,0

;~中略~

emax=1000 ;スプライトの最大数

;~中略~
;■ 配列定義
dim ef,emax ;敵固さ・生死
dim exv,emax ;敵x方向運動量
dim eyv,emax ;敵y方向運動量
dim et,emax ;敵出現からの時間

;~中略~

es_get ex,enemy,3 ; x座標取得
es_get ey,enemy,5 ; y座標取得
es_get en,enemy,12 ; キャラクタ番号取得
if en=31 : gosub *enemy1

;~中略~
;■ 敵1
*enemy1
eyv.enemy=4
dx=mx-ex : dy=my-ey
if dy<350 {
if dx<0 {
exv.enemy--
if exv.enemy<-25 : exv.enemy=-25
}
else {
exv.enemy++
if exv.enemy>25 : exv.enemy=25
}
}
if dx<0 : dx=-dx
if dx<50 : gosub *tamaborn

es_apos enemy,exv.enemy,eyv.enemy,100
return

まぁ要するに、配列変数を使って移動速度や配置からの時間なんかを一機一機に対して保持しておく、という形は変わらないです。ただ、es_getでスプライトの様々な情報が取得できるので、用意する配列変数は少なくて済みます。座標なんかは上記スクリプトのように簡単に取得できますしね。

後はes_aposを使ってx方向y方向の移動速度を変化させるだけです。基本的な部分は解剖記の敵の生成と移動アルゴリズム1となんら変わりありません。

ただし、注意しなければならないことがあります。それはスプライト数と用意する配列の数です。ちょっと工夫をしないと「配列の要素が大きすぎます」というエラーに悩ませられることになります(俺だけか?)。私なりのやり方なのですが、es_iniで宣言するスプライトの数だけ配列変数を用意しちゃいます。

;■ 敵生成
*enemyborn
rnd temp,7
if temp=0 : return
es_new enemy,600 ; 空きスプライト番号取得
if temp=1 : ef.enemy=1 : en=31
if temp=2 : ef.enemy=1 : en=31
if temp=3 : ef.enemy=1 : en=31
if temp=4 : ef.enemy=2 : en=32
if temp=5 : ef.enemy=2 : en=32
if temp=6 : ef.enemy=3 : en=33
; ef:硬さ en:キャラクタ番号
rnd ex,640 ; 初期出現位置x軸
et.enemy=0 ; タイマー初期化
es_set enemy,ex,-50,en ; スプライト登録
es_type enemy,8 ; 敵type値(8)
es_adir enemy,0,100 ; 初期移動処理
exv.enemy=0 : eyv.enemy=10
return

すると、上記スクリプトのように、スプライト登録をするときに何も考えずにそのまま空きスプライト番号を配列の要素として使えます。なので、これは敵に限らず全てのキャラで使えるように、汎用の配列として用意しておくのがいいのかもしれません。

[改造記]真っ直ぐ進むスプライト

敵や弾のように、随時登録するスプライトの場合、条件分岐でサブルーチンへ飛ぶ形で処理します。この前の項でも説明しましたが、es_setによる登録は一度行えば消える(消す)まで有効です。

;■ 自機弾発射
*weapon

es_new temp,501
if temp<504 {
es_set temp,mx+12,my,3
;↑自機弾スプライト設置(キャラクタ番号3)
es_type temp,4 ;自機弾type値(4)

es_adir temp,32,4000 ;移動方向指示
shottime=1 ;連射制御
}
return

まずes_newで未使用のスプライト番号を検索します。ここで指定する番号は前項で決めた表示の優先順位に従ってください。別に重ね合わせの順番なんか気にしないや~いという方は何も考えずに0でいいでしょう^^;

ただその場合「特定のキャラは画面上の表示数を制限する」といった作業が面倒になります。自機の弾なんかは画面上の表示数を決めておかないとゲーム性が崩 れます。上記スクリプトの場合、スプライト番号501~503は完全に自機弾専用になってます。こうすることで画面上に自機弾は3つ以上表示されないよう になってます。

設置してから後は移動を制御しない(ずっと直進する)キャラの場合、最初に一回だけes_adires_aimes_aposを行えば、あとはほったらかしでいいです。上記スクリプトの場合es_adirを使って画面上方向に直進するよう指示をしています。es_adirは下図のように下方向が0で、半時計回りに64段階の方向指定ができます。

es_adirの方向

つまりこの自機弾の場合32なので画面上方向に真っ直ぐ飛んでいくというわけです。これが例えば自機を狙う敵弾などの場合はこんな感じになります。

;■ 弾生成1
*tamaborn
es_new tama,0
if tama<500 {
es_set tama,ex+12,ey+12,4
;↑敵弾スプライト設置(キャラクタ番号4)
es_type tama,32 ;敵弾type値(32)
rnd dv,8 : dv+=level/2+2 ;弾速

es_aim tama,mmx,mmy,dv*100 ;移動方向
}
return

(mmx,mmy)は自機の座標です。たったこれだけの命令で狙い弾を撃たせることができます。めちゃめちゃ簡単でしょ?解剖記の敵弾の生成と移動のスクリプトと比較してみてください。はっきり言ってあの苦労はなんだったんだ?っていう感じです^^;

[改造記]スプライトの登録

es_sizeとes_patで登録したキャラクターは、es_setで 登録することで初めてスプライトとしての機能を果たします。一旦es_setで登録したスプライトは、自分で削除(es_killまたは es_clear)するか、移動してスプライト有効範囲(es_areaで設定)をはみ出すまで有効となります。それゆえes_setはキャラの生成時の みに使用し、後は他の命令で制御する形になります。毎フレームes_setで登録しなおす必要は全く無いので注意しましょう。

;■ スタート前準備
;~中略~

es_window 0,0,640,480 ;スプライト描写範囲設定
es_area -50,-50,640,480 ;スプライト有効範囲設定
es_cls ;一旦画面消去
es_sync ;消去した画面の描写
wait 100 ;最初だけ若干ウェイトをかまします

es_set 300,295,300,5 ;自機スプライト登録
es_type 300,1 ;自機Type値設定(1)

;■ メインルーチン
*main
es_cls 0,0,0 ;画面クリア
;~中略~・・・もろもろの処理

es_draw ;スプライト描写
;~中略~・・・スプライトより手前に描写したいものがある場合はここで処理
await 0
es_sync 32 ;画面描写とウェイト
goto *main

メインルーチンに入る前に、スプライトを描写する範囲と有効範囲を設定しておきます。これらは一度設定すれば以後はずっと有効になるため、前準備(メイン ルーチンなど永久ループに入る前)の段階で一度やれば二度とやる必要がありません。もちろんゲーム中に変更する必要がある場合は別ですが^^;

まずes_windowで スプライトを描写する範囲を決めます。例えば縦スクロールシューティングなどで、画面の縦横比を変えたい場合はここで設定します。あくまでこれはスプライ トの描写範囲の設定なので、es_copyなどを使えばこの範囲の外側にも描写はできます。例えばステータスウィンドウ(得点とかシールド残量などを表 示)のように、そこにスプライトが描写されると困るような範囲がある場合は使うといいでしょう。画面いっぱいに描写してもかまわないのであれば、設定する 必要はありません。

次にes_areaでスプライトの有 効範囲を決めます。これも前準備の段階で一度行えばオッケーです。es_windowを行うと、その範囲+上下左右128ピクセルが自動的にスプライトの 有効範囲に設定されます。なので、スプライト有効範囲を変更したい場合は必ずes_windowの後に行いましょう。このes_areaで指定された範囲 から外にスプライトがはみ出すと、そのスプライトは自動的に削除されます。スプライトの座標はその画像の左上になるので、見かけ上不合理が生じないように するには上記スクリプトのように、スプライト有効範囲を画面上側と左側に若干大きめにとる必要があります。下図を参考にしてみて下さい。

es_windowとes_areaの関係

シューティングゲームで言う自機のように最初から画面に現れてるキャラクターをスプライト登録する場合は、上記のスクリプトのようにメインルーチンに入る前に設置を行います。es_typeに関しては、当たり判定の部分で説明するのでここでは割愛させていただきます。

スプライトの重ね合わせはスプライト番号によって決まります。番号が若い方が手前に表示さ れるのです。例えばシューティングゲームで言うと、敵の弾が爆風などの後ろで描写されてると隠れて見えず、非常にプレイヤーにストレスがかかります(そう いうゲーム性だったらいいでしょうけど^^;)。なので、敵の弾は必ず自機のストライプ番号より少ない番号を選ぶようにしなければなりません。重ね合わせ の優先順位があるのならば、使用するスプライト番号の範囲を自分で決めておきましょう。例えばこんな感じです。

  1. 敵弾・・・0-500の範囲
  2. 自機・・・501
  3. 自機弾・・・502-510
  4. 爆風・・・511-550
  5. 敵機・・・551-650

スプライトの登録の際はこの優先順位がポイントとなります。自分の作るゲーム性に合わせていろいろ考えてみましょう。es_iniで初期化を行う際に、か なり多めにスプライト数を設定しておいて、使う範囲をわかりやすい番号で区切っておくというのも一つの手です。そうすれば、作ってる最中で最大数などを変 更したい場合にラクです。

そして、メインルーチン(永久ループになる部分)では、es_syncより前にes_drawを 行い、スプライトの描写をしましょう。これを忘れてるとスプライトだけが表示されないので「?」ってなことになります(私はサンプルv3.0を作ってる時 にやらかしました^^;)。スプライトより手前に描写したいものがある場合は、es_drawとes_syncの間で処理しましょう。例えばメッセージと か数字とか。そういった、スプライトに隠れては困るものはes_syncの直前に描写処理を行えばいいのです。

[改造記]スプライトを使う準備

それではいよいよ後半部分の解説に入ります。hspdx.dllのスプライト機能です。

まず最初に、スプライトの機能を使うと何ができるのか?という事を整理しておきましょう。単純に描写速度を速くする目的のみでDirectXを用いるのであれば、ここまでの講座内容だけで充分です。新出の命令の半分も使ってないですしね。覚えることも少なくて済みます。

しかし、スプライトの機能を使うことで、次のようなことが容易になります。

  • 当たり判定をDirectXの機能を使ってできる・・・アルゴリズムが簡単になる
  • キャラクタの管理ができる・・・新規のキャラクタ登録や削除が容易になる
  • 移動の制御ができる・・・例えば「弾を発射する」といったアルゴリズムが簡単になる
  • アニメーションができる・・・視覚効果を派手にするのが容易になる
  • 方角が調べられる・・・(※hspdxfix.dllのみ・・・スプライトじゃなくてもできます)

もうシューティングゲームを作ってください!と言っ てるのも同然の機能ですね。標準命令では苦労してた事が、スプライトを使うことによって非常にラクになります。「プログラミングの醍醐味はアルゴリズム だ!」という声も聞こえてきそうですが、私はできることならラクしたいです。ただし、厳密なこと(ex.正確な角度を求める)をやろうとするとどうしても 無理はでてくるので、そういう場合はdllを複数使うなりして対処しましょう。

さて、スプライトを使う場合最初にやらなければいけないのはキャラクタの登録です。使用する画像の大きさ当たり判定の範囲アニメーションのパターンなどは最初に設定しておきます。

;■ バッファへ画像の読みこみ
buffer 2,640,480,1
pos 0,0
picload "sampledx.bmp",1
es_buffer 0,0
if stat=1 : goto *dxerr3
;↑オフスクリーンバッファへの転送と成否判定


es_size 50,50,80,0
repeat 11 : es_pat cnt,50*cnt,0 : loop
;キャラクタ1・・・0-10

es_size 40,40,60,0
repeat 8 : es_pat 11+cnt,40*cnt,50,1 : loop
es_link 18,11
;キャラクタ2・・・11-18

まずes_sizeで切り抜いてくる画像の大きさと、当たり判定の大きさを決めます。例えば50x50の大きさのキャラクタで、当たり判定の範囲は40x40にしたいのなら、第3パラメータに80(%)を指定します。当たり判定の範囲は下図のようになります。

キャラクタ1の場合この図のように、中心から80%の範囲が当たり判定の範囲になります

スプライトを使う際に透過しない設定にすることはまずありえないので、第4パラメータには0を入れることになります。

そして次に、切り抜いてくる場所をes_patで 指定します。第1パラメータがキャラクタ番号になります。es_sizeが同じ設定の画像を連続で切り抜いてくるのなら、上記のスクリプトのように repeat~loopで一気に指定するとラクです。そうする為にも、最初にbufferへ読み込む画像は下図のようにつなげておくと便利です。

自機のロール画像です。このように使う順番で保存しておくと後がラクです

次に、自動でアニメーションさせる画像の場合の設定です。上記のスクリプトの場合、キャラクタ2は自動的にアニメーションさせるキャラクタです。このようにes_patの第4パラメータに アニメーションする早さを指定しておきます。これはes_syncで何回画面描写したら次のコマへ移るか?という指定になりますので、数を多くするとコマ 送りのようにになってしまいます。30FPSくらいの描写速度だと、2フレームに1回くらいならあまり不自然にはなりません。

連続したアニメーションの場合、es_linkで キャラクタ番号の何番から何番をつなげるかを指定します。アニメーションを途中で止める場合(設置直後はアニメーションするが、最後のコマでアニメーショ ンしなくなる)は、最後のコマのes_patの第4パラメータに0を指定します。0指定がなかったりes_linkでつなげるのを忘れると笑える現象が起 こりますので注意しましょう。全然違うキャラでアニメしてくれます^^;

                               
毎回描写ウェイトかけ過ぎes_linkの番号間違い
ウェイト1だとこのくらいコマ送り状態番号を間違えた

キャラクタの登録で最大のポイントになるのは、数が多くなると何番がどのキャラかわからなくなるということです。笑い事のようですが、真実です。上記のスクリプトのように、必ずコメントに「このキャラは何番から何番のキャラクタ番号」というのを書くことをお勧めします。私はこれで泣きました。キャラが多すぎて書いてても泣きました(笑)。

[改造記]解剖記のスクリプトを改造しよう

実はここまでの所さえ理解できていれば、DirectX対応に改造することは可能です。ただし、もちろんスプライトの機能は使えません^^;

というわけで「解剖記」のサンプルスクリプトを改造しちゃいましょう。当然スプライトの機能は使いません。否、まだ使えないでしょって^^;

 

; シューティングゲームサンプルv2.1
; by たかのん
; HSP v2.5 で作成されています
; 敵と自機の距離によって画面に倍数が表示されます。
; この倍数は、距離が近いほど大きくなります。

; v1.1との違い
; 画面描写をDirectXに対応
; ただし、スプライトの機能は使ってません

; ジョイスティックに対応


;■ DirectX 初期化&チェック
#include "hspdxfix.as"
#include "hspjsis.as"

es_ini : es_screen 640,480,8,0
if stat=1 : goto *dxerr1
if stat=2 : goto *dxerr2
goto *gamesetting

*dxerr1
dialog "DirectXの初期化に失敗しました。",1    : end
*dxerr2
dialog "スクリーンの初期化に失敗しました。",1 : end
*dxerr3
es_bye : wait 100
dialog "VRAMの容量が不足しています。",1       : end


*gamesetting
cls 4
highscore=5000
randomize
move=5 ;自機の移動速度を指定
smax=50 ;星の最大数
emax=50 ;敵の最大数
tmax=200 ;敵弾最大数
wmax=3 ;自機弾最大数
imax=10 ;倍数表示最大数
vmax=10 ;情報表示最大数
bmax=10 ;爆風最大数
cmax=50 ;カスリ効果最大数
level=1 ;難易度


;■ バッファへ画像の読みこみ
buffer 2,640,480,1
pos 0,0
picload "sample2.bmp",1

es_buffer 0,0
if stat=1 : goto *dxerr3
;↑オフスクリーンバッファへの転送と成否判定


;■ 効果音とBGMの読みこみ
sndload "Int01.mid",0,1
sndload "dmg.wav",1,0
sndload "exp.wav",2,0
sndload "shot.wav",3,0
sndload "kin.wav",4,0
snd 0


;■ 配列定義
*gamestart
dim sx,smax ;星のx軸座標
dim sy,smax ;星のy軸座標
dim sc,smax ;星の色
dim ss,smax ;星の移動速度

dim ex,emax ;敵x軸座標
dim ey,emax ;敵y軸座標
dim ef,emax ;敵固さ・生死
dim en,emax ;敵種類
dim exv,emax ;敵x方向運動量
dim eyv,emax ;敵y方向運動量
dim et,emax ;敵出現からの時間

dim tx,tmax ;敵弾x軸座標
dim ty,tmax ;敵弾y軸座標
dim tf,tmax ;敵弾on/off
dim txv,tmax ;敵弾x方向運動量
dim tyv,tmax ;敵弾y方向運動量

dim wx,wmax ;自機弾のx軸座標
dim wy,wmax ;自機弾のy軸座標
dim wf,wmax ;自機弾のOn/Off
dim wyv,wmax ;自機弾のy軸移動距離

dim ix,imax ;倍数x軸座標
dim iy,imax ;倍数y軸座標
dim iff,imax ;倍数表示時間
dim iv,imax ;倍数フォントサイズ
dim in,imax ;元得点

dim bx,bmax ;爆風x軸座標
dim by,bmax ;爆風y軸座標
dim bf,bmax ;爆風表示時間


 dim cx,cmax ;カスリ効果x軸座標
dim cy,cmax ;カスリ効果y軸座標
dim cxv,cmax ;カスリ効果x方向移動距離
dim cyv,cmax ;カスリ効果y方向移動距離
dim cf,cmax ;カスリ効果表示時間


;■ 星の準備
repeat smax
rnd sx.cnt,640
rnd sy.cnt,480
rnd sc.cnt,10
ss.cnt=10-sc.cnt
sc.cnt=sc.cnt*5
loop

;■ 平方根の準備
dim calc,900
repeat 900
temp=cnt : calc.cnt=temp*temp
loop

;■ スタート前準備
mx=295 : my=400 ;自機の初期位置を指定
mf=8 ;自機の初期シールドを指定
gameovertime=0
score=0
frame=0
highdst=0
gsel 0

es_cls
es_sync
wait 100

;■ メインルーチン
*main

es_cls bgcolor,0,0 : bgcolor=0
gosub *message ;ゲーム情報
gosub *haikei ;星の描写
gosub *jikimove ;自機移動
gosub *weapon ;自機弾
if frame\(25-level)=0 : gosub *enemyborn ;敵生成
gosub *enemymove ;敵移動
gosub *tekitamamove ;敵弾移動
if pat&128>0 : gosub *pausegame ;ポーズ
getkey returntop,123 : if returntop=1 : end ;強制終了(F12)
if mf<1 : mxv=0 : myv=0 : gameovertime++ : gosub *bomb
if gameovertime>100 : goto *gameover

await 0
es_sync 30
frame++
level=frame/500+1
if level>20 : level=20
goto *main


;■ 自機移動
*jikimove
if mf>0 : stick pat,79 : jstick 79

if stat!0 : pat=stat
;↑ジョイスティック入力判定
shottime--
if mf>0 : if (pat&64>0)&(shottime<0) : shot=1
xv=(pat>>2&1)-(pat&1)
yv=(pat>>3&1)-(pat>>1&1)
if mf>0 : mx=xv*move+mx : my=yv*move+my
if mx<0 : mx=0
if mx>590 : mx=590
if my<0 : my=0
if my>430 : my=430
pos mx,my

gmode 1,50,50 : es_copy 0,xv*50+50,0
return


;■ 背景描写
*haikei
repeat smax
sy.cnt=sy.cnt+ss.cnt
if sy.cnt>480 : sy.cnt=0 : rnd sx.cnt,640

pos sx.cnt,sy.cnt
gmode 1,5,5 : es_copy 0,400,sc.cnt
loop
return


;■ 自機弾発射
*weapon
repeat wmax
if (shot=1)&(wf.cnt<1) {
wf.cnt=1
wx.cnt=mx+12
wy.cnt=my
shot=0
shottime=1
; snd 3
;↑効果音がうるさいので切ってあります^^;
}
if wf.cnt<1 : continue
wy.cnt=wy.cnt-40
if wy.cnt<-50 : wf.cnt=0
pos wx.cnt,wy.cnt

gmode 1,25,50
es_copy 0,150,0
loop
return


;■ 敵生成
*enemyborn
rnd temp,7
if temp=0 : return
repeat emax
if ef.cnt>0 : continue
if temp=1 : en.cnt=1
if temp=2 : en.cnt=1
if temp=3 : en.cnt=1
if temp=4 : en.cnt=2
if temp=5 : en.cnt=2
if temp=6 : en.cnt=3
ef.cnt=en.cnt ; 固さ
rnd ex.cnt,640 ; 初期出現位置x軸
ey.cnt=-50 ; 初期出現位置y軸
exv.cnt=0 ; 初期移動量x軸
eyv.cnt=10 ; 初期移動量y軸
et.cnt=0
break
loop
return


;■ 敵移動
*enemymove
repeat emax
et.cnt++
if ef.cnt<1 : continue
if en.cnt=1 : gosub *enemy1
if en.cnt=2 : gosub *enemy2
if en.cnt=3 : gosub *enemy3
if ef.cnt<1 : continue
ent=cnt
repeat wmax
if wf.cnt<1 : continue
ddx=(ex.ent+25)-(wx.cnt+12)
if ddx<0 : ddx=-ddx
ddy=(ey.ent+25)-(wy.cnt+25)
if ddy<0 : ddy=-ddy
if (ddx<20)&(ddy<40) {
ef.ent-- : wf.cnt=0
if ef.ent>0 {
pos wx.cnt,wy.cnt

gmode 1,25,25
es_copy 0,175,25
snd 4
score++
}
else {
pos ex.ent,ey.ent

gmode 1,50,50
es_copy 0,200,0
snd 2
gosub *messageborn
}
}
loop
loop
return

;■ 敵1
*enemy1
dx=mx-ex.cnt
dy=my-ey.cnt
if dy<350 {
if dx<0 {
exv.cnt=exv.cnt-1
if exv.cnt<-25 : exv.cnt=-25
}
else {
exv.cnt=exv.cnt+1
if exv.cnt>25 : exv.cnt=25
}
}
if dx<0 : dx=-dx
if dx<50 : gosub *tamaborn
eyv.cnt=4
ex.cnt=ex.cnt+exv.cnt
ey.cnt=ey.cnt+eyv.cnt
if ex.cnt<-50 : ef.cnt=0
if ex.cnt>640 : ef.cnt=0
if ey.cnt<-50 : ef.cnt=0
if ey.cnt>480 : ef.cnt=0
pos ex.cnt,ey.cnt

gmode 1,50,50 : es_copy 0,250,0
return


;■ 敵2
*enemy2
dx=mx-ex.cnt
if et.cnt<2 {
if dx<0 {
exv.cnt=-6
}
else {
exv.cnt=6
}
}
if et.cnt>25 : eyv.cnt-- : if eyv.cnt<-10 : eyv.cnt=-10
if ey.cnt>200 : gosub *tamaborn
ex.cnt=ex.cnt+exv.cnt
ey.cnt=ey.cnt+eyv.cnt
if ex.cnt<-50 : ef.cnt=0
if ex.cnt>640 : ef.cnt=0
if ey.cnt<-50 : ef.cnt=0
if ey.cnt>480 : ef.cnt=0
pos ex.cnt,ey.cnt

gmode 1,50,50 : es_copy 0,300,0
return


■ 敵3
*enemy3
dx=mx-ex.cnt
if et.cnt<2 {
if dx<0 {
exv.cnt=-3
}
else {
exv.cnt=3
}
}
if ey.cnt>200 : eyv.cnt--
if et.cnt>20 : if ey.cnt<50 : eyv.cnt++
if ey.cnt>200 : gosub *tamaborn
if et.cnt>20 : if ey.cnt<50 : gosub *tamaborn
ex.cnt=ex.cnt+exv.cnt
ey.cnt=ey.cnt+eyv.cnt
if ex.cnt<-50 : ef.cnt=0
if ex.cnt>640 : ef.cnt=0
if ey.cnt<-50 : ef.cnt=0
if ey.cnt>480 : ef.cnt=0
pos ex.cnt,ey.cnt

gmode 1,50,50 : es_copy 0,350,0
return


;■ 弾生成
*tamaborn
if frame\(8-(level/4))>0 : return
dx=mx-ex.cnt : px=dx : if dx<0 : dx=0-dx
dy=my-ey.cnt : py=dy : if dy<0 : dy=0-dy
dst=(dx*dx)+(dy*dy)
edx=ex.cnt+12 : edy=ey.cnt+12
repeat 900
if dst<calc.cnt : dst=cnt : break
loop
repeat tmax
if tf.cnt>0 : continue
tf.cnt=1
tx.cnt=edx : ty.cnt=edy
rnd ds,8 : ds=level/5+3+ds
txv.cnt=px*ds/dst
tyv.cnt=py*ds/dst
break
loop
return


;■ 敵弾移動 & 当り判定
*tekitamamove
repeat tmax
if tf.cnt<1 : continue
tx.cnt=tx.cnt+txv.cnt
ty.cnt=ty.cnt+tyv.cnt
if tx.cnt>640 : tf.cnt=0 : continue
if tx.cnt<-25 : tf.cnt=0 : continue
if ty.cnt>480 : tf.cnt=0 : continue
if ty.cnt<-25 : tf.cnt=0 : continue
dx=(mx+25)-(tx.cnt+12) : if dx<0 : dx=-dx
dy=(my+25)-(ty.cnt+12) : if dy<0 : dy=-dy
if (dx<5)&(dy<5) {
repeat tmax : tf.cnt-- : loop
pos tx.cnt,ty.cnt

gmode 1,25,25 : es_copy 0,175,25
bgcolor=128
snd 1
mf--
}
if (dx<30)&(dy<30) {
score++

ent=cnt
repeat cmax
if cf.cnt>0 : continue
cx.cnt=tx.ent+10
cy.cnt=ty.ent+10
rnd cxv.cnt,3 : cxv.cnt-1
rnd cyv.cnt,3 : cyv.cnt-1
cf.cnt=10
break
loop

}
pos tx.cnt,ty.cnt

gmode 1,25,25
es_copy 0,175,0
loop
return


;■ 倍率表示
*messageborn
dmy=my-ey.ent : if dmy<0 : dmy=-dmy
dstv=480-dmy/50
if highdst<dstv : highdst=dstv
dstlevel=level
dstcore=dstv*dstlevel
score=en.ent*100*dstcore+score
repeat imax
if iff.cnt>0 : continue
ix.cnt=ex.ent
iy.cnt=ey.ent
iv.cnt=dstv
iff.cnt=40
in.cnt=en.ent*100
il.cnt=dstlevel
break
loop
return

;■ ゲーム情報
*message

repeat cmax
if cf.cnt<1 : continue
cx.cnt=cx.cnt+cxv.cnt
cy.cnt=cy.cnt+cyv.cnt
gmode 1,5,5
cw=10-cf.cnt*5
pos cx.cnt,cy.cnt
es_copy 0,405,cw
cf.cnt--
loop

repeat imax
if iff.cnt<1 : continue
rnd icr,8 : icr=icr*32+31
rnd icg,8 : icg=icg*32+31
rnd icb,8 : icb=icb*32+31
color icr,icg,icb
font "Impact",iv.cnt*5+12
pos ix.cnt,iy.cnt

es_fmes ""+in.cnt+" x "+iv.cnt
iy.cnt++
iff.cnt--
loop

font "MSゴシック",12,3
if highscore<score : highscore=score
color 0,255,255
pos 10,10

es_fmes "HIGHSCORE "+highscore

color 255,255,255
pos 10,30

es_fmes "SCORE "+score

color 0,255,0
pos 10,50

es_fmes "HIGH DISTANCE "+highdst

color 255,255,0
pos 10,70

es_fmes "LEVEL "+level

color 255,255,255
pos 10,460

es_fmes "SHIELD "
if mf<1 : return
repeat mf
color 255,cnt*32,0
pos cnt*10+60,460

es_fmes "■"
loop
return


;■ 自機爆風
*bomb
repeat bmax
bf.cnt--
if bf.cnt<1 {
rnd bf.cnt,10 : bf.cnt=bf.cnt+10
rnd bx.cnt,50 : bx.cnt=25-bx.cnt+mx
rnd by.cnt,50 : by.cnt=25-by.cnt+my
}
pos bx.cnt,by.cnt

gmode 1,50,50 : es_copy 0,200,0
rnd dbx,10 : dbx=dbx-5
bx.cnt=bx.cnt+dbx
by.cnt=by.cnt+10
loop
rnd effect,10
if effect=0 : snd 1 : bgcolor=128
if effect=1 : snd 2 : bgcolor=255
return


;■ ポーズ処理
*pausegame
es_cls
gosub *message ;ゲーム情報
font "MSゴシック",40,3
rnd icr,8 : icr=icr*32+31
rnd icg,8 : icg=icg*32+31
rnd icb,8 : icb=icb*32+31
color icr,icg,icb
pos 250,200

es_fmes "PAUSE"
stick pat : stick pat,79 : jstick 79 : if stat!0 : pat=stat
if pat&128>0 : return
getkey returntop,123 : if returntop=1 : end ; 強制終了(F12)
es_sync 30
await 0
goto *pausegame


;■ ゲームオーバー
*gameover
es_cls
gosub *message ;ゲーム情報
font "MSゴシック",40,3
rnd icr,8 : icr=icr*32+31
rnd icg,8 : icg=icg*32+31
rnd icb,8 : icb=icb*32+31
color icr,icg,icb
pos 200,200

es_fmes "GAME OVER"
pos 30,250
es_fmes "PUSH RETURN TO RESTART"
stick pat : stick pat,79 : jstick 79 : if stat!0 : pat=stat
if pat&32>0 : goto *gamestart
getkey returntop,123 : if returntop=1 : end ; 強制終了(F12)
es_sync 30
await 0
goto *gameover

hspdxfix.dll対応にする際に変更を加えた箇所はこの色になってます。大きく異なるのは基本設定の部分と、ポーズ&ゲームオーバーの処理です。その瞬間の画面のまま静止させるのが面倒だったので、ポーズ&ゲームオーバーのルーチンはその部分だけでループさせてます。画面上には文字しか表示されないです。もしやるとしたら、敵や自機などの表示だけをさせるルーチンを用意しましょう。試しに作ってみましたがやっぱり面倒でしたes_getbufを使って画面全部をbufferに保存して表示する・・・というやり方も試してみたんですが、こちらはうまくいかなかったです^^;

後はmes命令が全部es_fmesに変わってます。この命令は単純に置き換えできるので、エディタの置換機能で一気に変更しちゃいましょう。gcopyの部分も単純置換できるかな?

後は、カスリ効果を視認できるようにしたのと倍率表示を派手にしたのが変更点。もちろん改造元のスクリプトでやらかしてた部分(無駄な計算や、動かす前に表示しちゃってた事)も直してます^^;

[改造記]画面描写(es_copy)

さていよいよhspdx.dllを使う場合の画面描写です。実はスプライトの機能を使わなければ、変更点はgcopyの代わりにgmodeとes_copyを使うだけです。では「奮闘記」の配列を使って星の動きを制御するのだのスクリプト(若干異なります^^;)を改造してみましょう。やっと「改造記」の名に相応しい状況になってきました(笑)

改造前スクリプトと改造後スクリプトのダウンロード(lzh)
※hspdxfix.dllとhspdxfix.asはご自分でご用意ください

サンプルにはstars.bmpが添付されてます。こんな感じ。

小さくてわかりませんね(笑)左から順に、明るい色から暗い色まで1dotの点がうってあります。ワンブロック5x5の大きさでコピーするようにしています。まぁどんな大きさでもいいんですけど^^;

;DirectX 初期化&チェック
#include "hspdxfix.as"
es_ini : es_screen 640,480,8,0
if stat=1 : goto *dxerr1
if stat=2 : goto *dxerr2
goto *setting

*dxerr1
dialog "DirectXの初期化に失敗しました。",1 : end
*dxerr2
dialog "スクリーンの初期化に失敗しました。",1 : end
*dxerr3
es_bye : wait 100
dialog "VRAMの容量が不足しています。",1 : end


;初期設定
*setting
randomize
smax=100 ;星の最大数
dim sx,smax ;星のx軸座標
dim sy,smax ;星のy軸座標
dim sc,smax ;星の色
dim ss,smax ;星の移動速度
repeat smax
rnd sx.cnt,635
rnd sy.cnt,475
rnd sc.cnt,14
rnd ss.cnt,4 : ss.cnt++
loop

;バッファへ画像の読みこみ
buffer 2,640,480,1
picload "stars.bmp",1

es_buffer 0,0
if stat=1 : goto *dxerr3


;メインルーチンへ行く前の儀式(笑)
gsel 0

es_cls
es_sync
wait 100

;----------------------------------------------------------
;メインルーチン
*main
getkey returntop,123
if returntop=1 : end ;強制終了(F12)

es_cls 0,0,0
gosub *stars
await 0
es_sync 20

goto *main
;----------------------------------------------------------
;流れる星を表示するためのサブルーチン
*stars
repeat smax
pos sx.cnt,sy.cnt

gmode 1,5,5 : es_copy 0,sc.cnt*5,0
sy.cnt+=ss.cnt
if sy.cnt>480 : sy.cnt=-5 : rnd sx.cnt,640
loop
return

hspdxfix.dll対応にする際に変更を加えた箇所はこの色になってます。この色になってる部分が今回の改造のキモです。この*starsのルーチンをもし標準命令で書くとすると、こんな感じです。

;流れる星を表示するためのサブルーチン
*stars
repeat smax
pos sx.cnt,sy.cnt

gcopy 0,sc.cnt*5,0,5,5
sy.cnt+=ss.cnt
if sy.cnt>480 : sy.cnt=-5 : rnd sx.cnt,640
loop
return

このサンプルの場合gcopyをgmodeとes_copyに変更してますが、じつは標準命令の場合もgmodeでコピーサイズを指定しておけるので、実 質はgcopyをes_copyに差し替えるだけです。この程度のスクリプトなら、もろもろの準備をしなければならない分、DirectXを使うほうが面 倒だったりします^^;

stars.asとstarsdx.asをじっくり見比べてみてください。実行時の見た目は、chgdispで全画面表示をしてるので殆ど差が無いと思い ます。大きく変更されているのは今までの項で解説してきたDirectX対応にする為の準備部分だけのはずです。そしてこの部分はDirectX対応にす る限り、全く同じような形でスクリプトを組むこととなります。

[改造記]メインルーチン

では今度は、実際のメインルーチンで使う場合にどうするか?を説明しましょう。スプライトの機能を使わない限り基本的な部分はほとんど変わりないです。


gsel 0
es_cls
es_sync
wait 100

メインルーチンに入る前に、上記のように一旦画面クリアして一定時間待つ処理を入れましょう。あまり早く画面を切り替えてしまうと、画面にウィンドウの残骸が見えてしまいます。


;■ メインルーチン
*main
es_cls
; いろいろな処理
await 0
es_sync 30
goto *main

メインルーチン(主要な処理をループさせる部分です)は、上記のように「画面消去」→「画面更新とウェイト」という流れになります。そのどこでもいいの で、「await 0」を入れておきましょう。これは他のWindowsタスクに処理時間を渡すためです。これをやらないと結構やばい事になるので注意しましょう。

また、このように永久ループをさせる部分では、強制終了させるための処理も入れておきましょう。実際にDirectXの画面描写処理に入ると、CTRL+ALT+DELが効かなくなります。ALT+F4は効きますが、ソフト的に終了する手段を入れておいたほうが安全です。


;■ メインルーチン
*main
es_cls
; いろいろな処理
await 0
es_sync 30
getkey endgame,123 : if endgame=1 : end
goto *main

こんな感じです。

参考までに標準命令だけでやる場合のメインルーチンを併記しておきます。


;■ メインルーチン
*main
redraw 2
color 0,0,0
boxf 0,0,640,480
; いろいろな処理
await 30
redraw 1
goto *main

[改造記]使えなくなる命令

このようにHspをDirectX対応にすると、Hsp標準命令の画像描写関係のものは一切使えなくなります。否、標準命令の画像描写を行ってもDirectXの画面には反映されません。使ってもエラーが出るわけじゃないです。ただ単に見えないだけです^^;

でも見えないだけで実はちゃんと描写は行ってます。なので、buffer上にpsetやmes命令などで描写しておき、それをes_bufferに転送す れば他の画像と同じようにes_copyで画面に反映することが可能です。ただ、リアルタイムでこれをやっちゃうとめちゃめちゃ遅くなり、DirectX でやってる意味が無いです。なんで、使えなくなる、と考えておいて問題はないでしょう。

じゃ、使えなくなる(意味の無い)命令と、hspdx.dllで追加される命令と関係するものをまとめてみましょう。

  • プログラム制御命令関係・・・1つだけ使い方が変わります

    • await・・・画面更新速度はes_syncで一定にしますが、Windowsへタスクを渡すためにウェイト無しで使用します


  • 基本入出力制御命令関係・・・画面表示に絡むものは駄目です。

    • cls・・・代わりにes_clsを使います
    • dialog・・・es_byeでDirectXから通常画面に戻らないと見えません^^;
    • mes・・・代わりにes_meses_fmesを使います
    • print・・・代わりにes_meses_fmesを使います
    • text・・・試してません^^;
    • title


  • オブジェクト制御命令関係・・・1つを除き全滅です。

    • button
    • chkbox
    • clrobj
    • combox
    • input
    • listbox
    • mesbox
    • objmode
    • objprm
    • objsel
    • objsend
    • objsize・・・拡大縮小コピー(es_zoom)の際の大きさ指定に使います


  • 画面制御命令関係・・・ほぼ全滅。その代わりに使う命令があります。
    • bgscr
    • bmpsave・・・es_getbufでHspウィンドウに逆読み込みしてからならDirectXの描写画面を保存できます
    • boxf・・・代わりにes_boxfes_fillを使います
    • buffer・・・es_bufferの転送元として使います
    • chgdisp・・・解像度はes_screenで指定できます
    • gcopy・・・代わりにes_copyを使います
    • getpal・・・試してません^^;
    • gmode・・・es_copyのコピーサイズ指定に使います
    • gsel・・・Hspのウィンドウに対してしか意味が無いのでes_getbuf等の際には使うのでしょうか?
    • gzoom・・・代わりにes_zoomを使います
    • line
    • palcolor・・・試してません・・・が、多分使えます^^;
    • palcopy・・・es_bufferの際に自動的に設定されます
    • palette・・・試してませんが、es_palsetが代わりに使えるはずです
    • palfade・・・代わりにes_palfadeを使います
    • pget・・・試してません^^;
    • picload・・・es_bufferの転送元のbufferへの読み込みに使います
    • pos・・・健在です
    • pset
    • redraw・・・画面更新はes_syncで行います
    • screen・・・代わりにes_screenを使います
    • sysfont・・・試してません・・・が、多分使えます^^;
    • width・・・解像度がes_screenで決まるので使いません

私の場合、使えなくなって一番痛いのはlineですかね。「Aeolianation」のAUTO SHOTはDirectXだと再現できないのです(笑)。垂直か水平の線を引くのであれば、es_boxfなどで代用はできるのですが・・・。

より以前の記事一覧