#はじめに
こんにちは。
3DはDirectX10で挫折した@minimoです。
趣味は2DでSTG作る人なので、3Dは次元の違う世界の話と思ってます。
…が!せっかくphina.jsが3Dライブラリに対応した機能を持っているのなら使ってみたい!
という事で、俺的二大3Dライブラリ、Three.jsとGLBoostについて、phina.jsっぽく使ってみました。
現在、phina.jsは開発中の為、3Dのサポートに関しては別途レイヤーを用意して、緩く対応という事になっております。
phina.jsのGitterの方でもお話がありますが、phina.jsにおける3D対応に関してはどのように対応するか検討中です。
あくまでも、やってみた的な記事として捉えて頂けると幸いです。
phina.jsの3D機能については@emadurandalさんと@daishi_hmrさんがエントリお書きになってますのでそちらも是非ご参照ください。
国産ゲームライブラリ「phina.js」で3Dゲーム作ろうぜ(emadurandalさん)
せっかくだから俺も3Dやってみた(daishi_hmrさん)
#Three.js編
まずはThree.jsを使ってみます。
こちらは、@daishi_hmrさんがtmlib.js用に作成したtm.hybridをパクってphina.js用に修正し、phina.threeとして使用しています。
正常に表示されたら、グラドリエル王女がぴょこぴょこしつつ回ってるのが見えると思います。
ちょっと、ソースを見てみましょう。
//Three.js用レイヤー
var layer = this.layer = phina.display.ThreeLayer({
width: SCREEN_WIDTH,
height: SCREEN_HEIGHT
}).addChildTo(this);
基本的な部分は同じです。
ThreeLayerを作成し、シーンに追加しています。
//3Dメッシュをレイヤーに追加
var mesh = phina.three.Mesh('gradriel').addChildTo(layer);
mesh.update = function() {
//ぐるぐる
this.rotation.y+=0.02;
}
//Tweenerが使える
mesh.tweener.clear()
.to({scaleY: 0.9}, 500, "easeOutSine")
.to({scaleY: 1}, 500, "easeOutElastic")
.setLoop(true);
phinaのSpriteと同じ形でメタセコイアのモデルをアセットとして読み込み、Meshにアセット名をパラメータとして渡す事が出来ます。
phina.main(function() {
var app = phina.game.GameApp({
assets: {
image: {
'hiyoko': 'assets/hiyoco_nomal_full.png',
},
mqo: {
'gradriel': 'assets/gradriel_pose.mqo',
},
},
startLabel: 'main',
width: SCREEN_WIDTH,
height: SCREEN_HEIGHT
});
app.run();
});
アセットはMQOとして、Imageと同じ形で指定して読み込んでます。
##どういう事をしてるのか
phina.define("phina.three.ThreeElement", {
superClass: "phina.app.Element",
init: function(threeObject) {
this.superInit();
this.threeObject = threeObject || new THREE.Object3D();
},
...
_accessor: {
scaleX: {
get: function() {
return this.threeObject.scale.x;
},
set: function(v) {
this.threeObject.scale.x = v;
},
...
@daishi_hmrさんの昨日の記事と同様の形で、three.jsのオブジェクトをラップし、各プロパティに対しアクセッサを定義してあります。
これにより、Tweenerクラスを利用して、座標移動や回転、スケールの変更が出来るようになります。
#GLBoost編
お次はGLBoostです。
GLBoostとは、@emadrandalさんが開発中の新進気鋭のWebGLライブラリです。
玄人向けとの事ですが、裏を返せば自由度があるという事であり、開発者の@emadurandalさんがphina.jsの開発にも参加してると言う事で、個人的には非常に期待しております。
(玄人向けの部分は時間が経てばサンプルも出てくるでしょうし、ある程度は敷居も下がるのでは?とも思ってます(他力本願))
MQOモデルの読み込み対応が間に合わなかったので、@emadurandalさんの19日の記事のサンプルを元にしています。
オブジェクトに対してちゃんとTweenerが効いてるのが確認できると思います。
ただ、スケール変更がそれぞれのオブジェクトの原点ではなく、ワールド座標の原点に対してのスケール変更になっている感じです。
調査して修正したかったのですが、ちょっと間に合わなかったのでそのままです(そんなのばっかでごめんなさい><)
ちょっと中身を見てましょう。
//GLBoost用レイヤー
var layer = this.layer = phina.display.GLBoostLayer({
width: SCREEN_WIDTH,
height: SCREEN_HEIGHT
}).addChildTo(this);
//カメラ設定
var lookat = {
eye: new GLBoost.Vector4(0.0, 1.5, 10.0, 1),
center: new GLBoost.Vector3(0.0, 1.5, 0.0),
up: new GLBoost.Vector3(0.0, 1.0, 0.0)
};
var perspective = {
fovy: 45.0,
aspect: 1.0,
zNear: 0.1,
zFar: 1000.0
};
var camera = phina.glboost.Camera(lookat, perspective).addChildTo(layer);
this.createObject();
layer.prepareForRender();
ここらへんは、@emadurandalさんのサンプルとほぼ同じですね。
GLBoostLayerをシーンに追加して、カメラも追加しています。
createObject: function() {
var material = new GLBoost.ClassicMaterial(this.layer.canvas);
material.shader = new GLBoost.PhongShader(this.layer.canvas);
var geometry = new GLBoost.Cube(
new GLBoost.Vector3(20,20,20),
new GLBoost.Vector4(Math.random()*1,Math.random()*1,Math.random()*1,1),
this.layer.canvas);
for (var i = 0; i < 2000; i ++) {
var object = phina.glboost.Mesh({
geometry: geometry,
material: material
});
object.translate = new GLBoost.Vector3(Math.randint(0, 800) - 400, Math.randint(0, 800) - 400, Math.randint(0, 800) - 400);
object.rotate = new GLBoost.Vector3(Math.randfloat(0, 2)*Math.PI, Math.randfloat(0, 2)*Math.PI, Math.randfloat(0, 2)*Math.PI);
object.scale = new GLBoost.Vector3(Math.randfloat(0.5, 1.5), Math.randfloat(0.5, 1.5), Math.randfloat(0.5, 1.5));
object.dirty = true;
if (i%3 == 0) {
//phina.jsのオブジェクトと同様にtweenerが使える
object.tweener.clear()
.to({scaleX: 3}, 500, "easeOutSine")
.to({scaleX: 1}, 500, "easeOutElastic")
.setLoop(true);
}
object.addChildTo(this.layer);
}
},
});
オブジェクトの生成は、分かり易くする為に別関数に切り出しました。
var object = phina.glboost.Mesh({
geometry: geometry,
material: material
});
違うのはこの部分ですね。
GLBoostのMeshをラップして、ジオメトリとマテリアルを元に生成出来る様にしてあります。
連携の手法については、Three.jsと同様の為、割愛致します。
positionがtranslateだったり、ratationがrotateだったりと、機能が同じでも名前が違うプロパティがあるので注意が必要です。
もし、本格的にこの手法で連携を進める場合、こういった細かい差異が吸収出来る様に設計する必要があると思われます。
#まとめ
人様のコードを切った貼ったのヤクザ仕事ばかりで非常に恐縮ですが、以上で本エントリは終了となります。
冒頭の繰り返しになりますが、あくまでも勝手に作ってみたよ!という事で、今後phina.jsに組み込まれるというものではありません。
私感ではありますが、この緩い対応のまま、各WebGLのライブラリで使用されるクラスをラップして、phina.jsの機能を扱えるようにする、というのが一番直感的で早道かなという気がします。
phina.jsにはTweener等、使いこなせば超強力な機能が沢山ありますので、2Dゲームを作る感覚で3Dのゲームが作れる様になると良いですね。
phina.jsもGLBoostも開発が始まったばかりで、この先も発展が期待できるライブラリです。
今後も地味に開発に参加しつつ、発展に寄与する事が出来ればと思ってます。
と、固い締めをした所でさようなら。
おまけ
phina.jsでMMD再生してみたよ(音が出ます)
takahiroxさんのThree.js用mmdローダーを使用しております。