Help us understand the problem. What is going on with this article?

チュートリアル - 3D空間を使った簡単webサイト 3/5

前回

3Dオブジェクトでマウスのイベントを起こす

3Dオブジェクトとマウスカーソルとの関係性を作ります。

ヒエラルキーからchara->Modelを選択。
ADD COMPONENTからScriptを選択し、hotspotのscriptを登録します。
このModelを一つずつ選択する動作は面倒ですが、複数選択して設定を行うこともできます。

スクリーンショット 2019-07-31 12.05.20.png

hotspotの中身をコードエディターで開きます。

配置された3Dオブジェクトとマウスカーソルとの関係を持たせるにはレイキャストと呼ばれるものを使用します。
レイキャストとは、特定の地点から特定の方向へ直線の線を引き、その線上のどこかでぶつかるものがあるか検知するものです。
ここでのレイキャストは、「マウスがクリックされた時、カメラから見て、3Dオブジェクトとそのマウスカーソルが重なっているか」を取得するような処理を作ります。

ここから以下の処理を追加していきます

  • 3Dオブジェクトにレイキャストの線が当たるヒットエリアを作成
  • 使用するカメラのEntityとヒットエリアの大きさをAttributesでEditorから設定
  • ヒットエリアにマウスホバーしてイベントが取れる

以下のコードをhotspot.jsに書き換えます。

var Hotspot = pc.createScript('hotspot');

Hotspot.attributes.add("cameraEntity", {type: "entity", title: "Camera Entity"}); // カメラのEntityを取得
Hotspot.attributes.add("radius", {type: "number", title: "Radius"}); // Entityのヒットエリアの範囲を指定

Hotspot.prototype.initialize = function() { // init
    this.hitArea = new pc.BoundingSphere(this.entity.getPosition(), this.radius); // ヒットエリアを作成。BoundingSphereがentityの境界エリアを作成(Photoshopでいうバウンディングボックス的な)
    this.ray = new pc.Ray(); // cameraからentityへ直進する線のデータを作成。(Rayは光線の意でstart pointからentityまでの距離を測ったりすることが可能)

    this.directionToCamera = new pc.Vec3(); // Vector座標の型を取得

    this.app.mouse.on(pc.EVENT_MOUSEMOVE, this.onMouseHover, this); // マウスカーソルがホバーした時

};

Hotspot.prototype.onMouseHover = function(screenPosition) { // マウスホバー時
    this.cameraEntity.camera.screenToWorld(screenPosition.x, screenPosition.y, this.cameraEntity.camera.farClip, this.ray.direction); // ポジションを2Dスクリーンから3D空間へ変換
    this.ray.origin.copy(this.cameraEntity.getPosition()); // レイのオリジナルのポジションにカメラのポジションをコピー
    this.ray.direction.sub(this.ray.origin).normalize(); // 3次元ベクトルを他の場所から減算し、単位ベクトルに変換

    if (this.hitArea.intersectsRay(this.ray)) { // ヒットエリアとレイが交差した場合
        console.log("hover");
    }
};

新しくattributesというのが言葉がありますが、これは属性設定ができ、PlayCanvasのEditorからデータを変更することができるようになります。
ここではカメラのEntityの選択と、対象の3Dオブジェクトのヒットエリアの大きさを指定できます。

Hotspot.attributes.add("cameraEntity", {type: "entity", title: "Camera Entity"}); // カメラのEntityを取得
Hotspot.attributes.add("radius", {type: "number", title: "Radius"}); // Entityのヒットエリアの範囲を指定

以下画像のように入力エリアが表示されていない場合は、Editアイコン横のParseで更新され表示されます。

スクリーンショット 2019-06-20 12.00.58.png

initializeではヒットエリア、レイキャストのRayの設定、マウス移動のイベントを取得しています。

Hotspot.prototype.initialize = function() {
    this.hitArea = new pc.BoundingSphere(this.entity.getPosition(), this.radius); // ヒットエリアを作成。BoundingSphereがentityの境界エリアを作成(Photoshopでいうバウンディングボックス的な)
    this.ray = new pc.Ray(); // 直進する線のデータを作成。(Rayは光線の意でstart pointからentityまでの距離を測ったりすることが可能)

    this.app.mouse.on(pc.EVENT_MOUSEMOVE, this.onMouseHover, this); // マウスカーソルがホバーした時
};

マウス移動のイベントで3Dオブジェクトとカメラから見てホバーしているかonMouseHoverで取得しています。

Hotspot.prototype.onMouseHover = function(screenPosition) { // マウスホバー時
    this.cameraEntity.camera.screenToWorld(screenPosition.x, screenPosition.y, this.cameraEntity.camera.farClip, this.ray.direction); // ポジションを2Dスクリーンから3D空間へ変換
    this.ray.origin.copy(this.cameraEntity.getPosition()); // レイのオリジナルのポジションにカメラのポジションをコピー
    this.ray.direction.sub(this.ray.origin).normalize(); // 3次元ベクトルを他の場所から減算し、単位ベクトルに変換

    if (this.hitArea.intersectsRay(this.ray)) { // ヒットエリアとレイが交差した場合
        console.log("hover");
    }
};

マウス移動のイベントで3Dオブジェクトとカメラから見てホバーしているかonMouseHoverで取得しています。

これでマウスオーバー(ホバー)のイベントを取得できました。
次はクリックのイベントです。

initializeに以下を追加

    this.app.mouse.on(pc.EVENT_MOUSEDOWN, this.onMouseDown, this); // クリックが押された時

クリックイベントの左クリックを取得

Hotspot.prototype.onMouseDown = function(event) { // クリックが押されている時
    if (event.button == pc.MOUSEBUTTON_LEFT) { // 左クリックが押された時
        this.doRayCast(event); // レイキャストを呼ぶ
    }
};

クリックしたらレイキャストを走らせる

Hotspot.prototype.doRayCast = function (screenPosition) { // レイキャスト処理(ある地点から特定方向に直線で線を引いて、その線上で物体があるか検知する処理)
    if (this.hitArea.intersectsRay(this.ray)) { // ヒットエリアとレイが交差した場合
        console.log("click!!");
    }
};

Launch画面をリロードしてコンソールから確認ができます。

ここまでのコードは以下になります。(hotspot.js)

var Hotspot = pc.createScript('hotspot');

// canvasのclickやhoverなどの処理を行う

Hotspot.attributes.add("cameraEntity", {type: "entity", title: "Camera Entity"}); // カメラのentityを取得
Hotspot.attributes.add("radius", {type: "number", title: "Radius"}); // entityのヒットエリアの範囲を指定

Hotspot.prototype.initialize = function() { // init
    this.hitArea = new pc.BoundingSphere(this.entity.getPosition(), this.radius); // ヒットエリアを作成。BoundingSphereがentityの境界エリアを作成(Photoshopでいうバウンディングボックス的な)
    this.ray = new pc.Ray(); // cameraからentityへ直進する線のデータを作成。(Rayは光線の意でstart pointからentityまでの距離を測ったりすることが可能)

    this.directionToCamera = new pc.Vec3(); // Vector座標の型を取得

    this.app.mouse.on(pc.EVENT_MOUSEMOVE, this.onMouseHover, this); // マウスカーソルがホバーした時
    this.app.mouse.on(pc.EVENT_MOUSEDOWN, this.onMouseDown, this); // クリックが押された時
};


Hotspot.prototype.doRayCast = function (screenPosition) { // レイキャスト処理(ある地点から特定方向に直線で線を引いて、その線上で物体があるか検知する処理)
    if (this.hitArea.intersectsRay(this.ray)) { // ヒットエリアとレイが交差した場合
        console.log("click!!");
    }
};


Hotspot.prototype.onMouseHover = function(screenPosition) { // マウスホバー時
    this.cameraEntity.camera.screenToWorld(screenPosition.x, screenPosition.y, this.cameraEntity.camera.farClip, this.ray.direction); // ポジションを2Dスクリーンから3D空間へ変換
    this.ray.origin.copy(this.cameraEntity.getPosition()); // レイのオリジナルのポジションにカメラのポジションをコピー
    this.ray.direction.sub(this.ray.origin).normalize(); // 3次元ベクトルを他の場所から減算し、単位ベクトルに変換

    if (this.hitArea.intersectsRay(this.ray)) { // ヒットエリアとレイが交差した場合
        console.log("hover");
    }
};

Hotspot.prototype.onMouseDown = function(event) { // クリックが押されている時
    if (event.button == pc.MOUSEBUTTON_LEFT) { // 左クリックが押された時
        this.doRayCast(event); // レイキャストを呼ぶ
    }
};

次は3Dオブジェクトに応じてHTMLのページを表示させるようにします。

次回

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした