プチコンTips

プチコンTips 便利ルーチン

 ここではプチコンでプログラムを作る際に使える(かもしれない)便利なルーチンや便利な使用法を紹介していきます。変数名は自分で使っているプログラムとダブらないように適時変更して使用してください。(このページに記載のルーチンは自由に使ってもらって構いません)
 よく使うルーチンは「PETIT EDITOR」などのテキストエディタで入力してMEMリソースとしてセーブしておくと良いでしょう。それによって、ファンクションキー経由で簡単にプログラムに挿入できます。LOAD"MEM:(ファイル名)":KEY 5,MEM$とすることで、指定した行のところでF5を押せば挿入できる。ただし改行等は反映されるものの合計256文字分しか一度に入力できない)


《 入力系 》 《 出力系 》 《 その他 》

 「1行プログラム」コーナーにも便利なルーチンが多数あります。「ラベルスタート」「CHRセーバー」「簡易RGB指定プログラム」は便利なのでおすすめです。
 あと、「リスト短縮テクニック」コーナーにも便利なルーチンがありますので参考にしてみてください。


本体の開閉を瞬時にチェックするルーチン



FOR I=0TO 1:WAIT 1:GCLS:A=B:B=MAINCNTL:I=B-A==2:NEXT:RETURN


 本体を閉じて開けた場合にそれを瞬時(1フレーム)で取得可能にするルーチンです(TIME$を使った場合には1〜2秒かかってしまう)。スリープしている時間は必要ないけど開閉が行われたかどうかを瞬時に取得したい場合に便利です。
 原理は簡単でスリープ復帰直後の微妙な処理落ちが起きるのを利用しているだけです。意図的に処理落ちをさせるため最低でも0.1フレーム程度の何らかのウェイトを入れる必要があります。ここではウェイトのためGCLSをループ内に入れていますがGCLSが使えないプログラムではここにループなど(FOR〜NEXTループならば80回以上)を入れてください。

↑TOP


スリープ時間取得ルーチン



@SLEEP
T$=TIME$:FOR A=0TO 1:A=MAINCNTL*(T$!=TIME$):NEXT:TMREAD(T$),H,M,S:X=H*3600+M*60+S:?"READY"
FOR U=0TO 1:FOR T=0TO 1:B=MAINCNTL:TMREAD(TIME$),H,M,S:Y=H*3600+M*60+S:T=(Y-X)*60-60-B+A
WAIT 1:NEXT:A=B:X=Y-1:U=T>2:NEXT:RETURN
 QRコード(ファイル名:OCHASLEP) ver.1.2
 ※QRコードには簡単な動作用サンプルプログラムを含んでいます。
 本体を適当に開け閉めを行えば閉じていた時間が何秒かを表示します。バッテリー消費のほとんどないストップウォッチとして使うことができます。(瞬時にスリープに入るわけではないので測定誤差はそれなりに出てしまいますが)


 スリープ時間を1フレーム単位で取得できるルーチンです。(2フレーム以上、524287フレーム以下の時間に対応)
 GOSUB @SLEEPを実行してスリープ状態(本体を閉じた状態)にして本体を開けば変数Tにスリープ時間のフレーム数を返します。計測開始や計測結果が出るまで最大で1秒かかります。(計測開始が可能になったら画面に「READY」と表示されるようにしているけどこれは組み込むプログラムに合わせて変更すると良い)
 MAINCNTLTIME$のみで判断しているためプチコンを連続で145分以上稼働した場合や日付を跨いでスリープした場合には測定ができません。必要ならばMAINCNTHDATE$を併用するように改造してください。(このルーチンの初期化処理を行ってスリープに入るまでの間にMAINCNTLが524227を越えてなければ、つまり約145分のラスト1秒を除けば145分以上連続稼働しておいても大丈夫)

 計測開始までの最大1秒が待てないという場合はこのルーチンの1行目(初期化処理部分)をこのルーチンを使用するプログラムの適当な場所に移動してあらかじめ1回実行しておけばすぐに計測開始が可能になりますが、MAINCNTLは正確に1/60秒単位でインクリメントしているわけではない(1秒≒59.8フレーム)ので初期化してからスリープに入るまでの経過時間の0.3%くらいの計測誤差がでます。
 そのため即時性が要求されるプログラムでは2行目と3行目の間にメインルーチンを挟む形(1行目以外を常に実行する)にして常時TIME$とMAINCNTLのずれを監視しておくと良いです。それでも結果が出るまでの最大1秒の待ち時間は無くすことができないため使えるものは限られてくるかもしれません。

 ※上記のプログラムで1秒以上のスリープを行った場合には正確な時間を取得できないことがあるというバグが見つかりました。このリストだと修正では対応ができないため根本部分から作り直す必要がありますが、スリープから復帰直後はMAINCNTLの動作が不安定であるというプチコンの仕様のため正確な時間を計測するルーチンを作るのは難航しています。(本体の開閉を瞬時にチェックするルーチンではスリープ復帰直後の不安定さを逆に利用している)

↑TOP


「ハンドルコントローラー」ルーチン



@HANDLE
GPAGE 1:GCLS:R=64
PT=T:T=TCHST:TX=TCHX:TY=TCHY
PA=AN:AN=ATAN(TX-R,TY-R)
SA=SGN(A)
A=A+(AN-PA)*PT-!T*SA/64
IF ABS(A)>1.57THEN A=SA*1.57
CS=COS(A)*R:SN=SIN(A)*R
GCIRCLE R,R,R,15
GLINE R-CS,R+SN,R+CS,R-SN,15
GLINE R,R,R+SN,R+CS,15
RETURN
 QRコード(ファイル名:OCHAHNDL)

 レースゲームなどで使える「ハンドル型のコントローラー」を擬似的に実現するルーチンです。
 変数Rにハンドルの半径(96以下の値)を入れます。ハンドルを左右に回すことで変数Aにハンドルを回している角度がラジアンで入ります。Aの範囲は-1.57+1.57(=-π/2〜π/2、言い換えると-90度〜+90度)となっています。この値を使ってキャラを移動させれば十字ボタンのように0か1といったデジタル的なものではなくアナログ的な操作が可能になります。
 ちなみにハンドルは円内であればどこを基準に回転させても同様の操作が可能なので外周に拘らず自由なホームポジションを選択できます。また、画面から手を離すことで自動的にハンドルは元の位置(0度の位置)に戻ります。
 具体的な使用方法はQRコードに含まれるサンプルプログラムを参考にしてみてください。サンプルプログラムではハンドルの回転角度は「度」に変換して表示しています。[A]ボタンを押すと加速、離すと減速になります。
 また、このハンドルコントローラールーチンをPETIT RUN mkIIに組み込んだものをこちらに用意しました。具体的な組み込み方の参考にしてみてください。

↑TOP


「アナログPAD」ルーチン



@PAD
GPAGE 1:GCLS:R=64
M=R/2:T=TCHST/M
X=(TCHX-R)*T:Y=(TCHY-R)*T
S=SQR(X*X+Y*Y)
IF S>1 THEN X=X/S:Y=Y/S
GCIRCLE R,R,R,15
GCIRCLE R+X*M,R+Y*M,M,2
RETURN
 QRコード(ファイル名:OCHAAPAD)

 3DSのスライドパッドのようなアナログPAD機能を擬似的に実現するルーチンです。
 変数RにPADの半径(96以下の値)を入れます。赤い丸をタッチで操作することで変数X変数Y-11の値が入ります。この値を使ってキャラを移動させれば十字ボタンのように0か1といったデジタル的なものではなくアナログ的な操作が可能になります。
 具体的な使用方法はQRコードに含まれるサンプルプログラムを参考にしてみてください。

↑TOP


一時停止機能を付ける



IF BTRIG()AND 1024THEN BEEP 7WAIT 1FOR I=1TO BTRIG()<1024I=0NEXT

 ゲーム等で一時停止(ポーズ)を行うことができるルーチンです。ポーズは設定、解除ともに[START]ボタンで可能です。
 なお、このルーチンは60fpsで動作しているプログラムでの動作を前提としています。60fps以外ではBTRIG関数が正常動作しないためボタン連射入力を参考にしてBTRIGの代用を行ってください。FOR I=1TO BTRIG()<1024のBTRIGにおいては代用ルーチンは不要)

↑TOP


簡易マルチタッチ検出



LR=TCHST*51%((0OR(TCHX-86)/86)+6)*256

 DSのタッチパネルはマルチタッチに対応していませんが、簡易マルチタッチ検出によって下画面を[L][R]ボタンの代わりに使えるかもしれないというルーチンです。(左右同時入力ができるので[L][R]ボタンが壊れてしまっているという人の代用に使えるかも)
 画面の左側(F1キーの下付近)と右側(F5キーの下付近)を使い左をタッチすれば256、右をタッチすれば512、左右両方をタッチしている場合には768が変数LRに入るというだけのものです。BUTTON関数の値が変数Bに入っているならばB=B OR LRとすれば[L][R]ボタンと共存できます。
 タッチしている座標そのものを求めるルーチンはこちらの方で公開しています。リンク先の詳細を読んで分かるようにマルチタッチで座標検出する場合にはマルチタッチか否かを検出するのはそこまで難しくないものの誤差を減らすのが非常に難しいです。しかし、このように「左と右だけの検出」のようなおおざっぱな場所を検出するだけならば簡単にできます。

↑TOP


ボタン連射入力



◎BTRIGの代用ルーチン
B=BUTTON()C=B-(A AND B)A=B
◎[A]ボタン専用ルーチン
B=BUTTON()C=16AND!A*B:A=16AND B

 mkIIではBUTTON関数1,2,3のオプション設定ができるようになったりBTRIG関数(=BUTTON(1)と同等なもの)が加わりました。しかし、第2回プチコン講座のコラムで書いているように常時60fpsで動作してないと入力の取りこぼしが出てしまいます。このルーチンでは、変数CにBTRIG相当の値が入ります。
 連射(一度ボタンを離さないとボタン入力判定がない)[A]ボタンだけしか使用しないならばC AND 16が別途不要な分だけ「[A]ボタン専用ルーチン」の方が短くなります。

 また、B=BTRIG():WAIT 6B=0:FOR I=1TO 6:B=B OR BTRIG():WAIT 1:NEXT に置き換えることが可能です。この場合の前者を上記の代用ルーチンで置き換えた場合にはボタン入力判定は離して押す必要があるため2回の実行(12フレームの時間)が必要ですが、後者のように1フレーム間隔で読み出せば1回の実行(6フレームの時間)で済むためボタンの反応は良くなっています。WAITを入れておらず6フレームの時間がかかる処理の場合はメインルーチン内で1フレームにつき1回のBTRIG入力を行えば後者と同様のことが行えます。

↑TOP


INKEY$バッファクリア



FOR I=0TO""<INKEY$()I=0NEXT

 これによって、INKEY$のバッファをクリアできます。
 キーボードから入力した文字はバッファに蓄積されているため処理落ち時にもINKEY$は有効に働いているのですが、このバッファのためプログラムによっては誤動作の原因になる(例えば何かキーを押すまで待つという処理をする場合にも事前にキーを押していたらそのバッファから読み込んでしまうため入力待ちがスルーされてしまう)ことがあります。それを防ぐにはINKEY$を実行してバッファが空になるまでループを繰り返すという処理が必要になります。
 その、処理方法はいくつかありますが、恐らくこのルーチンが一番短いのではないかと思われます。

↑TOP


[SELECT]ボタンの取得



IF BUTTON()AND 2048THEN @DEBUG
[SELECT]ボタンを押したらラベル@DEBUGへジャンプする場合

 BUTTON関数では[SELECT]ボタンは2048が割り当てられているためこれで[SELECT]ボタンの取得ができます。
 ただし、通常時は[SELECT]ボタンを押した場合はプログラムを停止してしまうため取得はできません。停止するのは[SELECT]ボタンを押した瞬間であるため起動する前から押しておけば問題ありません。[SELECT]ボタンを離すまではずっと取得可能ですが、一旦離して再入力したら停止してしまうので実質プログラムの先頭でしか使えないでしょう。[SELECT]ボタンを押したらデバッグモードに行くなどの使い方をする場合には便利)
 ちなみに[SELECT]ボタンをプログラム起動前から押すのではなく起動した瞬間に押せば普段は見られないBREAK IN 0が表示されます。この場合はCONT命令によってコンティニュー不可となります。BTRIG()は押した瞬間しか取得できないためこのBREAK IN 0によって[SELECT]ボタンは取得できません。

↑TOP


円形フェードイン/フェードアウト



◎フェードイン
@C_IN
GPRIO 0:GCLS 14:WAIT 60
FOR I=1TO 32
 GCIRCLE 128,96,I*5,0
 FOR J=0TO 3
  A=J%2*2-1:B=(0OR J/2)*2-1
  GPAINT 128+A-A*I*4,96+B-B*I*3,0
 NEXT
 BEEP 2,I*30:WAIT 2
NEXT
RETURN

◎フェードアウト
@C_OUT
GPRIO 0:WAIT 60
FOR I=31TO 0STEP -1
 GCIRCLE 128,96,I*5,14
 FOR J=0TO 3
  A=J%2*2-1:B=(0OR J/2)*2-1
  GPAINT 128-A-A*I*4,96-B-B*I*3,14
 NEXT
 BEEP 2,I*30:WAIT 2
NEXT
RETURN
 QRコード(ファイル名:OCHAC_IN)
 ※QRコードにはフェードイン、フェードアウトの両方とそれを動作させるサンプルプログラムが含まれています。


 ロードランナーなどで使われているような円形で画面を徐々に表示したり、徐々に消していったりするルーチンです。GRP面でマスクしている関係上、ゲーム画面にGRPを使っているものや優先順位0のスプライトを使っているものには使用ができません。
 フェードイン、フェードアウトは共通部分が多いため両方使いたい人は1つのルーチンとしてまとめてもいいかもしれません。

↑TOP


GRP2軸回転ルーチン



@3DROTATE
SN=SIN(RAD(PA))
CS=COS(RAD(PA))
SI=SIN(RAD(CA))
CO=COS(RAD(CA))
TA=TAN(RAD(90-CA))
MY=191
FOR I=1 TO PY
 DW=CL/I/RS-TA
 DX=CL/(DW+!DW)
 DY=DX*SQR(1+TA*TA)
 IF DY<0OR DY>512THEN DY=512
 XR=(CL*SI+DX*TA)/CL*RS
 AX=X+SN*DY:AY=Y-CS*DY
 BX=CS*XR:BY=SN*XR
 CX=AX-BX*PX/2:CY=AY-BY*PX/2
 FOR MX=0 TO (PX-1)*KX STEP KX
  GPAGE 1:G=GSPOIT(CX,CY)
  GPAGE 0,C,2-C
  GFILL MX,MY,MX+KX,MY-KX,G*(G>0)
  CX=CX+BX:CY=CY+BY
 NEXT
 MY=MY-KX
NEXT
RETURN
 QRコード(ファイル名:OCHA2ROT)

 下画面にGRPによる画像を用意して、それを2軸回転することで疑似3D表示を行えるプログラムです。QRコードには実際に使うためのサンプルプログラムを含んでいます。
 このサンプルプログラムの使い方やこのルーチンの解説については別途ページを作りましたのでこちらをご覧ください。

↑TOP


魚眼レンズ風フィルタ



@FISH
FOR I=-R+0.5TO R-0.5
 A=0OR SQR(R*R-I*I)
 FOR J=-A TO A
  B=0OR SQR(A*A-J*J)
  C=0OR SQR(R*R-I*I-J*J)
  X=ATAN(B,J)/PI()*(PX-1)
  Y=ATAN(C,I)/PI()*PY
  GPAGE 1:G=GSPOIT(X+0.5,Y)
  GPAGE 0:GPSET 128-J,96-I,G
 NEXT
NEXT
RETURN
 QRコード(ファイル名:OCHAFISH)

 下画面にGRPによる画像を用意するとそれを円形の全周魚眼のように変形させることができます。PXPYが下画面に表示する画像のサイズでRが上画面に表示する円形の半径となります。(四角形を円形に変形している関係上、実際の魚眼レンズのように中心からの距離と角度が比例したものになってないためあくまで魚眼レンズ風として考えてください)
 ちなみに作例はこんな感じですが、この作例(QRコードでのリストに入っているGRP:OCHAKON)のGRPデータはこちらに置いてあります。

 この魚眼レンズ風フィルタを使って「簡易地球儀」とそれを改良した「簡易地球儀2」を作ってみましたので参考にしてみてください。なお地球儀には256x192の正距円筒図法の地図画像が必要ですが、こちらに用意したので活用してください。

↑TOP


3D座標をスクリーン座標に変換



SX=128+X/Z*10:SY=96-Y/Z*10

 プレイヤーの座標が三次元空間において原点、つまり、座標(0,0,0)にある場合にそこから見たとき(プレイヤーの座標=カメラ座標のとき)座標(X,Y,Z)にある物体(オブジェクト)を画面上の座標(SX,SY)に変換する式です。(ここでのZ座標は奥行き側の座標であり、この3D座標というのは一般的には「ワールド座標」や「グローバル座標」と呼ばれている)
 プレイヤー(カメラ)が原点になく(PX,PY,PZ)という座標にある場合は、そこから見える物体の座標はSX=128+(X-PX)/(Z-PZ)*10:SY=96-(Y-PY)/(Z-PZ)*10のように相対座標を計算すれば良いため難しくはありません。(プレイヤーの向きがZ軸のプラス方向でZ軸に対して平行になっている場合)
 プレイヤー(カメラ)はZ軸の正の方向を向いている場合の計算式であるためカメラより後方にある(Z座標が小さい)オブジェクトの座標は中心点に対して上下、左右逆になってしまいます。本来は見えないものであるためゲームでこの式を使う場合は後方にあるものは表示しないという処理が必要です。あとプレイヤー(カメラ)と映し出すオブジェクトZ座標が等しい場合は無限大になるためそうならないための処理が必要です。(現実的に考えてもZ座標の差分が0というのはカメラの内部を示すため理論上それを映し出すことは不可能であることが分かる)

 10という値はただの拡大率なので必要に応じて変えてください。ワールド座標における「1」という座標を画面上の何ドットにするのかを考えた上で設定します。(この拡大率は3D表示に用いる画面の縦横のドット数とカメラの画角を決めてしまえば三角関数を使って求められる)
 また12896という値は3D空間内でプレイヤーの正面向き無限遠に見えるものが画面のほぼ中心(128,96)になるように設定した場合のものです。水平線より低い高さに移動することがないゲームであれば96という数値は120〜160程度にしてもいいかもしれません。
 これはあくまでプレイヤー視点(一人称視点)における計算式なので斜め見下ろしタイプのゲームだとカメラ視点とプレイヤー視点が異なる(カメラは見下ろし視点なのにプレイヤーはZ軸に平行な正面向きの視点となっている)ためこの式では誤差が出てしまいますが疑似3Dならばそれほど問題はないでしょう。(斜め見下ろしにおいて正確に計算するならばカメラが原点にくるように平行移動した後に一次変換を行えばよい)

↑TOP


塗りつぶし三角形描画ルーチン



◎GLINE使用版
@TRI
SORT 0,3,X,Y:X=X(0):Y=Y(0):Z=Y:M=X(2)-X(0):Q=(Y(2)-Y(0))/(M+!M)
FOR J=0TO 1:M=X(J)-X(J+1):P=(Y(J)-Y(J+1))/(M+!M):Y=Y+(Y(1)-Y(0))*!M
FOR I=X(J)TO X(J+1)-1:GLINE I,Y,I,Z,C:Y=Y+P:Z=Z+Q:NEXT:NEXT:RETURN

◎GPAINT使用版
@TRI2
FOR J=0TO 1
FOR I=0TO 2:GLINE X(I),Y(I),X((I+1)%3),Y((I+1)%3),C*J+!J*255:NEXT
IF !J THEN GPAINT (X(0)+X(1)+X(2))/3+0.5,(Y(0)+Y(1)+Y(2))/3+0.5,C,255
NEXT:RETURN
 QRコード(ファイル名:OCHATRI) ※GLINE使用版とGPAINT使用版の両方が入っています

 塗りつぶした三角形を描画できます。2つのバージョンがありますが両者とも基本的には同じものです。(GPAINT使用版の方が速い場合が多いけど時々妙に遅くなるのでそれが足を引っ張って平均ではGLINE使用版の方が速い)
 変数X(0),Y(0)X(2),Y(2)に三角形の頂点の座標を入れて変数Cに描画色を入れてください。プチコンでポリゴン表示をしたい人は使ってみてください。ただし、画面上からランダムに3点選んで三角形を描画した時に1回描画するのに平均1.2フレームの時間がかかったので毎秒50ポリゴン程度と考えるとかなり厳しいですが。
 速度面では現状で限界に近いのですがGLINE使用版は縦長の三角形に最適化してあるため横長の三角形に最適化したルーチンを別途作り、縦長か横長かを事前に判断(3点を並び替えた時の最大と最小の差がX方向、Y方向でどちらが大きいかを判断)を行えばさらに高速化は可能です。

 GPAINT使用版の方は境界線にGCOLOR 255を使用している関係上、その色は描画に使用できません。あと極端に細長い三角形はGPAINTの仕様上塗りつぶすことができません。また高さがほぼ0の超細長い三角形の場合は誤差によって三角形の外部を塗りつぶしてしまう場合があります。(GLINE使用版の方はこのような問題はない)

 《 追 記 》
 上記のGLINE使用版をさらに高速化してみました。

◎GLINE使用版 高速バージョン
@TRI
SORT 0,3,X,Y:P=X(2)-X(0)SORT 0,3,Y,X:Q=Y(2)-Y(0)IF Q>P THEN @T
X=X(0)Y=Y(0)M=Y-Y(2)Q=(X(0)-X(2))/(M+!M)Z=X
FOR J=0TO 1M=Y(J)-Y(J+1)P=(X(J)-X(J+1))/(M+!M)X=X+(X(1)-X(0))*!M:A=Y(J)B=Y(J+1)-1
FOR I=A TO B:GLINE X,I,Z,I,C:X=X+P:Z=Z+Q:NEXT:NEXT:RETURN
@T
SORT 0,3,X,Y:X=X(0)Y=Y(0)M=X-X(2)Q=(Y(0)-Y(2))/(M+!M)Z=Y
FOR J=0TO 1M=X(J)-X(J+1)P=(Y(J)-Y(J+1))/(M+!M)Y=Y+(Y(1)-Y(0))*!M:A=X(J)B=X(J+1)-1
FOR I=A TO B:GLINE I,Y,I,Z,C:Y=Y+P:Z=Z+Q:NEXT:NEXT:RETURN
 QRコード(ファイル名:OCHATRIH)

 縦長の三角形に最適化しているルーチンと横長の三角形に最適化しているルーチンの両方を含み最初の段階でどちらかを判断して高速な方を選択するようにしています。それ以外にも細かい部分で処理の高速化を行っています。(このリストを使ってポリゴン表示プログラムも作ってみました)
 これによって、全画面からランダムに座標を選んで三角形を表示した場合、上記の1.24フレームから0.72フレームへと1.7倍高速化できました。ちなみに3行目のIF文ににあるQ>PP>Qに変更すると少し遅くなりますが、細長い三角形をよりキレイに表示できるようになります。この状態でも平均1.16フレームなのでデフォのルーチンよりも速いです。
 実際にゲーム等で使う場合は三角形を全画面のサイズで表示する機会はあまりなくせいぜい画面半分程度のサイズに収まると仮定し、その状態でランダムに3点を選んで三角形を表示したら平均0.36フレームという速度を得ることができました。この状態でさらに1ドット単位ではなく2ドット単位で描画すれば精度は下がりますが、平均0.24フレーム(250ポリゴン/秒)まで高速化可能です。

↑TOP


塗りつぶし円(楕円)描画ルーチン



◎GLINE使用版 ※正円と楕円両対応
FOR I=-R TO R:A=F*SQR(R*R-I*I):GLINE X+I,Y-A,X+I,Y+A,C:NEXT

◎GPAINT使用版 ※正円専用
GCIRCLE X,Y,R,255:GPAINT X,Y,C,255:GCIRCLE X,Y,R,C

 塗りつぶした円を描画できます。縦横比を変えることで楕円にもできます。
 変数X、Yに中心の座標、変数RにX座標半径、変数Cに描画色、変数Fに縦横比(F=1が正円)を入れて実行するだけです。正円をメインに使いF=1とするのが面倒ならばFORの前にF=F+!Fを入れておけばFの値が0の時は自動的にF=1にしてくれます。
 横長の楕円を表示する機会が多いという場合は縦横比を入れ替えてGLINE X-A,Y+I,X+A,Y+I,Cとすれば比率の2乗分くらい高速化できます。

 GPAINTを用いた正円専用版は境界線にGCOLOR 255を使用している関係上、その色は描画に使用できません。(このルーチンは円の下に別のものが描画されていてもそれに影響されることなく塗りつぶし円が描画できるルーチンなので円の下に何も描画されていないならば境界線を分けたりする必要なく普通に描画色で円や楕円を描いてそれをGPAINTで塗りつぶせばいい)

↑TOP


擬似的な色の重ね合わせを行う



TM=150:COL=2:GPRIO 0:FOR I=1 TO TM*2:GCLS I%2*COL:WAIT 1:NEXT

 擬似的に画面全体を特定の色を重ねることが可能です。
 変数TMに重ねる時間(1/30秒単位)変数COLに重ねる色(GRPのパレット色)を指定して実行するだけです。(上記サンプルでは5秒間赤色を重ねる)
 原理すごく単純で1フレームごとに特定色の表示と消去を繰り返しているだけです。60fpsで動作しているゲームならばこのルーチンと同様に1フレームごとに特定色の表示と消去を繰り返せば色を変えた状態でキャラを動かしたりできます。(ただし、偶数フレームで終了させないと色が表示されたままになるので注意)

 注意点としては、表示と消去を繰り返しているわけなので表示がちらついて見えてしまうということと描画にGRPを使っているゲームでは使用できないということとスプライトを使用する場合は優先順位を「0」以外にするということがあります。この点が気になる人やさらに表現力の高い重ね合わせをしたい人は「画面に任意の色の重ね合わせを行う」ルーチンを使用してください。(このルーチンでは不透明度50%固定だけど任意の割合で重ね合わせができるし、乗算やスクリーンといった通常ではできない重ね合わせができるしパレットそのものを書き換えているので重ね合わせでちらつかないというメリットがある)

↑TOP


画面に任意の色の重ね合わせを行う



@BLENDCOL
FOR J=0 TO 2
 RS$(J)=MID$("GRSPBG",J*2,2)+"P"*!J
 RS$=RS$(J):GOSUB @COLREAD
 COL(J)=VAL("&H"+MID$(COL$,J*2,2))
NEXT
COR=COL(0):COG=COL(1):COB=COL(2)

FOR J=0TO 2
 RS$=RS$(J)
 ON BM GOSUB @BMNORMAL,@BMMULTI,@BMSCREEN
NEXT
RETURN

@BMNORMAL
FOR I=0 TO 255
 CR=CR(I,J)+(COR-CR(I,J))*OP
 CG=CG(I,J)+(COG-CG(I,J))*OP
 CB=CB(I,J)+(COB-CB(I,J))*OP
 COLSET RS$,I,HEX$(CR,2)+HEX$(CG,2)+HEX$(CB,2)
NEXT
RETURN

@BMMULTI
FOR I=0 TO 255
 CR=CR(I,J)-(255-COR)*OP:CR=CR*(CR>0)
 CG=CG(I,J)-(255-COG)*OP:CG=CG*(CG>0)
 CB=CB(I,J)-(255-COB)*OP:CB=CB*(CB>0)
 COLSET RS$,I,HEX$(CR,2)+HEX$(CG,2)+HEX$(CB,2)
NEXT
RETURN

@BMSCREEN
FOR I=0 TO 255
 CR=CR(I,J)+COR*OP:IF CR>255 THEN CR=255
 CG=CG(I,J)+COG*OP:IF CG>255 THEN CG=255
 CB=CB(I,J)+COB*OP:IF CB>255 THEN CB=255
 COLSET RS$,I,HEX$(CR,2)+HEX$(CG,2)+HEX$(CB,2)
NEXT
RETURN

@COLREAD
FOR I=0 TO 255
 COLREAD(RS$,I),CR(I,J),CG(I,J),CB(I,J)
NEXT
RETURN

@COLINIT
FOR J=0TO 2:COLINIT RS$(J):NEXT
RETURN
 QRコード(ファイル名:OCHABLND)

 表示しているGRP面、スプライト、BG面全体に任意の色を重ね合わせるルーチンです。(上画面専用)
 例えば、夕暮れや海の中にいるような感じに色を変えることが可能になります。

 事前にDIM CR(256,3),CG(256,3),CB(256,3)としておきます。そして、変数COL$6桁の16進数色情報(COLSET命令と同じもの)を入れて、変数OPに不透明度(「0」が0%、「1」が100%となるように小数で指定)変数BMはブレンドモードなので0〜2を指定(0が「通常」、1が「乗算」、2が「スクリーン」)して、GOSUB @BLENDCOLとすれば全体の色がそのパラメータ通りに変わります。
 なお、このルーチンを実行した場合には色が変わったままなので複数の設定で多重の重ね合わせ処理が可能なのですが、色を元に戻したい場合はGOSUB @COLINITとすれば初期化されます。ACLSを使っても初期化できますが、ACLSが使えない場合にご利用下さい。(初期化Tips参照)

 「通常」はそのまま普通に重なった色になるのに対して、「乗算」は重ねた分だけ暗く(白からの差分だけ暗く)なり、スクリーンは重ねた分だけ明るく(黒からの差分だけ明るく)なります。
 例えば、赤(FF0000)、緑(00FF00)、青(0000FF)の3つの色を使った絵があるとしてこれにオレンジ色(FF8000)を不透明度100%で重ねるとブレンドモードが「通常」の場合は(不透明度100%では下が全く透けないため)全体がオレンジ色になるのですが、「乗算」の場合は赤は黒(000000)、緑は濃い緑(008000)、に変わり(赤は赤のまま)、「スクリーン」の場合は青はピンク(FF80FF)になり、緑は黄色(FFFF00)、赤はオレンジ(FF8000)に変わります。重ねる色や不透明度によって変わってくるのでいろいろ変えて試してみてください。Photoshopなどの画像処理ソフトを使った経験があれば難しいことは何もないでしょう。
 フェードイン/フェードアウトルーチンのように少しずつ不透明度を変化させれば段階的に色を変えることが可能です。(すでにフェードイン/フェードアウトルーチンを導入の人はサブルーチン@COLREADは共通であるためAPPENDした際は削ってください)

 このルーチンをゲームで使用する場合には文字の色も変わってしまうためゲーム内で使う文字のみ色を変えないようにする措置をした方がいいかもしれません。変えるパレット番号は各ブレンドモードのサブルーチン(@BMNORMAL、@BMMULTI、@BMSCREEN)のループカウンタの値(変数Iの値)となっています。

◎用途別設定例
夕暮れ
COL$="FF8000"
OP=0.5
BM=2
深夜
COL$="004080"
OP=0.6
BM=1
海中
COL$="00C0FF"
OP=0.4
BM=0
COL$="FFFFFF"
OP=0.4
BM=0
 ※何だかイメージと違うという人は適当にパラメータを変えて試してください

↑TOP


フェードイン/フェードアウト



@FADEIN
FOR K=1 TO ST
 FOR J=0 TO 2
  RS$=RS$(J)
  FOR I=0 TO 255
   COLSET RS$,I,HEX$(CR(I,J)*K/ST,2)+HEX$(CG(I,J)*K/ST,2)+HEX$(CB(I,J)*K/ST,2)
  NEXT
 NEXT
NEXT
RETURN

@FADEOUT
FOR J=0 TO 2
 RS$(J)=MID$("GRSPBG",J*2,2)+"P"*!J
 RS$=RS$(J):GOSUB @COLREAD
NEXT
FOR K=1 TO ST
 FOR J=0 TO 2
  RS$=RS$(J)
  FOR I=0 TO 255
   COLSET RS$,I,HEX$(CR(I,J)*(ST-K)/ST,2)+HEX$(CG(I,J)*(ST-K)/ST,2)+HEX$(CB(I,J)*(ST-K)/ST,2)
  NEXT
 NEXT
NEXT
RETURN

@COLREAD
FOR I=0 TO 255
COLREAD(RS$,I),CR(I,J),CG(I,J),CB(I,J)
NEXT
RETURN
 QRコード(ファイル名:OCHAFADE)

 表示画面のフェードイン、フェードアウトをするルーチンです。(上画面専用)
 事前にDIM CR(256,3),CG(256,3),CB(256,3)としておきます。変数STに切り替わる段階の値を入れ(1〜任意の整数)GOSUB @FADEINでフェードイン、GOSUB @FADEOUTでフェードアウトとなります。

 STの値を大きくすれば細かく明るさは変化するのですが、速度面の問題からSTに入れる値は4〜10くらいが妥当ではないかと思われます。GRP(グラフィック)面、スプライト、BG面のすべての面においてすべてのパレットの色を変えているためゲームで使用しているのがGRPのみとかであれば他の部分のパレット変更処理を行わなくすることで切り替え速度を高速化できます。(フェードイン/フェードアウト両ルーチンにおいてFOR K=1 TO STの次にあるFOR J=0 TO 2FOR J=0 TO 0に変えればGRP専用になるけど3倍くらい速くなる)
 また、このルーチンは必ずフェードアウトルーチンの方を先に使ってください。スタート時にフェードインから実行したい場合はVISIBLE命令を使い特定画面を非表示にして、その間にフェードアウトを実行すれば良いでしょう。

↑TOP


縦持ち用フォント表示ルーチン



@TPRINT
LN=LEN(MES$)
L1=INSTR(MES$,",")
L2=INSTR(MES$,":")
X=11.5-FR*11.5+VAL(MES$)*FR
Y=15.5+FR*15.5-VAL(RIGHT$(MES$,LN-L1-1))*FR
FOR I=L2+1 TO LN-1
 Z$=MID$(MES$,I,1)
 IF!CP THEN COLOR COL:LOCATE Y,X:?Z$;
 IF CP THEN PNLSTR Y,X,Z$,COL
 X=X+FR
 IF X>23THEN X=0 :Y=Y-FR
 IF X<0 THEN X=23:Y=Y-FR
NEXT
RETURN

@FROTATE
FR=-1:Z=3.5
GPAGE 1
FOR J=0 TO 1
 FOR I=0 TO 255
  GCLS:C$=""
  GPUTCHR 0,0,"BGF"+"L"*!J,I,0,1
  FOR X=Z-FR*Z TO FR*Z+Z STEP FR
   FOR Y=FR*Z+Z TO Z-FR*Z STEP -FR
    C$=C$+HEX$(GSPOIT(X,Y))
   NEXT
  NEXT
  CHRSET "BGF"+"L"*!J,I,C$
 NEXT
NEXT
GCLS
RETURN
 QRコード(ファイル名:OCHATPRI)

 縦持ち用のフォントを手軽に表示するルーチンです。縦持ち用のフォントが用意できても表示する際には1文字ずつ座標を計算して表示しなくてはならないのですが、このルーチンを使うことで非常に簡単に表示が可能になります。
 まずは、GOSUB @FROTATEとして縦持ち用のフォントを生成します。ルーチン内にある変数FRの意味は縦持ち用フォント生成ルーチンと同じものです。このプログラムでは上下画面に対応した縦持ち用フォントの生成を行っています。(フォントの生成前にフォントの初期化を行った方が良いです)

 表示する際には変数MES$に表示座標と内容を入れてGOSUB @TPRINTとするだけで良いです。表示座標は縦持ちした時に左上が(0,0)、右下が(23.31)になるような見た目通りの座標を指定します。MES$="2,9:アイウエオ"で座標(2,9)に「アイウエオ」と表示します。なお、改行は自動的に行われますがスクロールは行わないので注意してください。
 座標が0の時は引数の省略も可能です。(5,0)のときはMES$="5,:アイウエオ"、(0,5)のときはMES$=",5:アイウエオ"、(0,0)のときはMES$=",:アイウエオ"とできます。また、MES$内に「,」「:」が含まれてない時はMES$="アイウエオ"で(0,0)を示します。
 色を変える場合には変数COLに色番号を入れてください。CP=0とすれば上画面、CP=1とすれば下画面に表示します。

 フォントを元に戻すにはCHRINIT"BGF":CHRINIT"BGFL"を実行するか、VRAMリセットコマンドを実行してください。(初期化Tips参照)

↑TOP


縦持ち用フォント生成ルーチン



@FROTATE
FR=1:Z=3.5
GPAGE 1
FOR I=0 TO 255
 GCLS:C$=""
 GPUTCHR 0,0,"BGF",I,0,1
 FOR X=Z-FR*Z TO FR*Z+Z STEP FR
  FOR Y=FR*Z+Z TO Z-FR*Z STEP -FR
   C$=C$+HEX$(GSPOIT(X,Y))
  NEXT
 NEXT
 CHRSET "BGF",I,C$
NEXT
GCLS
RETURN
 QRコード(ファイル名:OCHATATE)

 縦持ち用のフォントを生成します。(上画面専用)
 サブルーチン内にある変数FRの値が1ならば時計回り(十字ボタンを下にして持つ)FRの値が-1ならば半時計回り([A][B][X][Y]ボタンを下にして持つ)に90度回転したフォントを生成します。(このルーチンを2回実行すれば180度回転となり上下逆フォントが生成されるためこのルーチンを実行前にフォント初期化処理を行った方が良いかも)
 なお、GPAGE 1(下画面のGRP面)を作業用に使っているので都合が悪い場合は別のページを指定してください。(MID$を使って1ドット単位で入れ替えるという方法もあるけどこのルーチンのようにGRPを使った方が速くて短い)

 フォントを元に戻すにはCHRINIT"BGF"を実行するか、VRAMリセットコマンドを実行してください。(初期化Tips参照)

↑TOP


ラスタースクロール



CY=2:SC=30:SP=2
GPAGE 0
FOR J=0 TO 9999
 FOR I=0 TO 191
  A=SIN((I+J*SP)*CY/30.56)*SC
  GCOPY 1,A,I,A+255,I,0,I,1
 NEXT
NEXT
 QRコード(ファイル名:OCHARAST)

 GRP面をラスタースクロールのようにサインカーブで歪ませるルーチンです。
 あらかじめGPAGE 1に適当な画像を用意しておいて、変数CYに周期、変数SCに振幅、変数SPにスクロールスピードを入れて実行すればラスタースクロールします。
 ちなみにSIN関数の計算部分にある30.56という数字は(360/192)*(PI()/180)を計算したもので画面の上下を360度(sin関数の1周期分)と見なす処理と度をラジアンに変換する計算を簡略化したものです。(最初からラジアンで考えて周期が2πだから2*PI()/192とした方が分かりやすいけど)

 これを応用したサンプルプログラムを作ったので参考にしてみてください。

旅の扉風 ラスタースクロール
旅の扉風の演出で2枚の画像を入れ替える
テキストラスタースクロール
コンソールキャラを書き換えてラスタースクロールさせる
神風の術
ラスタースクロールを使った簡単なお遊び

↑TOP


ダブルバッファリング



GPAGE 0,P,!P:P=!P
※ページ0とページ1を使用し、上画面に表示する場合

 グラフィック(GRP)面に重い描画をする場合に描いている途中の描画が見えてしまい画面が激しくちらついてしまうことがあります。その場合は、描画画面と表示画面を交互に切り替えることで描画のちらつきを無くすことが可能になります。(※mkIIではGRPが4ページ分確保され表示していない別画面に描画できるようになった)
 !Pこちらに書いている変数Pの値が「0ならば1」に「1ならば0」にしてくれるためP=!Pとするだけで0と1を繰り返してくれます。ページ0とページ2を使用する場合はGPAGE 0,P*2,!P*2:P=!PもしくはGPAGE 0,P,2-P:P=2-Pとすればよいです。

↑TOP


FPS表示を付ける



◎TIME$ 使用版
FPS=FPS+1IF TIME$!=T$THEN LOCATE 0,0?"FPS"FPS,:FPS=0T$=TIME$
◎MAINCNTL 使用版
FPS=FPS+1IF MAINCNTL-CNT>59THEN LOCATE 0,0?"FPS"FPS,:FPS=0CNT=MAINCNTL

   どちらも同様の動作をしますがMAINCNTL使用版の方は最初にCLEARもしくはCNT=0を実行しておいてください。TIME$使用版の方はリストが短く初期設定も不要ですが、処理時間はTIME$使用版が1回当たり16.41ミリフレーム(1ミリフレーム=1/6000秒)、MAINCNTL使用版は1回あたり2.46ミリフレームとなっていてMAINCNTL使用版の方が6倍以上速いのでお好きな方を使ってください。(これだけの大差となっているのはTIME$を1回取得するのに約12ミリフレームの時間がかかるため)

 画面左上に1秒ごとにFPS(プログラムの実行速度)を表示します。最初の表示は不正確なので2回目以降の数字(開始2秒後以降の数字)を見てください。
 ちなみにこれは小数点以下は切り上げで表示(FPS10ならば9〜10の間を示す)しています。切り捨ての場合はFPS=0FPS=-1にしてください。(この改造をしたらFPS10ならば10〜11の間を示すようになる)
 小数点以下四捨五入を希望する場合はIF MAINCNTL-CNT>119THEN 〜 のようにして2秒に1回の計測にして0OR FPS/2-0.5のように2で割って四捨五入表示すればOKです。+0.5ではなく-0.5としているのは標準では切り上げとなっているためなのでFPS=-1として切り捨て表示にすでにしている場合は-0.5ではなく+0.5とする

↑TOP


任意の周波数の音を出す



◎直接指定版
N=LOG(F)*17.313-36.378BGMPLAY"T1@D"+STR$(0OR N%1*64)+"N"+STR$(0OR N)
◎MML内変数使用版
N=LOG(F)*17.313-32.378BGMPLAY"T1@D$0N$1"BGMSETV 0,0,N%1*64BGMSETV 0,1,N

 BGMPLAY命令で任意の周波数の音が出せるルーチンです。変数Fに出力する音の周波数(Hz)を入れておいてください。
 2つのルーチンは同一なものなのでお好きな方を使用してください。「直接指定版」の方が短いですが、「MML内変数使用版」の方はMMLT1@D$0N$1BGMSET命令で定義すればトラック0以外で使用することができます。(「MML内変数使用版」の方はMML内変数を使ってNコマンドの値を指定するとなぜか半音4つ分ずれてしまうためその補正も行っている)
 ちなみにNの値を求めている式はLOG(F)/LOG(POW(2,1/12))-LOG(440)/LOG(POW(2,1/12))+69を最適化したものです。あとこのルーチンを使って救急車のサイレンの音踏切の警告音のドップラー効果も再現してみたので良かったら参考にしてみてください。

↑TOP


10の524287乗まで計算できるべき乗ルーチン



E=LOG(A)*B/2.3026D=0OR E:C=POW(10,E-D)

 プチコンは固定小数点で最大524287しか扱えないためべき乗計算ではOverflowになりやすいです。そこで、それを大きく越えるべき乗計算ができるルーチンが有用になる場合があります。
 変数A、Bに計算式の値を入れると変数C、Dに計算結果を返します。このルーチンはLOGで桁数を求めているだけというきわめて単純なアルゴリズムa^b=e^(log(a)*b) ※ここでのeは自然対数の底 )ですが唯一特筆すべき点といえばプチコンの演算精度をギリギリまで高めているということです。
 例えば 123.456^987.654 を計算する場合、A=123.456:B=987.654 としてこのルーチンの後に ?A"^"B"="C"*10^"D" を置けば 123.456^987.654=4.834*10^2065 と表示します。これはWindows付属の関数電卓で計算すると 4.9022108458016892212866000586939e+2065 となっており、誤差は1.4%程度ありますが、プチコンでは定数そのものに1〜2%程度の誤差が出ることもあるためかなり正確といえるでしょう。
 2.3026LOG(10)をプチコンで最も正確に記述した数値(プチコンでは小数第5位以下まで正確に計算してそれを切り上げで小数第4位まで記述するのが最も正確になる)であり、?LOG(10)で表示される値2.302のように小数第3位までしか記述しない場合には計算結果が 2.197*10^2065 になってしまい50%を大きく越える誤差になるということから小数第4位まで正確な値を記述するというのはここでは重要であることが分かります。もっともそのままLOG(10)と記述すればいいだけの話ですが、定数でおくことで短くかつ速くなります。

↑TOP


ATAN(アークタンジェント)からアークサイン、アークコサインを求める



◎アークサイン
ASIN=ATAN(X,SQR(1-X*X))

◎アークコサイン
ACOS=PI()/2-ATAN(X,SQR(1-X*X))

 プチコンには三角関数でよく使われるSIN、COS、TANは用意されていますがその逆関数としてはATAN(アークタンジェント)しか用意されていません。しかし、そのATANの値を使用することで、アークサインやアークコサインの値を求めることができます。
 これはアークサインやアークコサインはATAN(アークタンジェント)と同様に長さ(距離)から角度を求めるものです。
 例えば直交座標を極座標に変換する場合に使えます。極座標への変換は魚眼レンズ風フィルタ「簡易地球儀」を参考にしてみてください。(ただし、上記の式をそのまま使用しているわけではなくリスト短縮と高速化のため変形して使用している)
 また、三角形の3辺の長さが分かっているときに余弦定理からその間の角度を求めたりするのにも使えます。求めたい角に向かい合った辺の長さをA、残りの辺の長さをBとCとしたとき余弦定理によって求める角の大きさは(B*B+C*C-A*A)/(2*B*C)の値をとるCOS αから求めることができますが、このαを求めるためにはアークコサインが必要になります。X=(B*B+C*C-A*A)/(2*B*C)として上記の変換式を使えばACOSにその角度が弧度法(ラジアン値)で入ります。
 これ以外にも活用できる場面はあるので利用してみてください。(誤差の出やすい平方根とATANを組み合わせているため正確な値を要求するようなプログラムでは求める解答は得られないかもしれません)

↑TOP


再現性のある乱数



Y=(Y+0.1)%1.1X=(X*117+Y)%1

 プチコン標準のRND関数はシードを与えることができず、同じ乱数列を発生させたくてもできないためゲームで乱数を使うと毎回動作が異なるという問題が発生します。しかし、このルーチンを使えばそのような問題はありません。
 使用する場合には最初に変数X変数Yに0〜1の適当な初期値を入れておきます。そうすればこのルーチンを呼び出すごとに変数Xに0〜1の範囲の乱数値(厳密には0〜4095/4096)が入ります。このルーチンは線形合同法をベースにプチコンの演算精度の低さを利用して改良を加えた疑似乱数となっており、短く高速な上、周期が18452480と非常に長くなっています。
 例えばサイコロで使用するために1〜6の乱数としたい場合には6倍して1を足したものを整数化すると良いでしょう。(具体的には 0OR X*6+1 とする)

 詳しい解説や注意点についてはこちらを参照してください。

↑TOP


32bit整数演算変換ルーチン



◎固定小数点→32bit整数変換
@INT
Z=4096:IL=INT%(10000/Z)*Z:IH=0OR(INT-IL/Z)/10000*Z
INT$="-"*!IH*(INT<0)+STR$(ABS(IL)):INT$=STR$(IH)*!!IH+"0"*(4-LEN(INT$))*!!IH+INT$
RETURN
 QRコード(ファイル名:OCHAINT)

◎32bit整数→固定小数点変換 (上記の逆変換)
@INT2
Z=4096:IS=LEFT$(INT$,1)=="-"IN=LEN(INT$)-IS:INT$=RIGHT$(INT$,IN)IL=VAL(RIGHT$(INT$,4))/Z
INT=(1-IS*2)*(IL+VAL(LEFT$(INT$,(IN-4)*(IN>3)))/Z*10000
RETURN
 QRコード(ファイル名:OCHAINT2)

 プチコンでは固定小数点演算をしており、こちらに書いているように符号部1bit、整数部19bit、小数部12bitとなっているため±524287までの数しか扱えません。このルーチンはその小数部分を有効活用することで擬似的に符号付きの32bit整数演算を可能にするものです。
 このルーチンではあらかじめ4096で割った値を変数INTに入れることで符号付き32bit整数を変数INT$に返し±2147483647まで演算可能にしてくれます。例えば変数Aを32bit整数型にするとします。変数Aに1億を入れる場合はA=10000/4096*10000のようにして途中で524287を越えないように注意しながら4096で割るだけでよいのです。(1億を代入するのにわざわざ10000を2回に分けて掛けるのが面倒ならば1億を4096で割り、A=24414.0625としてもよい。このように数値を直接代入する場合は電卓で4096で割ったものを正確に計算して小数第5位以下を切り上げしたものを使用する。)
 あと、逆となる32bit整数から固定小数点に変換するルーチンも作りました。INT$"100000000"を入れてGOSUB @INT2とすればINT24414.0625という値を返してくれます。524288相当以上の整数値を頻繁に代入したい場合に便利です。すでに文字列に変換済みの32bit整数値同士で大小比較を行いたい場合も一旦この逆変換をすることで簡単にできるようになります。(※この逆変換ルーチンにおいてINT$には±2147483647ではなく、±2147483646までの整数値しか入れることはできません)

 ※プチコンでは私の解析結果より構文解析の際に小数第6位までは数値として読み取り6桁目を四捨五入して上で内部の変数用のメモリに格納する際に1/4096単位に丸めているため小数第5位まで正確に求めることには意義があります。

 あとは自由に四則演算が可能です。ただし、下記のような計算ルールがあります。見た目は複雑そうですが小数計算を行った場合に小数点の位置が変わる場合があるということを理解していれば難しいことは全くありません。

◎定数および固定小数点設定(デフォ)の変数の値を加減乗除する場合
加算、減算
4096で割ったもので加算(減算)を行う
(例)456を足す → A=A+456/4096
乗算、除算
そのまま乗算(除算)を行う
(例)789を掛ける → A=A*789
剰余(%)
4096で割ったもので計算を行う
(例)123で割った場合の剰余を求める → A=A%(123/4096)

◎32bit整数に設定している変数の値を加減乗除する場合
加算、減算
そのまま加算(減算)を行う
(例)32bit整数演算用の変数Bに456に相当する値が入っている場合
 456を足す →
A=A+B
乗算、除算
乗算を行うごとに4096を掛け除算を行うごとに4096で割る
※乗算の場合は変数同士の演算を行う前に4096を掛けてください
(例)32bit整数演算用の変数Bに789に相当する値が入っている場合
 789を掛ける →
A=A*4096*B 789で割る → A=A/B/4096
剰余(%)
そのまま計算を行う
(例)32bit整数演算用の変数Bに123に相当する値が入っている場合
 123で割ったときの剰余を求める
A=A%B
平方根
計算したあと64で割る
(例)32bit整数演算用の変数Bに5678に相当する値が入っている場合
 5678の平方根を求める
A=SQR(B)/64

 《 注意点 》
 上記の例はいずれも変数Aを32bit整数に設定した場合のものです。
 どの変数を固定小数点演算用として使っているか、整数演算用として使っているかはちゃんと把握しておく必要があります。
 32bit設定の変数同士の乗算を行い演算可能範囲内にも関わらずOverflowとなる場合には被乗数と乗数を入れ替えて計算をしてみてください。
 除算などで小数が発生する時には自動的にFLOOR関数と同一の処理が行われます。(3.5ならば3という値になり、-3.5ならば-4という値になる)


 このルーチンは1回あたり20.4ミリフレーム(1ミリフレーム=1/60000秒)ということで、1フレームに49回実行できますがあまり速いとも言えません。しかし、あくまでこのルーチンは最終的に数値を表示するときに32bit固定小数点の数値を32bit整数型に変換するだけのルーチンであり、普段は上記のルールに従って1/4096にするだけでいいため問題ありません。
 4096で割るだけならば1回あたり0.37ミリフレームであり、1000回実行しても約1/3フレームで済みます。したがって、多倍長演算のようなことを行うよりも速度面で非常に有利であり、四則演算がそのままできるため処理の自由度も非常に高いです。(4096という値は良く使うため変数に入れておくとよいかも。その際には1文字変数を使用すれば処理の低下も抑えられなおかつ4096を入力するより3バイト節約できる。上記のルーチンを変数名を変えずに使用する場合は最初にGOSUB@INTを実行しておけば自動的にZ=4096となる。)

《 使用例 》
A=12345/4096
B=A*56789
INT=B
GOSUB @INT
?INT$
12345×56789を行っているだけですが、これを実行すると701060205という答えを出してくれます。

おまけで32bit整数に設定している値を変数INTに入れることでHEX32$にそれを16進数で返すルーチンも作りました。(サイズ短縮を行っている関係上、負数には対応していません)
H$=HEX$(INT%1*4096):HEX32$=(HEX$(INT)+"0"*(3-LEN(H$)))*(INT>=1)+H$
パッケージ文字列を計算するときなどに使えるかもしれません。

↑TOP


じゃんけんの思考ルーチン



◎データベースの初期化
W=0FOR I=0TO 8Z(I%3,I/3)=33NEXT
◎データベースを元にコンピュータの手を選択
R=RND(99)Y=((R>=Z(X,0))+(R>=Z(X,0)+Z(X,1))+2)%3
◎データベースに蓄積
E=(X+1)%3F=(X+2)%3Z(W,E)=Z(W,E)/2Z(W,F)=Z(W,F)/2Z(W,X)=Z(W,X)+Z(W,E)+Z(W,F)

 プレイヤーのパターン分析を行うことでイカサマではなくそこそこ強いコンピュータ対戦型のじゃんけんゲームが作れます。
 変数Xがプレイヤーが出す手(グー=0、チョキ=1、パー=2)、変数Yがコンピュータの出す手、変数Wが前回プレイヤーが出した手を示しています。
 最初の使用時にデータベースの初期化を行います。使用時には「データベースを元にコンピュータの手を選択」を実行することで、コンピュータの出す手(0〜2)が変数Yに入ります。
 お互いの手を出し終わったら「データベースに蓄積」を実行すればそれが次の手に反映されます。じゃんけんの勝敗判定についてはこちらをご覧になってください。
 簡単に仕組みを説明するとプレイヤーが出した手と前回プレイヤーが出した手の組み合わせをデータベースに記録することでプレイヤーの出す手の順番をパターン化して次に出す手を予想してして、それに勝てる手を出すというだけです。1手古くなるごとに重みを半減しているためより新しいパターンに素早く対応できます。これによって例えば「グー」ばかりを出したりとか「グー」の後に「チョキ」を出すことが多かったりといった偏りがある場合にはコンピュータに勝つのは難しくなります。(この重み付けに関しては好みに応じて変えてもいいかも)

 この処理を組み込んだ1画面プログラムをこちらに用意しました。ちなみにこの思考ルーチンは「ダミーちゃん危機一髪」で使用してます。

↑TOP


FOR文をブロックIFやREPEAT〜UNTILの代わりに使う



◎REPEAT〜UNTILの代わり
FOR I=1TO (条件式)
(処理内容)
I=0
NEXT


◎ブロックIFの代わり(上記からI=0を省いただけです)
FOR I=1TO (条件式)
(処理内容)
NEXT

 詳細や注意事項は「プログラムリスト短縮テクニック」のFOR〜NEXTの項目を参照してください。

↑TOP


負数であっても正数と変わらない整数化



A-A%1

 FLOORだと整数化の際に負数だと例えば-3.5は-4になりますが、このルーチンを使えば-3.5は-3になり、正数と同じように整数化できます。
 詳細や注意事項は「プログラムリスト短縮テクニック」のFLOORの項目および「剰余を使ったリスト短縮テクニック」の「小数部分を求める」の項目を参照してください。

↑TOP


編集時Tips



 プログラムではないですが、プログラムを作る際に知っておくと便利なTipsを紹介します。

◎エラーが出た行を表示する

 実行モードでLIST ERLと入力する。
 システム変数ERLにはエラーが発生した行が入っているためです。

◎プログラムの最終行を表示する

 最終行にラベル@EOFを入れておいた状態でLIST @EOFとする。
 数1000行もあるプログラムだと最終行に移動するだけでも大変なので事前に@EOFなどの最終行の目印を入れておくと良いでしょう。

◎プログラムを任意のラベルの行から実行する

 (あらかじめ「ラベルスタート」を先頭行に入れた状態で)RUN @ラベルもしくは@ラベル[START]とする。
 プチコンでは任意の行から実行することはできないですが、これによって実現可能となります。

↑TOP


初期化Tips



 プチコンでは初期化においては各画面ごとのクリア命令だけではなく初代から伝統のVRAMリセットコマンド([R][START][←]の同時押し)もあります。それに加えてmkIIからACLS命令やホームメニューによる初期化が加わりましたその違いを書いておきましょう。

◎ACLS

 ACLS命令を実行することによってマニュアルに記載してあるようにCLSGCLS(ページ0、1のみ)SPCLRBGCLRICONCLRGDRAWMD 0GPRIO 3COLOR 0VSIBLE 1,1,1,1,1,1が実行された状態になり、なおかつ、上下画面の各画面(コンソール、スプライト、BG、GRP)のカラーパレットも初期化された状態になります。
 初代プチコンでは実行前に各種初期化命令が必要不可欠でVISIBLEを変更しているプログラムのためにVSIBLE 1,1,1,1,1,1も書き記す必要があったのですが、ACLS命令によってそんな心配は要らなくなりました。プログラムの先頭にACLS:CLEARを置いている人も多いのではないでしょうか。

 ACLSを使用する場合の注意点としてはカラーパレットが初期化されるということです。このためパッケージ保存する場合にはACLS命令を使用するならばカラーパレットのみ別リソースにしなければなりません。逆にいえばパッケージにカラーパレットを含めるならばACLSは使えないということになります。
 また、ACLSではGRPのページ2、3は初期化されないためそれを使用したプログラムを実行してそのプログラムを終了した際に初期化が行われてなかったら下記のVRAMリセットコマンドを実行するかホームメニューに戻らない限り残り続けます。これはセーブデータなどACLSで消えては困るものをGRPのページ2、3に書き込むという感じで積極的な活用をするのが良いかもしれません。

◎VRAMリセットコマンド

 ACLSの各種初期化に加えてGRPのページ2、3の初期化BGF(コンソールキャラ)の初期化(CHRINIT "BGF")が行われます。mkIIではこれにBGMSTOPも加わりました。
 初代プチコンにおいては大活躍してくれたコマンドですが、ACLS登場によって活用する機会は減ったのではないかと思われます。(コンソールキャラを書き換えたPCGを使用する場合には必要になってきますが)

◎ホームメニューに戻る

 ホームメニューに戻るとVRAMリセットコマンドに加えてスプライトとBGキャラの初期化CLEARが行われます。あとファンクションキーに登録されている内容の初期化を行いCLEAR命令でも初期化されないMEM$もホームメニューに戻ると初期化します。
 したがって、CLEAR命令を書いていないプログラムやMEM$を使ったプログラム(MEMリソースとして一旦保存したのではなくCLEAR命令でも消えないため一時保存用としてMEM$を使用している場合)では「プログラムを作る」で正常動作していてもホームメニューでは正常動作がされない場合もあるため注意が必要です。(もちろん、その逆でホームメニューでは正常動作しても「プログラムを作る」では正常動作しない場合もある)
 あと、ホームメニュー上の「プログラムを見る」で動作させる場合は停止やプログラムのエラーやプログラムの終了によって自動的にホームメニューに戻ります。ホームメニューに対応させる場合にはその対策も必要になってきます。

 ホームメニューに戻ることでほとんど初期化されるのですが、中には初期化されないものもあります。TALKSTOPは行われないためホームメニューで長い文字をしゃべっている途中で[SELECT]で停止した場合にはホームメニューに戻ってもしゃべり続けます。またBGMSETで定義したBGMも初期化されないため初期化したい場合はBGMCLEAR命令を実行する必要があります。もっともBGMが初期化されなくても特に弊害はないため初期化をわざわざしなければならないことはないのですが。

 ホームメニューに戻っても初期化されない項目もプチコンを終了すればすべて初期化されます。プチコンそのものを終了した場合に残るのはファイルとしてセーブしたものに限られます。 プチコン上でセーブしたファイルはプチコンそのものを消さない限りは基本的に残り続けますが、プチコンの保存領域の問題があるため一杯になる前にこまめに消すかSDカードにPTCファイルとしてバックアップを取っておく方が良いでしょう。

↑TOP


RETURN/RETURN *MAIN

inserted by FC2 system