BASIC言語

aukimiのクラシックなBASIC方言:型、構造体、2つの省略記法、そして誰もがはまる規則。その裏には2 237個のコマンドがある。

一言で言うと#

aukimiはクラシックなBASIC方言を同梱しています。おもちゃのマクロシステムではなく本格的な言語で、型付き数値、レコード、メソッド、参照、構造化されたエラー、コルーチン、WebGPUコンピュートを備えます。これはEngineのTXTビューポートモードと、スクリプトエディタのBASICビューの中に存在します。

JavaScriptが正規のスクリプティングAPIであることに変わりはありません。 BASICはオプションのフロントエンドです。両方とも同じ非公開ディスパッチャーにコンパイルされるので、BASICプログラムとJavaScriptプログラムはまったく同じエンジンに届きます。何も二流ではなく、別のランタイムでもありません。

誰もがはまる規則#

コマンド呼び出しには常にかっこが付きます。

mode = ClipSpaceMode()
SpritePosition(player, 320, 180)

これはスタイルの問題ではありません。コンパイラがコマンドと変数を区別するために必要なもので、これによりスペルミスがコンパイル時に捕まえられるようになりました。

書いたものかつて起きていたこと今起きること
SpritePositon(1, 10, 20)問題なくコンパイルされ、スプライトは動かず、失敗は後になってから... is not definedとして表面化したコンパイルエラーになり、カタログ上でいちばん近いスペルが提案される
Blorp(1, 2)生成されるJavaScriptにそのまま転送されていたコンパイルエラー
ceci nest pas du basic文として発行されていたコンパイルエラー

書き間違いエンジンのバグとして現れていたのは、かつての話です。84個のデモからなるコーパスで測定したところ、このガードは誤検出ゼロで、実際に静かに失敗していた2件、SetMusicSystemVolumeGetLightSensorExistsを見つけました。カタログはこれらをMusicSystemVolumeLightSensorExistsとしてしか知りません。

2つの抜け穴は意図的に残されています。console.log("x")window.x = 1は今も通ります。これらを塞ぐガードは書かれ、測定され、そして撤回されました。BASICはコンパイラが列挙できない名前を提供するコンテキストの中で動くので、未知のルートを拒否すると正当なプログラムまで拒否してしまうからです。ここでの誤検出は、抜け穴そのものより悪いものです。

Printは描き、Consoleはログを出す#

いちばんよくある驚きです。

Print(...)画面に描画するもので、プログラムは毎フレームこれを呼ぶ
Console(...)エディタのコンソールに書き込むだけで、他には何もしない
Debug(...)PureBasicの名前をそのまま使った同じコマンド。指が覚えている人のために
ConsoleWarningConsoleErrorClearConsoleログ側の残り

フレームループの中でPrintを呼ぶと本当のエラーがコンソールに埋もれてしまいます。だからこそ、この2つはフラグ1つで切り替える1つのコマンドではなく、別々のコマンドになっています。

#

数値型は本物で、エディタ、HTML書き出し、Bevy/ネイティブビルドのどのターゲットでも同じように振る舞います。

  • 符号付きおよび符号なしの8、16、32、64ビット整数。 決定的な型変換、整数のDIV、そしてラップアラウンドするオーバーフロー付き。
  • float32とfloat64。
  • クラシックな接尾辞記号も引き続き使えます。name$は文字列、speed#は浮動小数点数です。

OPTION EXPLICITはオプトインです。 クラシックな暗黙変数は既定のままなので、インポートされたプログラムも変わらず動きます。これをオンにすることは、それなしでは安全になり得ない2つの機能、数値型付けと参照、の前提条件です。スペルミスした代入が静かに変数を作ってしまうと、その両方が台無しになるからです。

構造体#

レコード#

TYPEはレコードを宣言します。任意にネストでき、値セマンティクスを持ちます。代入すると、ネストした配列やコレクションを含めてディープコピーされます。

メソッド#

METHODは関数をインスタンスに束縛し、内部ではSELFが使え、Newで構築、Deleteで破棄します。ディープコピーはメソッドとの整合性を保ったままです。

参照#

REFBYREFは2つの綴りを持つ同じものであり、どちらもスカラー、レコード、単一フィールド、配列、リストカーソル、マップエントリに対して、読み書き可能なライブなパラメータを与えます。参照は呼び出しを越えて転送され、境界では固定幅の型に変換されます。

ハンドル#

@targetは第一級の参照を取ります。HANDLEPOINTERはその型付きの形で、チェック付きの読み取り、書き込み、有効性確認、解放を持ち、削除されたレコードへのハンドルは、放置されたダングリングポインタになるのではなく無効化されます。

制御と安全性#

構造化エラーTRY / CATCH / FINALLY。ネスト可能で、THROW、保護されたRETHROW、構造化キャッチと文字列キャッチの両方があります。

条件付きコンパイル#IF / #ELSEIF / #ELSE / #ENDIF。ネスト可能で、機能フラグと、エディタ・HTML・Bevy・ネイティブ/wasm・デバッグ/リリース向けの明示的なプロファイルを持ちます。どのターゲットでも、インクルードよりに解決されます。

リッチな関数:デフォルト値、OPTIONAL、末尾のPARAMARRAY(または...)、安全な引数の数によるオーバーロード、型付きの戻り値変換、そしてクローンされたレコードと配列の戻り値。

複数ファイルからなるプログラム#

#INCLUDE#INSERTIncludeFileは、1つのプログラムを複数ファイルに分けます。エディタはプロジェクトモジュールを作成・インポートし、インクルードパスを補完し、依存関係グラフを保持し、あらゆる実行ターゲットがそれに対応するよう配線されています。

コルーチン、そして意図的に外されたもの#

協調的なタスクはラベルベースで、型付きハンドル、結果とエラーの状態、yield時点でのキャンセル、1つのタスクまたはグループへのawaitを持ちます。どのターゲットでも同じゲームスケジューラを共有します。

共有メモリを伴うスレッドは意図的に除外されています。 「まだ実装していない」のではなく、除外です。原因を追えないデータ競合よりも、理由の追える協調スケジューラの方が優れています。

WebGPUコンピュート#

型付きのf32 / i32 / u32ストレージバッファ、生のWGSLパイプライン、フレーム順のディスパッチとメモリブロックの読み戻し。BASICから行う本物のGPUコンピュートです。

⚠️ Engine限定です。 HTMLターゲットは明示的なcapabilityエラーを送出し、Bevyターゲットは最初のディスパッチで失敗するバイナリを生成する代わりに、ビルドに非互換性を報告します。

同じ問題に対する2つの省略記法#

コマンドの597個はSet/Get<Object><Property>という名前です。 Spriteが66、Tweenが58、Textが32、Editが29。オブジェクトの名前が関数名の中に埋め込まれており、そのidが常に各行の最初の引数です。以下の2つの省略記法はどちらもこの重複を取り除きますが、どちらも新しいAPIではありません。コンパイラが本当のコマンド名を組み立て直し、カタログの中でそれを探すだけです。

WITH <family>、ブロックの中で#

WITH SPRITE s
  \position 100, 200      ` SpritePosition(s, 100, 200)
  \size 64, 64            ` SpriteSize(s, 64, 64)
  x# = \x                 ` SpriteX(s)
ENDWITH

文の先頭にある\prop書き込み、それ以外の場所では読み取りです。位置がすべてを決めるので、覚えるべき第2の記法はありません。WITH hero、1つのトークンは、依然としてレコードブロックです。WITH SPRITE sはカタログのファミリーを選びます。

s.position(...)、それ以外のあらゆる場所で#

s = CreateSprite(img)     ` the family is DEDUCED from the creation command
s.position(100, 200)      ` SpritePosition(s, 100, 200)
s.visible = 1             ` SpriteVisible(s, 1)
x# = s.x                  ` SpriteX(s)

ファミリーはCreate<F> / Load<F> / Clone<F>から、あるいはidが別の場所から来る場合は明示的なs AS Spriteから得られます。ファミリーがわかっている変数だけが書き換えられます。レコードは自分のフィールドを保ち、リストは自分のメソッドを保ち、SELF.fieldには手を付けません。

⚠️ 型付きパラメータは認識されません。 FUNCTION move(s AS Sprite)sにファミリーを与えません。このパスが読むのは文レベルの宣言と代入だけです。これはバグではなく実際の制限なので、ここに明記しておきます。

⚠️ カタログはプロパティでは動詞を落とし、ライフサイクルでは残します。 SpritePositionは書き込み、SpriteXは読み取りで、どちらもSetGetを持ちません。カタログ全体でSetから始まるコマンドは4個Getから始まるものはゼロです。動詞が残っているのは、何かが生まれる、または終わる場合だけです。CreateSpriteLoadImageDeleteSpriteです。

これを知っておく価値があるのは、スペルミスはコンパイルエラーになり、Set…こそが人々がやってくる典型的なスペルミスだからです。カタログこそが権威で、リファレンスはそのカタログから生成されています。

文字列補間、オプトイン#

Print($"score: {points}")        ` "score: " + Str(points)

{{}}は文字どおりの中括弧を書きます。

⚠️ $接頭辞は必須で、単なる飾りではありません。 すべてのリテラルを補間してしまうと、world APIが日常的に受け取る手書きのJSONが壊れます。

CreateTerrain("island", "{\"preset\":\"island\",\"size\":256}")

ここでは中括弧は穴ではなくデータです。接頭辞のない文字列は、中身が何であれ手を付けられません。

名前を付けることは公開すること#

クラシックなプログラムは何百ものオブジェクトを作ります。そのうち名前を付けたものだけがScene Graphに入ります。 SpriteName(id, "player")とその仲間は、エンジンの残りの部分から見えるツリーの中に、オブジェクトを配置するものです。LightNameParticlesNameも、ライトとエミッターに対して同じことをします。

プログラムが作るものに名前を付けるというチュートリアルでは、同じシーンを、一度はツリーが空のまま、もう一度は3つの項目が入った状態で、実際に歩いて確認します。

コマンドカタログ#

2 237個のコマンド、2 550個の公開オーバーロード、43のカテゴリー。 最大のものです。

3D286Studio106File61
Core200Input95Memblock43
Tweening149Input-Raw943D Particles41
3D Physics144Multiplayer91World40
Sprite135Text68Particles38
Platform1112D Physics66Sound37

加えてJSON(36)、Image(35)、Skeleton(34)、XML(34)、Music(29)、 Preference(27)、Extras(21)、HTTP(21)、String(21)、Math(19)、 Regular Expression(19)、Video(18)、Dictionary(17)、Benchmarking(15)、 Compute(14)、Date(13)、Maths(12)、Time(12)、StringBuilder(9)、 Compression(7)、Font(8)、Error(4)、Array(3)、Cipher(2)、Sort(2)。

StudioComputeはaukimi独自の追加分です。コンソールコマンド、オブジェクトの命名、WebGPUパイプラインです。

**コマンドリファレンス全体**にはすべてのコマンドが、あらゆるパラメータとあらゆるオーバーロードとともに、独自の検索機能付きで一覧化されています。このページが数えているのと同じカタログから生成されているので、遅れが生じることはありません。エディタ自身のAPIパネルも同じカタログを読んでいます。

このページは言語を扱い、あちらはコマンドを扱います。

制限とよくあるつまずき#

  • コマンドのかっこは省略できません。 素のPureBasic形式(Debug "x")は拒否されます。それを許すと、コンパイル時のガードが依存している変数/コマンドのあいまいさが再び開いてしまうからです。
  • 同名のカタログコマンドより、ユーザー関数が優先されます。 変数も、型付き宣言の中や、代入の左辺、あるいはパスの根として、コマンドと同じ名前を共有できます。
  • 変数とユーザー関数は大文字小文字を区別しません。 そしてそのシンボル空間は、カタログのものとは本当に別です。
  • Printはログではありません。 上記参照。これがコンソールを埋め尽くす間違いです。
  • Computeは書き出しに対応しません。 頼る前に、フォールバックのパスを書いておいてください。