カテゴリー「1.奮闘記」の17件の記事

[奮闘記]インデックス

01:はじめに
02:文章を表示させるのだ(その1)
03:文章を表示させるのだ(その2
04:文字を動かしてみるのだ(その1)
05:文字を動かしてみるのだ(その2)
06:星空を描いてみるのだ
07:星空を一気に表示するのだ
08:流れる星空を表示するのだ
09:流れる星空を背景に、静止したキャラを表示するのだ
10:流れる星空を背景に、自分が好きなように動かせるキャラを表示するのだ
11:配列を使って星の動きを制御するのだ
12:数式ってのはこういうものなのだ
13:ちゃんと自機がカーソルに従って動いた理由の解析
14:計算式と条件式(その1)
15:実はそんなに難しいことではなかった、カーソルで動くわけ
16:2点間の距離を算出するのだ

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

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

[奮闘記]2点間の距離を算出するのだ

だ~いぶ間があいてしまったんで、題材がすっとんじゃってますけど、あんま気にしないで下さい(^^ゞ

今作成中の『Distance』というシューティングゲームのメインコンセプトは「自機から近い位置で倒すほど得点倍率が高い」というもの。ということは、必然的に自機と敵との間の距離を算出しなきゃいけなくなってくる。ちょっと下の図を見ていただきたい。

要は、(mx,my)~(ex,ey)の距離を出したいのである。ここで数学の知識が必要となってくるわけだが、直角三角形の場合dxとdyの長さがわかってれば斜め辺の長さは求めることができる。それがいわゆる三平方の定理である(すっかり忘れてた^^;)。

2=dx2+dy2

つまり、(dx2+dy2)の平方根を求めれば、2点間の距離が算出できるというわけだ。しかし、これには致命的な問題点がある。HSP2.4には標準状態のままだと平方根を求める命令がないのである。もちろん拡張dllなんかでその命令を追加することはできるが、別にそこまでして正確な平方根を求めようと思わないのなら、基本命令だけでもなんとかできちゃうのである。もちろんけっこう値はいいかげんなんで、正確に求めたいなら拡張dllを使いましょう。


; 三平方の定理を利用した距離算定スクリプト

screen 0,640,480,1,50,0 : title "2点間のだいたいの距離"
mx=320 : my=240


; ■■■■■ 要素番号の2乗を格納
*start
dim y,900
repeat 900
x=cnt : y.cnt=x*x
loop

goto *main


; ■■■■■ メイン
*main
redraw 2
color 255,255,255 : boxf 0,0,640,480

gosub *dotmove
gosub *measure

redraw 1
await 16
goto *main


; ■■■■■ 距離の算出
*measure
dx=320-mx : if dx<0 : dx=-dx
dy=240-my : if dy<0 : dy=-dy
dst=(dx*dx)+(dy*dy)

repeat 900
if dst<y.cnt : dst=cnt : break
loop

return


; ■■■■■ 点の移動と画面描写
*dotmove
stick pat,79
if pat&128>0 : end
if pat&64>0 : move=4 : else : move=1
mxv=(pat>>2&1)-(pat&1)
myv=(pat>>3&1)-(pat>>1&1)
mx=mxv*move+mx : my=myv*move+my
if mx<0   : mx=0
if mx>640 : mx=640
if my<0   : my=0
if my>480 : my=480

color 192,192,192
pos 320,0 : line 320,480
pos 0,240 : line 640,240
color 0,0,0
pos 320,240 : line mx,my
boxf 319,239,321,241
boxf mx-1,my-1,mx+1,my+1
cx=mx-320 : cy=-my+240
pos 0,0  : color 0,0,0 : mes "座標:("+cx+","+cy+")"
pos 0,20 : mes "距離:"+dst+"pt"
return

※CTRLを押しながらだと、点が素早く動く。

このスクリプトのポイントは3点。

  1. あらかじめ2乗の値を配列に格納しておく
  2. 2点のx,y座標の差(dx,dyの絶対値)を求め、その2乗を足す( → dst )
  3. dstと格納しておいた配列とを比較し、配列がdstより大きくなったらその要素番号が距離になる

要は、用意しておいた値と比較するっていうところが今回のミソ。この方法だと正確な値を求めることはできないけど、だいたいの距離を把握したいのなら必要にして充分。拡張dll使って平方根求めると、特に必要のない(と言うよりむしろ邪魔)な小数点以下まで出てきちゃうしね。

ちなみに乗数を900個用意したのは、640x480の斜めの長さは約832だから。つまり、これだけ用意しておけば640x480のウィンドウをややは み出すような2点間の距離も求められるってわけ。今回のスクリプトの場合中心からの距離を求めてるから、本当は400個も用意しておけば充分。例えば10 段階程度で距離を求めたいなら、10個用意すれば充分なわけで、処理速度にもそれほど影響はない。

[奮闘記]実はそんなに難しいことではなかった、カーソルで動くわけ

再度確認の意味で、例の部分を書き出しましょ。


*JIKI
STICK PAT,15
XV=(PAT>>2&1)-(PAT&1)
YV=(PAT>>3&1)-(PAT>>1&1)

MX=XV*MOVE+MX : MY=YV*MOVE+MY
IF MX<0   : MX=O
IF MX>566 : MX=566
IF MY<0   : MY=0
IF MY>364 : MY=364
POS MX,MY : GMODE 2 : GCOPY 2,0,0,32,32
RETURN

※ MX , MY ・・・ 自機の座標
※ MOVE ・・・ 自機の移動速度

んで、STICK命令で返される値が次の通りね。

1 2 4 8

じゃ、これをそのまま2進法で表記してみましょ。

%0001 %0010 %0100 %1000

全部違う桁の数字が1になってるよね?要はこれがスイッチなわけ。一番右の桁 は「←」のスイッチ、右から2つ目の桁は「↑」のスイッチになってると。そう考えると、「←↑」同時押しの場合でも、ちゃんと異なった値が出てくるのって むしろ自然なことなんだよね。仮に日本語106キーボード全てのキーを全ての組み合わせで判定させようと思ったら、2進法で106桁あれば全部の組み合わ せが判定できるわけだ。

・・・ん?2進法で106桁???

2106=81129638414606681695789005144064

・・・コンピュータって偉いね^^;

[奮闘記]計算式と条件式(その1)

しばらく更新が停滞してたけど、手をつけてなかったわけじゃないです。結構思ったとおりにいろんなことができるようになったんで、製作に熱中してたのだ^^;

っつーわけで(どういうわけだ?)、今回のお題は「条件式と計算式」。数学嫌い~(T-T)などと言ってるわけにもいかないのだ。もちろん今までのプログラムでも、計算式とか条件式は使ってきた。でも、ちょっとした工夫で、式を短くしたり複雑なことができるようになるのだ。

例えば「変数Aに1づつ加えていき、Aが20になったら0に戻す、ということを繰り返す」てのを普通に書くとこんな感じになる。


*TEST
A=A+1
IF A=20 : A=0

GOTO *TEST

この2行、ちょっと工夫すれば1行で書けるのだ。嘘ではない。本当である。下を見てみなさい。


*TEST
A=A+1 : IF A=20 : A=0
GOTO *TEST

うーん・・・確かに1行になってる・・・ってをい(-_-;

これは単なる冗談。本当は、こういうふうに書く。


*TEST
A=(A+1)\20
GOTO *TEST

以前「よく使い方がわからん」って言ってた『』。そう、これは 割り算の余りを算出する記号だ。要するに20で割り切れる場合ってのは、余りが0なわけだよな。一番最初、Aが0の場合から順番に考えてみんさい。0に1 足して20で割った余りがAに代入されるから、A=1になるでしょ?次には1に1足して20で割るから余りは2。次には2に1足して20で割るから余りは 3、、、、、。嘘だと思ったら、この行の次に「PRINT A」って追加してみな。0~19の数字が順番に並ぶから。

え?これをどう使うかって?そりゃーあんた、考えてみなって(なんか偉そう^^;)。例えばあるループ中で、数回に一回だけ処理したいことがあったとしよ う。例えば「自機に弾を撃たせる条件」なんかを考えてみるとわかりやすいかな?あんまり連射ができないようにしたいなら、3~4回に一回だけ弾を撃たせる ようにすれば良いわけだ。そういう場合、今までだったらこういう感じに書いてたと思う。


*JIKI
STICK PAT,15
T=T+1 : IF T=4 :  SHOT=1 : T=0
XV=(PAT>>2&1)-(PAT&1)
YV=(PAT>>3&1)-(PAT>>1&1)
MX=XV*MOVE+MX : MY=YV*MOVE+MY
IF MX<0   : MX=O
IF MX>566 : MX=566
IF MY<0   : MY=0
IF MY>364 : MY=364
POS MX,MY : GMODE 2 : GCOPY 2,0,0,32,32
RETURN

んで、「SHOT=1」の場合だけ弾を撃たせるようにすればいいわけだ。でもこれを『¥』を使って書くとこうなる。


*JIKI
STICK PAT,15
T=(T+1)\4 : IF T=0 : SHOT=1
XV=(PAT>>2&1)-(PAT&1)
YV=(PAT>>3&1)-(PAT>>1&1)
MX=XV*MOVE+MX : MY=YV*MOVE+MY
IF MX<0   : MX=O
IF MX>566 : MX=566
IF MY<0   : MY=0
IF MY>364 : MY=364
POS MX,MY : GMODE 2 : GCOPY 2,0,0,32,32
RETURN

まぁあんまり短くなってないけど、Tを0に戻す手間は省けてるよね。これがもっと複雑なことになってくると、重宝するような気がしない?「T=0」っての を忘れてて、一回撃ったら次から二度と撃てないなんていうマヌケなことやっちゃうのを防ぐこともできるし(そういうのやるの俺だけ?^^;)

[奮闘記]ちゃんと自機がカーソルに従って動いた理由の解析

確認の意味で、謎だった部分を書き出してみましょ。「暗記しましょう」はいいんだけど、訳がわからないと悔しいもんね^^;


*JIKI
STICK PAT,15
XV=(PAT>>2&1)-(PAT&1)
YV=(PAT>>3&1)-(PAT>>1&1)

MX=XV*MOVE+MX : MY=YV*MOVE+MY
IF MX<0   : MX=O
IF MX>566 : MX=566
IF MY<0   : MY=0
IF MY>364 : MY=364
POS MX,MY : GMODE 2 : GCOPY 2,0,0,32,32
RETURN

※ MX , MY ・・・ 自機の座標
※ MOVE ・・・ 自機の移動速度

んで、STICK命令で返される値が次の通りね。

1 2 4 8

ここで注意なのは、同時に押された場合。←→が同時に押されてる場合、返される値は「 5 」になる。↑→なら「 6 」だね。論理演算は2進法で考えざるを得ないんで、全部の組み合わせを2進法になおして表記してみましょう。

- %0000 0
%0001 1
%0010 2
←↑ %0011 3
%0100 4
←→ %0101 5
↑→ %0110 6
←↑→ %0111 7
%1000 8
←↓ %1001 9
↑↓ %1010 10
←↑↓ %1011 11
→↓ %1100 12
←→↓ %1101 13
↑→↓ %1110 14
←↑→↓ %1111 15

見事に全部違う値が返されました。これはあくまで想像だけど、STICKの命令で返される値は、全部の組み合わせが違う数値になるんじゃないかな?こんなこと考える人って天才か変態かどっちかだと思う。あっしのような凡人にはとても真似できない^^;

閑話休題

さて、←↑が押された場合ってのを考えてみましょう。この場合「PAT=3」なので、この2行はこんな感じになる。


XV = (%0011)>>2 & %0001 - %0011 & %0001
YV = (%0011)>>3 & %0001 - (%0011)>>1 & %0001

XV = %0000 & %0001 - %0011 & %0001
YV = %0000 & %0001 - %0001 & %0001

XV = %0000 - %0001
YV = %0000 - %0001

はい、見事にXVもYVも-1になりました。はっきり言って、お見事!の 一言です。さすがに全部の組み合わせを試してみる気にはなれないけど、完璧に動いてるってことは、全ての組み合わせがちゃんとこういう感じになるってこと ですね。ちなみに←→っていうような、相反する組み合わせで押された場合でも、ちゃーんとx軸の運動量は0になります。つまり動かないっつーこと。こう やって論理演算で処理させてるから、キビキビとカーソルに反応するプログラムにできるんですね。CPUに優しく、操作する人にも優しく、プログラムを組む 人には厳しく・・・(爆)

[奮闘記]数式ってのはこういうものなのだ

今回・次回の解説には、OXさん・ToYさん・MIAさん・Ma_Tsさんなどの多大なご協力を頂きました。この場を借りて御礼申し上げます。深謝。

じゃぁ、マニュアルの「数式」のとこに書いてある順番に見てきましょう。なんかそのうち中学・高校で使ってた数学の教科書引っ張り出さなきゃいけなくなりそうだな^^;



10進整数 -2147483647  ~  2147483647
16進整数 $0  ~  $FFFFFFFF
2進整数 %0  ~  %111111...

10進数字→2進数字とか、16進数字→10進数字を暗算でやれって言われると「ごめんなさい」なんだけど、まぁ意味はわかる。要はこういうことでしょ?

2進法 10進法 16進法
%1 1 $1
%101101 45 $2D
%1110011 115 $72
%11111111 255 $FF

Windowsに標準で入ってる「電卓」を使いました。プログラマに関数電卓が必須だっての、よくわかるね・・・こんなの手計算じゃやっとれん^^;



文字コード(1バイト) 'A'

最初意味がよくわからんかったのだけど、やってみて納得。それぞれの文字が持ってるコード番号ってのがあって、それがそのまま入るらしい。例えば 'a'=97 、 'A'=65 っていう感じ。全角文字は調べられないのかな~と思ったら、できちゃった。 'あ'=-126 だった。入力した文字を調べて何か計算する(占いとか暗号とかに使えるかな?)ような使い方ができるのかな?



加減乗除算 + - * /

これは特に問題なし。ところでこれはHSPの特殊な仕様らしいんだけど、計算式は常に左から順番に処理されるん だって。乗除算の優先は無いそうな。優先させたかったら()をつけるってのは同じらしいんだけど。例えば「A=1+5*2」は「A=12」と同じ。数学的 に正しく計算すれば「A=11」だもんね。そういうふうにしたかったら「A=1+(5*2)」というような感じで記述しなきゃいけないそうだ。



論理演算(and) &
論理演算(or) |
論理演算(xor) ^

今まで一番わからなかった部分。要は2進法で考えなきゃいけないそうだ。

例1: 12&20
12 %01100
20 %10100
結果 %00100

ってことで、結果は「4」になる。これが信じられない人は、スクリプトエディタで下のように書いてみてくだされ。F5を押すと「4」と表示されるはずだぁ。


PRINT 12&20
STOP

つまり、「and」の計算は、2進法に直して、同じ位置のどちらにも「1」があれば、「1」になるということ。「論理積」と言うそうな。縦に見るとわかりやすいね。

例2: 12|20
12 %01100
20 %10100
結果 %11100

今度の結果は「28」。信じられない人は、上のプログラムの「&」を「|」に書き換えてF5を押してみなはれ。「or」の計算は、2進法に直して、同じ位置のどちらかに「1」があれば「1」になると。「論理和」と言うそうな。

例3: 12^20
12 %01100
20 %10100
結果 %11000

今度の結果は「24」。同じ位置の数字が違ったら結果は「1」になるそうな。「排他的論理和(xor)」と言って、あまり見かけないけど暗号化なんかに使えるらしい。

彼ら(笑)の使い道ってすぐには思いつかないんだけど、元々CPUの根っこでは2進法で計算してるんだから、人間様は苦労するけどコンピュータだと処理が速くなるそうな。得意な方法でやらせてあげましょうってことだね・・・。



割り算の余り \

こいつは簡単。例えば 4\3=1って感じだね。ただし、何に使うのかは見当がつかん^^;



条件式(同じ) =
条件式(小さい) <
条件式(以下) <=
条件式(以上) >=
条件式(大きい) >
条件式(≠) !

これらはほとんどIF文で使うやつだね。「IF x>600 : x=600」っていう感じだな。



左方向へビットシフト <<
右方向へビットシフト >>

これはマジで、きちんと勉強した人じゃなきゃわかんないよぉ。要は、「2のn乗を掛ける」ということらしい。左とか右ってなんのこっちゃ?って思ってたんだけど、要は2進法で考えるらしい。

例1: 50>>1
50 %00110010
結果 %00011001

これは「 50×2-1 」ということになるから、「 50/2 」と結果は同じ。2進法に直すと、見事に右側にずれてる。これも論理演算と同様、2進法で人間様は苦労するけどコンピュータは楽チンになるそうな。

例2: 50<<2
50 %00110010
結果 %11001000

これは「 50×22 」だから、「 50*4 」と結果は同じ。2進法で考えると、今度は左に2つずれてるのがわかるでしょ?


さぁここまで来れば、あの謎の2行の意味がわかってくるぞぉ。解析は次回。

[奮闘記]配列を使って星の動きを制御するのだ

実は、今までのやり方の応用で多重スクロール(速度の異なったスクロールを重ね合わせる)をやってみたのだが、めったくそ遅い(T-T)/これに加えて、敵キャラ表示したり当り判定の処理をやったり、、、なんてこと想像もしたくなかったので、別のやり方を模索することにした。なんでも、敵キャラのデータを管理するのには「配列変数」というのを使うと便利らしい。いずれやらなきゃならんのなら、単純に制御できるもので勉強してみようっつーことで、今回のお題が決定。


;初期設定
SCREEN 0,600,400,1
CLS 4
RANDOMIZE
MX=300 :MY=200 ;自機の初期位置を指定
MOVE=4 ;自機の移動速度を指定
SMAX=100 ;星の最大数

;配列定義
DIM X,SMAX ;星のx軸座標
DIM Y,SMAX ;星のy軸座標
DIM C,SMAX ;星の色
DIM S,SMAX ;星の移動速度

;バッファー2に自機の画像を描きこみ
BUFFER 2,600,400,1
POS 0,0 : PICLOAD "myship.bmp"

;星をランダムに表示
GSEL 0 : COLOR 0,0,0 : BOXF 0,0,600,400
REPEAT
SMAX
RND X.
CNT,600
RND Y.CNT,400
RND C.CNT,3 : C.CNT=(C.CNT+1)*64-1
RND S.CNT,4 : S.CNT=S.CNT+1
COLOR C.CNT,C.CNT,C.CNT
PSET X.CNT,Y.CNT
LOOP

;--------------
;メインルーチン
;--------------
*MAIN
REDRAW 2
COLOR 0,0,0 : BOXF 0,0,600,400
GOSUB *HAIKEI
GOSUB *JIKI
REDRAW 1
AWAIT 16
GOTO *MAIN


;流れる星を表示するためのサブルーチン
*HAIKEI
REPEAT SMAX
COLOR C.CNT,C.CNT,C.CNT
Y.CNT=Y.CNT+S.CNT
IF Y.CNT>400 :Y.CNT=0
PSET X.CNT,Y.CNT
LOOP
RETURN


;自機を動かすためのサブルーチン
*JIKI
STICK PAT,15
IF PAT=128 : END
XV=(PAT>>2&1)-(PAT&1)
YV=(PAT>>3&1)-(PAT>>1&1)

MX=XV*MOVE+MX : MY=YV*MOVE+MY
IF MX<0   : MX=O
IF MX>566 : MX=566
IF MY<0   : MY=0
IF MY>364 : MY=364
POS MX,MY : GMODE 2 : GCOPY 2,0,0,32,32
RETURN

ふむ、、、今までに比べると、なんか短くなったような気がする。特に失敗作(遅すぎる)の多重スクロールのプログラムなんて、この2倍ぐらいの長さだもんな(笑)

DIM (配列を割り当てる変数名),(要素の最大数)
指定した数の要素を持つ配列変数を作成
使うとき (変数名).(要素番号)

ちなみに変数名と要素番号の間は.(ピリオド)ね。

今までプログラムの本とか見ても、何に使うか一番わからなかったのがこの配列。でも仮に配列を使わないで、同じように星を100個制御しようと思うと、変 数をめちゃめちゃいっぱい作らなきゃいけなくなるということのようだ。今回の場合、星の配置・明るさ・移動速度をランダムに設定してるから、配列変数を使 わないと「;星をランダムに表示」のところはこんな感じになる。


;星をランダムに表示
GSEL 0 : COLOR 0,0,0 : BOXF 0,0,600,400
RND X0 ,600 : RND Y0 ,400 : RND C0 ,3 : RND S0 ,4
RND X1 ,600 : RND Y1 ,400 : RND C1 ,3 : RND S1 ,4
RND X2 ,600 : RND Y2 ,400 : RND C2 ,3 : RND S2 ,4
RND X3 ,600 : RND Y3 ,400 : RND C3 ,3 : RND S3 ,4


(中略)


RND X98,600 : RND Y98,400 : RND C98,3 : RND S98,4
RND X99,600 : RND Y99,400 : RND C99,3 : RND S99,4

こんなのやってられっか~~~(T-T)/
これを配列変数を使うことによって「n番目の要素」っていう感じに変数で処理ができるようになるというわけ。

んで、今回のミソ二つ目のシステム変数。一番上のプログラムで緑色になってるCNTってやつね。これはREPEAT~LOOPの中で、今は何回目のループなのか?っていうのを格納してるスグレモノ。正確に言うと「カウンター」を調べるものなので、一回目のループだとCNT=0になるらしい。だからX.CNTが意味するのは、Xのカウンター番目の要素ってことだな。要はREPEAT~LOOPを使うことによって、100行書く手間を省いてるというわけだ。これは便利。

さて、次のお題だけど、自機を操作するところで丸暗記^^;し た部分(青くなってる部分ね)、全く意味がわからなくて悔しかったんで、方々に尋ねてやっと理解できた。それを忘れないうちに脳味噌に叩き込んでやろうっ つーことで、ちょっと今までの奮闘記とは系統が異なるけど「解説」じみたことをやってみたいと思う。間違ってなきゃいいんだけど^^;

[奮闘記]流れる星空を背景に、自分が好きなように動かせるキャラを表示するのだ

よし、いよいよ自機を動かずぞ~!

と言っても、細かい計算なんかは先人の知恵を拝借した方が良さそうなんで、MIAさんのページに載ってた解説をそのまま利用させていただくことにしました。深謝。


SCREEN 0,600,400,1,100,50,600,400
CLS 4
RANDOMIZE
MX=300 :MY=200 ;自機の初期位置を指定
MOVE=2 ;自機の移動速度を指定

;バッファー3に星空の絵を描きこみ
BUFFER 3,600,400,1
COLOR 0,0,0 : BOXF 0,0,600,400
REPEAT 1000
RND X,600 : RND Y,400
RND R,7 : RR=(R+1)*32-1
RND G,7 : GG=(G+1)*32-1
RND B,7 : BB=(B+1)*32-1
COLOR RR,GG,BB
PSET X,Y
LOOP
BUFFER 4,600,400,1

;バッファー2に自機の画像を描きこみ
BUFFER 2,600,400,1
POS 0,0 : PICLOAD "myship.bmp"


;描画画面の準備
GSEL 0 : COLOR 0,0,0 : BOXF O,O,600,400
PALCOPY 3
POS 0,0 :GCOPY 3,0,0,600,400 ;星空を複写
POS MX,MY :GCOPY 2,0,0,32,32 ;自機画像を複写

;メインルーチン
*MAIN
REPEAT 400
REDRAW 0
GOSUB *HAIKEI
GOSUB *JIKI
REDRAW 1
STICK K : IF K=128 : END
AWAIT 16
LOOP
GOTO *MAIN

;流れる星を表示するためのサブルーチン
*HAIKEI
GMODE 1,600,400
GSEL 4
POS 0,0 : GCOPY 3,0,0,600,400 ;現在の画面を保存

GSEL 3
POS 0,0 : GCOPY 4,0,399,600,400
POS 0,1 : GCOPY 4,0,  0,600,399

GSEL 0
POS 0,0 : GCOPY 3,0,0,600,400
RETURN

;自機を動かすためのサブルーチン
*JIKI
STICK PAT,15
XV=(PAT>>2&1)-(PAT&1)
YV=(PAT>>3&1)-(PAT>>1&1)

MX=XV*MOVE+MX : MY=YV*MOVE+MY
IF MX<0   : MX=O
IF MX>566 : MX=566
IF MY<0   : MY=0
IF MY>364 : MY=364

POS MX,MY : GMODE 2 : GCOPY 2,0,0,32,32
RETURN

なんと今回は、新しい命令一つもなし。そのかわり、何やら難しげな条件判断が(赤い部分)。ここはMIAさん曰く、「こういうふうにやるもんだと暗記しちゃいましょう」とのこと。要は変数PATを調べて、自機の移動方向を決定するための公式らしい。で、次の行で自機の座標(MX,MY)を決定すると。移動速度をわざわざ変数(MOVE)にしたのは、後々スピードアップアイテムを登場させるため。なんて読みが深いんでしょ(笑)

あと、緑の部分の条件判断は、自機がウィンドウからはみださないようにするため。ウィンドウサイズが600×400なんで、右端と下端は若干少なめに設定してある。これは、カレントポジションを左上にした32×32のキャラを表示させてるから。

ちなみに最初に真っ黒な背景で動かしたら、自機のかけらが残っちゃったんだけど、今回の場合毎回背景の描きこみをしてるから、自動的に消えちゃうんだよね。なんか案ずるより生むが易しって感じ。無駄なことしないですむってのはいいよね。

[奮闘記]流れる星空を背景に、静止したキャラを表示するのだ

とりあえず早くキャラを動かしてみたいけど、慌てない慌てない。さっき作った流れる星空を背景に、まずは静止したキャラを表示させてみよう。ちなみに用意したキャラはこれ。

あれ?なんで縦向きなの?・・・実は横向きのキャラがうまく描けなかったのだ。だからそれにあわせてスクロール方向も縦方向に変えちゃったという^^;


SCREEN 0,600,400,1,100,50,600,400
CLS 4
RANDOMIZE

;バッファーに星空の絵を描きこみ
BUFFER 3,600,400,1
COLOR 0,0,0 : BOXF 0,0,600,400
REPEAT 1000
RND X,600 : RND Y,400
RND R,7 : RR=(R+1)*32-1
RND G,7 : GG=(G+1)*32-1
RND B,7 : BB=(B+1)*32-1
COLOR RR,GG,BB
PSET X,Y
LOOP
BUFFER 4,600,400,1 ;星空を保存するための画面を準備

;バッファーに自機の画像を描きこみ
BUFFER 2,600,400,1
POS 0,0 :
PICLOAD "myship.bmp"
MX=300 :MY=200 ;自機の初期位置を指定

;描画画面の準備
GSEL 0 : COLOR 0,0,0 : BOXF O,O,600,400
PALCOPY 3
POS 0,0 :GCOPY 3,0,0,600,400 ;星空を複写
POS MX,MY :GCOPY 2,0,0,32,32 ;自機画像を複写

;メインルーチン
*MAIN
REPEAT 400
REDRAW 0
GOSUB *HAIKEI
GOSUB *JIKI
REDRAW 1
STICK K : IF K=128 : STOP
AWAIT 16
LOOP
GOTO *MAIN

;流れる星を表示するためのサブルーチン
*HAIKEI
GMODE 1,600,400
GSEL 4
POS 0,0 : GCOPY 3,0,0,600,400 ;現在の画面を保存

GSEL 3
POS 0,0 : GCOPY 4,0,399,600,400
POS 0,1 : GCOPY 4,0,0,600,399

GSEL 0
POS 0,0 : GCOPY 3,0,0,600,400
RETURN


;自機を表示するためのサブルーチン
*JIKI
GMODE 2,32,32
POS MX,MY :GCOPY 2,0,0,32,32
RETURN
 

自機を表示する座標をわざわざ変数にしてるのは、今後のことを考えて。いずれは動かすわけだから、変数にしておくべきだもんね。今回のには意味ないけど^^;

しっかしだんだん長くなってきたなぁ~。でもこれだけ長くなっても、新しく出てきた命令は3つだけ。

PICLOAD "(読みこむ画像ファイル名)"
指定した画像を、カレントポジションに描画。

敵とか味方とか背景なんかを外部ファイルから読みこませるのはこれだね。今後活躍しそうな命令^^

GOSUB (ラベル名)
指定したフラグに移動し、RETURNで戻る。

RETURN
移動元のGOSUBへ戻る。

この二つも「REPEAT~LOOP」と同じように、必ずペアで用いるそうな。いわゆるサブルーチンジャンプって奴だな。このプログラムを最初に実行したとき、いきなり終わっちゃったんで「???」ってなったんだけど、単にRETURNを忘れてただけだったという^^;

今回はウィンドウを3枚も使っちゃったけど、バッファーを宣言するときにウィンドウサイズを大きくしておけば、2枚でできそうね。まぁ8枚使えるわけだから、今のうちはあんまり気にしなくても良いかもしれないけど、そのうち困るときが来そうだから注意しておこう。

さて、いよいよ次は、カーソルキーで自機を動かすプログラムだ!とりあえず最初の目標だったからね。感無量^^

[奮闘記]流れる星空を表示するのだ

※注:この項の内容ですが、星を流すやり方としては駄目な例です。もっといいやり方を「配列を使って星の動きを制御するのだ」で解説しています。命令の解説自体は間違ってないのですが・・・用法は真似しないで下さい(1枚絵を動かすやりかたとしてはさほど間違いではないのですが)。
(2004年5月7日追記)


今度は、RNDを使って描いた星空を、右から左にスクロールさせてみよう。GCOPYの応用技だな。


SCREEN 0,600,400,1,100,50,600,400
CLS 4
RANDOMIZE

;バッファーに星空の絵を描きこみ
BUFFER 2,600,400,1
COLOR 0,0,0 : BOXF 0,0,600,400
REPEAT 1000
RND X,600 : RND Y,400
RND R,7 : RR=(R+1)*32-1
RND G,7 : GG=(G+1)*32-1
RND B,7 : BB=(B+1)*32-1
COLOR RR,GG,BB
PSET X,Y
LOOP

;描画画面の準備
GSEL 0 : COLOR 0,0,0 : BOXF O,O,600,400
PALCOPY 2 : GMODE 1,600,400
POS 0,0 :GCOPY 2,0,0,600,400
;バッファーに描いた画面を複写

;星をスクロールさせる部分
*MAIN
GSEL 2 : POS 0,0
GCOPY 0,0,0,600,400
;現在の画面を保存
GSEL 0
REDRAW 0
;書き換える前に非表示
POS 0,0 : GCOPY 2,1,0,600,400
POS 599,0 : GCOPY 2,0,0,1,400

REDRAW 1
;書き換えた画面を表示
STICK K : IF K=128 : STOP
;ESCが押されたら停止
AWAIT 16
;ウィンドウズにタスクを渡す
GOTO *MAIN

新しい命令がいくつか出てきたぞ~。

SCREEN (ウィンドウID),
(初期化する横幅),(初期化する縦幅),
(初期化するモード)
(配置x軸),(配置y軸)
(ウィンドウの横幅),(ウィンドウの縦幅)
ウィンドウIDを指定したモード、配置、大きさで初期化

「BUFFER」の親戚だね。違いは実際表示する画面かどうかってとこ。「WIDTH」だと大きさと配置しか決められなかったけど、こっちは画面モードの切り替えができるってのが違いみたい。

PALCOPY (ウィンドウID)
別画面で使用されてるパレットを現在の画面に反映する。

2番のウィンドウで使った色を、そのまま0番ウィンドウでも使いますよ~っていう意味でつかってるんだけど、0番ウィンドウに対してのみ、この命令はやらなくてもいいみたい。

GMODE (コピーモード),
(コピーする横幅),(縦幅)
「GCOPY」で、どのような形でコピーをするかを指定する。

0だと普通にコピー(1670万色モードのときに使うのかな?)、1だと高速コピー、2だと高速コピー+(0,0,0)の色が透過するっていう感じらしい。2のモードはキャラの重ね合わせに使えそうだね。

REDRAW (描画モード),
(再描画する画面の左上x軸座標),(y軸座標)
(横幅),(縦幅)
描画モードを指定する。

0で描きこむと、画面上では変化せず、1にすると更新すると。つまり今回の場合、赤字になってる部分で星を流してる(コピーする座標をちょっとづつずらし て、流れてるように見せる)わけだけど、これを『見える』状態でやるとマヌケだから、『見えない』状態で描きこんでおいて、描き終わったら『見える』よう にするというわけ。キャラクタを動かすときなんかも、描き終わってから『見える』ようにしないと画面がチラチラしてしょうがないみたいね。

ここで納得。文字を動かすプログラムも、「描く」→「見えない部分で消す」→「位置を動かして描く」→「見えるようにする」ってやれば、きれいに動いたんだ。

STICK (代入先変数),(非トリガータイプキー指定)
(よく使われる)キーが押されてるか押されてないかをチェックし、押されてる場合はそのキーに対応する数値を変数へ代入。

キャラクタを動かすときなんかは「押してる間だけ動く」っていう感じにしたいわけだから、非トリガータイプキーとして指定するらしい。まぁ今回は「プログラムを停止したいときはESCキーを押す」っていうことをやりたかっただけだから、特に気にしなくてもいいみたい。

AWAIT (処理を待つ時間;単位1/10ms)
処理を一定時間中断する。

HSPの仕様なんだけど、こういう永久ループをさせるときは、必ずWAITかAWAITを入れなきゃだめなのだそうだ。でないと他の入力一切受け付けず、「応答なし」で止まっちゃう^^;

ちなみにAWAITは、前回中断した(AWAITした)時からの待ち時間ということになるそうな。つまり、ループの処理を常に一定に保つ効果があるわけだな。リアルタイムのゲームなんかには不可欠でしょ。ちなみに「 AWAIT 16 」だと、だいたい1秒間に60回くらいの処理速度になる。

さて、次はキャラクタを表示させて、その背景で星空をスクロールさせてみよう。いわゆる重ね合わせ処理って奴だな。