ボタンをクリックし、モーダルを表示
モーダルを表示させるために、ボタンをクリック時に処理をするスクリプトを作成していきます。
Entity「Root」に「showModal.js」を作成し追加する。
追加後、showModal.jsをEditorで開き、以下のコードに書き換えます。
var ShowModal = pc.createScript('showModal');
ShowModal.prototype.initialize = function() {
this.modalFlag = false;
document.body.style.fontSize = "16px"; // 基準となるフォントサイズを指定
this.app.on('modal:set', this.setModal, this); // setModal() を呼ぶイベントを作成
this.app.on('modal:remove', this.removeModal, this); // removeModal() を呼ぶイベントを作成
};
ShowModal.prototype.setModal = function() {
if(this.modalFlag) return false;
let targetModal = document.createElement("div"); // モーダルのdiv要素を作成
targetModal.id = "showModal"; // 表示非表示など処理をするために、idを設定
// 以下はstyle設定のため説明割愛
targetModal.style.display = "flex";
targetModal.style.flexWrap = "nowrap";
targetModal.style.flexDirection = "column";
targetModal.style.position = "fixed";
targetModal.style.top = "50%";
targetModal.style.left = "50%";
targetModal.style.transform = "translate(-50%, -50%)";
targetModal.style.width = "75%";
targetModal.style.height = "75%";
targetModal.style.maxWidth = "720px";
targetModal.style.maxheight = "540px";
targetModal.style.padding = "40px 20px";
targetModal.style.backgroundColor = "rgba(75, 75, 75, 1)";
targetModal.style.border = "3px solid #cccccc";
targetModal.style.borderRadius = "5px";
targetModal.style.boxSizing = "border-box";
targetModal.style.overflow = "auto";
let head = document.createElement("h2"); // モーダル内のheadテキストを作成
// 以下はstyle設定のため説明割愛
head.style.color = "rgb(255, 255, 255)";
head.style.fontSize = "1.6rem";
head.style.fontWeight = "bold";
head.style.lineHeight = 1.2;
head.style.letterSpacing = "0.01em";
head.innerHTML = "ヘッダー"; // テキスト内容を入力
targetModal.appendChild(head); // モーダル内に追加する
let inner = document.createElement("div"); // 本文となる要素を追加
// 以下はstyle設定のため説明割愛
inner.style.color = "rgb(255, 255, 255)";
inner.style.fontSize = "1rem";
inner.style.lineHeight = 1.5;
inner.style.letterSpacing = "0.05em";
inner.innerHTML = "<p>ここか本文のテキストになる</p>"; // 入るテキストはHTMlの要素で入れる想定
targetModal.appendChild(inner); // モーダル内に追加する
let closeButton = document.createElement("div"); // モーダルを閉じる要素を作成
// 以下はstyle設定のため説明割愛
closeButton.style.position = "absolute";
closeButton.style.top = 0;
closeButton.style.right = 0;
closeButton.style.padding = ".5rem 1rem";
closeButton.style.color = "#ffffff";
closeButton.style.fontSize = "2rem";
closeButton.style.fontWeight = "bold";
closeButton.style.lineHegiht = 1;
closeButton.style.letterSpacing = 0;
closeButton.style.cursor = "pointer";
closeButton.innerHTML = "×"; // 閉じるボタンのバッテンをかける記号で設定
targetModal.appendChild(closeButton); // モーダル内に追加する
closeButton.addEventListener("click", this.removeModal.bind(this)); // 閉じるボタンにクリックイベントの処理を設定
document.body.appendChild(targetModal); // モーダルをbody要素に追加
this.modalFlag = true; // モーダルが表示されている時はTrueのフラグ
this.app.fire("move:unable"); // playerMove.jsで設定したイベント。これでPlaneをクリックしても移動しなくなるようにイベントをoffしている
};
ShowModal.prototype.removeModal = function() {
if(!this.modalFlag) return false; // モーダルが表示されている時のみスルー
document.getElementById("showModal").remove(); // モーダルに設定したidを参照してモーダルを削除
this.modalFlag = false // モーダルが非表示の時はFalseのフラグ
this.app.fire("move:able"); // playerMove.jsで設定したイベント。これでPlaneをクリックすると移動するようになるようにイベントを再度onしている
};
コードのおおよそがDOM要素を作成するコードになっています。
ここで作成したイベント、「modal:set」をボタンをクリックしたときに呼び出します。
Template化したボタンのButton EntityにはTags「cursor」を設定しています。
このTagsを参照するようにしてイベントを呼び出す処理を「showCursor.js」の、
「setCursor()」と「removeCursor()」に追記していきます。
ShowCursor.prototype.setCursor = function(targetEntity) {
this.instance.enabled = true;
this.instance.setPosition(
targetEntity.getPosition().x,
3,
targetEntity.getPosition().z
);
// 以下を追加
for(let i=0; i < this.instance.findByTag("cursor").length; i++) { // クリックしたボタンのEntityの中にcursorのTagsを持つEntityを探す
let funcModalSet = () => {
this.app.fire('modal:set'); // ここでmodal:setのイベント
};
this.instance.findByTag("cursor")[i].element.on(pc.EVENT_MOUSEUP, funcModalSet.bind(this)); // cursorのTagsを持つEntityにイベントを設定
}
};
ShowCursor.prototype.removeCursor = function() {
this.instance.enabled = false;
// 以下を追加
for(let i=0; i < this.instance.findByTag("cursor").length; i++) { // クリックしたボタンのEntityの中にcursorのTagsを持つEntityを探す
this.instance.findByTag("cursor")[i].element.off(pc.EVENT_MOUSEUP); // cursorのTagsを持つEntityに設定したイベントを削除
}
};
上記コードを追記できたら、Launchで確認してみましょう。
オブジェクトに触れて表示されたボタンをクリックすると、以下画像のようなモーダルが表示されます。その後、右上の閉じるボタンの × をクリックするとモーダルが閉じます。
これで一通りの処理ができました。
次はモーダルのテキストをオブジェクトごとに設定できるようにしていきます。
オブジェクトごとにモーダルのテキストを設定する
まず、オブジェクトに設定していた「triggerCollision.js」に以下のスクリプト属性を追記します
TriggerCollision.attributes.add("headText",{ type:"string", default:"これがHEADです"}); // headのテキストを入力
TriggerCollision.attributes.add("innerText",{ type:"asset", assetType: "html"}); // 本文のHTMLをアセットからhtmlファイルを設定する
TriggerCollision.attributes.add("textColor",{ type:"rgb", default: [1,1,1}); // テキストのカラーを指定
TriggerCollision.attributes.add("backgroundColor",{ type:"rgba", default: [0,0,0,1]}); // モーダルの背景色を指定
これでオブジェクトごとにテキストや色を設定する用意ができました。
ここで使用するHTMLをNew Assetから作成しましょう。
アセット名やHTMLの内容はお好みで問題ありません。
例として以下のHTMLも用意しましたので活用ください
<p>
このオブジェクトに当たると<br>
ボタンが表示されて<br>
ボタンをクリックすると<br>
このモーダルが表示されるよ!
</p>
<p>モーダルを閉じる場合は右上の閉じるボタンをクリックしてね</p>
<p>
<a style="color: #fff;" href="https://playcanvas.jp/" target="_blank" rel="noopener">PlayCanvas日本公式サイトへ</a>
</p>
先にスクリプト属性を以下の画像のように設定しましょう。
設定したスクリプト属性を使ってモーダルのstyleに割り当てていきます。
まずは、「showModal.js」の「setModal()」に引数としてスクリプト属性を扱うように以下のように書き換えていきます。
ShowModal.prototype.setModal = function(headText, innerText, textColor, backgroundColor) { // 引数にスクリプト属性を入れて処理していく
if(this.modalFlag) return false;
let targetModal = document.createElement("div");
targetModal.id = "showModal";
targetModal.style.display = "flex";
targetModal.style.flexWrap = "nowrap";
targetModal.style.flexDirection = "column";
targetModal.style.position = "fixed";
targetModal.style.top = "50%";
targetModal.style.left = "50%";
targetModal.style.transform = "translate(-50%, -50%)";
targetModal.style.width = "75%";
targetModal.style.height = "75%";
targetModal.style.maxWidth = "720px";
targetModal.style.maxheight = "540px";
targetModal.style.padding = "40px 20px";
// 背景色はこのbackgroundで指定する。引数のbackgroundColorは0~1までを扱うため、色指定のため255で積を求めます。
targetModal.style.backgroundColor = "rgba(" + backgroundColor.r*255 + "," + backgroundColor.g*255 + "," + backgroundColor.b*255 + "," + backgroundColor.a + ")";
targetModal.style.border = "3px solid #cccccc";
targetModal.style.borderRadius = "5px";
targetModal.style.boxSizing = "border-box";
targetModal.style.overflow = "auto";
let head = document.createElement("h2");
// テキスト色はこのcolorで指定する。引数のtextColorは0~1までを扱うため、色指定のため255で積を求めます。
head.style.color = "rgb(" + textColor.r*255 + "," + textColor.g*255 + "," + textColor.b*255 + ")";
head.style.fontSize = "1.6rem";
head.style.fontWeight = "bold";
head.style.lineHeight = 1.2;
head.style.letterSpacing = "0.01em";
head.innerHTML = headText; // HEADのテキストはここで代入します
targetModal.appendChild(head);
let inner = document.createElement("div");
// テキスト色はこのcolorで指定する。引数のtextColorは0~1までを扱うため、色指定のため255で積を求めます。
inner.style.color = "rgb(" + textColor.r*255 + "," + textColor.g*255 + "," + textColor.b*255 + ")";
inner.style.fontSize = "1rem";
inner.style.lineHeight = 1.5;
inner.style.letterSpacing = "0.05em";
inner.innerHTML = innerText.resource; // HTMLのAssetはここでresourceで中身をそのまま代入します
targetModal.appendChild(inner);
let closeButton = document.createElement("div");
closeButton.style.position = "absolute";
closeButton.style.top = 0;
closeButton.style.right = 0;
closeButton.style.padding = ".5rem 1rem";
closeButton.style.color = "#ffffff";
closeButton.style.fontSize = "2rem";
closeButton.style.fontWeight = "bold";
closeButton.style.lineHegiht = 1;
closeButton.style.letterSpacing = 0;
closeButton.style.cursor = "pointer";
closeButton.innerHTML = "×";
targetModal.appendChild(closeButton);
closeButton.addEventListener("click", this.removeModal.bind(this));
document.body.appendChild(targetModal);
this.modalFlag = true;
this.app.fire("move:unable");
};
この「setModal()」は「modal:set」でイベントを作成して使用しています。
「modal:set」のイベントを使用しているのは「showCursor.js」の「setCursor()」です。
この「setCursor()」を以下のように書き換えていきます。
ShowCursor.prototype.setCursor = function(targetEntity) {
this.instance.enabled = true;
this.instance.setPosition(
targetEntity.getPosition().x,
3,
targetEntity.getPosition().z
);
for(let i=0; i < this.instance.findByTag("cursor").length; i++) {
let funcModalSet = () => {
this.app.fire('modal:set',
targetEntity.script.scripts[0].headText, // targetEntityが各オブジェクトなので、
targetEntity.script.scripts[0].innerText, // オブジェクトの持つScriptを参照しスクリプト属性を参照します。
targetEntity.script.scripts[0].textColor, // 左からの引数の順番を間違えないように、headText, innerText,
targetEntity.script.scripts[0].backgroundColor // textColor, backgroundColorを設定していきましょう。
);
};
this.instance.findByTag("cursor")[i].element.on(pc.EVENT_MOUSEUP, funcModalSet.bind(this));
}
};
ここまで書き換えができたら、Launchで確認してみましょう。
設定したスクリプト属性の通りにモーダルが切り替わっていれば、本チュートリアルは完成です!
あとはお好みでオブジェクトを増やしたり、オブジェクトの形を変えたり。
キャラクターの3Dモデルをインポートしてアニメーションなど。
参考: 3DキャラクターをPlayCanvasのState Graphで操作する
自由に追加・変更をして、自分だけのコンテンツにしましょう!
おまけ:Playerの移動速度を等速にしたい
クリック後にPlayerが移動する際に、Tweenでは目標地点へ指定した時間で移動する設定になっているため、距離に応じて移動速度が変わってしまう。
という問題があります。
これが気になる場合は時間で指定ではなく、等速な移動速度で目標地点へ移動するように設定をします。
これを解決するために、playerMove.jsを以下のコードに書き換えます。
let PlayerMove = pc.createScript('playerMove');
PlayerMove.attributes.add("targetCamera", {type:"entity"});
PlayerMove.attributes.add("targetTag", {type:"string", default:"field"});
// 追加
PlayerMove.attributes.add("moveType", {type:"boolean", enum:[{'Tween': true},{'Linear': false}]}); // TweenとLinear移動を属性から選択可能に
PlayerMove.attributes.add("speed", {type:"number", default:1, description: "Tweenなら移動時間、Linearなら移動速度を指定する" });
PlayerMove.prototype.initialize = function() {
this.time = 0;
this.duration = 1;
this.direction = new pc.Vec3();
this.targetPosition = new pc.Vec3();
this.lerpRotate = new pc.Vec4();
this.app.mouse.on(pc.EVENT_MOUSEUP, this.mouseUp, this);
this.on('destroy', function() {
this.app.mouse.off(pc.EVENT_MOUSEUP, this.mouseUp, this);
}, this);
this.app.on("move:able", () => {
this.app.mouse.on(pc.EVENT_MOUSEUP, this.mouseUp, this);
}, this);
this.app.on("move:unable", () => {
this.app.mouse.off(pc.EVENT_MOUSEUP, this.mouseUp, this);
}, this);
if(this.app.touch) {
this.app.mouse.on(pc.EVENT_TOUCHEND, this.mouseUp, this);
this.on('destroy', function() {
this.app.mouse.off(pc.EVENT_TOUCHEND, this.mouseUp, this);
}, this);
this.app.on("move:able", () => {
this.app.mouse.on(pc.EVENT_TOUCHEND, this.mouseUp, this);
}, this);
this.app.on("move:unable", () => {
this.app.mouse.off(pc.EVENT_TOUCHEND, this.mouseUp, this);
}, this);
}
};
// 追加
PlayerMove.prototype.update = function(dt) {
if(!this.moveType) { // Linearの場合
if (this.direction.lengthSq() > 0) { // lengthSq()は選択した3次元ベクトルの大きさの2乗を返す
let d = this.speed * dt; // 移動スピードを設定
let newPosition = new pc.Vec3();
newPosition.copy(this.direction).scale(d); // 座標を処理する変数にコピーし、移動速度の積を求める
newPosition.add(this.entity.getPosition()); // 移動する座標にEntityの座標を追加
this.entity.setPosition(newPosition); // 新しい座標をEntityに代入
this.distanceToTravel -= d; // 移動処理したベクトル分をマイナス
if (this.distanceToTravel <= 0) { // 移動するベクトル量がない場合
this.entity.setPosition(this.targetPosition); // Entityの座標にクリックした座標を設定
this.direction.set(0, 0, 0); // 移動するベクトルをリセット
}
}
}
};
PlayerMove.prototype.mouseUp = function (e) {
this.doRaycast(e.x, e.y);
// 追加
if(this.moveType) { // Tweenの場合
this.entity.tween(this.entity.getLocalPosition()).to(this.targetPosition, this.speed, pc.Linear).start();
}
};
PlayerMove.prototype.doRaycast = function(screenX, screenY) {
let from = new pc.Vec3();
let to = new pc.Vec3();
if(this.targetCamera.camera.projection !== pc.PROJECTION_ORTHOGRAPHIC) {
from = this.targetCamera.getPosition();
to = this.targetCamera.camera.screenToWorld(screenX, screenY, this.targetCamera.camera.farClip);
} else {
let farClip = this.targetCamera.camera.farClip;
let nearClip = this.targetCamera.camera.nearClip;
this.targetCamera.camera.screenToWorld(screenX, screenY, nearClip, from);
this.targetCamera.camera.screenToWorld(screenX, screenY, farClip, to);
}
// const result = this.app.systems.rigidbody.raycastFirst(from, to);
const result = this.app.systems.rigidbody.raycastAll(from, to).find(result => result.entity.tags.has(this.targetTag));
if (!result) return;
this.targetPosition = result.point;
this.entity.findByTag("player")[0].lookAt(
this.targetPosition.x,
this.entity.findByTag("player")[0].getPosition().y,
this.targetPosition.z
);
// 追加
this.movePlayerTo(this.targetPosition); // Linear移動用
};
// 追加
PlayerMove.prototype.movePlayerTo = function () { // Linear移動用
this.targetPosition.y = this.entity.getPosition().y; // y軸は同じなのでそのまま代入
this.direction.sub2(this.targetPosition, this.entity.getPosition()); // 現在の地点と目標点を互いに減算
this.distanceToTravel = this.direction.length(); // 移動するベクトル量を代入
if (this.distanceToTravel > 0) {
this.direction.normalize(); // ベクトルに変換
} else {
this.direction.set(0, 0, 0); // 移動する方向がなければリセット
}
};
合わせて、Player内に設定したModel(チュートリアルのままならBox)にTags「player」を設定してください。
Tweenと等速移動(コード上ではLinearの意)をスクリプト属性で切り替えができる等にしていますので、Launchでそれぞれ比較してみてください。
またTweenの移動時間、等速移動の速度も同様にスクリプト属性で変更ができるようにしています。
ここで追加した等速移動の処理は、Point and click movementというチュートリアルのコードから引用しています。
チュートリアル - 箱庭・ミニスケープなコンテンツを作ってみる 5/5
コメント
0件のコメント
サインインしてコメントを残してください。