haXeとは
おおざっぱに言うとhaXeはJavaScriptの皮を被ったOcamlです。いろいろ言いたいことはあるけどとてもいい言語。でも使っている人が少なくてもったいない。みんな使おう! 詳しくはhttp://haxe.org/?lang=jp を見てください。
Javaライクなクラスと強い静的型付けのJavaScriptに、Ocamlのようなヴァリアント型とパターンマッチが追加されてより関数型言語っぽくなっていると思ってもらえばだいたい間違いありません。
おまけに、haXeは1つのコードからC++,PHP,JavaScriptといった複数の言語のコードを生成する能力があります。同一コードからJavaScriptとC++のソースを生成できることが夢の実現のための重要な機能となります。
FlashDevelopとは
haXeを公式にサポートしているIDEです。http://flashdevelop.org/
F5キーで一発コンパイル・ブラウザ起動が可能です。
haXeのソースを理解して、型を考慮したコード補間をしてくれます!
haXeでenchant.jsする利点
例えば、
ga -> game
game. -> input
game.input. -> left,top,right,bottom
の様に、型まで考慮して候補が出てきます。
一例を挙げると、イベントは文字列の'enterframe'ではなく、イベント型のevent_ENTER_FRAMEで渡します。これは単に文字列を定数に置き換えたのではないので、別の定数を誤ってパラメーターに渡すこともありえません。要求されているイベント型以外はバグとしてコンパイル時に検出されます。
コード例
enchant.jsのsample1の移植
http://enchantjs.com/ja/sample.html
package ; import js.Lib; import EnchantHX; class Bear extends HxSprite { public function new() { super(32, 32); x = 8; y = 8; image = Main.game.assets.get('chara1.gif'); addEventListener(event_ENTER_FRAME, function(e) { // check input (from key or pad) on every frame if (Main.game.input.right) { x += 2; } if (Main.game.input.left) { x -= 2; } if (Main.game.input.up) { y -= 2; } if (Main.game.input.) { y += 2; } }); } } class Main extends HxGame { public static var game : HxGame; function new() { super(320, 320); fps = 24; preload(['chara1.gif']); // The images used in the game should be preloaded onload = function() { var bear = new Bear(); // add bear to rootScene (default scene) rootScene.addChild(bear); // display d-pad var pad = new HxPad(); pad.x = 0; pad.y = 224; rootScene.addChild(pad); rootScene.backgroundColor = '#aaaaaa'; }; start(); } static function main() { game = new Main(); } }
解説
クラスがJavaScriptのプロトタイプベースからJavaライクなクラスに変わっているので見た目がだいぶ違いますが、基本的に一対一でベタに移植したものです。
名前が衝突しないようにSprite -> HxSpriteのように、クラス名にHxが付きます。
haXeは可変長の引数を許さないため、preloadはリストを受け取るようになっています。
preloadしたリソースはassetsから取得しますが、assetsは単なるオブジェクトではなくHxAssetクラスのgetメソッドで取得します。
enchant.jsのシューティングゲームサンプルの移植
package ; import js.Dom; import js.Lib; import EnchantHX; import EnchantUtil; class Main extends HxGame { public static var game : Main; public static var player : Player; public var touched : Bool; public var score : Int; public static var enemies : IntHash<Enemy>; public static var scoreLabel : HxScoreLabel; public static function rand(r:Float) : Float { return Math.random() * r; } public function new(w:Int, h:Int) : Void { super(w, h); } static function main() { game = new Main(320, 320); Lib.window.onload = game.run; } function run(e:Event) : Void { game.fps = 24; game.score = 0; game.touched = false; game.preload(['graphic.png']); game.onload = function() { player = new Player(0, 152); enemies = new IntHash<Enemy>(); game.rootScene.backgroundColor = 'black'; game.rootScene.addEventListener(event_ENTER_FRAME, function(e){ if( rand(1000) < game.frame / 20 * Math.sin(game.frame / 100) + game.frame / 20 + 50){ var y = rand(320); var omega = y < 160 ? 1 : -1; var enemy = new Enemy(320, y, omega); enemy.key = game.frame; enemies.set(game.frame,enemy); } scoreLabel.score = game.score; }); scoreLabel = new HxScoreLabel(8, 8); game.rootScene.addChild(scoreLabel); } game.start(); } } class Player extends HxSprite { public function new(x:Int, y:Int) : Void { super( 16, 16); this.image = Main.game.assets.get('graphic.png'); this.x = x; this.y = y; //this.frame = 0; Main.game.rootScene.addEventListener(event_TOUCH_START, function(e:Dynamic) { Main.player.y = e.y; Main.game.touched = true; } ); Main.game.rootScene.addEventListener(event_TOUCH_END, function(e){ Main.player.y = e.y; Main.game.touched = false; }); Main.game.rootScene.addEventListener(event_TOUCH_MOVE, function(e) { Main.player.y = e.y; } ); this.addEventListener(event_ENTER_FRAME, function(e) { if(Main.game.touched && Main.game.frame % 3 == 0){ var s = new PlayerShoot(this.x, this.y); } }); Main.game.rootScene.addChild(this); } } class Enemy extends HxSprite { var omega : Float; var direction : Float; var moveSpeed : Float; var time : Float; public var key : Int; public function new(x:Float, y:Float, omega){ super(16, 16); image = Main.game.assets.get('graphic.png'); this.x = x; this.y = y; frame = 3; time = 0; this.omega = omega; this.direction = 0; moveSpeed = 3; addEventListener(event_ENTER_FRAME, function(e){ move(); if(this.y > 320 || this.x > 320 || this.x < -this.width || this.y < -this.height){ this.remove(); }else if(this.time++ % 10 == 0){ var s = new EnemyShoot(this.x, this.y); } }); Main.game.rootScene.addChild(this); } public function move(){ direction += Math.PI / 180 * this.omega; x -= this.moveSpeed * Math.cos(this.direction); y += this.moveSpeed * Math.sin(this.direction); } public function remove() : Void{ Main.game.rootScene.removeChild(this); Main.enemies.remove(this.key); //delete this; } } class Shoot extends HxSprite { var direction : Float; var moveSpeed : Float; public function new (x, y, direction){ super(16, 16); image = Main.game.assets.get('graphic.png'); this.x = x; this.y = y; this.frame = 1; this.direction = direction; moveSpeed = 10; addEventListener(event_ENTER_FRAME, function(e){ this.x += moveSpeed * Math.cos(direction); this.y += moveSpeed * Math.sin(direction); if(this.y > 320 || this.x > 320 || this.x < -width || this.y < -height){ remove(); } }); Main.game.rootScene.addChild(this); } public function remove(){ Main.game.rootScene.removeChild(this); } } class PlayerShoot extends Shoot { public function new (x, y) : Void { super(x, y, 0); addEventListener(event_ENTER_FRAME, function(e){ for(i in Main.enemies){ if(i.intersect(this)){ remove(); i.remove(); Main.game.score += 100; } } }); } } class EnemyShoot extends Shoot { public function new (x, y) : Void{ super(x, y, Math.PI); this.addEventListener(event_ENTER_FRAME, function(e){ if (Main.player.within(this, 8)) { Main.game.end(Main.game.score, "SCORE: " + Main.game.score); } }); } }
ダウンロード
まだサンプルがJavaScriptで動作するだけですが、夢への第一歩です。
EnchantHX.zip