情報システム実験 K-13 組み込みオペレーティングシステム No.5
ボールが壁に反射し続けるアニメーションに加えて, 「キーで左右に動かせるラケットに当たっても,ボールが反射する」 という動きを今回の目標としましょう.
ボールのアニメーションをするプログラムは, おおよそ次のように書けました.
while (1) {
ボールを1ステップ動かす
delay()で待つ
}
ボールとラケットの2つのものが動作するプログラムも同様に,
while (1) {
ボールを1ステップ動かす
ラケットを1ステップ動かす
delay()で待つ
}
のように書けますが,
このループの中でやることは今後ますます増えていきますから,
手に負えなくならないようにプログラムの構造をこのあたりで
考えておきましょう.たとえば以下のようにします.
main関数は,ball.h で公開される「ボールを1ステップ動かす」関数, racket.h で公開された「ラケットを1ステップ動かす」関数を利用して, たとえば以下のように書くことにします.
#include "gba.h"
#include "ball.h"
#include "racket.h"
// delay()関数などを定義...
int main(void)
{
// 画面を初期化
// タイマーを初期化
while (1) {
ball_step();
racket_step();
delay(INTERVAL);
}
}
racket.h には racket_step() 関数の宣言を
extern void racket_step(void);のように書いておきます.また,ball_step()も同様にball.hの中に書いておきます. 箱を描くのに必要な struct box の定義や,draw_box(), move_box()などの関 数も,box.c というモジュールに分け,box.hにインターフェースを書いてお きます.
モジュールがそれぞれ完全に独立していれば, 他のモジュールでやっていることを気にする必要がないのですが, 全体で一つのプログラムを動かしているわけですから, モジュール間でのデータのやりとりがどうしても必要になります.
今回の課題では,ballモジュールとracketモジュールの間で, ボールがラケットに当たったかどうかを判定する必要があります.
この判定は,どちらのモジュールが行なっても基本的にかまわないのですが, ここではracketモジュールが判定するとして説明します. racketモジュールのプログラム racket_step() の中からボールの位置を 問い合わせたり,ボールの速度(dy)を読み書きする必要があるわけですが, 直接 ball モジュールの変数を読み書きするのではなく,ballモジュールが 用意したインターフェース関数を利用して読み書きするようにします. 具体的には,ball.h に以下のようなインターフェースを記述します.
extern int ball_get_dy(void); // ボールのy方向の速度を返す. extern void ball_set_dy(int new_dy); // ボールのy方向の速度をセットする. extern struct box *ball_get_box(void); // ボールの箱の位置を返す. extern void ball_step(void); // アニメーションの1ステップを行なう.そして,ball.cの内容は以下のようになります.
#include "gba.h"
#include "box.h"
#include "ball.h"
static int dx, dy; /* ボールの現在の速度 */
static struct box b; /* ボールの箱の現在の位置 */
int ball_get_dy(void) { return dy; }
void ball_set_dy(int new_dy) { dy = new_dy; }
struct box *ball_get_box(void) { return &b; }
void ball_step(void)
{
...
}
static と宣言しておくと,このファイルの外からは参照できない
変数となります. 他のファイルと名前がぶつかる心配もいらなくなります.
このように,インターフェース関数だけでモジュールのデータを読み書き するように作ると,以下のような利点があります.
ラケットとボールが衝突したかどうかを判定するには, 2つの箱が画面上で重なっているかどうかを判定する関数を作っておくと便利です.
int cross(struct *b1, struct *b2)
{
// b1とb2の領域が重なっていれば1,重なっていなければ0を返す.
}
重なっているかどうかを判定する条件は,
まず x座標については
箱に関するモジュールのインターフェース box.h は次のようになります.
struct box { ... };
extern void draw_box(struct box *b, int x, int y, hword color);
extern void move_box(struct box *b, int x, int y, hword color);
extern int cross(struct box *b1, struct box *b2);
全体をまとめるMakefileを以下のように作っておくと良いでしょう.
AS = as-arm
CC = gcc-arm
LIBGCC = `gcc-arm -print-libgcc-file-name`
CFLAGS = -Wall -O -fno-builtin -fomit-frame-pointer -finhibit-size-directive \
-fno-ident
all: racket.bin
racket.bin: ball.o box.o crt.o main.o racket.o
ld-arm -o racket.out -T gcc.ls \
crt.o ball.o box.o main.o racket.o ${LIBGCC}
objcopy-arm -O binary racket.out racket.bin
clean:
rm -f *.o *.s *.out *.bin
ball.o: gba.h box.h ball.h
box.o: gba.h box.h
main.o: gba.h ball.h box.h racket.h
racket.o: gba.h box.h ball.h racket.h