X68000 ブロック崩しファーストステップの締め
ゲームを作ってみたいという人のお役立ちになれたらというのとX68000パーソナルワークステーションの令和復活に敬意を!というわけで結構長く書いてきましたが、今回は第一段階の完成版を示します。コメント空行込みで318行です。このままXCでコンパイルしてできたxファイルを実行していただければ動くと思います。
cc /Y block.c [ret]
この段階では、X68000固有の機能は使用していませんので少し変えれば他の機種でも動くと思います。
初めて買ったパソコンがカシオのFP1100だったので当時は移植は日常茶飯事でした。
雑談ついでにX68000でほとんどの操作をファイラーを通して行っています。懐かしのFD(PC98ファイラープログラム)クローンのやどかり(YD)です。もっとメジャーなものがあるようですが、見た目がFDそっくりなことがお気に入りな点です。実は、Windowsのマウス操作よりこういったキーボードだけで完結する操作法の方が使いやすいと思っています。(個人の感想です 笑)


ゲーム画面です。いろいろ改良が必要ですが、とりあえず動くものを作ることが大切です。
今後は、これを改造していきます。
#include <class.h>
#include <iocslib.h>
/* X68000の16ビットカラーコード定義 */
#define COLOR_BLACK 0x0000
#define COLOR_WHITE 0xffff
#define COLOR_BLUE 0x003f
#define COLOR_RED 0x07c1
#define COLOR_GREEN 0xf801
#define COLOR_YELLOW 0xffc1
#define COLOR_CYAN 0xf83f
#define COLOR_MAGENTA 0x07ff
/* ブロックの配置設定 */
#define BLOCK_ROWS 5
#define BLOCK_COLS 12
int main(void) {
int old_mode;
struct FILLPTR pad;
struct FILLPTR ball;
struct FILLPTR wall;
struct FILLPTR blk; /* ブロック用の一時構造体 */
/* パドルの変数宣言 */
int pad_width;
int pad_height;
int pad_x;
int pad_y;
int speed;
int prev_time;
/* ボールの変数宣言 */
int ball_x;
int ball_y;
int ball_vx;
int ball_vy;
int ball_size;
/* ブロック管理の変数宣言 */
int blocks[BLOCK_ROWS][BLOCK_COLS];
int block_width;
int block_height;
int gap_x;
int gap_y;
int start_x;
int start_y;
int row;
int col;
/* 現在の画面モードを保存 */
old_mode = CRTMOD(-1);
/* 画面初期化 */
C_CUROFF();
CRTMOD(6);
G_CLR_ON();
/* 3. WINDOWの解像度を設定 */
WINDOW(0, 0, 255, 255);
/* 4. カラーパレット登録 */
GPALET(0, COLOR_BLACK);
GPALET(1, COLOR_WHITE);
GPALET(2, COLOR_BLUE);
GPALET(3, COLOR_RED);
GPALET(4, COLOR_GREEN);
GPALET(5, COLOR_YELLOW);
GPALET(6, COLOR_CYAN);
GPALET(7, COLOR_MAGENTA);
/* 外壁の描画 */
wall.x1 = 0;
wall.y1 = 0;
wall.x2 = 3;
wall.y2 = 240;
wall.color = 5;
FILL(&wall);
wall.x1 = 252;
wall.y1 = 0;
wall.x2 = 255;
wall.y2 = 240;
wall.color = 5;
FILL(&wall);
wall.x1 = 0;
wall.y1 = 0;
wall.x2 = 255;
wall.y2 = 3;
wall.color = 5;
FILL(&wall);
/* ブロック群の初期化と描画 */
block_width = 19;
block_height = 8;
gap_x = 1;
gap_y = 2;
start_x = 6; /* 左右の壁の内側 */
start_y = 25; /* 天井より少し下から配置 */
for (row = 0; row < BLOCK_ROWS; row++) {
for (col = 0; col < BLOCK_COLS; col++) {
blocks[row][col] = 1; /* 存在 */
/* 描画座標を計算 */
blk.x1 = start_x + col * (block_width + gap_x);
blk.y1 = start_y + row * (block_height + gap_y);
blk.x2 = blk.x1 + block_width - 1;
blk.y2 = blk.y1 + block_height - 1;
blk.color = 3 + row; /* パレット3番から7番を行ごとに割り当て */
FILL(&blk);
}
}
/* パドルの初期座標とサイズ設定 */
pad_width = 30;
pad_height = 6;
pad_x = (256 - pad_width) / 2;
pad_y = 205;
speed = 4;
/* 最初にパドルを描画 */
pad.x1 = pad_x;
pad.y1 = pad_y;
pad.x2 = pad_x + pad_width - 1;
pad.y2 = pad_y + pad_height - 1;
pad.color = 1;
FILL(&pad);
/* ボールの初期座標と速度設定 */
ball_size = 4;
ball_x = 128;
ball_y = 100; /* ブロックの下から発射 */
ball_vx = 2;
ball_vy = 2;
/* ゲームのメインループ */
prev_time = ONTIME();
while (1) {
int current_time;
int is_moved;
int new_x;
int b_hit;
/* ウェイト処理(10ミリ秒経過を待つ) */
while (1) {
current_time = ONTIME();
if (current_time != prev_time) {
prev_time = current_time;
break;
}
}
/* ESCキーで終了 */
if ((BITSNS(0) & 2) != 0) {
break;
}
/* --- パドルの移動処理 --- */
is_moved = 0;
new_x = pad_x;
/* カーソル左キー (グループ7, bit3) */
if ((BITSNS(7) & 8) != 0) {
new_x = pad_x - speed;
if (new_x < 5) {
new_x = 5;
}
is_moved = 1;
}
/* カーソル右キー (グループ7, bit5) */
if ((BITSNS(7) & 32) != 0) {
new_x = pad_x + speed;
if (new_x > 251 - pad_width) {
new_x = 251 - pad_width;
}
is_moved = 1;
}
if (is_moved != 0 && new_x != pad_x) {
/* 旧パドル消去 */
pad.x1 = pad_x;
pad.y1 = pad_y;
pad.x2 = pad_x + pad_width - 1;
pad.y2 = pad_y + pad_height - 1;
pad.color = 0;
FILL(&pad);
pad_x = new_x;
/* 新パドル描画 */
pad.x1 = pad_x;
pad.y1 = pad_y;
pad.x2 = pad_x + pad_width - 1;
pad.y2 = pad_y + pad_height - 1;
pad.color = 1;
FILL(&pad);
}
/* --- ボールの移動と消去 --- */
ball.x1 = ball_x;
ball.y1 = ball_y;
ball.x2 = ball_x + ball_size - 1;
ball.y2 = ball_y + ball_size - 1;
ball.color = 0;
FILL(&ball);
ball_x += ball_vx;
ball_y += ball_vy;
/* --- 壁との衝突判定 --- */
if (ball_x < 4) {
ball_x = 4;
ball_vx = -ball_vx;
} else if (ball_x > 251 - ball_size) {
ball_x = 251 - ball_size;
ball_vx = -ball_vx;
}
if (ball_y < 4) {
ball_y = 4;
ball_vy = -ball_vy;
}
/* --- パドルとの衝突判定 --- */
if (ball_vy > 0) {
if (ball_y + ball_size >= pad_y && ball_y <= pad_y + pad_height) {
if (ball_x + ball_size >= pad_x && ball_x <= pad_x + pad_width) {
ball_y = pad_y - ball_size;
ball_vy = -ball_vy;
}
}
}
/* --- ブロックとの当たり判定 --- */
b_hit = 0;
for (row = 0; row < BLOCK_ROWS && b_hit == 0; row++) {
for (col = 0; col < BLOCK_COLS; col++) {
if (blocks[row][col] == 1) {
/* 各ブロックの座標を算出 */
int blx1 = start_x + col * (block_width + gap_x);
int bly1 = start_y + row * (block_height + gap_y);
int blx2 = blx1 + block_width - 1;
int bly2 = bly1 + block_height - 1;
/* ボールとブロックが交差しているか(矩形の当たり判定) */
if (ball_x <= blx2 && ball_x + ball_size - 1 >= blx1 &&
ball_y <= bly2 && ball_y + ball_size - 1 >= bly1) {
/* 該当ブロックを破壊状態にする */
blocks[row][col] = 0;
/* ブロックを黒(背景色)で上書きして消去 */
blk.x1 = blx1;
blk.y1 = bly1;
blk.x2 = blx2;
blk.y2 = bly2;
blk.color = 0;
FILL(&blk);
/* --- 修正の肝:位置補正 --- */
/* ボールが「上から下」へ進んでいて当たり判定に触れた場合、
ボールの底辺をブロックの上端に合わせる(めり込み防止) */
if (ball_vy > 0) {
ball_y = bly1 - ball_size;
ball_vy = -ball_vy;
}
/* ボールが「下から上」へ進んでいて当たり判定に触れた場合、
ボールの上辺をブロックの下端に合わせる(めり込み防止) */
else if (ball_vy < 0) {
ball_y = bly2 + 1;
ball_vy = -ball_vy;
}
/* 1フレーム中に何個も同時破壊しないようループを脱出 */
b_hit = 1;
break;
}
}
}
}
/* --- ミス判定(ボール落下時の処理) --- */
if (ball_y > 230) {
ball_x = 128;
ball_y = 100;
ball_vx = 2;
ball_vy = 2;
/* ウェイト処理(ONTIMEが変化する=1000ms経過するまで待つ) */
while (1) {
current_time = ONTIME();
if (current_time -100 > prev_time) {
prev_time = current_time;
break;
}
}
}
/* --- 新しい位置にボールを描画 --- */
ball.x1 = ball_x;
ball.y1 = ball_y;
ball.x2 = ball_x + ball_size - 1;
ball.y2 = ball_y + ball_size - 1;
ball.color = 1;
FILL(&ball);
}
/* 画面モードを元に戻して終了 */
C_CURON();
CRTMOD(old_mode);
return 0;
}2026.7.22
