ゲームプログラマになる前に覚えておきたい技術
http://www.shuwasystem.co.jp/support/7980html/2118.html
サンプルプログラムの荷物君をz88dkでビルドしてPV-1000で動かしてみます。
>zcc +pv1000 main.c -create-app
main.c
/* Nimotsu Kun for PV-1000 by tanam [2019] */ #include "games.h" typedef int bool; typedef int Object; typedef unsigned char u8; typedef unsigned short u16; #define true 1 #define false 0 #define KEY_UP1 MOVE_UP #define KEY_DOWN1 MOVE_DOWN #define KEY_LEFT1 MOVE_LEFT #define KEY_RIGHT1 MOVE_RIGHT #define KEY_A MOVE_FIRE1 #define KEY_B MOVE_FIRE2 unsigned char *screen=0xB800; u16 game[224]; /* #壁 _空間 .ゴール oブロック p人 */ const char* gStageData = "\ ########\n\ # .. p #\n\ # oo #\n\ # #\n\ ########\n\ \n\ 0\ ###### \n\ #. # \n\ # o o # \n\ # op# # \n\ # # # \n\ #.###.# \n\ ### ### 0"; #define gStageWidth 8 #define gStageHeight 7 enum OBJ { OBJ_SPACE, OBJ_WALL, OBJ_GOAL, OBJ_BLOCK, OBJ_BLOCK_ON_GOAL, OBJ_MAN, OBJ_MAN_ON_GOAL, OBJ_UNKNOWN, }; Object state[56]; void screen_set_char(u8 xts, u8 yts, u8 xte, u8 yte, u16 *map) { int i, j, k; k=0; for (i = 0; i < 24; i++) for (j = 0; j < 32; j++) if (j > xts && j < xte && i > yts && i < yte) screen[(i+1)*32+j+1]=map[8*(i-1)+j-xts-1]; return; } /* 関数プロトタイプ */ void initialize( Object* state, char* stageData ); void draw( Object* state, int w, int h ); int update( Object* state, u16 input, int w, int h ); bool checkClear( Object* state, int w, int h ); int main(){ int stage; clg(); for (stage=0; stage<2; stage++) { RESET: initialize( state, gStageData+stage*63); /* ステージ初期化 */ /* メインループ */ while ( true ){ /* まず描画 */ draw( state, gStageWidth, gStageHeight ); /* クリアチェック */ if ( checkClear(state, gStageWidth, gStageHeight ) ){ break; /* クリアチェック */ } /* 更新 */ if (-1 == update( state, joystick(0), gStageWidth, gStageHeight )){ goto RESET; } } } return 0; } void initialize( Object* state, char* stageData ){ const char* d = stageData; /* 読み込みポインタ */ int x = 0; int y = 0; while ( *d != '0' ){ /* NULL文字でない間 */ Object t; /* 特に意味はないが使う回数が多い変数に私は良くtを使う。temporaryの略。たぶんよくない習慣だが、無駄に長い名前にして読みにくいのも困り物だろう。 */ switch ( *d ){ case '#': t = OBJ_WALL; break; case ' ': t = OBJ_SPACE; break; case 'o': t = OBJ_BLOCK; break; case 'O': t = OBJ_BLOCK_ON_GOAL; break; case '.': t = OBJ_GOAL; break; case 'p': t = OBJ_MAN; break; case 'P': t = OBJ_MAN_ON_GOAL; break; case '\n': x = 0; ++y; t = OBJ_UNKNOWN; break; /* 改行処理 */ default: t = OBJ_UNKNOWN; break; } ++d; if ( t != OBJ_UNKNOWN ){ /* 知らない文字なら無視するのでこのif文がある */ state[y * gStageWidth + x ] = t; /* 書き込み */ ++x; } } return; } void draw(Object* state, int width, int height ){ int x, y; char font[] = {' ', '#', '.', 'o', 'O', 'p', 'P'}; /* u16 font[] = {10,11,12,13,14,15,16}; /* Object列挙の順 */ for ( y = 0; y < height; ++y ){ for (x=0; x < width; ++x ){ Object o = state[ y * width + x ]; game[y * gStageWidth + x] = font[ o ]; } } screen_set_char(0, 0, 9, 8, game); } /* 第一引数はほかの関数ではstateとしているが、あまりに頻繁に使うので 短いsで済ませている。w,hもそれぞれwidth,heightである。 */ int update( Object* s, u16 input, int w, int h ){ /* 移動差分に変換(dはdifferenceでもdeltaでもお好きな方の略だと思って欲しい) */ int dx = 0; int dy = 0; int i = -1; int x, y, tx, ty, p, tp, tx2, ty2, tp2; switch ( input ){ case KEY_LEFT1: dx = -1; break; case KEY_RIGHT1: dx = 1; break; case KEY_UP1: dy = -1; break; case KEY_DOWN1: dy = 1; break; case KEY_A: return -1; } /* 人座標を検索 */ for ( i = 0; i < w * h; ++i ){ if ( s[ i ] == OBJ_MAN || s[ i ] == OBJ_MAN_ON_GOAL ){ break; } } x = i % w; /* xは幅で割ったあまり */ y = i / w; /* yは幅で割った商 */ /* 移動後座標(tに意味はない。ごめんなさい) */ tx = x + dx; ty = y + dy; /* 座標の最大最小チェック。外れていれば不許可 */ if ( tx < 0 || ty < 0 || tx >= w || ty >= h ){ return; } /* A.その方向が空白またはゴール。人が移動。 */ p = y*w + x; /* 人位置 */ tp = ty*w + tx; /* ターゲット位置(TargetPosition) */ if ( s[ tp ] == OBJ_SPACE || s[ tp ] == OBJ_GOAL ){ s[ tp ] = ( s[ tp ] == OBJ_GOAL ) ? OBJ_MAN_ON_GOAL : OBJ_MAN; /* ゴールならゴール上の人に */ s[ p ] = ( s[ p ] == OBJ_MAN_ON_GOAL ) ? OBJ_GOAL : OBJ_SPACE; /* もともとゴール上ならゴールに */ /* B.その方向が箱。その方向の次のマスが空白またはゴールであれば移動。 */ }else if ( s[ tp ] == OBJ_BLOCK || s[ tp ] == OBJ_BLOCK_ON_GOAL ){ /* 2マス先が範囲内かチェック */ tx2 = tx + dx; ty2 = ty + dy; if ( tx2 < 0 || ty2 < 0 || tx2 >= w || ty2 >= h ){ /* 押せない */ return; } tp2 = ( ty + dy )*w + ( tx + dx ); /* 2マス先 */ if ( s[ tp2 ] == OBJ_SPACE || s[ tp2 ] == OBJ_GOAL ){ /* 順次入れ替え */ s[ tp2 ] = ( s[ tp2 ] == OBJ_GOAL ) ? OBJ_BLOCK_ON_GOAL : OBJ_BLOCK; s[ tp ] = ( s[ tp ] == OBJ_BLOCK_ON_GOAL ) ? OBJ_MAN_ON_GOAL : OBJ_MAN; s[ p ] = ( s[ p ] == OBJ_MAN_ON_GOAL ) ? OBJ_GOAL : OBJ_SPACE; } } return 0; } /* ブロックのみがなければクリアしている。 */ bool checkClear(Object* s, int width, int height ){ int i; for (i = 0; i < width*height; ++i ){ if ( s[ i ] == OBJ_BLOCK ){ return false; } } return true; }