はじめに
今回はWebGL/HTML5ゲームエンジンである
PlayCanvasを触っていこうと思います。
PlayCanvasは、デスクトップとモバイルブラウザ向けに作られた
PLAYCANVAS
WebGL/HTML5ゲームエンジンです。豊富な機能を揃えた3Dエンジンと
クラウドホスティングされた開発環境およびツールセットを備えています。
PlayCanvasの基礎的な機能を抑えながら
簡単なデモを交えて解説していきます。
導入
アカウントを下記公式サイトから作成します。
【参考リンク】:PlayCanvas
その後、アカウントのHome画面からプロジェクトを作成します。

事前に理解しておきたい概念
事前に理解しておきたい概念が3つあります。
EntityとComponentとScriptです。
EnityはPlayCanvas上で実体を持つ全てに付与される概念です。
Entityにはいろいろな機能を付与することができます。
その付与できる機能のことをComponentと言います。
音を鳴らしたり、物理演算を行ったり、アニメーションを行ったりと、
様々なComponentが用意されています。
また、そのComponentは独自に定義することができます。
例えば、音を鳴らす機能を呼び出した後に物理演算をオフにしてアニメーションを開始する…といった複雑な処理を行いたいとなった場合は
自分でその一連の処理を定義する必要があります。
この自分で定義した一連の処理のことをScriptと言います。
Scriptは私たちエンジニアが介入できる領域です。
JavaScriptでPlayCanvasのルールに則った処理を記述可能です。
上記の3つの概念、
EntityとComponentとScriptさえ抑えてしまえば
あなたもPlayCanvasマスターです。
ボタンを配置してみる
まずはボタンを配置していきましょう。
下記画像の赤い四角の箇所を順に押していき、
HIERARCHYにボタンを配置します。

ButtonなどのUIをまとめておく空のEntityを作成し、
その下の階層に配置しておきます。

加えて、UIをまとめておく空のEntity(上記画像のUIGroup)に
Screen Componentを追加します。

Button配下のTextはボタンの上にテキストを表示して、
何に使うボタンなのかを示すことができます。
しかし、デフォルトで使用可能なフォントは
新規プロジェクト内には存在しないので、
今回は下記チュートリアルプロジェクトからダウンロードしてきます。
Buttonのチュートリアル
フォントを選択してダウンロードボタンを押せばダウンロードが可能です。

自身のプロジェクトにダウンロードしたフォントをアップロードした後、
Buttonの子階層にあるTextにダウンロードしたフォントを設定すれば
文字を表示できます。

Buttonの配置位置に関して、今回は画面上中央に表示してみます。
下記の設定で画面上中央に表示できます。

無事中央に表示されました。

画面サイズに応じたUIのサイズ調整
モバイルで表示させた際に画面解像度の違いからUIが崩れないように
ある程度いい感じに調整してくれるComponentが存在します。
先ほど追加したScreen Componentです。
これだけでUIの表示はある程度担保できます。
【参考リンク】:Screens
ボタン押下でScriptの処理を実行する
次に簡単なScriptを作成して、ボタン押下に伴う処理を追加してみます。
Scriptにはテンプレートが用意されています。作り方は簡単です。
下記画像のようにAssetの横の+ボタンからScriptを選択すれば作成されます。

コード
では、今回書いたコードを見ていきましょう。
処理は”ボタン押下で画面中央のキューブの表示、非表示が切り替わる“
という内容です。
buttonObjOnOffTestというScriptを作成しました。
//Scriptの宣言 自動的に生成される
var ButtonObjOnOffTest = pc.createScript('buttonObjOnOffTest');
//アトリビュート このScript内で他のEntity、及びComponentを利用できる
ButtonObjOnOffTest.attributes.add('cubeEntity', {type: "entity", title: "Cube"});
ButtonObjOnOffTest.prototype.initialize = function() {
//クリックイベントの登録
this.entity.button.on('click', this._onClick, this);
};
//押下時の処理
ButtonObjOnOffTest.prototype._onClick = function() {
this.cubeEntity.enabled = !this.cubeEntity.enabled;
};
アトリビュート
Scriptが代入されている変数.attributes.add(必要な引数群) という構文で
アトリビュートを定義できます。
今回のScriptで言うと下記の一文です。
ButtonObjOnOffTest.attributes.add('cubeEntity', {type: "entity", title: "Cube"});
このアトリビュートいう機能を使えば、
下記画像のようにEditor上で任意のEntityを設定することが可能です。

すなわち、buttonObjOnOffTest内でcubeEntityという変数を定義し、
その変数にEditor上で参照を与えたEntityを代入して利用できる
ということを意味します。
Editor上でbuttonObjOnOffTestにCubeの参照を与えるには、
buttonObjOnOffTestを先ほど作成したButtonに追加します。

追加したのちに、Parseボタンを押してScriptを読み込みます。

あとは表示されたアトリビュートに任意のオブジェクトを設定すればOKです。
initialize
initializeは初期化時に呼び出されるメソッドです。
ページを読み込んだ際の最初の一回だけ呼び出されるメソッド
と解釈して問題ないです。(厳密にはタイミング等異なりますが割愛します)
今回はこのinitializeの中でボタンのイベントを登録しています。
Componentの呼び出し
Componentの呼び出し方は非常にスッキリとしています。
任意のentity.既定のComponentの変数名 です。
今回作成したデモのScriptで言うと
Buttonのクリックイベントの登録を行っている下記箇所です。
this.entity.button.on('click', this._onClick, this);
正確な変数名を知っていないと呼び出せないので
事前にドキュメントをよく読んでおく必要があります。
下記がComponent呼び出し時の変数名一覧となります。
“animation” – pc.AnimationComponent
引用元:API Reference
“audiolistener” – pc.AudioListenerComponent
“button” – pc.ButtonComponent
“camera” – pc.CameraComponent
“collision” – pc.CollisionComponent
“element” – pc.ElementComponent
“layoutchild” – pc.LayoutChildComponent
“layoutgroup” – pc.LayoutGroupComponent
“light” – pc.LightComponent
“model” – pc.ModelComponent
“particlesystem” – pc.ParticleSystemComponent
“rigidbody” – pc.RigidBodyComponent
“screen” – pc.ScreenComponent
“script” – pc.ScriptComponent
“scrollbar” – pc.ScrollbarComponent
“scrollview” – pc.ScrollViewComponent
“sound” – pc.SoundComponent
“sprite” – pc.SpriteComponent
デモ
以上で”ボタン押下で画面中央のキューブの表示、非表示が切り替わるデモ”が
完成しました。

タイマーを作る
次に、”カウントダウンタイマー“を作成していきます。
この処理で解説したいことはupdateというメソッドについてです。
コード
updateメソッドを実際に使用しているコードに沿って解説していきます。
var Timer = pc.createScript('timer');
Timer.attributes.add('timerTextEntity', {type: "entity", title: "Timer Text"});
//スライダー付きアトリビュートをEditorに表示
Timer.attributes.add('gameTime', {
type: 'number',
title: 'Game Time',
default: 10,
min: 1,
max: 30,
});
Timer.prototype.initialize = function() {
//開始時間記録
startTime = Date.now();
};
//毎フレーム呼ばれる
Timer.prototype.update = function(dt) {
//残り時間計算
var timeLeft = Math.floor((startTime + this.gameTime * 1000 - Date.now()) / 1000);
if (timeLeft < 0) return;
//テキストに残り時間をセットする
this.timerTextEntity.element.text = timeLeft;
};
update
タイマーの時間をどのように更新しているかというと、
updateメソッドの中に秘密があります。
updateはフレームという概念の上で動いています。
1秒間に何十回もフレームが更新されます。
すなわち、このupdateが1秒間に何十回も呼ばれるということです。
この1秒間に何十回も呼ばれるupdateの中で、
開始した時刻 + カウントダウンタイマー開始時の値 – 現在時刻
という処理を行うことでタイマーのカウントが1秒ずつ減っていきます。
※厳密にはもっと細かい単位で時間は減少します。表示を秒単位にしています。

おわりに
今回は基本概念の理解を軸に簡単なデモシーンを作成しました。
下記に今日解説した内容のサンプルプロジェクトを公開しましたので
参考にしてみてください。
基礎概念理解デモプロジェクト