[HOME] [INDEX] [J3Cの言語仕様] [J3Cの組み込み関数]

J3Cプログラミング入門

3次元ポリゴンアニメーションキット
Copyright (c) 1998 - 2003 Jun Mizutani 5/13/2003

【0】はじめに
【1】オブジェクト指向言語としてのJ3C
【2】最小のプログラム
【3】Hello World
【4】コンパイル,アセンブル,実行
【5】定数,変数
【6】文
【7】クラスインスタンス
【8】インポートファイルの使用
【9】継承の利用
【10】物体の表示
【11】インスタンスの引数
【12】階層構造の物体
【13】メッセージの送受信
【14】速度と重力
【15】形状の実行時生成
【16】物体を接続するポリゴン
【17】座標系の拡大縮小
【18】グラフィック画面への文字と線分の描画
【19】光源の移動

[J3Cの言語仕様]
[J3Cの組み込み関数]

[top]

【0】はじめに

J3W(J3DASM)ではアセンブラ形式でプログラムする必要がありましたが, J3Cは,よりプログラミングとメンテナンスしやすくするためにj3dasm のフロントエンドとして,JAVA風の文法を持つ言語として設計しました.

実行までの流れは j3c → j3dasm → j3w となります.

ここではJ3Cを用いて,J3Wで実行するためのプログラムの作成法を簡単な ものから順に解説します.

文法の詳細は J3Cの言語仕様を, 組込み関数の詳細は J3C組込み関数 を 参照してください.

J3C言語の雰囲気を示すための例です.ヒープソートをプログラムしてみました.

// ヒープソート
//
class heapsort {
    final int       MAX = 2000;
    volatile int    a[MAX];
    volatile int    start, finish;


    int disp_array() {
        volatile int i;
        for(i=0;i<MAX;i=i+1) {
            String("a["); Number(i); String("] = ");
            Number(a[i]); Char(13);
            Throw();
        }
    }

    int downheap(int k, int r) {
        volatile int j, v;

        v = a[k];
        j = 2 * k;
        while (j <= r)  {
            if (j != r)
                if (a[j+1] > a[j]) j = j + 1;
            if (v >= a[j]) break;
            a[k] = a[j];
            k = j;
            j = 2 * k;
        }
        a[k] = v;
//        Throw();
    }

    int hsort(int n) {
        volatile int i, t;

        for (i=n/2; i>=0; i=i-1) downheap(i, n);
        for (i=n; i>0; i=i-1) {
            t = a[0];
            a[0] = a[i];
            a[i] = t;
            downheap(0, i-1);
        }
    }

    int INIT(){
        volatile int i;
        String("START"); start = SystemTime();
        for(i=0;i<MAX;i=i+1) a[i] = Random(5000);
        // disp_array();
        hsort(MAX-1);
    }

    int RUN(){
        finish = SystemTime();
        // disp_array();
        Char(13);
        Number(MAX); String(" of data takes ");Number(finish - start);
        String("msec"); Char(13);
        Stop();
    }
    int EVENT(){}
}

class main{
    int INIT(){
        new(heapsort,3000);
    }
    int RUN(){}
    int EVENT(){}
}
[top]

【1】オブジェクト指向言語としてのJ3C

この解説ではクラス,インスタンス,メソッド,データメンバ,継承, オーバーライドなどのオブジェクト指向言語で使用される用語を使って いますが,J3CではJ3W(仮想マシン)の特徴を生かすため JAVA, C++, Delphi などの一般のオブジェクト指向言語での意味と異なる部分があります. オブジェクト指向言語に慣れていない方はこの章を時々参照する事になる と思います. C++, JAVA, Delphiなどの言語に慣れている方は異なる部分 だけ注意して下さい.既存のオブジェクト指向言語も言語毎に用語が少し づつ異なっていますが,この解説では以下の用語を使用することにします.

データメンバ

クラスに属する変数.J3Cでは32ビット整数型のみです. スコープは protected のみ.つまりクラス内と派生クラス以外から アクセスできません.

メソッド

クラスに属する関数,手続き. J3Cでは引数,返り値ともに32ビット 整数型のみです. スコープは protected のみ.つまりクラス内と派生クラス以外から アクセスできません.このスコープが一般のオブジェクト指向言語と 最も異なる部分です. クラス(インスタンス)の外部からメソッドを 介してクラスを操作することができません.

クラス

クラスはデータメンバとメソッドをカプセル化したものです. データメンバとメソッドがクラス(インスタンス)の外部からアクセス できないため非常に独立性の高いものとなります.

インスタンス

クラスがクッキーの型とするとインスタンスは型から作ったクッキー 1つ1つです. クラスが犬の遺伝子とすると,インスタンスはポチ やクロです.J3Cのインスタンスは概念ではなくて実体を持つものです. 後述するように目に見える3次元空間内の物体となることもできます. インスタンス間の結合は非常に弱く,メッセージを送る以外に外部から 操作できません.new組込み関数で生成された 瞬間から独自に実行が始まります. 仮想マシンであるJ3W上でインスタンスは別々のCPUとメモリを使用して 実行されます.クラスはその内容を定義するものとなっています.

コンストラクタとデストラクタ

J3Cでは,int INIT() というメソッドがコンストラクタに相当します. インスタンスは new組込み関数で生成された瞬間からINIT()メソッドから 実行が始まります.インスタンスはインスタンス自身がdelete組み込み 関数を実行することで消滅します.したがってデストラクタに相当する 機能はありません.

メッセージ

あるインスタンスは別のインスタンスへメッセージを送ることができます. メッセージは32ビット整数です.送ったメッセージに反応するかどうかは 送り先のインスタンスに依存します.メッセージ処理がプログラムされて いないクラスのインスタンスは外部からの操作は全くできないことになり ます.山クラスのインスタンスを怒鳴りつけても動いてくれないというこ とです.

継承

単一継承のみをサポートしています.Cのような手続き型の言語では,よく 使われる処理を(関数,サブルーチンとして)共通化します.オブジェクト 指向言語ではクラスの一部を共有することによって実現します. C++, Delphiなどの言語と異なり,J3Cではクラスが別のクラスのインスタ ンスをデータメンバとして所有することができないため,継承は特に重要 な手段となっています.

オーバーライド

派生クラスで基底クラスと同名のデータメンバ,メソッドを宣言すること ができます.派生クラスのインスタンスでメソッドを宣言すると基底クラス のメソッドが上書きされ,派生クラスのメソッドが実行され,基底クラスの インスタンスでメソッドを使用すると基底クラスのメソッドが実行されます. J3C v.1.01では仮想関数(バーチャルメソッド)をサポートしていません. すべて静的メソッドで,定義されコンパイルされたように動作します.継承 を利用する上での注意点は"【9】継承の利用"を参照してください. 継承また同一クラスで引数の型,数が異なるが同一のメソッド名を持つよ うな関数オーバーロードの機能もありません.

ライブラリ

変数のデータメンバを持つことができないクラスです.定数をデータメン バとして持つことはできます.ライブラリの特徴はスコープがグローバル なことです.どのクラスもライブラリ中のメソッドを呼ぶことができ, ライブラリ中の定数を参照することができます. ライブラリ中のメソッドは呼び出したクラスのデータメンバにアクセスする ことはできません.データメンバにアクセスするような処理を共有するため には継承を使用します.グローバルな関数,定数として使用します.

多態性

オブジェクト指向言語としては重要な概念の1つかもしれませんが,J3C では,クラスインスタンスはすべて実体を持ち,独立に動作するする「物」 であるため,クラスインスタンスのポインタ,代入,メソッド呼び出しなど が不可能なため多態性が活躍する余地はありません.

[top]

【2】最小のプログラム

以下に示す例はJ3Cの最小のプログラムです.実行しても何もしないループ となります. Windows版ならQUITボタン,Linux版ならctrl-Cなどで強制的に 終了させる必要があります.

/*--------- list_1 ----------------------------*/
class main {                    // クラス宣言
    int INIT() {}               // メソッド宣言
    int RUN() {}                // メソッド宣言
    int EVENT() {}              // メソッド宣言
}

この例で示されるようにJ3Cのプログラムでは,mainクラスが必須であり, 各クラスにはINIT, RUN, EVENTの3つのメソッドが必須となります.

任意の名前のクラス,メソッドを宣言することができますが,その中で mainクラス, および各クラスのINIT, RUN, EVENTメソッドは特別な意味を持つ と覚えておいて下さい.

あとの章で説明するようにクラスは継承することができるため,基底クラスで宣言 されていれば,すべてのクラス宣言でINIT, RUN, EVENTの3つのメソッドを記述する 必要はありません.

J3Cでは,mainクラスは最初に自動的にインスタンスとして生成され,INIT メソッドが最初に実行されます.INITメソッドが終了すると次にRUNメソッド が繰り返し実行され,ほかのクラスインスタンスからのメッセージを受信し た場合に限ってEVENTメソッドが実行されます.

上記の例では,INIT(), RUN(), RUN(), RUN()... というように強制終了されるまで実行します.

通常のプログラムでは mainクラスのINIT, RUNまたはそれらから呼ばれたメソッド が,別のクラスのインスタンスを生成し,生成されたインスタンスは独立して 自分のクラス宣言の定義にしたがって INIT(), RUN(), RUN(), RUN()... を実行 します.j3c(j3w)のプログラムの実行中は複数のインスタンスが RUN を実行し続 けます.メッセージを受信した場合だけ EVENT を実行し,その後 RUN を実行 します.

[top]

【3】 Hello World

最初のプログラムは,Hello Worldという文字列をテキストウインドウに 表示するプログラムです.グラフィックウインドウは開きません.

/*--------- list_2 ----------------------------*/
class main {
    int INIT() {                // メソッド宣言
        String("Hello World");
        Char(13);
        Stop();
    }
    int RUN() {}                // メソッド宣言
    int EVENT() {}              // メソッド宣言
}

Stringが文字列表示用の組込み関数です. Char(13)は改行文字を出力します. (Linux版では 13[CR] は 10[LF] に内部で変換されます.) Stringで "Hello World"を表示し,Char(13)で改行し, Stopで終了します.

Stop()はすべてのインスタンスを終了させる組込み関数です. J3C(J3W)では,すべてのインスタンス(J3Wの解説ではプロセスと呼んで いる)が終了した時点でプログラムが終了します. この場合は1つのインスタンスしか存在しないので,delete()で プログラムは終了しますが,通常は他のインスタンスの寿命がわからな いため,プログラムを終了させる場合には,プログラムを終了させる責任 のあるインスタンスがStop()を実行します.

この例でStop()がないと,INIT実行後にRUNが繰り返し実行されます. RUNの中にStop()をおくこともできます.INIT実行後,最初のRUNの実行時に プログラムは終了します.

[top]

【4】コンパイル,アセンブル,実行

list_2.j3cというソースファイルをコンパイルして実行するまでを解説します.

[Windows 95/98/NT/2k/XP]

コマンドライン(DOSプロンプト)から

>j3c list_2.j3c

のように実行するか,j3c.exeのアイコンに list_2.j3cのアイコンをドラッ グアンドドロップするとDOSウインドウが開き,list_2.j3cと同じフォルダ にlist_2.j3mが生成されます.さらに

>j3dasm list_2.j3m

のように実行するか,list_2.j3mのアイコンを,j3dasm.exeのアイコンにドラッ グアンドドロップすると list_2.j3d が生成されます. j3w.exeのNEWボタンから指定するか,j3w.exeのアイコンにlist_2.j3dのアイコン をドラッグアンドドロップし,STARTボタンで実行されます.

あるいは j3cc.bat にlist_2.j3cのアイコンをドラッグアンドドロップするとエラー がなければ,ターミナル画面(DOS画面)が開いてアセンブルまで終了します. ただし生成されるファイル名は list_2.j3c.j3m, list_2.j3c.j3d というファイル名 になります.この場合はlist_2.j3c.j3dをJ3Wから実行します.

[Linux]

j3wの実行には Xが必要です. ターミナル画面(日本語を表示するならkterm)を開いて

$ j3c list_2.j3c
$ j3dasm list_2.j3m

で list_2.j3m は list_2.j3d へアセンブルされます.

$ j3w list_2.j3d

で実行されます. j3ccというシェルプログラムが用意されています. コンパイルとアセンブルを同時に行ないたい場合は次のように指定 します.

$ j3cc list_2.j3c

これで list_2.j3m と list_2.j3d が生成されます. 更に,コンパイルとアセンブル,j3w の実行まで行ないたい場合は

$ j3cc -r list_2.j3c

とすると j3w list_1.j3d の実行まで自動的に行われます. 実際にj3cのソースを直接実行できるような環境となります.

j3cのソースに構文エラーがある場合,またj3dasmのアセンブリソースにエラー がある場合は,エラーを見つけた段階でスクリプトは終了します.

[top]

【5】変数,定数

変数,定数には大きく分けてそれぞれ2種類あります.データメンバと 局所変数(定数)です. 定数は多くの言語で constant 宣言で指定しますが, j3c では JAVA と同じく final で指定します.値が変化しない変数という 扱いですが,j3cではコンパイル時に値を決定してしまいます.final宣言 した変数(定数)はメモリを消費しませんが,必ず初期化する必要があります. 通常変数は volatile を指定する必要があります.したがって int i; と 言う宣言はできません.変数の場合は必ず volatile int i; とする必要が あります.

変数,定数の型は 現在 int (32bit 符号付き整数)のみです.

宣言の例
    volatile int i,j,k;
    volatile int a[10];
    final int const = -9999999;
    final int id[5] = {1, 2, 3, 4, 5};

配列は,宣言時にサイズを指定できない JAVA と異なって,サイズを指定する 必要があります.また配列名のみを参照した場合はメモリの位置が返りますが, j3cではポインタはありませんので,一部の組み込み関数で使用するだけです.

局所変数は JAVA, C++ と異なってブロック中の任意の位置で宣言することは できません. Cと同じようにブロックの先頭部分でまとめて宣言する必要があ ります.また局所変数では配列を宣言することはできません.

変数名,定数名,メソッド名,クラス名などの識別子は最大63文字までです.

数値は10進数,16進数,キャラクタで記述できます.

    val = 123456789;
    val = 0xABCDEF0;
    val = 'A';
[top]

【6】文

文は処理の内容を実際に記述する単位で,代入文,条件文(if, switch), 繰り返し文(while, for, do) また処理の流れをコントロールするbreak, continue, returnという文,アセンブラに直接渡す asm文, 別に定義した処理を実行するメソッド呼び出しライブラリ呼び出し,j3wの機能を直接指定する 組み込み関数呼び出しがあります.

代入文,条件文,繰り返し文等は C, C++, JAVAとほとんど同じです. ここで詳細は 説明しませんが,C, C++, JAVAの解説書を参照して下さい.

C, C++, JAVAと特に異なる文は if文と return文です. if文では elseの前にセミコロンを置くことは できません(pascalまたはDelphiと同じです).

return文は式を評価するために使用し,関数の 途中からの脱出の目的に使用することはできません.

また局所変数宣言と演算子の制限から,for文では C++のように

    for (int i = 0; i < 100; i++) {...}

の形式は使用できません.

    {
        volatile int i;
        for (i = 0; i < 100; i = i + 1) {...}
    }

とする必要があります. もちろん 変数 i が宣言済みの場合は,

        for (i = 0; i < 100; i = i + 1) {...}

だけでかまいません.

【7】クラスインスタンス

j3cの特徴は複数のインスタンス(物体)が独立して実行される(運動する)ことにあります. これから順に独立性の高いクラスインスタンスを組み合わせて3Dアプリケーションを組み立 てていきます.

まず複数のインスタンスを同時に実行する場合の例です.

/*--------- list_3 ----------------------------*/
class esc_key {
    volatile int key;

    int INIT() { }

    int RUN() {
        key = InKey();            // キ−コ−ド入力
        if (key == 0x1B) Stop();  // 全インスタンスの終了
    }
    int EVENT() {}
}

class main {
    volatile int i;

    int INIT() {
        new(esc_key, 50);
        i = 0;
    }

    int RUN() {
        Number(i);
        String(" Hello World");
        Char(13);
        i = i + 1;
    }
    int EVENT() {}
}

この例では,まず new(esc_key, 50) でインスタンスを生成しています. 「50個のデータメモリを持つクラスesc_keyのインスタンスを生成して,INIT メソッドから実行しなさい.」と言う意味です.

実行に必要なデータメモリとはデータメンバの格納領域とスタック領域を加え たものです. 配列や再帰を使用しなければ,50か100で十分と思います. 必要ならばデータメモリを大きくしてください.1000でも10000でも制限はありません.

ここで2つのインスタンスの動作を考えます. 1つめの main クラスのインスタンスは0から順に永久に1を加えてそれを表示し 続けます. 2つめのインスタンスはESCキーが押されるまで永久に待ちますが,ESCキーが押 されるとStopを実行します.

個々に考えるとどちらも無限ループかそれに近いものですが,2つのプロセ スが同時に実行されていると「ESCキーが押されるまで0から順に1を加えてそ れを表示する」プログラムとなります.2つめのインスタンス(今後esc_key クラスインスタンスと呼びます)はESCキーが押されると Stop(); に処理が移り, 他のすべてのインスタンスを終了させるためです.

RUN()メソッドは一回実行すると,他のインスタンスに実行を渡します(内部的に Throw組込み関数を呼び出している).つまり各インスタンスのRUNが1度づつ順に 実行されています. 物体の移動などの時間を指定する組込み関数では自動的に 必要なタイミングで別のインスタンスに実行が渡されます.時間を指定する組込み 関数では長時間の指定でも問題ありません.一方,INIT,RUN中またはそこから呼 ばれるメソッド中で計算などで長時間かかるループを実行すると別のインスタンス に実行が渡らず,また画面の更新もされません.Throw組込み関数をループ内に置 いて他のインスタンスに実行するチャンスを与えてください.

基本的にはループを避けて RUNが繰り返し実行されることを利用する方がよいで しょう.

esc_keyクラスインスタンスの最初の文 RQ = 9 はインスタンスの識別番号を 9に設定するための組込み関数です.他のインスタンスからメッセージを送る 場合などに使用しますが,list_4の場合は使用されていません.

このesc_keyクラスのインスタンスを任意のj3cのプログラム中で new(esc_key, 100); を実行してインスタンス化するだけでESCキーで強制終了という機能を追加できます.

【注】 InKeyによるキー入力はグラフィックウィンドウか テキストウィンドウがアクティブになっている必要があります. アクティブでなくてもキー入力を有効にするためには以下のリストを使用してください.

    int RUN() {
        key = RawKey(0x1B);   // キ−コ−ド入力
        if (key) Stop();
    }

この場合は別のウィンドウで実行されているアプリケーション(例えば vi)でESCが
押されても実行中のj3cのプログラムは終了します.

[top]

【8】インポートファイルの使用

list_4の以下の部分をesckey.j3cというファイルに保存します.

/*--------- esckey.j3c ------------------------*/
class esc_key {
    volatile int key;

    int INIT() { }

    int RUN() {
        key = InKey();            // キ−コ−ド入力
        if (key == 0x1B) Stop();  // 全インスタンスの終了
    }
    int EVENT() {}
}

以下のように別ファイルで指定するとアセンブル時にesckey.j3cが挿入され ます.list_3と同等です. この様に使用するファイルをインポートファイルと呼び,j3cという拡張子 を付けます.

/*--------- list_4 ----------------------------*/
import "esckey.j3c";

class main {
    volatile int i;

    int INIT() {
        new(esc_key, 100);
        i = 0;
    }

    int RUN() {
        Number(i);
        String(" Hello World");
        Char(13);
        i = i + 1;
    }
    int EVENT() {}
}
[top]

【9】継承の利用

別のクラスまたは同じクラスのインスタンスでも自分自身のデータメンバとメソッド しかアクセスできないという特徴からC++やDelphiと比較して継承がより重要となって います.C++ や Delphiでは継承を利用しなくても C や Pascalのプログラムとして アプリケーションを作成できてしまいます.これは C や Pascal のプログラマには 利点でもありますが,一方,オブジェクト指向の考え方を身につけるには邪魔になる 点でもあります.

既存のクラスを再利用しやすくするには,継承する場合のことを考えて クラスを設計しなければなりません.

J3Cにおいては INIT, RUN, EVENT メソッドを派生クラスで再定義(オーバー ライド)することで派生クラスをカスタマイズします.

以下のリストをeye0.j3cとして保存して下さい.視点またはカメラとして後ろの章で 使用します.

/*--------- eye0.j3c -------------------------*/
class eye0 {
    int Initialize() {
        NewObject(0, 0);         // 視点用オブジェクトの生成  頂点も面も不要
        See();                   // このオブジェクトが見る
    }
    int Position(int x, int y, int z, int h, int p, int b) {
        RX = x; RY = y; RZ = z;
        RH = h; RP = p; RB = b;
        SetPosition();           // 位置と角度を視点に設定
    }
    int INIT() {
        Initialize();
        Position(-2000, 0, 0, 0, 0, 0);
    }
    int RUN() {
        Wait();
    }
    int EVENT() {}
}

この eye0 クラスを継承して INITを再定義すれば視点の位置を変更できます. さらに EVENT を再定義することでキー入力で視点位置の変更などの機能を追加 できます.

EVENTを再定義した場合は,RUNも再定義してください.EVENTだけの再定義はできません.

よく使用するプログラムを集めてライブラリとして利用できます. インポートファイル中にインポートファイルを記述できます. ディレクトリは import "../lib/abc.j3c" のように指定できます.

ここで2章で紹介した何もしないクラスを少し変更して empty というクラス名で ファイル名 empty.j3c として保存します.

/*--------- empty.j3c ------------------------*/
class empty {
    int INIT() {}
    int RUN() {
        Wait();
    }
    int EVENT() {}
}

emptyクラスはそれ自体をインスタンスとして生成することはありませんが, 3章のプログラムは以下のように書き換えることができます.

/*--------- list_5 ---------------------------*/
import "empty.j3c";
class main extends empty {
    int INIT() {                // メソッド宣言
        String("Hello World");
        Char(13);
        Stop();
    }
}

このように emptyクラスをextends節 で基底クラスとして指定することに よって派生クラス(この場合main)ではINITメソッドのみ再定義すれば 何もしないRUN, EVENTはemptyクラスのメソッドを使用(継承)すること ができます.

共通の性質を基底クラスで定義して動作,形状,色,位置など少しづつ異なる性質 を持った物体の差分だけを各クラスで定義することで見通しが良くメンテナンスの 容易なプログラムとすることができます.

*** 継承を利用する上での注意点 ***

以下のプログラムを読んで実行結果を予想してみて下さい. j3cのプログラムを解読する場合は class main のINITから始めます.

クラス B のインスタンスを生成して10秒待ってプログラムを終了します. main から生成されたクラス B のインスタンスは INIT をクラス A から 継承していて再定義されていないのでクラス A の INITを実行します.

IntroduceMyselfメソッドはクラス B で再定義されており,このプログラムの 作者は継承したINITからクラス B用のIntroduceMyselfメソッドが実行されることを 期待しているようです.

クラス B のインスタンスが実行するクラス A の INIT中の IntroduceMyself メソッドは クラス A か クラス BのどちらのIntroduceMyselfメソッドを 実行するのでしょうか?

/*--------- note.j3c -------------------------*/
import "empty.j3c";

class A extends empty {
    int IntroduceMyself() {
        String("I am A.");
        Char(13);
    }
    int INIT() {
        IntroduceMyself();
    }
}
class B extends A {
    int IntroduceMyself() {
        String("I am B.");
        Char(13);
    }
}
class main extends empty {
    int INIT() {
        new(B);
        Pause(10000);
        Stop();
    }
}

実行結果 I am A.

残念ながら期待した動作をしていないようです.この例からわかるように クラス B のインスタンスが実行するクラス A の INITは,あくまでクラス A の INITです. したがって当然クラス A のIntroduceMyselfメソッドを実行することになります.

クラスを継承して一部のメソッドを再定義した場合は,自動的に呼び出されるINIT, RUN, EVENTメソッド以外のメソッドに関しては呼び出し元のメソッドも再定義する 必要があります.この部分(仮想関数未サポート)は今後改善するかもしれませんが, 当面クラス設計時に注意しておいて下さい.

[top]

【10】物体の表示

物体を表示する場合は,空間に「物体」と「物体を見ている物体」が存在す る必要があります.1つのインスタンスは1つの物体になることができます. したがって最低でも,2つのインスタンスが必要になります.以下の例では 最初のインスタンスが空の(形のない)オブジェクト(物体)を生成し, 位置をSetPositionで設定して See組込み関数で視点を取得します. 2番目のインスタンス(Triangle)は3角形のオブジェクトとなります.

位置を指定するために座標を指定します. すべての物体はそれぞれ自分の向 いている方向を基準として,前がX,右がY,下がZの増加する方向です. これを座標系と呼びます.回転はZ軸回りの回転(右を向く)をヘッド回転, Y軸回りの回転(上を向く)をピッチ回転,X軸回りの回転(首を右に傾け る)をバンク回転と呼びます.

一つだけ常に存在する固定された特別な座標系があり,すべての物体の座標 系の基準となります. 北を向いて立ち,北がX,東がY,下がZとなりま す.これをワールド座標系と呼びます.「【14】速度と重力」で解説する重 力はワールド座標系の下方向(Zが正の方向)に働きます.

ここで使用している座標系は航空力学で使用されるNED系(X:North [前], Y:East [右],Z:Down [下] )で3次元グラフィックで使用している座標系 とは異なりますが,読み換えは簡単です. 座標値は符号付き32ビット整数の範囲(+20億〜-20億)が可能です.

あまり難しく考えず「 前がX,右がY,下がZ」とだけ覚えて下さい.

/*--------- list_6 ----------------------------*/
import "empty.j3c";

class Triangle  extends empty{
    final int color1 = 6;             // 面の色指定
    final int color2 =14;
    int INIT() {
        NewObject(6, 2);        // オブジェクトの生成  6頂点 2面
        DefPoint(0, 0,   500);  // 頂点0 三角形の頂点座標を登録
        DefPoint(0, 500,-500);  // 頂点1
        DefPoint(0,-500,-500);  // 頂点2
        DefPoint(0, 0,   500);  // 頂点3 頂点0と同じ
        DefPoint(0, 500,-500);  // 頂点4 頂点1と同じ
        DefPoint(0,-500,-500);  // 頂点5 頂点2と同じ
        DefPlane(color1, 3, 0, 1, 2); //面の定義 表から見て左回りに指定
        DefPlane(color2, 3, 5, 4, 3);
        ClearRegisters();     // RX - RBに0を代入
        SetPosition();        // 初期位置と角度の設定
     }
     int RUN() {
        RotHead(6000, 2880);  // 6秒間で360度回転
        DeleteObject();       // オブジェクト
        Stop();               // 全インスタンス終了
     }
}

class main extends empty {
    int INIT() {
        new(Triangle, 50);
        NewObject(0, 0);         // 視点用オブジェクトの生成
        ClearRegisters();        // RX - RBに0を代入
        RX = -2000;              // 位置の指定 20m 後方
        SetPosition();           // 位置と角度を視点に設定
        See();                   // このオブジェクトが見る
        BackgroundColor(0x000);  // 背景色を黒に設定
        GraphMode();             // グラフィックウインドウを開く
     }
     int RUN() {
        Wait();
    }
}

物体の生成には NewObject() 組込み関数に最大頂点数と最大面数を与えて生成します. 頂点数, 面数は実際に使用する数より多くても問題ありませんが,少ないと エラーが発生します.注意して下さい.視点には頂点も面も無いのでそれぞ れ,0を渡しています.見えなくても位置と角度の情報を持ちます.また, 後述(【12】章)するchild組込み関数で,頂点も面も持つ 子オブジェクトを持つこともできます.

実際に表示するためにはGraphMode組込み関数を実行してグラフィックウインドウを開 く必要があります.この組込み関数はどのインスタンスが実行してもかまいませんが, 複数のウインドウを開くことはできません. 形状の定義はDefPoint組込み関数でX,Y,Z座標を指定して1つずつ頂点を登録し ます.頂点番号はオブジェクト毎に0から順に付けられます(VertexRelative 組込み関数で変更可能). レジスタに面の色を代入してDefPlane組込み関数でレジスタ,頂点数,頂 点番号列を指定することで面を定義します.面には表と裏があり,DefPlane組込み 関数1つでは裏からは不可視になります.

注)「j3w ver.5.xx特有」 1つの平面の表と裏を表示するためには頂点を表と裏で共有させないで下さい. list_6では同じ頂点を2回づつ登録しています.1平面の表と裏ではなく,例 立方体の頂点のような場合には頂点の共有は問題ありません. 1つの平面の表と裏で頂点を共有すると照明に使用する法線ベクトルが計算で きないためです.

物体の運動は RotHead(6000, 2880)で指定しています. この場合6秒間で360度 ヘッド回転(Z軸回り)して終了します.Up, Forwardなどの組込み関数を 順次実行することで実行しているインスタンスのオブジェクトを運動させることが できます.

[top]

【11】インスタンスの引数

大きさが異なるとか速度が異なるといった,よく似たインスタンスを生成する ために異なるクラスを作成するのは面倒です.同じクラスでもインスタンスを 生成するときに引数を渡すことができればインスタンスの初期状態をコントロ ールできます.

インスタンスは生成されるとき,親インスタンスのレジスタ変数 (RX, RY, RZ, RH, RP, RB)を引き継ぎます.レジスタ変数に値を設定して new することで,同じプログラムを複数のインスタンスで共有して,与え る引数により異なった動作をさせることができます.したがって6個までの引数 を渡すことができます.

以下の例では3つの三角形が同じプログラムで別の動きをする例です. 視点を含めて4つのインスタンスが同時に動作します.三角形の初期位置を RX, RY, RZレジスタ変数に,初期バンク角をRBレジスタ,回転時間をRHレジスタ, 角度をRPレジスタ変数に設定して三角形クラスのインスタンスを生成しています.

/*--------- list_7 ----------------------------*/
import "eye0.j3c"; // インポートファイル 視点用オブジェクトの生成
import "empty.j3c";

class Triangle extends empty {
    volatile int time, angle;
    final int color1 = 13, color2 = 15; // 面の色指定

int INIT () { NewObject( 6, 2); // オブジェクトの生成 6頂点 2面 DefPoint( 0, 0, 500); //頂点0 三角形の頂点座標を登録 DefPoint( 0, 500, -500); //頂点1 DefPoint( 0, -500, -500); //頂点2 DefPoint( 0, 0, 500); //頂点3 頂点0と同じ DefPoint( 0, 500, -500); //頂点4 頂点1と同じ DefPoint( 0, -500, -500); //頂点5 頂点2と同じ DefPlane(color1, 3, 0, 1, 2); // 面の定義 表から見て左回りに指定 DefPlane(color2, 3, 5, 4, 3); time = RH; angle = RP; RH = 0; RP = 0; SetPosition(); // 初期位置と角度の設定 } int RUN () { RotHead(time, angle); DeleteObject(); // オブジェクトの削除 Stop(); // 全インスタンス終了 } } class main extends empty { int INIT () { BackgroundColor(0x624); GraphMode(); new(eye0, 50); ClearRegisters(); // RX - RBに0を代入 RY = -1000; RH = 9000; // 時間 RP = 2880; // 回転角 new(Triangle, 50); // 2つめのインスタンス ClearRegisters(); // RX - RBに0を代入 RY = 1000; RH = 6000; // 時間 RP = 5760; // 回転角 new(Triangle, 50); // 3つめのインスタンス ClearRegisters(); // RX - RBに0を代入 RX = 1000; RB = 720; RH = 6000; // 時間 RP = 8640; // 回転角 new(Triangle, 50); // 4つめのインスタンス } }

list_7のTriangleは回転後,直ちにオブジェクトを削除し全インスタンス終了と しているため,2つめのインスタンスは9秒間回転する指定であっても,最初に Stopを実行したインスタンス(6秒後)によって終了させられます.

Stopの代わりに delete組込み関数を置くと順次インスタンスが終了して物体が 消え,最後のインスタンスが終了した時点で視点インスタンスのみが残ります. 何も存在しないグラフィックウンドウが残ります. 終了には QUITボタンを 押す(Windows版)かターミナル画面でctrl-Cを押す(Linux版)必要があります. RotHead組込み関数の後ろに Wait組込み関数を挿入した場合も,別のインスタンス からメッセージが送られるかQUITボタンをクリックするまで一時停止します.

[top]

【12】階層構造の物体

例えば人体のような関節を持つ物体を表現するためには階層構造を持つオブ ジェクトが必要になります. 腰をひねれば肩も腕も頭も動きます.つまり 肩も腕も首の関節も動かしていないにもかかわらず,地面に対してその位置 も角度も変化します.これは腰の座標系(フレームと呼ぶ場合もある)上に 肩の座標系が固定され,肩の座標系に頭の座標系が固定され,という具合に 親子関係を形作っています.

以下の例では同じ形の3角形の下側の頂点が関節となっています.関節は物 体の原点(0, 0, 0)の位置に来るように形状を定義すると,回転組込み関数を普通 に使用すれば関節の動きとして表現できます.

とにかく座標系などを気にしないでlist_8を書き換えて実際に色々試してみ て下さい. 文章で読むほど難しくはないと思います.

/*--------- list_8 ----------------------------*/
import "eye0.j3c";   // インポートファイル 視点用オブジェクトの生成
import "empty.j3c";

class TriangleObject extends empty {
    final int color1 = 11;          // 面の色指定
    final int color2 = 13;

int def_shape() { DefPoint( 0, 0, 0); // 頂点0 三角形の頂点座標を登録 DefPoint( 0, 180, -400); // 頂点1 DefPoint( 0, -180, -400); // 頂点2 DefPoint( 0, 0, 0); // 頂点3 頂点0と同じ DefPoint( 0, 180, -400); // 頂点4 頂点1と同じ DefPoint( 0, -180, -400); // 頂点5 頂点2と同じ DefPlane(color1, 3, 0, 1, 2); // 面の定義 表から見て左回りに指定 DefPlane(color2, 3, 5, 4, 3); ClearRegisters(); // RX - RBに0を代入 } int def_move() { // すべての物体の運動を定義 RotHead(2000, 620); // 2秒間で回転 RotPitch(1000, 360); RotPitch(1500, -360); RotHead(1000, 620); RotBank(1500, 360); RotBank(1000, -360); } int RUN() { volatile int i; for (i = 0; i< 15; i = i + 1) def_move(); Stop(); } } class GrandChild3 extends TriangleObject { int INIT () { def_shape(); RZ = -500; RY = 100; RH = 720; SetPosition(); // 初期位置と角度の設定 } } class GrandChild2 extends TriangleObject { int INIT () { def_shape(); RZ = -400; RY = -100; SetPosition(); // 初期位置と角度の設定 child(GrandChild3, 50, 6, 2); } } class GrandChild extends TriangleObject { int INIT () { def_shape(); RZ = -400; SetPosition(); // 初期位置と角度の設定 child(GrandChild2, 50, 6, 2); // このインスタンスは child(GrandChild3, 50, 6, 2); // 子が2人 } } class Child extends TriangleObject { int INIT () { def_shape(); RZ = -400; SetPosition(); // 初期位置と角度の設定 child(GrandChild, 50, 6, 2); } } class Parent extends TriangleObject { int INIT () { NewObject(6, 2); // オブジェクトの生成 6頂点 2面 def_shape(); RZ = 1200; SetPosition(); // 初期位置と角度の設定 child(Child, 50, 6, 2); // 子オブジェクト } } class main extends empty { int INIT () { new(Parent, 50); new(eye0, 50); BackgroundColor(0x186); // 背景色を設定 GraphMode(); // グラフィックウインドウを開く } }

list_8の例では6関節の物体が画面中央で2分間運動します.どの関節もまっ たく同じ動き(def_move)をしていますが,全体としては,かなり複雑な動き をしていると思いませんか? それぞれの物体の形と動きを別に定義すれば, 色々試すことができると思います.

階層型オブジェクトを実現する組込み関数は1つだけ「child」組込み関数です. この組込み関数は「new()」と「NewObject()」 を加えた組込み関数です.ただし「生成される オブジェクトがchild組込み関数を実行したインスタンスのオブジェクト(の座標系)に 属している」という点が異なります.削除は deleteDeleteObjectを使用します.

表記法は以下の形式です.

   child(クラス名,  メモリサイズ,  頂点数,  ポリゴン数)
         ←── new ─────→  ←─NewObject──→

親オブジェクトが先に消滅した場合は,その子供たちはそれぞれ空間内で独 立したオブジェクトになります.

クラス継承の階層と物体インスタンスの階層構造を混同しないように注意してください. 未定義クラスエラーが発生する場合には混同している可能性があります.(経験者談) list_8では TriangleObjectクラスはインスタンス化されていません.常に継承されて 派生クラスのインスタンスが作られています.これを例えばParentからChildを派生 させて,ChildからGrandChildをは派生させて... とするとクラス宣言時または インスタンス生成時にクラスが未定義というエラーが発生します.

すでに宣言された識別子(クラス名,メソッド名,変数名などの名前)だけしか参照 できないのはJ3Cの仕様です.

[top]

【13】メッセージの送受信

複数のインスタンスが独立して運動可能であることは,これまで見てきました. さて,相互作用がある場合,例えば弾丸が「おまえに命中したぞ!」と当っ た相手に伝えて,それなりの行動を期待する時,インスタンスがどのように意志 疎通を行えばいいのでしょうか? これまでに何度も出てきた Stop(); がヒン トになります.Stop();は存在する他のインスタンスに対して「皆さん,もう終わ りだよ!後始末をして撤収!」とメッセージ(シグナルともいう)を放送し ています.これを特定の相手(インスタンス)に特定のメッセージを送ることが できれば目的は達成できます.

入力キーコードをインスタンス2に送るインスタンスと受信したメッセージを表示する インスタンスの例です.

/*--------- list_9 ----------------------------*/
class Proc2 {
    int INIT() {
       RQ = 2;
    }
    int RUN() {
        Wait();
    }
    int EVENT () {
        Number(RL); Char(13);
        if (RL == 0x1B) Stop();
    }
}

class main {
    volatile int key;
    int INIT() {
        new(Proc2, 50);
        RQ = 1;
    }
    int RUN() {
        key = InKey();
        if (key != 0) Send(2, key);
    }
    int EVENT () { }
}

ここで RQ = 1 はインスタンスのIDを1に設定する組込み関数です.RQレジスタ に設定されている値がインスタンスの識別番号になります.インスタンス生成直後は すべてのレジスタがインスタンスを生成したインスタンスから引き継がれるためプロ セスを特定するためにはRQを再設定する必要があります.インスタンスのIDが 重複する場合はシステムが管理しているインスタンス管理番号の最も若いプロセ スにメッセージが渡されます.

同時に複数のインスタンスが1つのインスタンスに対してメッセージを送る場合もあ ります.この場合は最も大きい値のメッセージが優先されます.またメッセ ージの送出元のインスタンスを識別する事はできません.インスタンス間で同期しな がら複雑な動作をする場合は共有メモリを使用する必要があるでしょう. 複数のインスタンス間の同期は難しい問題です.はじめはインスタンスが1対1の場 合から実験して見てください.

インスタンス管理番号はDistance組込み関数でのみ取得できます. 同一のインスタンス番号を持つインスタンスを識別できます. インスタンス管理番号を指定してメッセージを送るにはSendTo組込み関数を使用します. 同一のインスタンス番号を使用する事は混乱の元となります.なるべく避けてください.

[top]

【14】速度と重力

物体が勝手に運動する例です.以下のリストでは1つの立方体が速度を持ち, 重力の影響を受けながら壁にあたると反転して跳ね回ります.衝突は立方体 の重心座標で判定していますので床と後ろの壁とのオブジェクト間の衝突判 定はしていません.床には立方体の影が動いています.

/*--------- list_10 ---------------------------*/
//  立方体の弾性衝突  物体の重力,速度属性のデモ
import "esckey.j3c";
import "empty.j3c";

// 立方体のクラス 可視部分
class cube1 extends empty {
    int INIT() {
        final int color = 14;

        RQ = 666;
        DefPoint( 150,  150, -150);  // 頂点0  立方体の頂点座標を登録
        DefPoint(-150,  150, -150);  // 頂点1
        DefPoint(-150, -150, -150);  // 頂点2
        DefPoint( 150, -150, -150);  // 頂点3
        DefPoint( 150,  150,  150);  // 頂点4
        DefPoint(-150,  150,  150);  // 頂点5
        DefPoint(-150, -150,  150);  // 頂点6
        DefPoint( 150, -150,  150);  // 頂点7
        DefPlane(color, 4, 0, 1, 5, 4); 面の設定
        DefPlane(color, 4, 3, 7, 6, 2);
        DefPlane(color, 4, 1, 2, 6, 5);
        DefPlane(color, 4, 0, 4, 7, 3);
        DefPlane(color, 4, 4, 5, 6, 7);
        DefPlane(color, 4, 0, 3, 2, 1);
    }
    int RUN() {
        RotHead(3000, 1440);       // 親座標系内で3秒間で180度回転するだけ
    }
}

//  立方体の位置クラス 不可視 位置をコントロール
class cube0 extends empty {
    // 立方体の跳ね返り処理用メソッド
    int bounce() {
        volatile int position_x, position_y, position_z;
        volatile int ChangeFlag;

        ChangeFlag = 0;
        GetPosition();             // 現在位置を取得
        position_x = RX;
        position_y = RY;
        position_z = RZ;
        ChangeFlag = 0;
        GetVelocity();            // 現在の速度を取得
        if ((position_x > 850) & (RX > 0)) {
            RX = -RX;
            ChangeFlag = 1;
        }
        if ((position_x < -850) & (RX < 0)) {   // 右の壁への衝突チェック
            RX = -RX;
            ChangeFlag = 1;
        }
        if ((position_y > 850) & (RY > 0)) {    // 右の壁への衝突チェック
            RY = -RY;
            ChangeFlag = 1;
        }
        if ((position_y < -850) & (RY < 0)){   // 右の壁への衝突チェック
            RY = -RY;
            ChangeFlag = 1;
        }
        if ((position_z > -150) & (RZ > 0)){   // 床への衝突チェック
            RZ = -RZ;
            ChangeFlag = 1;
        }
        if (ChangeFlag) {          // 運動の方向に変化があれば速度を再設定
            Note(9, 50, 127);
            SetVelocity();
        }
    }
    int INIT() {
        RQ = 777;
        NewObject(0, 0);
        child(cube1, 50, 8, 6);
        ClearRegisters();          // RX - RBに0を代入
        RZ = -2000;
        SetPosition();             // 初期位置と角度の設定
        SetGravity(980000);        // 重力加速度 = 980cm/s^2 x 1000
        ClearRegisters();          // RX - RBに0を代入
        RX = 170000;
        RY = 250000;
        SetVelocity();
    }
    int RUN() {
         bounce();  // 立方体の跳ね返り処理用手続きを呼ぶ
    }
}

// 影のクラス
class SHADOW extends empty {
    int INIT() {
        final int color = 16;
        NewObject(4, 2);           // オブジェクトの生成
        DefPoint( 150,  150, 0);   // 頂点0  正方形の頂点座標を登録
        DefPoint(-150,  150, 0);   // 頂点1
        DefPoint(-150, -150, 0);   // 頂点2
        DefPoint( 150, -150, 0);   // 頂点3
        DefPlane(color, 4, 3, 2, 1, 0);
    }
    int RUN() {
        volatile int x, y, z;

        Where(777);                // 立方体の位置を取得
        RZ = -5;                   // 高さを床面の少し上に設定
        x = RX;                    // 立方体の位置を保存
        y = RY;
        z = RZ;
        Where(666);                // 立方体の角度を取得
        RX = x;                    // 立方体の位置を復帰
        RY = y;
        RZ = z;
        SetPosition();             // 初期位置と角度の設定
    }
}

//  床のインスタンス
class FLOOR extends empty {
    int INIT() {
        final int color = 29;
        NewObject(4, 2);           // オブジェクトの生成
        Priority(40000);
        DefPoint( 1000,  1000, 0); // 頂点0  正方形の頂点座標を登録
        DefPoint(-1000,  1000, 0); // 頂点1
        DefPoint(-1000, -1000, 0); // 頂点2
        DefPoint( 1000, -1000, 0); // 頂点3
        DefPlane(color, 4, 3, 2, 1, 0);
        ClearRegisters();          // RX - RBに0を代入
        SetPosition();             // 初期位置と角度の設定
    }
}

//  後壁の登録
class WALL extends empty {
    int INIT() {
        NewObject(4, 2);               // オブジェクトの生成
        DefPoint(1000,  1000,     0);  // 頂点0
        DefPoint(1000,  1000, -2000);  // 頂点1
        DefPoint(1000, -1000, -2000);  // 頂点2
        DefPoint(1000, -1000,     0);  // 頂点3
        DefPlane(13, 4, 0, 1, 2, 3);
        ClearRegisters();              // RX - RBに0を代入
        SetPosition();                 // 初期位置と角度の設定
    }
}

class main extends empty {
    int INIT() {
        new(esc_key, 100);     // ESCキーで終了させるインスタンス
        NewObject(0, 0);       // 視点用オブジェクトの生成
        ClearRegisters();      // RX - RBに0を代入
        RX = -2000             // 位置の指定 20m 後方
        RZ = -1000             // 位置の指定 10m 上方
        SetPosition();         // 位置と角度をオブジェクトに設定
        See();                 // 視点の設定
        new(cube0, 50);        // 跳ね回る立方体のインスタンス
        new(SHADOW, 50);       // 立方体の影となるインスタンス
        new(FLOOR, 50);        // 床 動かない
        new(WALL, 50);         // 壁 動かない
        BackgroundColor(0x005);// 背景色を黒に設定
        GraphMode();           // グラフィックモードに変更
    }
}

この例では7個のインスタンスが存在しますが,ESCキーチェックと立方体の位置 と立方体と影のインスタンスが動いているだけです.他のインスタンスは終了を待っ ています.立方体の位置インスタンスは自分の座標をチェックして範囲外なら方 向を反転しているだけです. 立方体の形状インスタンスは単にヘッド回転して いるだけです.影のインスタンスは立方体の位置を見て,さらに立方体の形状か ら角度を取得して自分を同じ位置の高さ-5に置くことを繰り返しているだけで す. 重力,加速度,速度の効果はシステムが画面表示する前に自動的に処理 します.

ここで立方体のインスタンスを2つに分けている理由は,立方体がどのように回 転しても地面との角度を固定するためです.自分が回転してしまうと運動の 方向の前後左右が回転してしまうためです.座標が回転しないインスタンスの子 オブジェクトとして回転する立方体の形状オブジェクトがあります.

重力を有効にするには,重力の効果が必要なオブジェクト毎に

       SetGravity(980000);  // 重力加速度 980cm/s^2 x 1000

の様に設定します. SetGravity に渡す値は16ビットの整数またはレジスタで指定します. 980000は16ビット整数の範囲にないため,レジスタで渡していま す. これは地球上の重力加速度ですが,2322600を使用すれば木星赤道上の 運動をシミュレートできます.ちなみに月面では166600です.

重力,速度,加速度は1000倍した値を与える必要があります.詳細はリファ レンスマニュアルを参照して下さい. 距離の単位としてcmを使用しています が,プログラム全体で合わせれば距離の単位は任意です.

list_10の例では一辺3mの立方体を重心が20mの位置から落として床で跳ね返り ます.つまり18.5m落ちて跳ね返り,20mの位置まで上昇して落ちはじめます. 計算上1.943秒で18.5m落ちるはずです.マシンの速さにかかわらず同じ速度 になると思います.試して見てください. 跳ね返り処理中 Note(9, 50, 127) の行でMIDIの9チャンネルから音を出し ています.パーカション系の音が出ているはずです.

もっともらしいフライトシミュレータを作成する場合は重力の影響を表現す る必要があると思います.バンクして水平旋回等を表現できます.

[top]

【15】形状の実行時生成

形状をプログラム作成時に定義しておくのではなく,実行時にプログラムに 生成させることができます. 以下の例は3角柱を生成するメソッドを 呼出しています.

/*--------- list_11 ---------------------------*/

import "eye0.j3c";
import "empty.j3c";
import "esckey.j3c";

class Tri extends empty {
    //---------------------------------------------------
    // 3角形の頂点の定義
    //  R = 正3角形の中心から頂点までの距離(30000以下のこと)
    //  P = SQRT(R*R*3)/2
    //  Q = R/2               o→y
    //         Z      Y      ↓         0
    //    0   -R      0       z     / \
    //    1    Q      P           /  .  \
    //    2    Q     -P          2 ------- 1
    //---------------------------------------------------
    int Def_Tri(int radius, int x) {
        PushRegisters();
        RX = x;
        RZ = -radius;         //  R
        RY = 0;
        Point();              // # 0
        RZ = radius / 2;
        RY = sqrt(radius * radius * 3) / 2;  //  P
        Point();              // # 1
        RY = -RY;             // -P
        Point();              // # 2
        PopRegisters();
    }
    //---------------------------------------------------
    //  3角柱の生成
    //            length
    //          +--------+
    //  radius1 *origin  |radius2  → X
    //          +--------+
    //    radius は正3角形の中心から頂点までの距離(30000以下のこと)
    //    頂点 6 面 5
    //---------------------------------------------------
    int Bar3(int length, int radius1, int radius2, int color) {
        Def_Tri(radius2, length);
        Def_Tri(radius1, 0);
        DefPlane(color, 3, 0, 1, 2);        //     3
        DefPlane(color, 3, 5, 4, 3);        //     0
        DefPlane(color, 4, 3, 4, 1, 0);     //    2  1
        DefPlane(color, 4, 4, 5, 2, 1);     //  5      4
        DefPlane(color, 4, 5, 3, 0, 2);
    }

    int INIT() {
        NewObject(16, 10);         // オブジェクトの生成  16頂点 10面
        Bar3(1200, 400, 400, 6);   // 面と頂点の生成
        ClearRegisters();          // RX - RBに0を代入
        RP = 720;
        SetPosition();             // 位置の設定
    }

    int RUN() {
        ClearRegisters();          // RX - RBに0を代入
        RP = 16720;
        RB = 4720;
        RH = 1000;
        Move0(60000);
        Stop();                    // 全インスタンス終了
    }
}

class main extends empty {
    int INIT() {
        new(Tri, 50);              // 3角柱のインスタンスを生成
        new(eye0, 50);
        new(esc_key, 50);          // ESCキーで終了させるインスタンス
        BackgroundColor(0x559);    // 背景色を黒に設定
        GraphMode();               // グラフィックモードに変更
    }
}

さらに詳細な例は revolutn.j3cを参照して下さい.任意の回転体を生成 することができます.

[top]

【16】物体を接続するポリゴン

例えば腕を曲げる場合には肘の皮膚は伸び縮みします.動物の体は骨と それを取り巻く筋肉,そして皮膚でできています.これまでの例では 多角形の大きさは定義したまま変化することはありませんでした.

階層構造の物体(【12】章)では親オブジェクトが存在して頂点を持つ場合 には,多角形の頂点番号に負の数を使用して親オブジェクトの頂点を指定 する事ができます.独立して運動する物体間に多角形(スキン)を張る事が できます.こうして,例えば肘の関節に伸び縮みする皮膚で腕を接続しま す.負の数を使用して親オブジェクトの頂点を指定するため,親の0番頂点 は指定できないことに注意して下さい.

/*--------- list_12 ---------------------------*/
import "../lib/empty.j3c";
import "../lib/fps_pos.j3c";
import "../lib/eye.j3c";

class CHILD1 extends empty {
    volatile int color;              // 面の色指定

    int INIT(){
        DefPoint( 200,  200,  -300); // 頂点0
        DefPoint(-200,  200,  -300); // 頂点1  3 ---------- 0 z=-1000
        DefPoint(-200, -200,  -300); // 頂点2  |            |
        DefPoint( 200, -200,  -300); // 頂点3  |   7    4   | z=-2000
        DefPoint( 200,  200, -1000); // 頂点4  |   6    5   |
        DefPoint(-200,  200, -1000); // 頂点5  |            |
        DefPoint(-200, -200, -1000); // 頂点6  2 -----------1
        DefPoint( 200, -200, -1000); // 頂点7
        color = 11;                       // 面の色指定
        DefPlane(color, 4, 7, 6, 5, 4);   // 面の定義 表から見て左回りに指定
        DefPlane(color, 4, 0, 3, 7, 4);
        DefPlane(color, 4, 0, 4, 5, 1);
        DefPlane(color, 4, 5, 6, 2, 1);
        DefPlane(color, 4, 6, 7, 3, 2);
        // 関節部の面を登録
        color = 4;                        // 面の色指定
        DefPlane(color, 3, -4,  3,  0);   // ねじれるため三角形を貼る
        DefPlane(color, 3, -4, -7,  3);   // -7 ---------- -4  親の頂点
        DefPlane(color, 3, -4,  0,  1);   //  |            |
        DefPlane(color, 3, -5, -4,  1);   //  |   3    0   |   子の頂点
        DefPlane(color, 3,  1,  2, -6);   //  |   2    1   |
        DefPlane(color, 3, -6, -5,  1);   //  |            |
        DefPlane(color, 3,  2,  3, -7);   // -6 ---------- -5
        DefPlane(color, 3, -7, -6,  2);

        ClearRegisters();
        RZ = -1300;
        SetPosition();                    // 位置の設定
    }

    int RUN() {
        RotPitch(3000, 720);
        RotPitch(6000, -1440);
        RotPitch(3000, 720);
        RotHead (3000, 360);
        RotHead (6000, -720);
        RotHead (3000, 360);
    }
}

class PARENT extends empty {
    volatile int color;                 // 面の色
    int INIT(){
        NewObject(20, 20);              // 親オブジェクトの生成  8頂点 5面
        child(CHILD1, 50, 8, 13);       // 子オブジェクトの生成  8頂点13面
        DefPoint( 200,  200,     0);    // 頂点0
        DefPoint(-200,  200,     0);    // 頂点1  3 ---------- 0 z=0
        DefPoint(-200, -200,     0);    // 頂点2  |            |
        DefPoint( 200, -200,     0);    // 頂点3  |   7    4   | z=-2000
        DefPoint( 200,  200, -1000);    // 頂点4  |   6    5   |
        DefPoint(-200,  200, -1000);    // 頂点5  |            |
        DefPoint(-200, -200, -1000);    // 頂点6  2 -----------1
        DefPoint( 200, -200, -1000);    // 頂点7
        color = 15;                     // 面の色指定
        DefPlane(color, 4, 0, 1, 2, 3); // 面の定義 表から見て左回りに指定
        DefPlane(color, 4, 0, 3, 7, 4);
        DefPlane(color, 4, 0, 4, 5, 1);
        DefPlane(color, 4, 5, 6, 2, 1);
        DefPlane(color, 4, 6, 7, 3, 2);
        ClearRegisters();
        RZ = 1000;
        SetPosition();                  // 位置の設定
    }

    int RUN() {
        RotHead(20000,  2880);
        RotHead(20000, -2880);
    }
}

class main extends empty {
    int INIT(){
       GraphMode();
       BackgroundColor(0x474);
       new(eye, 100);
       new(fps_pos, 50);
       new(PARENT, 50);                 // 親のプロセスを生成
    }
}

この例を実行して,赤い面がスキンにあたります.伸び縮みして変形している ことが分かります.ここでは親物体1つと子物体1つですが,親1つに複数の 子を持つ場合も同様に各々の子が親の頂点を指定できます.子は常に親の頂点 だけ参照することができます.

[top]

【17】座標系の拡大縮小

例えばビルが立ち並ぶ世界を作成するとします.1つ1つのビルは地面に固定さ れ,動くことはありません.色々なサイズの直方体を登録する必要があります. 指定したサイズの直方体の頂点を計算して登録するプログラムもできますが. 高さ,幅,奥行きを拡大縮小することができれば,1つの大きさの立方体を登 録するルーチンを作成して,どのビルのインスタンスもそのルーチンを呼び出した 後,高さ,幅,奥行きの拡大率を設定するだけで大きさの異なる直方体を作成 することができます.

物体が属している局所座標系を拡大縮小することによって,登録済みの物体の 形状を後から変更することができます.親子関係にある物体の場合も1個所だ け変更することができます.例えば,足が徐々に伸びるなどの表現ができるよ うになります.

SetScale, GetScale, Scale 組込み関数は物体の所属する座標系の縮尺を設定,取得,変更することができます.

以下のサンプルは簡単な人型の物体のアニメーションですが,ここでは特に 局所座標系のスケールの変更のデモンストレーションのために,すべて一辺の 長さが100の立方体のみで作成してみました.

J3Wには現在モデラーが用意されていませんが,モデラーを使用しなくても, ちょっとした3DCGのアニメーションを作成できる例としたつもりです.

/*--------- list_13 ---------------------------*/
class Parts {
    int DefineObject(int color) {  // 8頂点6面の立方体
        DefPoint(  0,  50,-50);    // 頂点0      奥
        DefPoint(  0,  50, 50);    // 頂点1  7 ---------- 4 x=100
        DefPoint(  0, -50, 50);    // 頂点2  |            |
        DefPoint(  0, -50,-50);    // 頂点3  |  3 ---- 0, | x=0
        DefPoint(100,  50,-50);    // 頂点4  |  2 手前 1  |
        DefPoint(100,  50, 50);    // 頂点5  |            |
        DefPoint(100, -50, 50);    // 頂点6  6 -----------5
        DefPoint(100, -50,-50);    // 頂点7
        DefPlane(color, 4, 0, 3, 2, 1);// 面の定義 表から見て左回りに指定
        DefPlane(color, 4, 4, 0, 1, 5);
        DefPlane(color, 4, 1, 2, 6, 5);
        DefPlane(color, 4, 2, 3, 7, 6);
        DefPlane(color, 4, 0, 4, 7, 3);
        DefPlane(color, 4, 4, 5, 6, 7);
    }
    int INIT()  { }
    int RUN()   { }
    int EVENT() { }
}

class  HEAD extends Parts {        // 頭
    int INIT () {
        DefineObject(15);          // 立方体の形状を設定
        ClearRegisters();
        RX = 120;
        SetPosition();             // 位置の設定
    }

    int RUN () {
        RX = 2000;                 // X軸の縮尺
        RY = 2000;                 // Y軸の縮尺
        RZ = 2000;                 // Z軸の縮尺
        Scale(5000);               // 大きくなる
        RX = 500;                  // X軸の縮尺
        RY = 500;                  // Y軸の縮尺
        RZ = 500;                  // Z軸の縮尺
        Scale(5000);               // 小さくなる
    }
}

class RightFArm extends Parts {   //  右前腕
    int INIT () {
        DefineObject(15);          // 立方体の形状を設定
        RX = 1500;                 // X軸の縮尺
        RY = 800;                  // Y軸の縮尺
        RZ = 800;                  // Z軸の縮尺
        SetScale();                // 縮尺の設定
        ClearRegisters();
        RX = 120;
        SetPosition();             // 位置の設定
    }

    int RUN () {
        RotPitch(2500,360);        // 前腕部の動作
        RotPitch(2500,-360);
    }
}

class LeftFArm extends Parts {     //  左前腕
    int INIT () {
        DefineObject(15);          // 立方体の形状を設定
        RX = 1500;                 // X軸の縮尺
        RY = 800;                  // Y軸の縮尺
        RZ = 800;                  // Z軸の縮尺
        SetScale();                // 縮尺の設定
        ClearRegisters();
        RX = 120;
        SetPosition();             // 位置の設定
    }

    int RUN () {
        RotPitch(2500,360);        // 前腕部の動作
        RotPitch(2500,-360);
    }
}

class RightArm extends Parts {     //  右腕
    int INIT () {
        child(RightFArm, 50, 8, 6);// 子オブジェクトの生成 右腕
        DefineObject(15);          // 立方体の形状を設定
        RX = 1500;                 // X軸の縮尺
        RY = 700;                  // Y軸の縮尺
        RZ = 700;                  // Z軸の縮尺
        SetScale();                // 縮尺の設定
        ClearRegisters();
        RX = 75;                   // 親座標系で測った関節の位置
        RY = 75;
        RH = 720;
        SetPosition();             // 位置の設定
    }

    int RUN () {
        RotPitch(2000,720);
        RotPitch(4000,-1440);
        RotPitch(2000,720);
    }
}

class LeftArm extends Parts {      //  左腕
    int INIT () {
        child(LeftFArm, 50, 8, 6); // 子オブジェクトの生成 右腕
        DefineObject(15);          // 立方体の形状を設定
        RX = 1500;                 // X軸の縮尺
        RY = 700;                  // Y軸の縮尺
        RZ = 700;                  // Z軸の縮尺
        SetScale();                // 縮尺の設定
        ClearRegisters();
        RX = 75;                   // 親座標系で測った関節の位置
        RY = -75;
        RH = -720;
        SetPosition();             // 位置の設定
    }

    int RUN () {
        RotPitch(2000,720);        // 左腕の動作
        RotPitch(4000,-1440);
        RotPitch(2000,720);
    }
}

class RightLeg extends Parts {     //  右足
    int INIT () {
        DefineObject(15);          // 立方体の形状を設定
        RX = 2000;                 // X軸の縮尺
        RY = 800;                  // Y軸の縮尺
        RZ = 800;                  // Z軸の縮尺
        SetScale();                // 縮尺の設定
        ClearRegisters();
        RX = 120;
        SetPosition();             // 位置の設定
        Wait();                    // ここでは何も動作していない
    }
}

class LeftLeg extends Parts {      //  左足
    int INIT () {
        DefineObject(15);          // 立方体の形状を設定
        RX = 2000;                 // X軸の縮尺
        RY = 800;                  // Y軸の縮尺
        RZ = 800;                  // Z軸の縮尺
        SetScale();                // 縮尺の設定
        ClearRegisters();
        RX = 120;
        SetPosition();             // 位置の設定
       Wait();                     // ここでは何も動作していない
    }
}

class RightThigh extends Parts {   // 右大腿部
    int INIT () {
        child(RightLeg, 50, 8, 6); // 子オブジェクトの生成 右腕
        DefineObject(15);          // 立方体の形状を設定
        RX = 2000;                 // X軸の縮尺
        RY = 700;                  // Y軸の縮尺
        RZ = 700;                  // Z軸の縮尺
        SetScale();                // 縮尺の設定
        ClearRegisters();
        RX = -10;                  // 親座標系で測った関節の位置
        RY = 25;
        RH = -1440;
        SetPosition();             // 位置の設定
        Wait();                    // ここでは何も動作していない
    }
}

class LeftThigh extends Parts {    // 左大腿部
    int INIT () {
        child(LeftLeg,  50, 8, 6); // 子オブジェクトの生成 右腕
        DefineObject(15);          // 立方体の形状を設定
        RX = 2000;                 // X軸の縮尺
        RY = 700;                  // Y軸の縮尺
        RZ = 700;                  // Z軸の縮尺
        SetScale();                // 縮尺の設定
        ClearRegisters();
        RX = -10;                  // 親座標系で測った関節の位置
        RY = -25
        RH = -1440;
        SetPosition();             // 位置の設定
        Wait();
    }
}

class BODY extends Parts {
    int INIT () {
        NewObject(8, 6);               // 親オブジェクトの生成 胴体
        DefineObject(15);
        child(RightArm,  50, 8, 6);    // 子オブジェクトの生成 右腕
        child(LeftArm,  50, 8, 6);     // 子オブジェクトの生成 左腕
        child(RightThigh, 50, 8, 6);   // 子オブジェクトの生成 右大腿部
        child(LeftThigh, 50, 8, 6);    // 子オブジェクトの生成 左大腿部
        child(HEAD,   50, 8, 6);       // 子オブジェクトの生成 頭
        RX = 3000;                     // X軸の縮尺
        RY = 2000;                     // Y軸の縮尺
        RZ = 1000;                     // Z軸の縮尺
        SetScale();                    // 縮尺の設定
        ClearRegisters();
        RP = 720;
        SetPosition();                 // 位置の設定
    }

    int RUN () {
        RotBank(3000, 720);        // 全体の動作の定義
        RotBank(6000, -1440);      // X軸まわりの回転
        RotBank(3000, 720);
        RotHead(3000, 360);        // Z軸まわりの回転
        RotHead(6000, -720);
        RotHead(3000, 360);
        RX = 2000;                 // X軸の縮尺
        RY = 2000;                 // Y軸の縮尺
        RZ = 2000;                 // Z軸の縮尺
        Scale(4000);               // 大きくなる
        RX = 500;                  // X軸の縮尺
        RY = 500;                  // Y軸の縮尺
        RZ = 500;                  // Z軸の縮尺
        Scale(5000);               // 小さくなる
    }
}

class main {
    int INIT() {
        new(BODY, 50);             // 親のプロセスを生成
        // デフォルトプロセスは視点オブジェクトになる
        NewObject(0, 0);           // 視点用オブジェクトの生成  頂点も面も不要
        ClearRegisters();
        RX = -1000;                // 視点の位置の指定
        SetPosition();             // 位置と角度を視点に設定
        See();                     // このオブジェクトが見る
        Throw();
        BackgroundColor(0x84A);    // 背景色を設定
        GraphMode();               // グラフィックウインドウを開く
        Wait();
        delete();
    }
    int RUN()   { }
    int EVENT() { }
}

例としてはチョット長くなってしまいました.画面を見ながら修正していくと, ドンドン長くなってしまいます.上の例では足に動きを付けていません. 腕の部分と同様に動きを設定することができます.さらにキー入力をチェック して各インスタンスにメッセージを送るインスタンスを追加し,メッセージを判断して 各関節の動作を決定するように変更すれば格闘ゲームも可能でしょう.

[top]

【18】グラフィック画面への文字と線分の描画

グラフィックウィンドウに文字と線分を重ね書きをすることができます. フライトシミュレータのヘッドアップディスプレイの表示のように, 例えば物体の位置,角度,経過時間などをリアルタイムに表示する場合に 使用できます.この2次元の文字と線分は一旦書き込むと常に表示されます.

文字の描画は gColorで表示色を設定し,gCursor で位置を設定します. この位置に設定は左上隅が 0 右下隅が 1999の1次元で指定します. 2次元で指定できないので少し面倒ですが (80*行)+桁 で計算できます. gCursorで位置を指定した後は, gString(文字列), gNumber(数値), gChar(文字)は 書き込み位置は更新されて続けて書き込まれます.

ウィンドウに直接描画する線分はウィンドウのピクセル位置(640x480) で位置を指定します.この線分は登録制となっていて,0番から99番の 100本までです.登録された線分は位置を書き換えるか, gLineClearで 消去されるまで表示されたままになります. 線分を表示する場合は以下の様にレジスタに設定したのち, gLine組込み関数 を実行します.

RX x0 始点
RY y0
RZ
RH x1 終点
RP y1
RB ライン番号 (0 - 99

以下の例では 3次元の回転する3角形に,0.5秒ごとに表示される文字列と 高速に書き換わる2次元の線分が重ね書きされて1分間後に終了します.

/*--------- list_14 ---------------------------*/
import "empty.j3c";

class Triangle extends empty {
    volatile int color1, color2;
    int INIT() {
        NewObject(6, 2);   //オブジェクトの生成  6頂点 2面
        DefPoint(0, 0,    500);  // 頂点0 三角形の頂点座標を登録
        DefPoint(0, 500, -500);  // 頂点1
        DefPoint(0,-500, -500);  // 頂点2
        DefPoint(0, 0,    500);  // 頂点3 頂点0と同じ
        DefPoint(0, 500, -500);  // 頂点4 頂点1と同じ
        DefPoint(0,-500, -500);  // 頂点5 頂点2と同じ
        color1 =  5;             // 面の色指定
        color2 = 14;
        DefPlane(color1, 3, 0, 1, 2); //面の定義 表から見て左回りに指定
        DefPlane(color2, 3, 5, 4, 3);
        ClearRegisters();       // RX - RBに0を代入
        SetPosition();          // 初期位置と角度の設定
    }
    int RUN(){
        RotHead(60000 28800);   // 60秒間で10回転
        DeleteObject();         // オブジェクト
        Stop();                 // 全インスタンス終了
    }
}
// ---------------------------------------------------
//   0.5秒ごとに文字列を表示するクラス
// ---------------------------------------------------
class StringDemo extends empty {
    volatile int count;
    int RandomStr() {
        gCursor(Random(25)*80 + Random(80));
        gString(" NUMBER = ");  // 文字列の表示
        gNumber(Random(100000));
        gChar(' ');             // 続けて空白を1つ書き込む
    }

    int INIT() {
       gColor(7);
       gClear();                // 画面上の文字をすべて消去
       count = 30;
    }
    int RUN() {
       RandomStr();
       if (count == 0) Stop()
       else count = count - 1;
       Pause(500);              // 0.5秒待つ
    }
}
// ---------------------------------------------------
//   ライン表示クラス 30本の線分を書き換え続ける
// ---------------------------------------------------
class LineDemo extends empty {
    int RandomLine(int LineNo) {
       RB = LineNo;
       RZ = Random(16);
       RX = Random(640);
       RY = Random(480);
       RH = Random(640);
       RP = Random(480);
       gLine();
    }
    int INIT() {}
    int RUN() {
       volatile int i;
       for (i=0; i<30; i=i+1) RandomLine(i);
    }
}
class main extends empty {
    int INIT() {
        new(Triangle, 50);         // 3角形の物体のインスタンス
        new(LineDemo, 50);         // 2次元の線分表示
        new(StringDemo, 50);         // 文字表示インスタンス
        NewObject(0, 0);           // 視点用オブジェクトの生成
        ClearRegisters();          // RX - RBに0を代入
        RX = -1000;                // 位置の指定
        SetPosition();             // 位置と角度を視点に設定
        See();                     // このオブジェクトが見る
        Throw();
        BackgroundColor(0x000);    // 背景色を黒に設定
        GraphMode();               // グラフィックウインドウを開く
        Wait();
        DeleteObject();            // オブジェクトの削除
        delete();                  // インスタンスの削除
    }

}
[top]

【19】光源の移動

光源の種類の指定と移動は j3w ver.x.30 以降でのみ可能です.

任意の物体(インスタンス)は光源となることができます.物体は Emit組込み関数を 実行すると発光するようになります.光源の種類は平行光源と点光源が可能です. 光源の種類はParallel組込み関数で指定します.

平行光源の場合の光の方向は初期値は画面手前から奥 (X軸方向)でLight組込み関数で変更できます. さらにオブジェクトが光源を持つ場合(Emit(1)),オブジェク トの姿勢で後方が変化します.Light組込み関数の方向指定がオブジ ェクトの局所座標系で指定されたものと解釈されます.

/*--------- list_15 ---------------------------*/
import "empty.j3c";
import "eye0.j3c";
import "fps_pos.j3c";

class Revolution extends empty {
    volatile int VertexIn[20 * 2];
    volatile int VertexWork[6];
    volatile int T, m, i, j;

    int Vertex(int x, int y, int z) {
        RX = x; RY = y; RZ = z;
        Point();
    }

    int Define(int color, int n, int m) {
        for(i = 0; i <= n+1; i=i+1)     // 初期頂点の登録
            Vertex(VertexIn[i*2], VertexIn[i*2 +1], 0);

        // 頂点座標の計算
        T = 2880 / m;
        // j = 0 の座標が初期値であるから
        for(j=1; j<m; j=j+1)
            for(i=2; i<=n+1; i=i+1)
                Vertex(VertexIn[i*2], VertexIn[(i*2+1)] * cos(T*j) / 10000,
                                      VertexIn[(i*2+1)] * sin(T*j) / 10000);

        // 上下の錐体を除いた部分の面(4角形)定義は
        VertexWork[0] = color;
        VertexWork[1] = 4;
        for(j=0; j<=m-2; j=j+1)
           for(i=2; i<=n; i=i+1) {
               VertexWork[2] = j * n + i;
               VertexWork[3] = j * n + i + 1;
               VertexWork[4] = (j + 1) * n + i + 1;
               VertexWork[5] = (j + 1) * n + i;
               Plane(VertexWork);
            }

        for(i=2; i<=n; i=i+1) {               // m-1 から 0 への戻り部分
            VertexWork[2] = (m-1)*n+i;
            VertexWork[3] = (m-1)*n+i+1;
            VertexWork[4] = i + 1;
            VertexWork[5] = i;
            Plane(VertexWork);
        }

        // 上の錐体の面定義
        VertexWork[1] = 3;
        for(j=0; j<=m-2; j=j+1) {
             VertexWork[2] = 0;
             VertexWork[3] = j*n+2;
             VertexWork[4] = (j+1)*n+2;
             Plane(VertexWork);
        }
        VertexWork[2] = 0;
        VertexWork[3] = (m-1)*n+2;
        VertexWork[4] = 2;
        Plane(VertexWork);

        // 下の錐体の面定義
        for(j=0; j<=m-2; j=j+1) {
             VertexWork[2] = 1;
             VertexWork[3] = (j+2)*n+1;
             VertexWork[4] = (j+1)*n+1;
             Plane(VertexWork);
        }
        VertexWork[2] = 1;
        VertexWork[3] = n +1;
        VertexWork[4] = m*n+1;
        Plane(VertexWork);
    }

    int INIT() {
        NewObject(3000,3000);
    }
}

class ball extends Revolution {
    int sphere(int r, int div) {
        volatile int i;
        VertexIn[ 0] =  r; //   x座標   TOP
        VertexIn[ 1] =  0; //   y座標
        VertexIn[ 2] = -r; //   x座標   BOTTOM
        VertexIn[ 3] =  0; //   y座標
        for (i=1; i<div-1 ; i=i+1) {
            VertexIn[i*2+2] = r * cos(i*1440/(div-1)) / 10000;
            VertexIn[i*2+3] = r * sin(i*1440/(div-1)) / 10000;
        }
    }
    int INIT() {
        RQ = 999999999;
        sphere(500, 16);    // 半径500, 緯度にそって16分割
        Define(3, 14, 30);  // 色3, 緯度(16分割-2), 経線30分割
        ClearRegisters();
        SetPosition();
    }

    int RUN() {
        RotHead(2000,2880);
        RotPitch(2000,720);
        RotBank(2000,2880);
    }
}

class moon extends ball {
    int INIT() {
        RQ = 888888888;
        sphere(100, 6);    // 半径100, 緯度にそって6分割
        Define(6, 4, 20);  // 色6, 緯度(6分割-2), 経線20分割
        ClearRegisters();
        RY = 1500;
        SetPosition();     // 半径1500の軌道を移動
        Version();
        if (RY >= 30) {
            String("The object of this version can emit.");
            Emit(1);
            Parallel(0);
        } else {
            String("The object of this version cannot emit.");
        }
    }
}

class orbit extends empty {
    int INIT() {
        NewObject(0,0);
        ClearRegisters();
        SetPosition();
        child(ball, 400, 2000,2000);
        child(moon, 400, 2000,2000);
    }
    int RUN () {
        RotHead(6000, 2880);
        RotPitch(0, 80);
    }
}

class main extends empty {
    int INIT(){
       GraphMode();
       BackgroundColor(0x474);
       new(eye0, 100);
       new(fps_pos, 50);
       new(orbit, 200);                 // 親のプロセスを生成
    }
}

この例では球を回転体として作成しています.例えば moonクラスのINITの

        sphere(100, 6);    // 半径100, 緯度にそって6分割
        Define(6, 4, 20);  // 色6, 緯度(6分割-2), 経線20分割

の部分を変化させると頂点数を変化させることができます.

また,点光源である moon の軌道半径はINITの以下の部分で指定しています.

        RY = 1500;
        SetPosition();     // 半径1500の軌道を移動

中央の球の照明のされ方が変化します.


[J3Cの言語仕様] [J3Cの組み込み関数] [INDEX]
ご意見・ご感想は,mizutani.jun@nifty.ne.jp まで.