- 注册时间
- 2004-11-1
- 最后登录
- 2018-4-24
版主
  
- 积分
- 548
|

楼主 |
发表于 2007-5-17 21:44:02
|
显示全部楼层
原文,复制过来格式上有点乱
[code]
実装
方針
大まかな方針として、ゲームウィンドウクラス、ゲームマスタークラス、モグラクラスの3つに分けることにします。それぞれのクラスの役割は以下の通りです。
クラス
役割
備考
ゲームウィンドウクラス
ウィンドウの表示、メニューの表示、残り時間の表示
Windowクラスを継承
ゲームマスタークラス
ゲームの進行役:得点の管理、モグラの管理を行う
モグラクラス
モグラ(の画像)の表示、マウスイベントの処理、効果音の再生
Layerクラスを継承
骨組みの実装:ゲームウィンドウクラス
まずは骨組みを実装してみましょう。メソッドの中身は徐々に実装していきます。最初はゲームウィンドウクラスの骨組みです。
// ゲームウィンドウクラス
class GameWindow extends Window
{
var file_menu; // ファイルメニュー
var start_menu; // ゲーム開始メニュー
var exit_menu; // 終了メニュー
var master; // ゲームマスター
// コンストラクタ
function GameWindow()
{
super.Window(); // スーパークラスのコンストラクタを呼び出す
}
// デストラクタ
function finalize()
{
super.finalize(); // スーパークラスのデストラクタを呼び出す
}
// メニュー選択を処理するaction()メソッド
function action(ev) { }
}
// ゲームウィンドウオブジェクトを作成
var win = new GameWindow();
?謦幞渐氓蒩ction()の説明は、「吉里吉里2リファレンス」の「イベントシステム」を参照してください。MenuItemオブジェクトがaction()メソッドを呼び出すとは明記されていませんが、同梱のTJSサンプル(viewerなど)から、それが推測できます。□
Windowクラスのオブジェクトは、それ単体ではメニューなどを一切持っていません。そこで、Windowクラスを継承し、必要なメニューなどを加えたゲームウィンドウクラスを作成するのです。
メンバ変数は次のように使うことにします。
メンバ変数
役割
備考
file_menu
[ファイル]メニューを作る
start_menu
[ファイル‐ゲーム開始]メニューを作る
exit_menu
[ファイル‐閉じる]メニューを作る
master
ゲームマスターオブジェクト
後述
骨組みの実装:ゲームマスタークラス
次はゲームマスタークラスの骨組みです。タイマーオブジェクトと、タイマーイベントを処理するメソッドが2つずつあることに注意してください。タイマー1の役割は、モグラを動かし、得点を更新することです。また、タイマー2の役割は、残り時間を更新することです。
■1つのタイマーオブジェクトでも事足りますが、判りやすくするために2つに分けています。□
// ゲームマスタークラス(モグラオブジェクトの管理などを行う)
class GemeMaster
{
var window; // ゲームウィンドウオブジェクト
var parent; // 親レイヤー:(ゲームウィンドウの)プライマリレイヤー
var fore; // 親レイヤーのコピー:表画面として使う
var moles = []; // モグラオブジェクトの配列
var timer1; // タイマー1:モグラ用
var timer2; // タイマー2:残り時間用
var rest_time; // 残り時間
var point; // 得点
// コンストラクタ
function GemeMaster(win, par) { }
// デストラクタ
function finalize() { }
// ゲームを開始
function start() { }
// ゲームを終了
function stop() { }
// タイマーイベントを処理:モグラを動かす
function onTimer1() { }
// タイマーイベントを処理:残り時間を更新する
function onTimer2() { }
}
骨組みの実装:モグラクラス
最後にモグラクラスの骨組みです。モグラクラスはレイヤークラスを継承します。もちろん、現実のモグラがレイヤーを継承することなどあり得ません。レイヤーの持つ機能――画像の表示・非表示やマウスイベントの処理――を特化し、ゲーム用のモグラに仕立てているだけです。モグラクラスではonMouseDown()メソッドをオーバーライドします。
モグラクラスではKAGの関数intrandom()を使います。この関数はKAGからしか使えないため、あらかじめkag3\\template\\system\\Util.tjsから該当コードをコピーしておきます。
■intrandom()では、いくつか説明していない演算子を使っていますが、各自で調べてみてください。□
// Util.tjsからintrandom()のコードを拝借
function intrandom(min = 0, max = 0)
{
// min 以上 max 以下の整数の乱数を返す
// 引数が一個だけの場合は 0 ~ その数までの整数を返す
if(min>max) { min <-> max; }
return int(Math.random() * (max-min+1)) + min;
}
// モグラクラス(のスケルトンコード)
class Mole extends Layer
{
var master; // ゲームマスター
var hit_sound; // 叩かれたときの効果音
var counter; // イベントのカウンター(-hidden_time~visible_time)
var hidden_time; // 隠れている時間
var visible_time; // 出現している時間
function Mole(gm, window, parent) // コンストラクタ
{
super.Layer(window, parent); // スーパークラスのコンストラクタを呼び出し
}
function finalize() // デストラクタ
{
super.finalize(); // スーパークラスのデストラクタを呼び出し
}
function reset() { } // パラメータを再設定
function move() { } // モグラの画像を表示したり非表示したりする
function onMouseDown(x, y, button, shift) { } // マウスイベントを処理
}
メンバ変数は次のように使うことにします。
メンバ変数
説明
備考
master
ゲームマスターオブジェクト
hit_sound
WaveSoundBufferオブジェクト。モグラが叩かれたとき、効果音を鳴らす
counter
タイマーイベントのカウンター。-hidden_timeからvisible_timeの間で変化する。負ならモグラを非表示、0以上なら表示させる
hidden_time
モグラが隠れている時間。reset()が呼び出されるたびに乱数で設定
単位はタイマーイベントの回数
visible_time
モグラが出現している時間。reset()が呼び出されるたびに乱数で設定
同上
ゲームウィンドウクラス:コンストラクタ、デストラクタ
ゲームウィンドウクラスのコンストラクタ、デストラクタを実装します。
// コンストラクタ
function GameWindow()
{
super.Window(); // スーパークラスのコンストラクタを呼び出す
// [ファイル]、[ファイル‐ゲーム開始]、[ファイル‐閉じる]メニューを作成
menu.add(file_menu = new MenuItem(this, "ファイル(&F)"));
file_menu.add(start_menu = new MenuItem(this, "ゲーム開始(&S)"));
file_menu.add(exit_menu = new MenuItem(this, "閉じる(&C)"));
// プライマリレイヤーを作成し、ゲームウィンドウ自身に登録
add(new Layer(this, null));
with (primaryLayer)
{
// 背景画像を読み込み、そのサイズに設定
.loadImages("stage");
.setSizeToImageSize();
}
// ゲームウィンドウをプライマリレイヤーのサイズに合わせる
setInnerSize(primaryLayer.width, primaryLayer.height);
caption = "モグラ叩きゲーム"; // ゲームウィンドウのタイトルを設定
visible = true; // ゲームウィンドウを表示
// ゲームマスターオブジェクトを作成
master = new GemeMaster(this, primaryLayer);
}
// デストラクタ
function finalize()
{
// ゲームマスターを無効化
invalidate master;
// メニューを無効化
invalidate exit_menu;
invalidate start_menu;
invalidate file_menu;
super.finalize(); // スーパークラスのデストラクタを呼び出す
}
デストラクタの説明は必要ないでしょうから、コンストラクタのみ説明します。
最初にメニューアイテムオブジェクトを作り、[ファイル]、[ファイル‐ゲーム開始]、[ファイル‐閉じる]メニューを作成しています。これらのメニューを選択したら、(後述の)メソッドaction()が呼ばれるようになります
次にプライマリレイヤーオブジェクトを作り、add()メソッドゲームウィンドウ自身に登録します。以降、プライマリレイヤーはprimaryLayerプロパティでアクセスできるようになります
プライマリレイヤーに背景画像などを読み込ませます。プライマリレイヤーは、後で裏画面として使います
最後にゲームマスターオブジェクトを作成します(引数はゲームウィンドウ自身とプライマリレイヤー)
以下のトピックも参照してください。
リファレンス
トピック
サブトピック
吉里吉里2リファレンス
Windowクラス
メソッドadd()、setInnerSize()
プロパティcaption、visible
MenuItemクラス
コンストラクタ
メソッドadd()
Layerクラス
コンストラクタ
メソッドloadImages()、SetSizeToImageSize()
プロパティwidth、height
プライマリレイヤーを作るときの引数nullは、「オブジェクトではあるが、何のオブジェクトも示していない」ことを示す、TJSのキーワードです。voidとは少し意味が異なります。
ゲームウィンドウクラス:メソッドaction()
ゲームウィンドウクラスのメソッドaction()を実装します。引数evは辞書オブジェクトで、キー"type"にイベントの種類が、キー"target"にイベントを発生させたオブジェクトが渡されます。
// メニュー選択を処理するaction()メソッド
function action(ev)
{
if (ev.type == "onClick")
{
switch (ev.target) {
case start_menu: // [ゲーム開始]メニューを選択
master.start();
break;
case exit_menu: // [閉じる]メニューを選択
close();
}
}
}
イベントの種類が"onClick"、イベント発生させたオブジェクトがstart_menuかexit_menuの場合のみ、処理します。
■メソッドaction()の詳細は、「吉里吉里2リファレンス」の「イベントシステム」を参照してください。□
ゲームマスタークラス:コンストラクタ、デストラクタ
ゲームマスタークラスのコンストラクタとデストラクタを実装します。
// コンストラクタ
function GemeMaster(win, par)
{
window = win;
parent = par;
// 親レイヤーのコピーを作る
fore = new Layer(win, par);
with (fore)
{
.visible = true;
.assignImages(parent);
.setSizeToImageSize();
// フォントの高さを30ピクセルに設定(残り時間、得点の文字に使う)
.font.height = 30;
}
// モグラオブジェクトを作成
moles.add(new Mole(this, window, fore));
moles.add(new Mole(this, window, fore));
moles.add(new Mole(this, window, fore));
// モグラの表示位置を設定
// (モグラの画像サイズによって調整してください)
moles[0].top = 100;
moles[0].left = 50;
moles[1].top = 100;
moles[1].left = 250;
moles[2].top = 100;
moles[2].left = 450;
// モグラ用タイマーオブジェクトを作成
timer1 = new Timer(onTimer1, "");
timer1.interval = 100; // タイマーイベントは100ミリ秒間隔で発生
// 残り時間用タイマーオブジェクトを作成
timer2 = new Timer(onTimer2, "");
timer2.interval = 1000; // タイマーイベントを1秒間隔で発生させる
}
// デストラクタ
function finalize()
{
// タイマーを無効化
invalidate timer2;
invalidate timer1;
// モグラを無効化
for (var i = 0; i < moles.count; i++) {
invalidate moles;
}
// 表画面を無効化
invalidate fore;
}
デストラクタの説明は必要ないでしょうから、コンストラクタのみ説明します。
親レイヤー(プライマリレイヤー)のコピーforeを作ります。以降、foreを表画面レイヤーとして扱い、モグラ、残り時間、得点の表示を行います。また、親レイヤーは裏画面として扱うことにします(後述)
モグラオブジェクトを作成し、表示する座標を設定します
タイマーオブジェクトを2つ作成します。timer1はモグラを動かすため、timer2は残り時間をカウントするためのタイマーです
以下のトピックも参照してください。
リファレンス
トピック
サブトピック
吉里吉里2リファレンス
Layerクラス
メソッドassignImages()
プロパティvisible、font、top、left
Fontクラス
プロパティheight
Timerクラス
コンストラクタ
プロパティinterval
親子関係を持つレイヤーは、特に指定しない限り、親レイヤーが奥、子レイヤーが手前に表示されます。今回のスクリプトでは、奥から順番にプライマリレイヤー、表画面レイヤー、モグラ(レイヤー)…となるようにします。
ゲームマスタークラス:メソッドstart()、stop()
ゲームマスタークラスのメソッドstart()とstop()を実装します。メソッドstart()では、得点や残り時間を設定し、タイマーを発生させています。メソッドstop()ではタイマーの発生を停止させます。
// ゲームを開始
function start()
{
point = 0;
rest_time = 20;
timer1.enabled = timer2.enabled = true;
}
// ゲームを終了
function stop()
{
timer1.enabled = timer2.enabled = false;
}
以下のトピックも参照してください。
リファレンス
トピック
サブトピック
吉里吉里2リファレンス
Timerクラス
プロパティenabled
ゲームマスタークラス:メソッドonTimer1()、onTimer2()
ゲームマスタークラスのメソッドonTimer1()、onTimer2()を実装します。
// タイマーイベントを処理:モグラを動かす
function onTimer1()
{
// モグラを動かす
for (var i = 0; i < moles.count; i++) {
moles.move();
}
// 得点を更新する
with (fore) {
.copyRect(500, 0, parent, 500, 0, 160, 30);
.drawText(500, 0, "得点:"+point, 0x000000);
}
}
// タイマーイベントを処理:残り時間を更新する
function onTimer2()
{
// 残り時間を更新する
with (fore) {
.copyRect(0, 0, parent, 0, 0, 160, 30);
.drawText(0, 0, "残り時間:"+(rest_time--), 0x000000);
}
if (rest_time < 0) {
stop();
}
}
どちらのメソッドも、copyRect()で親レイヤー(プライマリレイヤー)の画像の一部をコピーしていることに注意してください。drawText()で同じ座標に文字を描くと、以前の文字が残ったまま上書きされてしまうので、あらかじめ消しておく必要があるのです。
以下のトピックも参照してください。
リファレンス
トピック
サブトピック
吉里吉里2リファレンス
Layerクラス
メソッドcopyRect()、drawText()
■copyRect()、drawText()への引数(左上端の座標、幅、高さ)は、背景画像ファイルのサイズに合わせて調整してください。このスクリプトでは640×480ピクセルの画像ファイルを想定しています。□
メンバ変数rest_timeは、メソッドonTimer2()が呼び出されるたびに-1され、負になった時点でメソッドstop()を呼び出します。
メンバ変数pointを変更するのはゲームマスターオブジェクトではありません。後述のモグラオブジェクトが値を変更します。
モグラクラス:コンストラクタ、デストラクタ
モグラクラスのコンストラクタ、デストラクタを実装します。
■コンストラクタ引数のparentには、ゲームウィンドウのプライマリレイヤーではなく、ゲームマスターのforeが渡されます(ゲームマスタークラスのコンストラクタを参照)。モグラクラスにとっての親レイヤー、と言う意味です。□
// コンストラクタ
function Mole(gm, window, parent)
{
super.Layer(window, parent); // スーパークラスのコンストラクタを呼び出し
master = gm;
// 効果音ファイルを読み込む
hit_sound = new WaveSoundBuffer(window);
hit_sound.open("hit.wav"); // ←拡張子もつけないとダメらしい
// マウスイベントを受け取る
hitType = htMask;
hitThreshold = 0;
// モグラの画像を読み込み、レイヤーのサイズを画像のそれと同じにする
loadImages("mole");
setSizeToImageSize();
}
// デストラクタ
function finalize()
{
invalidate hit_sound;
super.finalize(); // スーパークラスのデストラクタを呼び出し
}
デストラクタの説明は必要ないでしょうから、コンストラクタのみ説明します。
WaveSoundBufferオブジェクトを作り、効果音ファイルを読み込んでおく
モグラ自身の設定を行う:マウスイベントを拾うように設定し、モグラの画像を読み込む
以下のトピックも参照してください。
リファレンス
トピック
サブトピック
吉里吉里2リファレンス
WaveSoundBufferクラス
コンストラクタ
メソッドopen()
Layerクラス
プロパティhitType、hitThreshold
モグラクラス:メソッドreset()、move()
モグラクラスのメソッドreset()、move()を実装します。
// パラメータを再設定
function reset()
{
visible = false; // 非表示にする
var interval = master.timer1.interval;
// 隠れている時間は1~5秒
hidden_time = intrandom(1*1000\\interval, 5*1000\\interval);
// 出現している時間は0.5~2秒
visible_time = intrandom(1\\2*1000\\interval, 2*1000\\interval);
counter = -hidden_time;
}
// モグラの画像を非表示したり表示したりする
function move()
{
if (counter > visible_time) { // counterがtimeより大きい
reset();
}
else {
if (counter == 0) { // counterが0なら、以降からモグラを表示
visible = true;
}
counter++;
}
}
メソッドreset()では、隠れている時間と出現している時間を乱数で決定します。メソッドmove()では、モグラ(の画像)をどのタイミングで表示させるかを決定します。counterが負の間は画像を非表示に、0以上になったら画像を表示させるようにします。
■メンバ変数hidden_timeには、大して意味がありません。継承したときに使い道があればどうぞ…と言う程度のものです。□
モグラクラス:メソッドonMouseDown()
モグラクラスのメソッドonMouseDown()を実装します(スーパークラスLayerのメソッドonMouseDown()をオーバーライド)。引数buttonにはマウスのどのボタンを押したかの情報が渡されます。
// マウスイベントを処理
function onMouseDown(x, y, button, shift)
{
if (button == mbLeft && visible) { // モグラが表示中にマウス左ボタンがクリックされた
hit_sound.play(); // 効果音を鳴らす
master.point++; // 得点を加算
reset();
}
}
もし、モグラが表示中に、モグラ上をマウス左クリックしたら、効果音を鳴らし、得点を加算します。また、メソッドreset()を呼び出し、非表示状態から再開します。
以下のトピックも参照してください。
リファレンス
トピック
サブトピック
吉里吉里2リファレンス
WaveSoundBufferクラス
メソッドplay()
Layerクラス
メソッドonMouseDown()
全コード
完成したモグラ叩きゲームの全コードを示します。プロジェクトフォルダ名はgame、ファイル名はstartup.tjsとします。
// モグラ叩きゲーム
// Util.tjsからintrandom()のコードを拝借
function intrandom(min = 0, max = 0)
{
// min 以上 max 以下の整数の乱数を返す
// 引数が一個だけの場合は 0 ~ その数までの整数を返す
if(min>max) { min <-> max; }
return int(Math.random() * (max-min+1)) + min;
}
// モグラクラス
class Mole extends Layer
{
var master; // ゲームマスター
var hit_sound; // 叩かれたときの効果音
var counter; // イベントのカウンター(-hidden_time~visible_timeの間で変化)
var hidden_time; // 隠れている時間
var visible_time; // 出現している時間
// コンストラクタ
function Mole(gm, window, parent)
{
super.Layer(window, parent); // スーパークラスのコンストラクタを呼び出し
master = gm;
// 効果音ファイルを読み込む
hit_sound = new WaveSoundBuffer(window);
hit_sound.open("hit.wav"); // ←拡張子もつけないとダメらしい
// マウスイベントを受け取る
hitType = htMask;
hitThreshold = 0;
// モグラの画像を読み込み、レイヤーのサイズを画像のそれと同じにする
loadImages("mole");
setSizeToImageSize();
}
// デストラクタ
function finalize()
{
invalidate hit_sound;
super.finalize(); // スーパークラスのデストラクタを呼び出し
}
// パラメータを再設定
function reset()
{
visible = false; // 非表示にする
var interval = master.timer1.interval;
// 隠れている時間は1~5秒
hidden_time = intrandom(1*1000\\interval, 5*1000\\interval);
// 出現している時間は0.5~2秒
visible_time = intrandom(1\\2*1000\\interval, 2*1000\\interval);
counter = -hidden_time;
}
// モグラの画像を非表示したり表示したりする
function move()
{
if (counter > visible_time) { // counterがtimeより大きい
reset();
}
else {
if (counter == 0) { // counterが0なら、以降からモグラを表示
visible = true;
}
counter++;
}
}
// マウスイベントを処理
function onMouseDown(x, y, button, shift)
{
if (button == mbLeft && visible) { // モグラが表示中にマウス左ボタンがクリックされた
hit_sound.play(); // 効果音を鳴らす
master.point++; // 得点を加算
reset();
}
}
}
// ゲームマスタークラス(モグラオブジェクトの管理などを行う)
class GemeMaster
{
var window; // ゲームウィンドウオブジェクト
var parent; // 親レイヤー:(ゲームウィンドウの)プライマリレイヤー
var fore; // 親レイヤーのコピー:表画面として使う
var moles = []; // モグラオブジェクトの配列
var timer1; // タイマー1:モグラ用
var timer2; // タイマー2:残り時間用
var rest_time; // 残り時間
var point; // 得点
// コンストラクタ
function GemeMaster(win, par)
{
window = win;
parent = par;
// 親レイヤーのコピーを作る
fore = new Layer(win, par);
with (fore)
{
.visible = true;
.assignImages(parent);
.setSizeToImageSize();
// フォントの高さを30ピクセルに設定(残り時間、得点の文字に使う)
.font.height = 30;
}
// モグラオブジェクトを作成
moles.add(new Mole(this, window, fore));
moles.add(new Mole(this, window, fore));
moles.add(new Mole(this, window, fore));
// モグラの表示位置を設定
// (モグラの画像サイズによって調整してください)
moles[0].top = 100;
moles[0].left = 50;
moles[1].top = 100;
moles[1].left = 250;
moles[2].top = 100;
moles[2].left = 450;
// モグラ用タイマーオブジェクトを作成
timer1 = new Timer(onTimer1, "");
timer1.interval = 100; // タイマーイベントは100ミリ秒間隔で発生
// 残り時間用タイマーオブジェクトを作成
timer2 = new Timer(onTimer2, "");
timer2.interval = 1000; // タイマーイベントを1秒間隔で発生させる
}
// デストラクタ
function finalize()
{
// タイマーを無効化
invalidate timer2;
invalidate timer1;
// モグラを無効化
for (var i = 0; i < moles.count; i++) {
invalidate moles;
}
// 表画面を無効化
invalidate fore;
}
// ゲームを開始
function start()
{
point = 0;
rest_time = 20;
timer1.enabled = timer2.enabled = true;
}
// ゲームを終了
function stop()
{
timer1.enabled = timer2.enabled = false;
}
// タイマーイベントを処理:モグラを動かす
function onTimer1()
{
// モグラを動かす
for (var i = 0; i < moles.count; i++) {
moles.move();
}
// 得点を更新する
with (fore) {
.copyRect(500, 0, parent, 500, 0, 160, 30);
.drawText(500, 0, "得点:"+point, 0x000000);
}
}
// タイマーイベントを処理:残り時間を更新する
function onTimer2()
{
// 残り時間を更新する
with (fore) {
.copyRect(0, 0, parent, 0, 0, 160, 30);
.drawText(0, 0, "残り時間:"+(rest_time--), 0x000000);
}
if (rest_time < 0) {
stop();
}
}
}
// ゲームウィンドウクラス
class GameWindow extends Window
{
var file_menu; // ファイルメニュー
var start_menu; // ゲーム開始メニュー
var exit_menu; // 終了メニュー
var master; // ゲームマスター
// コンストラクタ
function GameWindow()
{
super.Window(); // スーパークラスのコンストラクタを呼び出す
// [ファイル]、[ファイル‐ゲーム開始]、[ファイル‐閉じる]メニューを作成
menu.add(file_menu = new MenuItem(this, "ファイル(&F)"));
file_menu.add(start_menu = new MenuItem(this, "ゲーム開始(&S)"));
file_menu.add(exit_menu = new MenuItem(this, "閉じる(&C)"));
// プライマリレイヤーを作成
add(new Layer(this, null));
with (primaryLayer)
{
// 背景画像を読み込み、そのサイズに設定
.loadImages("stage");
.setSizeToImageSize();
}
// ゲームウィンドウをプライマリレイヤーのサイズに合わせる
setInnerSize(primaryLayer.width, primaryLayer.height);
caption = "モグラ叩きゲーム"; // ゲームウィンドウのタイトルを設定
visible = true; // ゲームウィンドウを表示
// ゲームマスターオブジェクトを作成
master = new GemeMaster(this, primaryLayer);
}
// デストラクタ
function finalize()
{
// ゲームマスターを無効化
invalidate master;
// メニューを無効化
invalidate exit_menu;
invalidate start_menu;
invalidate file_menu;
super.finalize(); // スーパークラ?工违钎攻去楗 |
|