// PIC32MX150F128B用ワンチップテレビゲーム サンプルプログラム #include #include "composite32-high4.h" #include "lib_composite32-high.h" //外付けクリスタル with PLL (15倍) #pragma config PMDL1WAY = OFF, IOL1WAY = OFF #pragma config FPLLIDIV = DIV_1, FPLLMUL = MUL_15, FPLLODIV = DIV_1 #pragma config FNOSC = PRIPLL, FSOSCEN = OFF, POSCMOD = XT, OSCIOFNC = OFF #pragma config FPBDIV = DIV_1, FWDTEN = OFF, JTAGEN = OFF, ICESEL = ICS_PGx1 #define MAX_ENEMY 5 //敵最大数 #define MAX_MISSILE 5 //ミサイル最大数 #define SHIPS 3 //自機の初期残数 #define XSIZE_ENEMY 12 //敵画像横サイズ #define YSIZE_ENEMY 12 //敵画像縦サイズ #define XSIZE_SHIP 12 //自機画像横サイズ #define YSIZE_SHIP 12 //自機画像縦サイズ #define XSIZE_MISSILE 4 //ミサイル画像横サイズ #define YSIZE_MISSILE 4 //ミサイル画像縦サイズ #define ONTEI PR3 //音程設定カウンタ(TIMER3の周期設定レジスタ) // // 画像データ // const unsigned char Bmp_ship[XSIZE_SHIP*YSIZE_SHIP]={ // 自機画像 0,0,0,0,0,7,7,0,0,0,0,0, 0,0,0,0,0,7,7,0,0,0,0,0, 0,0,0,0,0,7,7,0,0,0,0,0, 0,0,0,0,7,5,5,7,0,0,0,0, 0,0,0,7,7,5,5,7,7,0,0,0, 0,15,15,7,5,5,5,5,7,15,15,0, 6,6,15,7,5,5,5,5,7,15,6,6, 6,6,15,7,7,5,5,7,7,15,6,6, 0,0,0,0,0,7,7,0,0,0,0,0, 0,0,0,0,7,7,7,7,0,0,0,0, 0,0,15,7,7,7,7,7,7,15,0,0, 0,0,15,7,7,2,2,7,7,15,0,0 }; const unsigned char Bmp_enemy[XSIZE_ENEMY*YSIZE_ENEMY]={ // 敵画像 0,0,0,0,6,7,7,6,0,0,0,0, 0,0,0,6,6,7,7,6,6,0,0,0, 0,0,6,6,5,7,7,2,6,6,0,0, 0,6,6,5,5,7,7,2,2,6,6,0, 6,6,5,5,5,7,7,2,2,2,6,6, 7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7, 6,6,2,2,2,7,7,5,5,5,6,6, 0,6,6,2,2,7,7,5,5,6,6,0, 0,0,6,6,2,7,7,5,6,6,0,0, 0,0,0,6,6,7,7,6,6,0,0,0, 0,0,0,0,6,7,7,6,0,0,0,0 }; const unsigned char Bmp_missile[XSIZE_MISSILE*YSIZE_MISSILE]={ //ミサイル画像 0,6,6,0, 14,6,6,6, 14,14,6,6, 0,14,14,0 }; // // 効果音データ // const unsigned short sounddata1[]={ //ミサイル発射音 2000,2000,4000,4000,0 }; const unsigned short sounddata2[]={ //敵爆発音 1500,30000,1,30000,1,30000,1,30000,1,30000,0 }; const unsigned short sounddata3[]={ //自機爆発音 1000,2500,1,35000,35000,1,35000,1,36000,1,37000,1,38000,1,39000,1,40000,41000,42000,43000, 44000,45000,46000,47000,48000,49000,50000,1,50000,1,50000,1,50000,1,50000,1,50000,0 }; //sounddata配列 低いド〜3オクターブ分の周期カウンタ値。ONTEI(=PR3)に書き込むと音程設定される const unsigned short sounddata[]={ 25653,24213,22854,21572,20361,19218,18139,17121,16160,15253,14397,13589, 12826,12106,11427,10786,10180, 9609, 9069, 8560, 8080, 7626, 7198, 6794, 6413, 6053, 5713, 5393, 5090, 4804, 4534, 4280, 4040, 3813, 3599, 3397, 3206 }; //曲データ //musicdata配列 音階,長さ,音階,長さ,・・・・・ 最後に音階254で曲終了、253で最初へリピート // 音階 0:ド 12:上のド 24:その上のド 255:休符 // 長さ 60分のn秒 const unsigned char musicdata1[]={ //ゲームスタート音楽 26,20,23,20,21,20,19,20,21,20,23,20,26,20,23,20,21,20,19,20,21,10,23,10,21,10,23,10, 26,20,23,20,26,20,28,20,23,20,28,20,26,20,23,20,21,20,19,60,254 }; const unsigned char musicdata2[]={ //ゲーム中のBGM 28,15,27,15,28,15,27,15,28,15,23,15,26,15,24,15, 21,45,12,15,16,15,21,15,23,45,16,15,20,15,23,15, 24,45,16,15, 28,15,27,15,28,15,27,15,28,15,23,15,26,15,24,15, 21,45,12,15,16,15,21,15,23,45,16,15,24,15,23,15, 21,45,255,15,253 }; //曲演奏用構造体 struct { const unsigned char *p; //曲配列の演奏中の位置 const unsigned char *startp; //曲配列のリピート位置 unsigned char count; //発音中の音カウンタ unsigned short ontei; //発音中の音程(タイマ周期) unsigned char stop; //0:演奏中、1:終了 } music; //演奏中の音楽構造体 //グローバル変数定義 unsigned char stage; //ステージ unsigned char enemyleft; //敵残数 unsigned char ships; //自機残数 unsigned char gamestatus;//現在のゲームステータス // 0:ゲーム開始、1:ステージ数表示中、2:ゲーム中、3:プレイヤー1減、4:ステージクリア、5:ゲームオーバー表示、6:終了 unsigned short gamestatuscount;//ゲームステータスカウンタ unsigned short keystatus=0,keystatus2,oldkey; //最新のボタン状態と前回のボタン状態 unsigned int score,highscore; //得点、ハイスコア const unsigned short *sounddatap; //効果音配列の位置、演奏中の音楽よりこちらを優先 //オブジェクト構造体定義 typedef struct { char on; // 有効/無効 short x; // x座標 short y; // y座標 short vx; // x方向移動速度 short vy; // y方向移動速度 } _Object; _Object enemybuf[MAX_ENEMY];//敵情報格納配列 _Object missilebuf[MAX_MISSILE];//ミサイル情報格納配列 _Object ship;//自機情報 //関数のプロトタイプ宣言 void title(void); void game(void); void changegamestatus(void); void gameinit(void); void gameinit2(void); void gameinit3(void); void gameinit4(void); void gameover(void); void fire(void); void moveship(void); void moveenemy(void); void movemissile(void); void drawship(void); void drawenemy(void); void drawmissile(void); void drawscore(void); void erasechars(void); void addmissile(short x,short y,short vx,short vy); void addscore(int s); void keycheck(void); void collisioncheck(void); void enemydeath(_Object *p); void shipdeath(void); void sound(void); void startmusic(const unsigned char *m); void stopmusic(void); void sound(void); void playmusic1step(void); //プログラム開始 void main(void){ //ポートの初期設定 TRISA = 0x0010; // RA4は入力 CNPUA = 0x0010; // RA4をプルアップ ANSELA = 0x0000; // 全てデジタル TRISB = KEYSTART | KEYFIRE | KEYUP | KEYDOWN | KEYLEFT | KEYRIGHT;// ボタン接続ポート入力設定 CNPUBSET=KEYSTART | KEYFIRE | KEYUP | KEYDOWN | KEYLEFT | KEYRIGHT;// プルアップ設定 ANSELB = 0x0000; // 全てデジタル LATACLR=2;// RA1=0(ボタンモード) RPB13R=5;//RPB13ピンにOC4を割り当て OC4R=0; OC4CON=0x000b;// Timer3ベース、Toggleモード OC4CONSET=0x8000;//OC4スタート T3CON=0x0030;// プリスケーラ1:8 PR3=0;//カウンタクリア T3CONSET=0x8000;// タイマ3スタート init_composite(); // ビデオ出力システムの初期化 gameinit(); //ゲーム全体初期化 while(1){ title();//タイトル画面、スタートボタンで戻る game();//ゲームメインループ } } void title(){ //タイトル画面表示 //STARTボタン押下で戻る clearscreen();//画面消去 //タイトル表示 printstr(84,100,7,0,"SAMPLE GAME"); //得点、ハイスコア表示 printstr(0,0,7,0,"HI-SCORE"); printnum(9*8,0,7,0,highscore); printstr(128,0,7,0,"SCORE"); printnum(128+6*8,0,7,0,score); //STARTボタン入力待ち printstr(60,120,7,0,"PUSH START BUTTON"); while(1){ //STARTボタンが押されるまでループ keycheck();//ボタン入力チェック if(keystatus & KEYSTART) return;//STARTボタンで戻る //省電力化のため、なるべくCPUをアイドル状態で保つ while(drawcount==0) asm("wait");//60分の1秒ウェイト drawcount=0; } } void game(){ //ゲームメインループ //ゲーム中は常時ループを回し、gamestatus変数でゲーム動作の制御をする // //gamestatus変数の意味 //0:ゲーム開始、1:ステージ数表示中、2:ゲーム中、3:プレイヤー1減、4:ステージクリア、5:ゲームオーバー表示、6:終了 // gamestatus=0;//ゲーム開始 gamestatuscount=500;//ゲーム開始音楽時間 gameinit2();//ゲーム初期化 gameinit3();//ステージ初期化 while(gamestatus<6){ //映像区間終了待ち(約60分の1秒のウェイト) while(drawcount==0) asm("wait"); drawcount=0; sound();//サウンド処理 erasechars();//描画消去 keycheck();//ボタン入力チェック if(gamestatus==2 || gamestatus==3){ fire();//自機ミサイル発射処理 moveship();//自機移動処理 moveenemy();//敵移動処理 movemissile();//ミサイル移動処理 collisioncheck();//各種衝突チェック } drawenemy();//敵描画 drawship();//自機描画 drawmissile();//ミサイル描画 drawscore();//得点類描画 changegamestatus();//ゲームのステータス更新処理 } gameover(); //ゲーム終了処理 } void changegamestatus(){ //ゲームのステータス更新 //gamestatuscountで各ステータスの時間を制御する switch(gamestatus){ case 0://ゲームスタート case 1://ステージ数表示中 printstr(96,100,7,-1,"STAGE"); printnum(96+8*6,100,7,-1,stage); if(--gamestatuscount==0){ startmusic(musicdata2);//BGM開始 gamestatus=2;//ステータスを通常ゲーム中へ printstr(96,100,0,0," ");//ステージ表示消去 } break; case 2://通常ゲーム中 if(!ship.on){ //自機死亡 stopmusic();//曲演奏停止 gamestatus=3;//ステータスを自機死亡へ gamestatuscount=180;//3秒間停止 break; } if(enemyleft==0){ //敵全滅 stopmusic();//曲演奏停止 gamestatus=4; //ステータスをステージクリアへ gamestatuscount=120; //2秒間停止 } break; case 3://自機死亡 if(--gamestatuscount) break; ships--;//自機1減させる if(ships==0){ gamestatus=5; //ステータスをゲームオーバー表示へ gamestatuscount=300; //5秒間「GAMEOVER」表示 break; } if(enemyleft>0){ //敵が残っている場合 gameinit4();//自機初期化 gamestatus=1; //ステータスをステージ最初へ gamestatuscount=180; //3秒間ステージ数表示 break; } //自機死亡中に敵も全滅の場合、次ステージへ gamestatus=4; gamestatuscount=1;//待ち時間なし case 4://次ステージへ printstr(76,100,7,-1,"MISSION CLEAR"); if(--gamestatuscount) break; printstr(76,100,0, 0," ");//表示消去 if(stage<99) stage++;//ステージ+1する(ただし99まで) gameinit3();//ステージ初期化 gamestatus=1;//ステージ最初へ gamestatuscount=180; //3秒間ステージ数表示 break; case 5: //ゲームオーバー printstr(100,100,7,-1,"GAMEOVER"); if(--gamestatuscount==0) gamestatus=6; break; } } void gameinit(){ //リセット時1回だけの初期化処理 //カラーパレット設定や、初回データの読み込みなどをここで行う // //得点初期化 score=0; highscore=0; } void gameinit2(){ //スタートボタン時のゲーム開始処理 _Object *p; stage=1; //ステージ数 ships=SHIPS; //残機数 score=0; //得点クリア for(p=enemybuf;pon=0;//敵情報格納配列クリア for(p=missilebuf;pon=0;//ミサイル情報格納配列クリア startmusic(musicdata1);//ゲーム開始時の音楽開始 } void gameinit3(){ //ステージ開始処理 int i; _Object *p; clearscreen();//画面消去 sounddatap=NULL;//効果音なし ONTEI=0;//サウンド停止 //ステージ数と同じ数の敵を発生させる(ただしMAX_ENEMYまで) enemyleft=stage;//敵残数初期化 if(enemyleft>MAX_ENEMY) enemyleft=MAX_ENEMY; i=0; for(p=enemybuf;pon=1; //有効 p->x=i*50; //x座標 p->y=30; //y座標 p->vx=1-(i%2)*2; //x移動速度 p->vy=(i%3)-1; //y移動速度 i++; } for(p=missilebuf;pon=0;//ミサイル情報格納配列クリア gameinit4();//自機初期化 } void gameinit4(){ //自機初期化処理(ステージクリア時と死亡時) ship.on=1;//有効 ship.x=(X_RES-XSIZE_SHIP)/2;//x座標 ship.y=200;//y座標 } void gameover(){ //ゲーム終了時処理 if(score>highscore) highscore=score;//ハイスコア更新 ONTEI=0;//サウンド停止 } void fire(){ //自機ミサイル発射処理 if(!ship.on || ship.y<12) return;//自機死亡中と上端にいる場合は無視 if(keystatus2 & KEYFIRE){ //FIREボタン押下でミサイル発射(ただし連射禁止) addmissile(ship.x+(XSIZE_SHIP-XSIZE_MISSILE)/2,ship.y-YSIZE_MISSILE,0,-3); } } void moveship(){ //自機移動処理 //ボタン押下状態に合わせ上下左右に移動。画面端チェック if(!ship.on) return; if(keystatus & KEYLEFT && ship.x>0) ship.x--; if(keystatus & KEYRIGHT && ship.x8) ship.y--; if(keystatus & KEYDOWN && ship.yon) continue;//敵情報が入っていなければ次へ //x,yそれぞれ移動 p->x += p->vx; p->y += p->vy; //画面端で反転 if(p->x==0) p->vx=-p->vx; else if(p->x==X_RES-XSIZE_ENEMY) p->vx=-p->vx; if(p->y==8) p->vy=-p->vy; else if(p->y==Y_RES-YSIZE_ENEMY) p->vy=-p->vy; } } void movemissile(){ //ミサイル移動処理 _Object *p;//ミサイル情報格納構造体配列へのポインタ //全ミサイル情報格納配列分ループ for(p=missilebuf;pon) continue;//ミサイル情報が入っていなければ次へ p->y += p->vy; //y座標移動 if(p->y<8) p->on=0; //画面端で消滅 } } void drawship(){ //自機描画 if(ship.on) putbmpmn(ship.x,ship.y,XSIZE_SHIP,YSIZE_SHIP,Bmp_ship); } void drawenemy(){ //敵描画 _Object *p;//敵情報格納構造体配列へのポインタ //全ミサイル情報格納配列分ループ for(p=enemybuf;pon) continue;//敵情報が入っていなければ次へ putbmpmn(p->x,p->y,XSIZE_ENEMY,YSIZE_ENEMY,Bmp_enemy); } } void drawmissile(){ //ミサイル描画 _Object *p;//ミサイル情報格納構造体配列へのポインタ //全ミサイル情報格納配列分ループ for(p=missilebuf;pon) continue;//ミサイル情報が入っていなければ次へ putbmpmn(p->x,p->y,XSIZE_MISSILE,YSIZE_MISSILE,Bmp_missile); } } void drawscore(){ //最上行に得点類の描画 printstr(0,0,7,0,"SHIP"); if(ships>0) printnum(5*8,0,7,0,ships-1);//自機残数描画 printstr(70,0,7,0,"STAGE"); printnum(70+6*8,0,7,0,stage);//ステージ数描画 printstr(150,0,7,0,"SCORE"); printnum(150+6*8,0,7,0,score);//得点描画 } void erasechars(){ //描画オブジェクトの消去 //敵、自機など描画したキャラクタを全てここで消去する _Object *p; if(ship.on) clrbmpmn(ship.x,ship.y,XSIZE_SHIP,YSIZE_SHIP); //全敵分ループ for(p=enemybuf;pon) continue; clrbmpmn(p->x,p->y,XSIZE_ENEMY,YSIZE_ENEMY); } //全ミサイル分ループ for(p=missilebuf;pon) continue; clrbmpmn(p->x,p->y,XSIZE_MISSILE,YSIZE_MISSILE); } } void addmissile(short x,short y,short vx,short vy){ //ミサイル情報格納配列にミサイルを1つ追加 //(x,y):ミサイル位置 //(vx,vy):ミサイル速度 _Object *p;//ミサイル情報格納構造体配列へのポインタ //ミサイル情報格納配列内の空きを検索し、空き場所にミサイルを設定 for(p=missilebuf;pon) continue;//空いていないので次へ //配列に書き込み p->on=1; p->x=x; p->y=y; p->vx=vx; p->vy=vy; sounddatap=sounddata1;//ミサイル発射音設定 return; } //最後まで空きがなければ発射せずに戻る } void addscore(int s){ //得点追加(9999点まで) //s:追加する得点 score+=s; if(score>10000) score=9999; } void keycheck(){ //ボタン状態読み取り //keystatus :現在押されているボタンに対応するビットを1にする //keystatus2:前回押されていなくて、今回押されたボタンに対応するビットを1にする oldkey=keystatus; keystatus=~KEYPORT & (KEYUP | KEYDOWN | KEYLEFT | KEYRIGHT | KEYSTART | KEYFIRE); keystatus2=keystatus & ~oldkey; //ボタンから手を離したかチェック } void collisioncheck(){ //各種衝突チェック _Object *ep,*mp;//敵およびミサイル情報格納構造体配列へのポインタ //敵とミサイルの衝突チェック for(mp=missilebuf;mpon) continue; for(ep=enemybuf;epon) continue; //X座標チェック if(ep->x >= mp->x+XSIZE_MISSILE || ep->x+XSIZE_ENEMY <= mp->x) continue; //Y座標チェック if(ep->y >= mp->y+YSIZE_MISSILE || ep->y+YSIZE_ENEMY <= mp->y) continue; // ミサイルと敵が衝突した時の処理 enemydeath(ep);//敵死亡処理 mp->on=0; //ミサイル消滅 } } //自機と敵の衝突チェック if(!ship.on) return; for(ep=enemybuf;epon) continue; //X座標チェック if(ep->x >= ship.x+XSIZE_SHIP || ep->x+XSIZE_ENEMY <= ship.x) continue; //Y座標チェック if(ep->y >= ship.y+YSIZE_SHIP || ep->y+YSIZE_ENEMY <= ship.y) continue; // 自機と敵が衝突した時の処理 enemydeath(ep);//敵死亡処理 shipdeath();//自機死亡処理 } } void enemydeath(_Object *p){ //敵死亡処理 //p:敵情報格納構造体配列へのポインタ(死亡させる要素を指す) p->on=0;//敵消滅 enemyleft--; //敵残数減 addscore(10);//得点追加 sounddatap=sounddata2;//敵爆発音設定 } void shipdeath(){ //自機死亡処理 ship.on=0;//自機消滅 sounddatap=sounddata3;//自機爆発音設定 } void sound(){ //効果音とBGMを出力(効果音優先) //60分の1秒ごとに呼び出し unsigned short ontei;//音程カウンタ playmusic1step();//BGMの演奏を1つ進める ontei=2; //効果音 if(sounddatap!=NULL){ if(*sounddatap==0){ //効果音終了 sounddatap=NULL; if(music.stop) ontei=0; } else{ //効果音優先で音程設定 ontei=*sounddatap++; if(ontei==1) ontei=0;//休符 } } if(ontei!=2) ONTEI=ontei;//音程変更。ただしonteiが2の場合BGM優先 } void startmusic(const unsigned char *m){ // BGMスタート // music構造体に書き込むことで開始 // m:曲データ配列先頭 music.p=m; //演奏中の現在位置を表すポインタ music.startp=m;//リピート位置を表すポインタ music.count=1; //発音中の音符長さカウンタ music.stop=0; //BGM停止フラグ } void stopmusic(){ // BGM停止 music.stop=1; music.ontei=0; ONTEI=0;//サウンド停止 } void playmusic1step(){ //演奏中の曲を1つ進める if(music.stop) return; //演奏終了済み if(--music.count>0){ ONTEI=music.ontei;//音程設定 return; } //次の音を鳴らす if(*music.p==254){ //曲終了 music.stop=1; music.ontei=0; ONTEI=0;//サウンド停止 return; } if(*music.p==253){ //曲の最初に戻る music.p=music.startp; } if(*music.p==255){ //休符 music.ontei=0; ONTEI=0;//サウンド停止 } else{ music.ontei=sounddata[*music.p]; //周期データ ONTEI=music.ontei;//音程設定 } music.p++; music.count=*music.p; //音符長さ music.p++;//次の音へ }