ところで、前回のプログラムでは、同じBGの繰り返しになってしまいますので
意外性が全くありません。
今回は乱数を使ってBGデータを生成してみます。
ついでに簡単なタイトル画面を入れて、横スクロールするようにしてみました。
Test5d.c
意外性が全くありません。
今回は乱数を使ってBGデータを生成してみます。
ついでに簡単なタイトル画面を入れて、横スクロールするようにしてみました。
Test5d.c
#include <stdio.h>
#include <gb.h>
#include <rand.h>
// タイルデータ
#include "PenguinSpr.c"
#include "IceBkg.c"
// マップデータ
//#include "IceBkgMap.c"
#define SPR_HERO 0
#define SPR_HERO_SHADOW 2
#define HERO_ACT_NONE 0
#define HERO_ACT_WALK 1
#define HERO_ACT_JUMP 2
#define HERO_ACT_MISS 3
// プレイヤ用構造体
struct st_hero {
UBYTE act; // アクションno
UBYTE x; // X座標
UBYTE y; // Y座標
UBYTE cnt; // 汎用カウンタ
UBYTE jmp; // ジャンプカウンタ
UBYTE mtk; // 無敵カウンタ
} hero;
// アニメ用テーブル
UBYTE ani_tiles[]={0, 4, 12, 20};
// ジャンプ用テーブル
#define JMPTBL_MAX 31
BYTE jmptbl[]={
0, -2, -4, -6, -8, -9,-10,-11,-12,-12,-13,-13,-14,-14,-15,-15,
-15,-15,-14,-14,-13,-13,-12,-12,-11,-10, -9, -8, -6, -4, -2, 0,
};
UBYTE scrx;// スクロール用X座標
UBYTE scry;// スクロール用Y座標
UBYTE scrcnty;// スクロール用カウンタ
UBYTE bgsety;// BG描画用Y座標
UBYTE input_on, input_old, input_edge, input_trg;// キー入力用
fixed seed;// 乱数シード
unsigned char bgbuf[2];// BGデータ取得用
void hero_ctrl();
void hero_disp();
void make_bghole(UBYTE y);
void title_disp();
void main()
{
UBYTE i;// ループ用
DISPLAY_OFF;// 画面全体を非表示
HIDE_SPRITES;// スプライトを非表示
HIDE_BKG;// BGを非表示
SPRITES_8x16;// スプライトモードを8*16に設定
set_sprite_data(0, 52, PenguinSpTileLabel); // タイルデータをスプライトとして設定(0番から52個)
title_disp();// タイトル画面を描画
SHOW_BKG;// BGを表示
DISPLAY_ON;// 画面全体を表示
seed.b.l = DIV_REG;
// while (!(joypad() & J_START));// スタートキーが押されるまで無限ループ
waitpad(J_START);// スタートキーの入力待ち
seed.b.h = DIV_REG;
initrand(seed.w);// 乱数初期化
DISPLAY_OFF;// 画面全体を非表示
HIDE_SPRITES;// スプライトを非表示
HIDE_BKG;// BGを非表示
set_bkg_data(0, 6, IceBkgTileLabel); // タイルデータをBGとして設定
//set_bkg_tiles(0, 0, 20, 32, IceBkgMapLabel); // マップデータを設定する
cls();// 画面クリア
// 初期のBGデータを生成
for (bgsety = 0; bgsety < 19; bgsety+=2) {
make_bghole(bgsety);
}
SHOW_BKG;// BGを表示
SHOW_SPRITES;// スプライトを表示
DISPLAY_ON;// 画面全体を表示
// 初期値を設定
hero.act = 1;
hero.x = 88;
hero.y = 64;
hero.cnt = 0;
hero.jmp = 0;
hero.mtk = 60;
scry = scrcnty = 0;
while (1) {// 無限ループ
//キー入力
input_old = input_on;
input_on = joypad();
input_edge = input_on ^ input_old;
input_trg = input_on & input_edge;
if (hero.act != HERO_ACT_MISS) {
if (++scrcnty > 1) {
scrcnty = 0;
scry++;
// スクロール16ドット単位でBGデータを生成する
if ((scry & 0x0f) == 0) {
bgsety += 2;
bgsety &= 0x1f;// 0~31にする
make_bghole(bgsety);
}
}
}
move_bkg(scrx, scry);// BGの描画開始XY座標を設定
hero_ctrl();// ペンギンコントロール
hero_disp();// ペンギン表示
delay(10);// 10ミリ秒待つ
}
}
// タイトル表示
void title_disp()
{
print(" ");// ダミー描画
gotoxy(2, 5);
print("JUNPING PENGUIN");
gotoxy(5, 13);
print("PUSH START");
}
// ペンギンコントロール
void hero_ctrl()
{
UBYTE sx, sy;// BG調査用
// ミス中は移動できない
if (hero.act != HERO_ACT_MISS) {
// 右入力
if ((input_on & J_RIGHT) && (hero.x < 160)) hero.x++;
// 左入力
if ((input_on & J_LEFT) && (hero.x > 16)) hero.x--;
// A入力
if ((input_trg & J_A) && (hero.act == HERO_ACT_WALK)) {
hero.jmp = 1;// ジャンプ開始
hero.act = HERO_ACT_JUMP;
hero.cnt = 0;
}
}
scrx = (hero.x >> 1);
// ジャンプ中
if (hero.jmp > 0) {
hero.jmp++;
if (hero.jmp >= JMPTBL_MAX) {
hero.jmp = 0;// ジャンプ終了
hero.act = HERO_ACT_WALK;
hero.cnt = 0;
}
// 無敵中でなくてミス中でなければ当たり判定
} else if (hero.mtk == 0 && hero.act != HERO_ACT_MISS) {
sx = ((hero.x-8+scrx) >> 3);//
sy = ((hero.y-16+scry) >> 3);
get_bkg_tiles(sx,sy,1,1, bgbuf);// BGのデータを取得する
if (bgbuf[0] >= 3 && bgbuf[0] <= 5) {
hero.act = HERO_ACT_MISS;
hero.cnt = 0;
}
}
// ミス中なら
if (hero.act == HERO_ACT_MISS) {
if (hero.cnt > 60) {// 60カウントを超えたら歩きにする
hero.act = HERO_ACT_WALK;
hero.cnt = 0;
hero.mtk = 60;// 無敵時間の設定
}
}
// 無敵カウンタをデクリメント
if (hero.mtk > 0) hero.mtk--;
// カウンタをインクリメント
hero.cnt++;
}
// ペンギン表示
void hero_disp()
{
UBYTE i;// ループ用
UBYTE ani;// アニメ用
// 無敵中は2回に1回表示する
if ((hero.mtk & 0x01) == 0) {
// 左右の2枚を表示
for (i = 0; i < 2; i++) {
// ペンギン表示
ani = ani_tiles[(hero.act)] + (i*2) + ((hero.cnt/2) & 0x04);
set_sprite_tile( SPR_HERO+i, ani);// スプライトを設定
move_sprite( SPR_HERO+i, hero.x-8+(i*8), hero.y-16+jmptbl[(hero.jmp)]+(hero.act == HERO_ACT_MISS ? 3 :0));// スプライトの座標を設定
//影表示
if (hero.act < HERO_ACT_MISS) {
set_sprite_tile( SPR_HERO_SHADOW+i, 2);// スプライトを設定
move_sprite( SPR_HERO_SHADOW+i, hero.x-8+(i*8), hero.y-3);// スプライトの座標を設定
} else {
//表示座標を画面外にすることで非表示にしている
move_sprite( SPR_HERO_SHADOW+i, 0, 0);// スプライトの座標を設定
}
}
// 影の右側だけ左右反転
set_sprite_prop( SPR_HERO_SHADOW+1, S_FLIPX);
} else {
// 表示座標を画面外にすることで非表示にしている
for (i = 0; i < 2; i++) {
move_sprite( SPR_HERO+i, 0, 0);// スプライトの座標を設定
move_sprite( SPR_HERO_SHADOW+i, 0, 0);// スプライトの座標を設定
}
}
}
// BGに穴(または石ころ)を生成・描画する
void make_bghole(UBYTE y)
{
UBYTE i, r, ana = 0;
// 横32キャラ分を描画する
for (i = 0; i < 32; i++) {
r = rand();// 乱数取得
gotoxy(i, y);// 描画座標を指定
if (ana == 0) {
if ((r & 0x70) == 0) {
r &= 3;
setchar(r);
if (r == 3) ana = 1;// 3だったら穴モード
} else setchar(0);// 空白を描画
} else if (ana == 1) {// 穴モードだったら
setchar(4);// 穴の真ん中
if ((r & 1) == 0) ana = 2;// 乱数によっては穴モードを終了する
} else {
setchar(5);// 穴の右側
ana = 0;// 穴モード終了
}
}
}
随分と長いプログラムになってしまいました。
乱数を使うには、rand.hをインクルードします。
#include <rand.h>
そしてrand関数を呼べば0~255の値を返してくれますが
このままでは毎回同じパターンの乱数を返すため
乱数シードを使って乱数を初期化します。
fixed seed;// 乱数シード
この乱数シードを生成するために、タイトル画面で以下の事をやっています。
seed.b.l = DIV_REG;
waitpad(J_START);// スタートキーの入力待ち
seed.b.h = DIV_REG;
DIV_REGの値をseedの下位バイト・上位バイトへ代入しています。
DIV_REGは割込レジスタのようですが、詳細は分かりません。
waitpadで任意のキー入力待ちを行うことで、DIV_REGの値はプレイ毎に変化していく
ということです。
そしてinitrand関数に乱数シードを渡して乱数を初期化します。
initrand(seed.w);// 乱数初期化
文字列の表示にはprint関数を使用します。
GBDKでは、始めてprint関数(printfなども含む)またはsetchar関数を呼ぶと
BGのタイルデータへ自動的にASCII文字データをセットしてくれます。
(逆に言うと、それまでセットしておいたBGのタイルデータが上書きされる)
なので、タイトル画面の描画を行った後に「IceBkg.c」のタイルデータを上書きしています。
BGへの描画位置指定にはgotoxy関数を使用します。
BGの描画にはsetchar関数で描画したいタイルの番号を渡して呼びます。
setchar関数を呼んだ後、描画位置は変化しませんので
右にずらした値を設定して再度gotoxy関数を呼びます。
これで随分ゲームらしくなってきたような気がします。
次はスコアなどを入れてもっとゲームらしくしましょう。
0 件のコメント:
コメントを投稿