enchant.jsで役立つ使い方・ゲームの作り方をいろいろまとめました。
これくらい使えるとenchant.jsでもそこそこのクオリティのゲームを作ることができます。
※この記事はもともと別々だった記事を一つにまとめたものです。
★ちなみに筆者が作ったゲーム(2019年くらいまでがenchant.js) ▷ ウェブゲームセンターコスモ
☆簡単にゲームが作れるテンプレート公開してます ▷ enchant.jsテンプレート
※追記 —————————————–
PixiJSという新しいライブラリを使ったゲームの作り方書きました(‘ω’)ノ
—————- 追記終わり——————–
目次
enchant.jsのダウンロード
公式サイトが消滅してしまいダウンロード場所が分からない方はこちらからダウンロードできます。
▷ https://github.com/ghelia/enchant.js-builds
「build」フォルダにenchant.jsのファイルがあります。
アセットを連想配列にして一括で取り込む
enchant.jsのデータの読み込みをするpreload()という関数について、ちょっとでも楽になる方法の紹介です。
データの読み込みを本やサイトで調べるとだいたい
core.preload('aaa.png', 'bbb.png'); //使うとき var sprite = new Sprite(64, 64); sprite.image = core.assets['aaa.png'];
のように書かれているけど、こうすると後で違うファイル名の画像にしたりするときに結構めんどくさい(-_-;)
で、このpreload()関数は配列で渡すことができるようなので連想配列でわかりやすい名前を付けて作っておく。
var ASSETS = { 'start':'start.png', 'title':'title.png', };
読み込みは配列で一括。すごい楽ちん。
core.preload(ASSETS);
画像を使う場所で以下のように指定する。
title.image = core.assets['title']; start.image = core.assets['start'];
と、まぁこんな感じでファイル名ではなくキーで指定するからわかりやすいし、ファイル名が変わっても修正は上の配列部分を変えるだけで済むので楽ちん。
enchant.jsのクラスの作り方
enchant.jsには独自のクラスのようなものを作れる機能があります。
このクラスを上手く使えば処理を分割してわかりやすく書くことができます。さらに継承もできるのでプログラムを簡潔に書くことも出来るようになります。
書き方としては以下のようになります。
var Abc = enchant.Class.create({ //処理 }); var abc = new Abc();
まずクラスを記述して、newで生成するって感じです。これでクラスとして扱うことができます。
後はこの中にメンバやメソッドなどを書いていきます。
メソッドは以下のように書きます
var ABC = enchant.Class.create({ aaa: function(){//メソッドaaa //処理 },//カンマが必要 bbb: function(a, b){//メソッドbbb //処理 } });
各メソッドの間にはカンマが入ります。
使い方が決まってるメソッド
メソッドの中には使われ方が決まっているものもあります。
・initialize・・クラスをnewで生成したときに自動で行われる初期化処理です。初期値を設定しておきたいときなどに使います。もちろん後で呼び出して使うこともできます
・onenterframe・・フレームごとに行う処理を書きます。フレーム毎に勝手に処理してくれます。便利ですが個人的にはあまり使っていません。私はフレーム毎の処理はすべてメインループに書いています。その方が自分の考えた順番で処理させることができるし後で読み返したときに流れが分かるためです。ただし、基本的に流れに関係ないようなものはここに書くこともあります。(たぶんこれはSpriteなどにだけあるようです。なのでSpriteを継承しないとないみたい)
継承っていう機能
クラスには継承っていう別のクラスの機能を引き継いで新しいクラスを作る機能があります。たぶん最初はSpriteを継承してプレイヤークラスを作ったりすると思います。その際の書き方は以下のようになります
var Player = enchant.Class.create(enchant.Sprite, { initialize: function(w, h){ enchant.Sprite.call(this, w, h); //処理 } });
これでSpriteクラスを持った新しいクラスを作ることができます。もちろん自作のクラスも継承できます(enchant.Spriteの部分を自作クラスに変える)。
さらに、継承して作ったクラスを継承してまた新しいクラスをつくることもできます。
継承すると継承元のプロパティやメソッドがそのまま引き継がれます。つまりよく似た内容のクラスが複数必要な場合、一つのクラスに共通の処理を作っておいてそれを継承してクラスを作成し、あとはそれぞれの処理を付け足すだけで済みます。
enchant.jsのGroupクラスはとっても便利
enchant.jsでゲームを作っていて気付いたのがGroupクラスの便利さ。
実は最初は使い方が分からずあんまり使っていなかったんですが、何度か使っているうちに「そう使うものだったのか!」という発見がありました。
Groupは親になる
まずGroupとは何かって話ですが、簡単に言うと要素の親になります。スプライトを画面に表示する際にaddChild()を使っていますが、綴りの通り子をくっつけているわけです。つまりルートシーンにaddChildするとルートシーンが親になります。
で、Groupを使うときはGroupにaddChildします。そうすることでGroupが親になります。
ちょっと何言ってるかわからない、ってなってるかもしれませんがenchant.jsではaddChildされた要素は親要素に対して座標を持ちます。つまりシーンにaddChildされるとシーンに、GroupにaddChildされるとGroupに対しての座標になります。
Groupの座標を変えるだけで済む
enchant.jsではスプライトなどが持つ座標は画面に対しての絶対座標ではなく、相対座標になっています。つまり、
親が動けば子も動く
ということです。例えばこんな感じのメッセージボックスを表示させたい場合
Groupを使ってこのメッセージボックスを作ると後で表示位置を変えたくなった時にGroupの座標を変えるだけで済みます。Groupの座標を変えるだけで子の要素も移動します。なぜなら子の要素は親の座標を基準にした座標になるからです。
さらに便利なことはGroupを拡大・縮小すると子要素も同じくそうなります。
enchant.jsのロード画面を好きなように改造する
enchant.jsのゲームを起動すると一番最初に表示される画面。このバーが満たされていくことでロード状況を教えてくれるわけですが、何もない画面にこれだとなんか寂しいので改造してみましょう。
enchant.js内のloadingSceneの中身がこちら。
enchant.LoadingScene = enchant.Class.create(enchant.Scene, { initialize: function() { enchant.Scene.call(this); this.backgroundColor = '#000'; var barWidth = this.width * 0.4 | 0; var barHeight = this.width * 0.05 | 0; var border = barWidth * 0.03 | 0; var bar = new enchant.Sprite(barWidth, barHeight); bar.disableCollection(); bar.x = (this.width - barWidth) / 2; bar.y = (this.height - barHeight) / 2; var image = new enchant.Surface(barWidth, barHeight); image.context.fillStyle = '#fff'; image.context.fillRect(0, 0, barWidth, barHeight); image.context.fillStyle = '#000'; image.context.fillRect(border, border, barWidth - border * 2, barHeight - border * 2); bar.image = image; var progress = 0, _progress = 0; this.addEventListener('progress', function(e) { // avoid #167 https://github.com/wise9/enchant.js/issues/177 progress = e.loaded / e.total * 1.0; }); bar.addEventListener('enterframe', function() { _progress *= 0.9; _progress += progress * 0.1; image.context.fillStyle = '#fff'; image.context.fillRect(border, 0, (barWidth - border * 2) * _progress, barHeight); }); this.addChild(bar); this.addEventListener('load', function(e) { var core = enchant.Core.instance; core.removeScene(core.loadingScene); core.dispatchEvent(e); }); } });
なんとこの部分はjavascriptそのものではなく我々が慣れ親しんだenchant.jsのスタイルで書かれています!!これなら改造し放題!!
と、行きたいところですが、ここでは画像や音楽は使えません。だってロード前だから。読み込み状況を知らせてくれる画面だから。。
ただどうしても画像を出したいなら必要な画像だけ先に読み込んでしまえば使うことは可能です。
ちなみに私はこんな感じに作りました。
ラベルを追加して読み込み状況のバーの太さを変えました。barwidth、barHeight、borderあたりの値を変えてやれば簡単にできますよ(‘ω’)ノ
画面のフェードアウト処理
ゲームを作っていて画面遷移時に絶対欲しいのがフェードアウト処理です。このフェードアウト処理がないと突然画面が切り替わり、あまりにも不自然に感じてしまいます。
フェードアウト処理はopacityという透明度のプロパティを使います。これは1が不透明(見える)で0が透明(見えない)です。なので黒い画像を用意してこれが透明から不透明に変わっていけばフェードアウトしていっているように見えるわけです。
var FadeOut = enchant.Class.create(enchant.Sprite, { initialize: function(w, h, color) { enchant.Sprite.call(this, w, h); // Surface作成 var bg = new Surface(w, h); bg.context.fillStyle = color; bg.context.fillRect(0, 0, w, h); // Sprite作成 Sprite.call(this, w, h); this.image = bg; this.x = 0; this.y = 0; this.opacity = 0; this.isStart = false; }, //フェードアウト開始初期処理(引数にシーンが必要) start: function(scene){ if(!this.isStart){ scene.addChild(this); this.isStart = true; } }, //実行処理(先にstart()で初期処理しないと作動しない) do: function(speed){//引数:フェードアウトの速さ0.01~0.5(大きいほど速い) if(this.isStart){ this.opacity += speed; if(this.opacity >= 1){//終わったらtrueを返す return true; } return false; } } });
まず、
var fadeout = new FadeOut(640, 960, 'black');
のように画面サイズとともに色を指定してFadeOutクラスを作成します(色を指定できるようにしているので白や赤など状況によって使い分けられます)。
initializeでは指定の色とサイズのコンテキストの作成などの初期化処理をします。
startメソッドはフェードアウトを開始したい場所に書きます。
doメソッドはメインループ内に書いておきます。isStartフラグがtrueになるとフェードアウトを開始します。ループ毎に指定されたスピードでフェードアウトします。終わったらtrueを返します。
以上の方法で簡単にフェードアウトさせることができます(‘ω’)ノ
残り時間のカウントダウンする
ゲームって制限時間が決まっているものがたくさんありますよね。一定時間にゴールしたり、敵全滅させたり、逃げたり、時間制限があるおかげでゲームが成り立っているものって結構あります。
というわけで制限時間というルールがあると作れるゲームが増えるだろうと思い、制限時間を設けたいときに使えるクラスを作ってみました。
画面に文字を表示させるのでLabelクラスを継承して作りました。
var TimeCountDown = enchant.Class.create(enchant.Label, { initialize: function(min, sec){//引数に分と秒を入れる enchant.Label.call(this); this.color = "white"; this.font = '25px sens-serif'; this.isTimeUp = false; var tick = 0; var minute = min; var second = sec; this.addEventListener('enterframe', function(){ if(tick++ >= core.fps){ tick = 0; if(--second < 0){ second = 59; if(--minute < 0){ this.isTimeUp = true; } } } this.text = minute + ":" + ('00' + second).slice(-2);//時間 }); } });
使うときは引数に分と秒をして使います。
var timer = new TimeCountDown(5, 0);//分、秒を指定 timer.moveTo(10, 10); core.rootScene.addChild(timer);
残り時間が0になるとisTimeUpフラグがtrueになります。
tickがフレームごとに加算されてそれが設定されているfps以上になると1秒としています。
正確に1秒というわけではないですがJavaScriptのsetIntervalとか使うとポーズ画面とかのときなんかに面倒くさいことになりそうなのでこっちの方が良いかと思います。
円と四角形を簡単に書けるようにしておく
ゲーム作ってるとけっこう円や四角形を使いたい、またはとりあえず円か四角形か表示させて開発を進めたいと思ったりすることないですか?
わたしは画像書くのが面倒なんで手抜きするために円や四角形をサクッと書けるクラスを作りました。面倒くさがりはプログラマーの資質だと思います(そうなのか?)
というわけで私はこんな感じにクラスを作って用意してあります。
//円を描く var DrawCircle = enchant.Class.create(enchant.Sprite, { initialize: function(w, h, color){//w, hは幅と高さ enchant.Sprite.call(this, w, h); var surface = new Surface(w, h); // サーフェス生成 surface.context.beginPath(); surface.context.arc(w*0.5, h*0.5, w*0.5, 0, Math.PI*2, false);//原点X、原点Y、半径、始点ラジアン、終点ラジアン surface.context.fillStyle = color; surface.context.fill(); this.image = surface; // サーフェスを画像としてセット }, }); //四角を書く var DrawRectangle= enchant.Class.create(enchant.Sprite, { initialize: function(w, h, color){//w, hは幅と高さ enchant.Sprite.call(this, w, h); var surface = new Surface(w, h); // サーフェス生成 surface.context.beginPath(); surface.context.fillStyle = color; surface.context.fillRect(0, 0, w, h);//X、Y、W、H this.image = surface; // サーフェスを画像としてセット }, });
enchant.jsの記事が他にもあります
ここにまとめられなかったenchant.jsの記事が他にもあります!
音の再生の仕方について
シーンの切り替えについて
メッセージの表示について
紙芝居風のストーリーシーンの作り方
画面エフェクトのサンプル
ローカルストレージの使い方