ファミコンのプログラムを作ろう。





  =参考にしたホームページ=


    ギコ猫のファミコンプログラミング
    NES研究室
    ツインビーの無敵裏技



 ●ファミコンのプログラムを作ろう



   フリーのNESASMをダウンロードして、asmからnes形式へ…ファミコンのエミュレータを使って、
   nes形式のファイルがファミコンのソフトとして再現されます。




    ダウンロード先はNESASM
    ファミコンのエミュレータはエミュレーター
    またはエミュレーター総合サイト
    スプライト・BGパターンを作るならYY-CHR
    ファミベーのよっしんさんの作品はこちら→ザックナー?



		●パスをmkit251_dos/BIN/内に
		●適当な名前 sample.asmをメモ帳で作成
		●メモ帳で作成したsample.asmをNESASM.EXEへドラッグ
		●ドラッグした段階で、アクセス許可に関するエラーが出た場合は、
 		 ツールからインターネットオプション→アプリケーションと安全でないファイルの起動(セキュリティで保護されていない)項目を有効にします。
 		 インターネットへ接続するときは、また無効にしておきます。
		●成功するとsample.nesファイルが作成されます。
		●nesファイルをエミュレーターへ通します( ここでのエミュレーターはVirtuaNESです。) NESヘッダが異常です。のエラーが出たら成功です。
		●ここから先は長々とプログラムを書いていきます。
 		 作成してはasmで保存し、再度NESASM.EXEへドラッグしてnesファイルを作ります。
 		 nesファイルが作成されない場合は、文法などがあっていない場合なので、もういちどasmファイルを見直しします。




 2進数-10進数-16進数の変換から
 2進数の計算小数点の計算



   ●1ビットが8個集まって1byte(バイト)
   ●210byte=1024byte=1K(kilo) byte  1ページは$xx00-$xxffの256byte空間なので、1K byteは4ページ分に相当します。
   ●220byte=1048576byte=1M(mega) byte




    .inesprg→16kのプログラムサイズなので$8000〜$BFFFの16k空間?
    .ineschr→8kのキャラクタデータなのでVRAMの$0000〜$1FFFの空間と解釈出来ます。
    .inesmap→マッパー(拡張出来るそうなので、スプライトやBGを多く取り扱いたい時に使用します。)
    .inesmir→VRAMのミラーリング(BG面1,2を横へ配置するか縦へ配置するか)
     0-垂直に配置(水平にミラーリング)
     1-水平に配置(垂直にミラーリング)




    dw(デファインワード)-が連続した2バイトを指定します。ラベルでも16進数でも良いです。
    db(デファインバイト)-が1バイト。プログラムの本体部はアセンブラで記述しなくても、数値でも書けます。データとして置きたい時は、カンマで数値を区切って記述します。
     ※バイト数が多いと出力されません。
    org(オリジン)-開始アドレスを指定。

    .bank 0-プログラム本体部分と変数の確保
    ※注意-.bank 0命令の後に、引き続きプログラムを書かないと実行されません。
    ファミコンの仕様は何度やっても良く分からなくなるので、最初のBG( バックグランド )を表示させるまでに、窓口であるPPU制御レジスタ2000Hと2001Hに、
    垂直ブランク割り込みOFFと、背景とスプライトの表示を消した上で、最初にBGだけでも配色番号0〜3のパレットデータを指定しておきます。


     それから背景の初期化、メモリの初期化などを行ってBG、スプライト共にON→垂直ブランク割り込みをONにして、無限ループ( ウェイト )を作ってNMI割り込みを待ちます。



      ○変数-$0000〜$07FFのファミコン本体内ワークRAM
       0ページ目と1ページ目は、ゼロページアドレスとスタックなので、変数として使用したい時はゼロページの方を使います。
       ワークRAM初期化は多分こんな感じです。メモリ$00を下位アドレス、メモリ$01を上位アドレスとして
       インダイレクト・インデックスド命令でメモリクリアしていきます。

       ゼロページ記号<を使って間接アドレッシングの時は[]を使います。
       

      ○プログラム本体部分-$8000〜$BFFF
       ☆データとして使用したい時、$8000〜$BFFF内に置いて、先頭アドレスをラベルとしてインデックス付きアブソリュートにすると成功しますが、ファミコン本体内のワークRAMに
        ラベルとして設定すると不具合が出ます。
        プログラム本体部分にデータとして使用したい初期設定値を書いておいて、後でファミコン本体内RAMに転送するやり方が一般的です。

       ☆データとして使用したい時、インダイレクト・インデックスド命令でラベル扱いの場合はマクロ命令high(),low()が必要です。

    .bank 1-割り込みによる、ベクターアドレスを記述する。
      ○$FFFA NMI割り込み-(1/60秒割り込み)-NMI割り込みを使わないで、$2002の最上位ビットを読み込んで同期を取る方法もあります。
      ○$FFFC RESET割り込み-電源ON時やRESETした時
      ○$FFFE IRQ、BRK割り込み

    .bank 2-BG、スプライトデータをセット( プレーン2枚分(4色)で1キャラクタ8×8dot=16バイト必要 )


				




 ○RESET割り込みからスタート→SEIでIRQ割り込みを禁止→分岐命令を使うためのスタック退避命令…LDX #$FF→TXSとして、スタックポインタを$01FFにセットします。(設定なしでも自動で初期化)
 1ページ分のスタックエリア($0100〜$01FFが作られます。)
 ○プロセッサステータスレジスタの初期状態を知りたいためPHPした後、スタックをアキュムレータに入れた所、
 30(16)が返って来ました。(フラグの説明はCPU_INST.TXTより)

 
PレジスタNVTBDIZC
設定値00110000
 ○透明のBGをBG面全て(VRAMのBG1面が2000(16)〜23FF(16)、BG2面が2400(16)〜27FF(16))に貼り付けて、画面のクリアーを行います。  その時はNMI割り込みは禁止(NMI割り込みは禁止しても、v-blank(2002(16)の最上位ビット)は読めます。)にしておいて、スプライトとBGの表示スイッチをオフにしておきます。  こうする事で時間に制限無くPPUを操作出来ます。  ○ゲーム作成に用いるための変数の初期設定もここで行います。  ○全ての初期設定を行なった後、表示スイッチをオンにして割り込みを有効にし、空回しのループを作ってNMI割り込みを待ちます。  ○NMI割り込み中→  
PレジスタNVTBDIZC
設定値00110100
 ※(注意1)-NMI割り込みの先頭に2000(16)の最上位ビットを0にしておき、次回のNMI割り込みを受け付けないようにします。  ※(注意2)-各種レジスタ(A,X,Y)をスタックへ退避させます。(途中で処理が打ち切られて割り込みに入って来たので)  ※(注意3)-PPUを操作(32キャラクター分位まで)→スクロール値をセットし直し  -予備準備として32キャラクター分の今回のvblankに書き込む為のvramデータを事前に作っておき、すぐに書き込めるようにしておきます。-  ※(注意4)-スプライトDMA(4014(16)にスプライトRAM上位セット)→スクロール値をセットし直し  ※(注意5)-コントローラー値読み取り  スプライト制御レジスタの2003(16)と2004(16)は、2003(16)に0をセットして、  2004(16)にY座標,スプライト番号,ステータス情報,X座標の順に4バイトずつセットして行きます。  少ないスプライトを扱う時は2003(16)と2004(16)で問題ないのですが、  扱うスプライト数が多くなると大変なので、DMA(ダイレクトメモリーアクセス)転送によって、スプライトを制御するメモリを何ページ目と決めて、4014(16)に  スプライトRAMの上位アドレスであるページ数をセットします。  スプライトを操作する場合はPPUを操作した後で記述します。(まずはPPUが先になります。)  先にバックグランド関連のPPUを操作します。(パレット等も毎回指定しますが、シビアな世界です…(T▽T)。32キャラクター分位しか時間は取れません。)  時間を分けて、次のNMIではスクロール面を作り、次のNMIではメニュー画面を操作するBGを貼り付けたり、配色番号を書き込んだりとか分担していく作業になります。  ★注意-ファミコンのスプライト横並びの制限は8枚(8×8dot)だったのですが、NESでは制限はありません。  横並びのスプライト  ★スプライト番号0によるBG画面の2分割は良く知られています。画面分割したい所にバックグランドを1ライン敷き詰めて  そこから先のスクロール値を操作する事で、上下で独立した画面を作ります。いずれも透明であれば機能しないので目立たない工夫も必要です。(背景色と同じにする等、2002(16)のbit6で確認出来ます。)  画面分割のサンプル  スプライトやBGを使用した時、最後に必ずスクロールオフセット値を0または元へ戻す事を忘れないようにしましょう。画面位置が狂う等の問題点があります。  NMI割り込みから戻る時は、スタックからレジスタ値を元へ戻してやって、次回のNMI割り込みを受け付けるようにしましょう。RTI(リターンフロームインタラプト命令)で戻るようにします。  割り込みから抜けた後は、コントローラー処理、ゲーム処理と進行して、次のNMI割り込みに備えます。無限ループを作ったアドレスへ復帰させて( RTIで戻ります )再び割り込みを待ちます。   ※割り込み内にコントローラー処理やゲーム処理を書いてもOKです。 とりあえずはここまでです。↓は整理されていないので飛ばしてください!  タイトル画面のサンプル  6502対応コード表  ●乱数処理  乱数処理に関しては、M系列の乱数処理と、1バイトの値を5倍して1を加えるもの等(線形合同法)があります。  (途中で初期設定値を変更すると周期系列が変化しますので、それに合わせてみます。)  周期系列を変えてみた乱数(星)タイトルとの合成  スコア加算のアルゴリズム  加えられる側と加える側…2個の得点を最大8桁なら8バイト、7桁なら7バイトの合計8×2バイト(7×2バイト)の領域を使って得点計算します。
得点メモリ上のデータ構造
00
11
22
33
44
55
66
77
88
99
 0000 0000(0)  0000 1001(9)  ------ ------  0000 1001+6を加えてbit4が0→答えはそのまま  0000 1001(9)  0000 0001(1)  ------ ------  0000 1010+6を加えてbit4が1→6を加えたものが下1桁の答えで、上位桁に繰り上がりがひとつ  0000 1001(9)  0000 1001(9)  ------ ------  0001 0010+6を加えてbit4が1→6を加えたものが下1桁の答えで、上位桁に繰り上がりがひとつ  最初の桁上がりを0にしておいて、順に下1桁から計算させていき、キャリーと一緒に加えていきます。  最終桁で桁上がりが発生した場合は99999999(8バイトの場合)を超えた事になります。  ハードウェアスクロール  アセンブラで記述しよう。  LDA #$00 ´水平スクロールオフセット値  STA $2005  LDA #$00 ´垂直スクロールオフセット値  STA $2005  -$2005はスクロールオフセット値-順に水平、垂直を書き込む。-    ●BG面0が255dot目から0に差し掛かる時、又はBG面1が255dot目から0に差し掛かる時に画面切り替えを行います。   BG面が垂直方向にミラーされている時に有効です。BG面左端をマスクしておいて、書き込んでいる所を見せなくする方法です。  ●4方向スクロールさせる場合は、BG画面の水平方向にミラーを作っておいて、縦に2面持たせる仕様にします。(BG左端のクリップで横方向へ書き込む作業を見せなくしておきます)   更新されない画面をスクロールさせるのは比較的簡単で、BG画像を更新させたい場合は、X,Yスクロール量とBGテーブルを割り出す計算が必要になります。     PPU制御レジスタ  
アドレスbit7bit6bit5bit4bit3bit2bit1bit0
2000(16)垂直ブランキング期間中にNMIを
0:実行しない
1:実行する
スプライトサイズ指定
0:8×8dot
1:8×16dot
BGパターン先頭アドレス
0:0000(16)
1:1000(16)
スプライトパターン先頭アドレス
0:0000(16)
1:1000(16)
PPUアドレス自動インクリメント
0:+1(1キャラクタ)
1:+32(1ライン)
0:VRAM表示領域(2000(16)-23FF(16))
1:VRAM表示領域(2400(16)-27FF(16))
2:VRAM表示領域(2800(16)-2BFF(16))
3:VRAM表示領域(2C00(16)-2FFF(16))
2001(16)フィルターをかけた時のフィルター色
青(bit7):緑(bit6):赤(bit5)
スプライトの表示
0:オフ
1:オン
BGの表示
0:オフ
1:オン
スプライト左端のマスク
0:隠す
1:表示
BG左端のマスク
0:隠す
1:表示
フィルターをかける
0:カラーモード
1:フィルターをかける
 ★2000(16)と2001(16)に出力する為の出力記録をゼロページに作っておいて  画面を制御したい時にandしたり、orしたりします。  ★表示スイッチを消したり書いたりする時はv-blankを待ちます。v-blank処理がないと一瞬画面が乱れます。  ●コントローラー  
アドレスbit7bit6bit5bit4bit3bit2bit1bit0
4016(16)
4017(16)
コントローラー読み出しは4016(16)の最下位bitを1にし0にしてから4016(16)を8回読む。(4017(16)は2コン)
A(X)→B(Z)→select→start(Enter)→上→下→左→右の順番に入って来る。
 必要ないとも思いますが、リストはこんな感じです。     コントローラーを読み込んで処理をさせる事が一般的になっています。   ボタンを押し続けて動く状態と、ボタンを連打しないと動かない状態を考えてみます。     [-ボタンを押し続けて動く状態-]      コントローラー値を毎回(1/60秒)読み込んで、and #$ボタン値 と検出させれば押されているか判定できます。     [-ボタンを連打しないと動かない状態-]      コントローラー値のオールド値(1ターン前のコントローラー値)を使います。      
 New(今回のコントローラー値)  押されていない(0)  押されている(1)  押されていない(0)  押されている(1) 
 Old(前回のコントローラー値)  押されていない(0)  押されている(1)  押されている(1)  押されていない(0) 
     の部分のみが適用範囲となります。      ここの状態を取り出したいのであれば、( New ^ Old ) & New となります。( ^は排他的論理輪 )       イメージはこんな感じです。流れ図を書いてみましょう。            SPRITE ON  DEF SPRITE 0,(0,1,0,1,0)=CHR$(1)+CHR$(0)+CHR$(3)+CHR$(2)  SPRITE 0,120,120  ○スプライト表示をオンする。($2001のスプライト表示スイッチON)  ○デファインスプライトはがスプライト番号0〜7、が配色番号0〜3、がキャラクタ合成型0〜1、が表示優先度0〜1、がx反転とy反転指定…  配色番号は4パレット中の4色で割り当てられます。(1色は透明。この計算で行くと3色×4パレット×2(BG,SP)+1(背景色)=25色)  ★スプライトのステータスデータは1スプライトにつき、1バイトで表現されます。(表示優先度(プライオリティー)、配色番号、x-y反転の情報)  ○スプライト番号で指定されたスプライトを座標上に表示する  ★スプライトメモリアドレスには、Y座標,スプライト番号,ステータスデータ,X座標の順に4バイトずつ、64枚のスプライトが格納される。  ★ステータスデータは
bit7bit6bit5bit4bit3bit2bit1bit0
y反転x反転表示優先度(プライオリティー)
0:BG面の表側へ表示
1:BG面の裏側へ表示
配色番号
 ※(注意)-スプライトを表示させる時にスプライトDMAを使って、スプライトRAMを転送する時は使わないスプライトは透明なキャラクタコードで埋めておく  必要があります。(ファミリーベーシックでは透明スプライトは、225,230,239の3枚が用意されています。)  ★走査線の位置を把握しておき、画面上部(上半分)のポジションにいるスプライトを先に表示させて、走査線が画面下部を描き出した所で  下半分のポジションにいるスプライトを表示させれば、1枚のスプライトが2枚使える計算になります。  ペンペンのドット絵 グラディウスのカプセル 人参 ツインビーのモアイ像  32×32dotのモアイ像←ちょっと不気味…  素材だけ借りてオリジナルなゲームを作ってみるのも楽しいかも知れません。  作っていて、ビット操作を勘違いしてしまいます。0,1,2,0,1,2,0,1,2…の周期が欲しい時 and #$03としてしまったり等、全く駄目状態  スプライトの移動の際、斜め(45度)の移動に関しては√2倍動いてしまうので、距離を調節したい場合は  移動距離がRとするとx=R cos(45°)=√2*R/2、y=R sin(45°)=√2*R/2  だけ移動することになります。三平方の定理    斜めを制御して見ました。  整数部と小数部に1バイトずつの負の補数表記で記述していき、整数部では-128〜127まで、小数部では2の-1乗=0.5、2の-2乗=0.25、2の-3乗=0.125…2の-8乗=0.00390625の値を持たせる事にします。  2〜5°毎単位の三角関数テーブルを作って計算させると、円や楕円の動きと、放物線の動き等もシミュレート出来る事になります。  sinやcos値は90°の位相をずらしたデータなので、片方あったら良いです。    楕円の式の計算  cos θ=sin (π/2-θ)=sin π/2・cos θ-cos π/2・sin θ=cos θ  sin (θ)^2とcos (θ)^2 他の式に置き換え  余弦定理の証明+加法定理の証明 ついでに正弦定理の証明もやっておきます。  対数関数なんてものもあった(^^♪‥Log   ついでに言っておきます。もうここまで来ると頭が固くなってしまっただけなので、  階差数列なんてのもあったのか(・・?・・ 等差と等比数列は記憶にあるのですが、はて(・・?     参考 階差数列         5°単位の三角関数データ表  2°単位の三角関数データ表  目標物に向かってaベクトルで移動するアルゴリズムもtan(θ)があれば、算出出来ることになります。  対象物にvで移動する敵弾  ★スクリーン  スクリーン座標系が横256ドット、縦240ドットの横32キャラクター×縦30キャラクター  PPUアドレスの$2000〜$23bfまでが、BG面0のVRAM領域…(0〜255のキャラクター番号を保存する)  $23c0〜$23ffまでがBG面0の配色番号を保存する領域  2bitで2×2キャラクターの配色番号(0〜3)を生成し、4×4キャラクターで1バイトの配色番号を作ります。  横32キャラクター×縦30キャラクターなので、縦2キャラクター分が余る事になり、余った8バイトのデータ構造が他のデータ形式と違う事になります。   
配色番号の割り当て
右下2×2キャラクター(2bit)左下2×2キャラクター(2bit)右上2×2キャラクター(2bit)左上2×2キャラクター(2bit)
 PPUアドレスの$2400〜$27bfまでが、BG面1のVRAM領域…  $27c0〜$27ffまでがBG面1の配色番号を保存する領域で、BG面0と同じデータ構造になります。  ●カラーパレット(同色を含まないで52色発色。同色を含めると64色)  PPUの3F00(16)〜3F0F(16)までがBG面の着色(各パレット4色ずつ1色は透明で、配色番号0〜3に割り当てられます。)で、  PPUの3F10(16)〜3F1F(16)までがスプライト面の着色、背景色は3F10(16)になります。  PSG(プログラマブル・サウンド・ジェネレーター)は$4000〜$4015の範囲にあって  供給されているクロック周波数が1.78977267MHz…  ●レジスタに設定する値($4002、$4003)=CPUのクロック周波数/(再生したい周波数*32)-1  で計算されます。NES研究室より  ●周波数表(1オクターブ上げるには周波数を2倍、1オクターブ下げるには周波数を2分の1します)  
C#CD#DEF#FG#GA#AB
523.25Hz554.37Hz587.33Hz622.25Hz659.26Hz698.46Hz739.99Hz783.99Hz830.61Hz880.0Hz932.33Hz987.77Hz
 
アドレスbit7bit6bit5bit4bit3bit2bit1bit0
4000(16)4004(16)デューティー比:0〜30:不連続
1:連続
0:エンベロープ
1:音量
エンベロープまたは音量の長さ:0〜15
4001(16)4005(16)周波数加減算スイッチ周波数増減幅(0:大きい〜7:小さい)bit7がONの時
0:周波数低い方へ
1:周波数高い方へ
周波数増減時間(0:短い〜7:長い)
4002(16)4006(16)周波数下位8bit
4003(16)4007(16)周波数上位3bit(基本は下位2bitで4001(16)のbit1以降bit3までに1を書き込むと3bitまで拡張される
 ●ドレミのデータ表(Lは4002(16)、Hは4003(16)に書き込むデータ)  ●オクターブがひとつ上がる毎に、データを1/2(右へシフト)されますが±1〜2の誤差が出ます。  ●ノート番号とオクターブを1バイトに収めて、音の長さを1/60フレームの何個分と指定すれば簡単な曲が作れます。  ●1分間が60×60=3600フレームですので、3600÷テンポ(1分間の4分音符の個数)=4分音符のフレーム数となります。  ●音をリズム通りに発生させる場合はv-blank中でカウンターをデクリメントさせていって、0になった時、音発生のフラグを立てる事にします。   (フラグだらけって事も‥、大抵はこちらのフラグが立ったら今度は他のフラグを見て、処理をさせるなど重複した状態が続きます。)  ●4015(16)のレジスタは各チャンネルでの発音スイッチになります。後で気付くのですが、この発音スイッチは発音される終了時間が来るとオフされてしまいます。   $4003,$4007(16)の上位5bit(bit3〜bit7)が音の長さ(発音時間になります。)   vblank中でカウントさせてみましたが、定かではなく良く分かりません。#%〜の上位5bitを0〜31の間で色々試して見ましょう。   list   ☆$4015を読み込んでしまうと、フレームインタラプトフラグがクリアされるため、発音時間通りに再生するには$4015を読まないか、    $4017の最上位に1を書き込む方法があります。($4015を読まない場合でも、発音時間はずれてしまうため、$4017の最上位に1を書き込んで同期を取る方法が最有力とみます。)     FC音源サイトから転載  ●デルタサンプリングについては$4011の数値(ボリューム)を変えると、それなりの音になります。(内容の程は難しく、音声合成等も出来るみたいですが…)  ●mck mckcで曲だけ作ってみる。 その中でも簡単なスターラスターを楽譜見て入れてみました。   使用方法はmck mckcの環境設定リンクさせて頂きます。   コマンドラインから毎回メモ帳で作ったmmlデータを、〜.nsfや〜.nesへ変換させるのは手間がかかるため、     batファイルを作ってそのバッチファイルをダブルクリックするだけで変換するbatファイルを作ればOKです。   ツインビー ラウンド立ち上がり     途中でメモリが足りなくなるため●#BANK-CHANGE 0,1でmmlのメモリ不足を解消します。   同じのツインビー ラウンド立ち上がり     雰囲気としてはこちらが合うのですが、メモリ不足による命令が分からなかったので最後が切れています。 これで完成! NES版ツインビーのmmlリスト NSFファイル      大変じゃった!不満がある人は@v={ ... }のとこを本格的に探してみましょう。 難しいぞい!      このあたりはツインビーだらけじゃが、ポップ系も良いのでゆっくりした時にでも挑戦じゃ〜! 低音がどこかで半音ずれちゃったりしてるので、探してくりー!本人も分からん〜   ツインビー強めmid    音色部分を手直しして、それなりに出来た!   弱めのツインビーmid    ピントが合わせにくいので、それなりに時間かけてはまた他の日に聞いては修正してなどを繰り返した方が良いと思われます。    パワーアップ   本当はmck,mckcもっと凄いのでしょうけど、扱い出来ず…>^_^<難しいぃ。   失敗作の数々・・・失敗というよりmid→wav(timidi95ーサラウンド効果、響きは良し!)→mp3への変換に苦戦してしまい、音が変わってしまう(__) 決してソフトが悪いわけでもなく、   ソフト自体は大変良い・・ 扱い方が分からなかったため。  ( mck,mckcは直接wavへ変換してくれます。)          直接wavへ変換してくれるMuseScoreを見つけた(^^♪ テキスト音楽さくらも良かったのに・・しばらくはお休みです     下手なりに楽譜を打ち込んでいこっ!     →話はmckへ戻ります。  ひとつ3連符が問題になったのですが、テンポを変えたりして解決されます。音色設定の前にvコマンドも必要です。   ( 3連符の所だけテンポを3倍にして、3分割しないで済む方法を取ります。 )   ソフトウェアエンベロープを使いたい時は、vの代わりに@vn={n0,n1,n2,n3…}(1/60秒単位)で指定します。   ここに音量を指定する事で音符にアクセントを付けます。本物に近づけたいならここの数値を色々と変えてみましょう。( 見つけづらい(^^)! )   Dはディチューンで周波数のずれ数Hzを指定・・・キーの上げ下げとかエコー効果に適用されます。  1分間に20個のテスト音
  O0 O1 O2 O3 O4 O5
C H:6 H:3 H:1 H:0 H:0 H:0
L:173 L:86 L:171 L:212 L:105 L:52
C+ H:6 H:3 H:1 H:0 H:0 H:0
L:77 L:38 L:147 L:200 L:99 L:49
D H:5 H:2 H:1 H:0 H:0 H:0
L:242 L:248 L:124 L:189 L:94 L:46
D+ H:5 H:2 H:1 H:0 H:0 H:0
L:157 L:206 L:103 L:178 L:88 L:43
E H:5 H:2 H:1 H:0 H:0 H:0
L:76 L:165 L:82 L:168 L:83 L:41
F H:5 H:2 H:1 H:0 H:0 H:0
L:0 L:127 L:63 L:159 L:79 L:39
F+ H:4 H:2 H:1 H:0 H:0 H:0
L:184 L:92 L:45 L:150 L:74 L:36
G H:4 H:2 H:1 H:0 H:0 H:0
L:116 L:58 L:28 L:141 L:70 L:34
G+ H:4 H:2 H:1 H:0 H:0 H:0
L:52 L:26 L:12 L:133 L:66 L:32
A H:3 H:1 H:0 H:0 H:0 H:0
L:248 L:252 L:253 L:126 L:62 L:30
A+ H:3 H:1 H:0 H:0 H:0 H:0
L:191 L:223 L:238 L:118 L:58 L:28
B H:3 H:1 H:0 H:0 H:0 H:0
L:137 L:196 L:225 L:112 L:55 L:27
 ●(補足)無限ループの先頭にNMI待ちを行わない時のスプライトの移動は早くなってしまいます。(何回も呼ばれているため)  後へ続く処理が長くなれば、速度に変化が出てきてしまいます。同期を取るようにすると、一定の処理時間を得る事が出来ます。  スコアを固定してみました。  ※スプライト0番ヒットもvblank待ちと同じように、途中でBGとスプライトが当たってたら困るので最初にヒットしたか確認し、  次にヒットする直前の先頭まで待ちます。  ※アナログテレビのインターレースとノンインターレースの項を参照します。なぜ262.5本目に画面の左上トップに走査線が戻るのかが分かります。  ●オプション  オプションを付けるには数ターン前の自機のポジションが必要になります。付ける個数に応じてターン数も等倍とっておく必要があります。  オプション オプションイメージ  ●触手の動き       普段は敵をひとつひとつ出していけば問題ないのですが、多関節となると基本となる座標を中心に置いて、その座標から変化分をシフトしないといけないため、   出現時にテーブルを作る作業と、複数枚のキャラクターを用意しないといけないので、多関節用の処理も必要になってきます。   敵の名前は同じにしておいて、番号だけを多関節分用意し表示させます。    あとは消す作業・・これも多関節キャラクター並に処理が増えます。     ●敵出現データ  敵キャラクターの配置は、ゲーム進行のスクロール量または、時間で決める方法があります。  右へ進んでいくゲームなら、右の方へ16dotスクロールしたら+1カウントさせて、16回カウントされれば1画面進んだ事になりますが、  途中でスクロール量を変化させてしまった場合は、敵の出現時間も変わって来るので、時間で決めてしまった方が良い場合もあります。  イメージとしてはマップを作っておき、その上に出現する敵を載せていく感じです。  敵出現のテーブルデータを、x座標/16、y座標/16、敵の種類の3バイトずつのテーブルを作っておけば、スクロール計算と同時に、敵出現のテーブルを読み込んでくる事になります。  言葉で書けば易しい様でも、作る時は流れ図と綿密な計算が必要になります。 当たり判定サイズやダメージなども同時に、敵の種類と関係する項目にテーブル化しておけば、もっと効率化すると思います。  パワーカプセルを出す処理も、敵編隊のテーブルを作っておいて、Aテーブルの編隊5機が全機倒されたので、パワーカプセルを出現させる。  Bテーブルの編隊4機が全機倒されたので、パワーカプセルを出現させるなどの処理も必要になります。   Aテーブルカウンター、Bテーブルカウンター、Cテーブルカウンター・・といった感じです。  スプライト表示部分もNMIの先頭に書かれますので、表示→移動処理→表示→移動処理…の繰り返しと覚えましょう。  以下では敵出現データの内容を4バイトずつで、ひとつの敵に収めます。  ラウンド数に応じてテーブルも同数作っておきます。  メインカウンター値(2バイト=時間)、敵の種類(6bit)+編隊(4機編隊まで=2bit)、パワーカプセルを出すか出さないか+敵弾が発生する乱数値  出現させる敵によって、出現座標、敵の動き(パターン)を決める事になります。  キャラクタエディターを作ってみました。  動きが重かったので、innerHTMLは初回だけ読んでみました。ドットの色を変える時はセルの画像だけを変えるようにします。(BGTOOLも同じ)  作ったイメージを配置していく場合は、いちどビットマップファイルとしてC言語で作成して、その画像を配置していく事にします  8×8dotサイズのビットマップファイルを作成するCリストも、とりあえずは解決。list  ビットマップファイルのデータ構造より(一部)  90°ずつの回転処理等も入れようかと思います。少し考えると分かると思いますが、90°ずつの回転処理  こんな感じです。  エディターの流れとしては  Javascriptからデータを作成   →WSH(ウィンドウズ・スクリプティング・ホスト)でデータファイルを作成    →C言語からデータファイルを開いて、ビットマップ画像を表示     の流れになります。      圧縮ファイル デスクトップに置いてファイルごと中から出して使います。   Microsoft Visual C++2008で組んだため、もし、そのエディターがパソコン上に無い場合はエラーになってしまいます。  以下HSP(ホットスーププロセッサー)より、実行ファイルを作成したいならHSPがお勧めです。  初心者な自分にとっては便利なフリーソフト。   flag=0   loop_cnt=5000   do       await 1 loop_cnt-- if ( loop_cnt<=0 ):flag=1   until flag   end    これも昔のプロセッサーならデクリメントさせていって、ゼロフラグが立てばループを抜けるとしてた筈です。   現在のプロセッサーは速いので、どういった書き方が理想なのか良く分かりません。ソースも汚いので覚え書き・・    HSPエディターへ戻って来たときにデバッグウィンドウが消えていなければいけないため、   サブルーチンに入っている時に、割り込み処理を行った場合なども、画面が閉じられてしまった場合はend命令に辿り着くように   書かないと怪しくなってしまいます。ーなぜ(・・?ー   画面サイズが大きくなると時間落ちしてしまうため、リアルタイムなゲーム作りはお預けです(^^♪ ごみんね〜命令も多いので   混乱してしまいます。 なおawaitは必ず入れましょう。     画面を閉じる時は、プログラムの先頭にonexit命令で割り込みサブルーチンを作っておいて、画面が閉じられた時にginfo_act命令でスクリーンidを調べる必要があります。   gsel ginfo_act,-1で対象のスクリーンを消して、必ずend命令でプログラムを終了させます。     書式指定文字は使えそうです。pos命令でひとつひとつ指定場所に表示させるのは面倒なので、これで一発で表示してくれます。 pos 128,128 a=127 mes strf(" %4d 機",a) ;桁の調整と整数型なら%d a=8 mes strf(" %4d 機",a) a=32 mes strf(" %4d 機",a) name="korin" : age=32 mes strf("名前は %s です。年齢は %d 歳です。",name,age) 文字列型なら%s     整数型は4バイト 2の補数で表される範囲は、0x00000000(0)〜0x7fffffff(2147483647) 0x80000000(-2147483648)〜0xffffffff(-1)まで   排他的論理和の場合はxor( ^ )、論理積はand( & )、論理和はor( | )   全ビット反転は、x=(x) xor 0xffffffff      論理式は使えます。     c=3     d=( c=3 )     dialog d ; dは1     c=-1     d=( c=0 )     dialog d ; dは0     x=x-vx*double( key & $1 )+vx*double( key & $4 ) ← 左カーソルが押された時は-vxで良いのですが、右カーソルの時はvxには4倍されたものが入ってしまいます。    実数の場合は   100-0.8 ←× 100.0-0.8 ←○ C言語と同じように小数点を計算させる時は、引く数、引かれる数共に小数点にしておく等    実数型なら実数型同士、整数型なら整数型同士を計算させます。変数に格納する場合も、整数型用と実数型用に分けましょう。    後で突き止めてみると、整数型で使ってた変数を途中で、実数型へ変えた場合などはエラーにならず、答えも実数型で返ってきました。   良いのかもー! ただ配列などの添え字に実数型変数を使った場合などはエラーになるので注意。   整数同士で割る場合の余り記号は\       矩形領域にある番号から、x-yポインタを求めたい時などに使われます。     当たり判定時にも使用出来そうですが、敵のサイズが大きかったりすると、すり抜けたり衝突判定が曖昧になってしまいます。    absf関数で自機の中心と敵の中心の差をとって、自機の半分+敵の半分以下なら衝突したとか考えます。        ーすり抜けてしまう当たり判定ー       1ターン前の座標と現在の座標を調べて、その増加分が敵の座標を超えていたら衝突したと考える事も出来ます。( 同一方向の場合 )      注意 1/60秒の世界はとても速いので、自機と敵機が当たった瞬間、次の1/60秒フレーム先でも当たっています。     何度でも重なってしまうため、重複処理を避けましょう。( 点滅処理をさせて、しばらくは当たり判定を実行しないようにするなど )      それに合った当たり判定を使う必要もありそうです。   0による割り算をさせてしまう事があるので、割る数を0にしないようにしましょう。   Line文は終点の座標,始点の座標です。   ここも反対に組みやすいので注意しましょう。   スクロールをさせる場合は、screen命令で実画面よりも仮想画面を大きくとった場合に限り、groll命令でスクロールさせる事が出来ます。   頻繁にウィンドウを変える場合には、今どの面を使っているのかの指定gsel (画面id)を使用する必要があります。   オブジェクトを配置した場合も、どのスクリーンに対してのオブジェクト表示なのかが分からなくなるため、objprm命令などでオブジェクトのパラメーターを変える場合は、   特に注意しましょう。( オブジェクト部品をループ中に表示させた場合、オブジェクト番号が次へ行ってしまいます。なるべくループの外側で表示させましょう。 )   オブジェクト部品はスクロール不可能。合成する面の上側や左側に付けましょう。左側240ピクセルからスクロールさせる場合は、0〜239ピクセルまでが固定面として考え、   テキスト処理を行いましょう。240ピクセルから右端までをスクロールさせる面と考えましょう。   新しい画面を作るたびに、オブジェクト番号も再び0からカウントされます。     −スクロール例ー             良く考えてみると、スタッフロール用・・スクロールの向きが反対 星のスクロール      画面サイズが大、1000/60=16.6ミリ/s        256×240サイズでは少しぶれてしまいますが、時間通りに処理されます。( 処理が増えれば× )          グラディウスに近づきたいと思います           もうグチャグチャ・・gmodeの2〜4は少し処理落ちしてしまい            まだ命令を理解して使えてないので、使い慣れるには結構なセンスとハイレベルな知識が要るようにも思えます。           ーHSPは難しい(__)ー            タイトル画像           ベクターのプログラムを見つけた。Vector 癒された!。           ー注意点ー スペースキーをショットキーとして使う場合、スペースキーの押しっぱなしは変な機能が付いているため、スペースキーを他のキーに変えることをお勧めします。           ーgetkey命令ー はキーが入力されたら1、入力されなかったら0になるので、欲しいキーだけを集めて2進数形式にしましょう。 ;ファミコン風 *CONTROLLER ;ESC ENTER Z X ↑ ↓ ← → key_str="27,13,90,88,38,40,37,39" key_str+="," : key=0 repeat 8 key<<=1 w=instr( key_str,0,"," ) getkey key_data,int( strmid( key_str,0,w ) ) key+=key_data key_str=strmid( key_str,w+1,strlen(key_str)-(w+1) ) loop return         星を点滅させたい場合は、コピーモードに色加算合成コピーか色減算合成コピーを使い、255または0にクランプアップされたら元へは戻れないので、        画像を初期化させて繰り返す処理が必要です。          screen 1,256,240,0          screen 0,256,240,0          color 200,200,200:boxf          gmode 5,256,240,55( ブレンド率+輝度? )          gcopy 1,0,0,256,240         何かピンと来ないので、関数表記で改めて試してみた! gsel 0,-1 #module #defcfunc g_mode int back1,int back2,int c screen 1,256,240,0 color back1>>16 , ( back1>>8 ) & 0xff , back1 & 0xff:boxf screen 2,256,240,0 color back2>>16 , ( back2>>8 ) & 0xff , back2 & 0xff:boxf gmode 5,256,240,c gcopy 1,0,0,256,240 gsel 2,2 return 0 #global r=g_mode( 0x7f0080 , 0x80ff7f , 0 )                コピーする時は事前にコピーされる面を消すか、gmode 0で完コピーしましょう。       なおpos命令で事前にコピーする場所を指定しましょう。( 意外と忘れがち・・ )         その座標の点の色を調べるpget命令があるので、タイトル画像などの枠つきの色を調べて、同じ色ならグルっと1周させるなどの処理が出来そうですが、        画像が透明化されていたりした場合は、指定された色も変わってしまいます。        上に載せたようなタイトル画像の白枠を、星がクルクルと回っていくスタイルも考え中です。  線の幅が1dotの場合のみ くるり回り 実行ファイル 反時計回り  ●どっちも一緒・・ ( /_; )  なぜ?  ●最初に右か左を決める。  ●点を調べて、白い点がある間はその方向へ。  ●ない場合は角度を+90°変えて、その方向に白の点があったらその方向へ  ●無ければ-180°させます。( 考えとしては間違い・・ )   ●外側の背景の方から白いラインを見ます?。 背景スクロールの方も少し変えてみました。2重スクロール( 星 ) 2重スクロール タイトル 少しずつ編集して・・見やすいように 2重スクロールではなく、星が円弧型に広がるように、見せた方が良いのかも知れません           宇宙のはじまりビックバン( BigBang )説を見ながらイメージ。 HSPイメージ この場合はやっぱり、星を表示させる配列を固定しないと永遠と拡張してしまいます。 空いた配列に新しい、内側の星を作るようにしたいので、敵の出現フラグと同じようにフラグoffを見つけて表示させます。 星の拡散 ーオプション処理へ戻ります データシフトー N=4 dim data,N ;N個の配列作成 data(0)=8 ;data.0=8でもOKですが、添え字が変数でdata.a-1=8などは× data(1)=5 data(2)=-5 data(3)=9 repeat N data( N-cnt )=data( N-cnt-1 ) loop ;宣言されていない、data(4)へ代入してもERRORにならず ;data(5)〜に代入してもERRORにならないので、代入した時点で配列宣言が伸びると考えましょう dialog data(3) dialog data(2) dialog data(1) dialog data(4) ;ERRORにならず dialog data(0)     オプションループ( グラディウス2 )については、角度rad( 0〜2π )をシフト。ポジションのデータは入れず自機+半径×三角関数値をオプションの    座標とします。      グラディウスVー本物は凄い        検索文字に対しては、1文字ずつ検索させるよりは、instr命令が速い。   instr("文字列",検索開始位置,"検索する文字")で、例えば、instr("abcdcdf",0,"c")→2。   勘違いしてはいけないのが、instr("abcdcdf",3,"c")→1となる事です。        音楽を鳴らすためのmmloadやmmplay・・ショット音などリアルタイムで鳴らしたい場合は(^^♪       2MB以下のwavファイルのみをリアルタイムで鳴らせる事が出来ると書いてあるため、mp3へ変換も意味なし(__)       2MB以下へ収めましょう。         文字は2バイトで表されます。その他、復帰改行を示す\nの文字も2バイト( 0xd,0xa )です。    勘違いしやすい所なので注意しましょう。 s="ドライブ選択\nC:\\\nD:\\\n" repeat strlen(s) cnvstow pp,strmid( s,cnt,1 ) dialog pp loop end   文字列を取り出すサンプルも、ひとつ取り上げてみました。(色々考え方あると思います) string="5.2 5.9 7.8 5 12.8 23.5 32.0 8 11 " for i,0,(取り出したい項目数(0〜) )+1 ix=instr(string,0," ") d=strmid(string,0,ix);取り出した文字 string=strmid(string,ix+1,strlen(string)-(ix+1)) next ☆繰り返し処理を使う場合は、for文とrepeat〜loop文では、repeat〜loop文が速いみたいなので、後で使う人はrepeat〜loop文を推奨します(カウントのためのcntは、ループの最後でクリアされます)。          for〜nextループによるかかった時間。           v=640/8           w=480/8           color 255,0,0           s=gettime(7)           mes s+"ミリ秒"           for i,0,v           for j,0,w           next           next           s=gettime(7)           mes s+"ミリ秒"            かかった時間は、約1ミリ〜2ミリ秒になります。         repeat〜loopループによるかかった時間。          v=640/8          w=480/8          color 255,0,0          s=gettime(7)          mes s+"ミリ秒"          repeat v          repeat w          loop          loop          s=gettime(7)          mes s+"ミリ秒"           かかった時間は、約0ミリ〜1ミリ秒になります。                     メインに入る前に時間を調べておいて、ループの最後にもう一度時間を計測します。          その差がマイナスなら、1000(1秒)を加えて時間を修正します。          1ループの平均は16.666・・・ミリ秒くらいが望ましいですが、大抵はオーバーしてしまいます(__) −・・ー            カウンター値は、最大で2147483647までカウント出来るので、1ループに使われたミリ秒を加えていっても、ほとんどオーバーする事はないと思います。   画像ファイルをpackさせるには#pack命令、サウンドファイルはpack出来ないので、フォルダの中へ入れてリンクさせるかしないと駄目なようです。  画像もひとつひとつ作ってひとつのファイルとしてしまいがちですが、1枚の絵から部分的に拾う方法もあります。       フォルダの中にファイルを作らせる作業は少し面倒ですが「dirlist」で一度調べた後で   無い場合は、それなりの処理をさせていく必要があります。他にもカレントディレクトリーを変更するなどのファイル処理に関する命令が沢山あります。    −画像ファイルの読み込みサンプルー       HSPのリスト       それなりに開いてくれます。       dirlistで拡張子を指定する場合は大文字で、検索させる場合は大文字と小文字で検索させる必要も出てきます。       数値を表示させるテキストボックスなども、スクロールバーが付かない窓を表示させたりと出来るそうです。        ☆gif画像は1ドットを256色以内の1バイトに納めたデータなので、それをbmpに展開してもサイズが大きくなってしまいます。        ☆ペイントソフトを使った方が早く、bmpやjpgを256色以内へ変換するpadieが良いです(^^)v。        ☆回転処理なども出来るそうなので、画像を回転させたい、拡大縮小したい人は、gmode命令とgrotate命令を使うようにしましょう。   button(goto,gosub)"表示する文字",*ラベルは2種類の表記方法があります。   サブルーチンにさせるなら、button gosub"表示する文字",*ラベル ⇔ returnの、組み合わせになります。   関数内の変数(ローカル変数)と、関数外の変数(メインのグローバル変数)が衝突しないように、関数を定義させておく事が出来そうです。   #module    #defcfunc add int a,int b    return a+b    #defcfunc sub int a,int b     return a-b   #global     dialog add(2,8)     dialog sub(2,8)     a=8;グローバル変数    dialog a     という感じです。加算と減算の関数を定義させてみました。    値を返す必要がない時などは、return 0としておきましょう。関数を呼んだ時に他の変数に入れておく必要があります。(エラーが起こる可能性有り。)    #module #defcfunc STRING  dim s,3  s(0)="ABC"  s(1)="DEF"  s(2)="GHI"  return 0 #defcfunc MSG int i  dialog s(i)  return 0    #global    s=STRING() ;引数が無い場合でも括弧は付けて関数を呼ぶ時は、変数に入れて呼びます。    s=MSG(2)     関数で定義した文字列定数や数値定数などは、他の関数で呼んだ場合でも値が入っています。グローバル領域からs(0)〜s(2)を使った場合は    値は入っていません。     ( ☆処理速度の方も、#defcfuncで作った関数とgosubで作った関数とでは、#defcfuncの方が速そうなので、慣れてきたらこちらの方を優先しましょう。 )   シングルクオートを使って囲った文字は数値に変換されます。   'A'→65 ' '→32   1/60秒割り込み処理では(画面と同期)、HSPではcommonフォルダにあるhspdx.asファイルと、hsp3util.asファイルを   インクルードして拡張命令を使用します。それ専用の命令( es_〜 )を使っていく形です。      -vsync- ESCキーで元へ    これで、ちらつきが無くスムーズな同期は取れそうですが、ここまで行き着くまでが大変です!   話が脱線してしまったので、ファミコンへ戻ります。(HSPのページも作ってみたい)  バックグランドキャラクターを1画面作るだけでも大変なので、既存のキャラクターを真似てキャラクタ作りに…。    ファミコン初期グラディウスタイトルらしきものと弾の打ち出し、パワーゲージのダブルまで    汚かったのでもういちど作成  Zボタンでショット、Xボタンでパワーアップゲージの移動、Enterで確定(Test版)  当然の事ながら頭にイメージしたものを直接メモ帳に書いただけなので、無駄な処理も沢山あります。  A4の紙に書いた方が間違いもなく、イメージとしても捕らえやすいと思います。   曲のデータにH・MさんのMMLデータをお借りしてみました。かなり前なのですが今でも素敵に聞けます。    Zボタン(A) ショット流れ図ー   本物のイメージはこちらです。スナップショット
inserted by FC2 system