shawind 发表于 2007-5-17 21:43:22

[TJS][伪翻译]面向对象的思路实现一个游戏

整理硬盘时发现的。还是去年对krkr着迷的时候做的,我来翻译,当然是不准确,随意翻的,错误很多,而且还有很多地方没有翻。
更全面,更详细的还是看原文吧。(在二楼)
原文似乎是来自tjs primer.chm中的。

思路
大体的思路,使用3个类,游戏窗口类,游戏主类,鼹鼠类。类的说明如下:
类         作用                                    备注
游戏窗口类   窗口显示,菜单显示,剩余时间显示          从window类继承
游戏主体类   游戏流程控制:得分管理,鼹鼠的管理
鼹鼠类       鼹鼠图像的显示,声效的播放,鼠标事件处理从Layer类继承
构架游戏窗口类的骨架
///////////////////////////////////////////////////////////////////////
// 游戏窗口类
class GameWindow extends Window
{
var file_menu;   //文件菜单
var start_menu;//游戏开始菜单
var exit_menu;   //结束菜单
var master;      //游戏主体
//游戏窗口
function GameWindow()
{
    super.Window();// 从window类继承
}
//销毁资源
function finalize()
{
    super.finalize();//从上面继承
}
// 菜单选择后的响应动作action()
function action(ev)
{
//暂时还没有
}
}
// 从游戏窗口类创建一个实例
var win = new GameWindow();
//////////////////////////////////////////////////////////////////////
tjs中的window类原本都是不带菜单的。所以要加入菜单才能做成我们所需要的窗口类。
类中声明的变量的说明:
变量         作用             备注
file_menu    生成“文件”菜单
start_menu   文件-游戏开始
exit_menu    文件-关闭
master       游戏主体对象   在后面说明
构建游戏主体类的骨架
要注意时间类和时间事件的处理,时间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() { }
}
/////////////////////////////////////////////////
构建鼹鼠类的骨架
鼹鼠类是从layer类继承。layer类的作用是图片的显示\不显示,以及鼠标事件的处理。
鼹鼠类中需要用到intrandom()函数,这个函数在kag3\template\system\Util.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);//从layer类继承
}
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();//继承一个
}
////////////////////////////////////////////////////////
主函数的说明
1.一开始是创建菜单对像。创建了三个菜单。
以后选择菜单的时后,就用到后面说的action()函数。
2.接着创建了主层。用add()来登陆游戏窗口自身。
以后主层就可以以primarylayer的属性和访问。
3.主层读入了背景图片。后面会用到缓冲层。
4.最好创建了游戏主体类,这个类里包括了窗口类和主层类。
以下是类的继承关系
krkr2Window类      方法add()、setInnerSize()
                     属性caption、visible
       MenuItem类      主函数
                     方法add()
       Layer类         主函数
                     方法loadImages()、SetSizeToImageSize()
                     属性width、height
主层创建的参数是null,即什么都不显示
实装游戏窗口类的“方法”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();
    //设定字体
    .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.top= 100;
moles.left =50;
moles.top= 100;
moles.left = 250;
moles.top= 100;
moles.left = 450;
//鼹鼠用的时间对象
timer1 = new Timer(onTimer1, "");
timer1.interval = 100;// 100ms间隔
//创建剩余时间用的时间类
timer2 = new Timer(onTimer2, "");
timer2.interval = 1000;//1000ms间隔
}
//销毁函数
function finalize()
{
//时间类无效化
invalidate timer2;
invalidate timer1;
//鼹鼠对象无效化
for (var i = 0; i < moles.count; i++)
{
    invalidate moles;
}
// 主层副本无效化
invalidate fore;
}
/////////////////////////////////////////////////////////
1.亲层副本,用来缓冲用
2.创建了鼹鼠对象,并设定了其位置
3.两个时间对象
吉里吉里2   Layer类         方法assignImages()
                            属性visible、font、top、left
            Font类          属性height
            Timer类         主函数
                            属性interval
对于有父子关系的层,如果没有特别的限定,那么父层在里面,子层在前面。这个例子里面的顺序从里到外是,主层,主层副本,鼹鼠。
实装游戏主体类中的方法 start()和stop()
///////////////////////////////////////////////////////////
//游戏开始
function start()
{
point = 0;
rest_time = 20;
timer1.enabled = timer2.enabled = true;
}
//游戏结束
function stop()
{
timer1.enabled = timer2.enabled = false;
}
///////////////////////////////////////////////////////////
用到了timer类的一个属性enabled
实装游戏主体类的方法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()是在指定坐标画文字。如果原来有文字,自动覆盖以前的。没有比要特意去消。
用到了layer类中的方法copyRect()和drawText()
实装鼹鼠类中的主函数和销毁函数
主函数中的参数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();// 继承
}
////////////////////////////////////////////
1.创建WaveSoundBuffer对象,读入效果音
2.设定鼹鼠自身:读入图像,鼠标事件
WaveSoundBuffer类   主函数
                  方法open()
Layer类             属性hitType、hitThreshold
实装鼹鼠类中的方法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以上为显示。
实装鼹鼠类中的方法onMouseDown()
参数button传递鼠标点击的消息。
//处理鼠标事件
function onMouseDown(x, y, button, shift)
{
if (button == mbLeft && visible) {//在鼹鼠显示中按下了左键
    hit_sound.play();// 效果音
    master.point++;    // 增加得点
    reset();
}
}
////////////////////////////////////////////////////
鼹鼠显示中,在鼹鼠上点左键,播放效果音,增加得点。然后呼叫方法reset(),重新进人不显示的状态。
WaveSoundBuffer类      方法play()
Layer类                  方法onMouseDown()


shawind 发表于 2007-5-17 21:44:02

原文,复制过来格式上有点乱

実装
方針
大まかな方針として、ゲームウィンドウクラス、ゲームマスタークラス、モグラクラスの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.top= 100;

moles.left =50;

moles.top= 100;

moles.left = 250;

moles.top= 100;

moles.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.top= 100;

    moles.left =50;

    moles.top= 100;

    moles.left = 250;

    moles.top= 100;

    moles.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();// スーパークラ?工违钎攻去楗

Miliardo 发表于 2007-5-18 03:31:34

TJS Primer-v-
有这东西么-v-

shawind 发表于 2007-5-18 04:24:47

有的。是第三方写的。
只是我记不清这个东西是不是这个chm里面的了。
http://homepage1.nifty.com/gutchie/archive/tjs_primer.lzh
另外还有一本说kag3的。
http://homepage1.nifty.com/gutchie/archive/game_dev.lzh

sarah707 发表于 2007-5-18 07:55:25

好东西……多谢分享~慢慢看着先……

august 发表于 2007-5-18 14:49:57

这个是RGSS吗

Zelsazgh 发表于 2007-5-18 17:50:35

类似PASCAL/C 语法,不区分变量的类型,应该是RUBY....

shawind 发表于 2007-5-18 19:03:25

引用第5楼august于2007-05-18 14:49发表的:
这个是RGSS吗
当然不是了,是krkr中的tjs脚本语言。

引用第6楼Zelsazgh于2007-05-18 17:50发表的:
类似PASCAL/C 语法,不区分变量的类型,应该是RUBY....
也不是ruby,tjs是采用了c风格,类似javascript的脚本语言。
在运行机制上和ruby一样的,都是脚本语言。

本来tjs的东西是应该发在avg区,归在krkr中,可我觉得这东西不但思路上是面向对象,实现的出来,也并不是一个avg。相对来说,来这个区的人更需要,也更容易理解这个东西,所以就发这里了。

lw 发表于 2007-5-18 21:38:29

都已经这个样子了,建议不如直接用C++模拟C风格+一个引擎写巴...

偶个人对脚本只习惯于KAG3这层或者是RMXP的脚本这层,否则感觉是不是有点那个——

shawind 发表于 2007-5-18 22:28:46

不理解这些思路的话,也就没法理解kag3的原理,也就是说,没法对kag作较大幅度的个人定制。
只能是看着fate里面的特效流口水,却实现不了。
rmxp也是一样的。
而改这些东西,远比自己从头写一个引擎容易得多得多。
别的先不说,光资源打包这一块,就够c++写半天的了。
不光语言运用要过关,还得有不错的数据结构基础(设计自己的文件格式),以及加密解密知识(如des算法)。
而用krkr,rmxp,却得来不费半点工夫。
特别适合中阶制作者。
页: [1] 2
查看完整版本: [TJS][伪翻译]面向对象的思路实现一个游戏