Hi, I ripped out the hotspot.js script from the hotspot demo and added some hotspots to my template (replicated exactly), but when my app runs and the template is instantiated the hotspots are not drawn.
I have made sure that my hotspot.js code finds the camera entity in code (rather than defined in the editor) and this seems to by finding the camera without a problem as far as I can tell through debugging.
I get no errors but the hotspots are just never drawn.
Are the cameras including the layers that the hotspots are on?
As the hotspots are a plane and billboarded to face the camera, are they a child of the entity being billboard and rotated correctly so that the plane face is facing the camera?
All that seems to be in order. Oddly I can now see the hotpots (I think there was a rotation issue maybe?) but as soon as I click the mouse anywhere on the screen I get the error: Uncaught TypeError: Cannot read property âscreenToWorldâ of undefined
on the following line: this.cameraEntity.camera.screenToWorld(screenPosition.x, screenPosition.y, this.cameraEntity.camera.farClip, this.ray.direction);
This would indicate to me that the camera is not found but when I do the following:
Without more code/information, itâs really hard to tell.
It could be anything from being in the wrong scope context so this isnât referencing the script instance or that there is an Entity named Camera that doesnât have a camera component in the scene somewhere.
Add a breakpoint on where it does the raycast and have a look at the variables and values to find ideas on what it can be.
var Hotspot = pc.createScript('hotspot');
//Hotspot.attributes.add("cameraEntity", {type: "entity", title: "Camera Entity"});//OLD from demo
Hotspot.attributes.add("radius", {type: "number", title: "Radius"});
Hotspot.attributes.add("fadeDropOff", {
type: "number",
default: 0.4,
title: "Fade Drop Off",
description: "When to start fading out hotspot relative to the camera direction. 1 for when hotspot is directly inline with the camera. 0 for never."
});
// initialize code called once per entity
Hotspot.prototype.initialize = function() {
this.cameraEntity=this.app.root.findByName("Camera");
if( this.cameraEntity===null)
{console.log("HOTSPOT CANT FIND CAMERA");}
else
{console.log("HOTSPOT FOUND CAMERA: "+this.cameraEntity.name);} //Returns Camera correctly
// Create a hit area using a bounding sphere
this.hitArea = new pc.BoundingSphere(this.entity.getPosition(), this.radius);
// More information about pc.ray: http://developer.playcanvas.com/en/api/pc.Ray.html
this.ray = new pc.Ray();
this.defaultForwardDirection = this.entity.forward.clone();
this.directionToCamera = new pc.Vec3();
this.sprite = this.entity.children[0];
// Register the mouse down and touch start event so we know when the user has clicked
this.app.mouse.on(pc.EVENT_MOUSEDOWN, this.onMouseDown, this);
if (this.app.touch) {
this.app.touch.on(pc.EVENT_TOUCHSTART, this.onTouchStart, this);
}
};
Hotspot.prototype.postInitialize = function() {
};
// update code called every frame
Hotspot.prototype.update = function(dt) {
var cameraPosition = this.cameraEntity.getPosition();
// Always face the camera
this.entity.lookAt(cameraPosition);
// Get the current direction to the camera
this.directionToCamera.sub2(cameraPosition, this.entity.getPosition());
this.directionToCamera.normalize();
// Get the dot product of the direction to the camera and the original direction of the hotspot
// which is relative to the angle between the two vectors
// Start fading out the hotspot if the dot product is below fadeDropOff
var dot = this.directionToCamera.dot(this.defaultForwardDirection);
if (dot < 0) {
if (this.sprite.enabled) {
this.sprite.enabled = false;
}
} else {
if (!this.sprite.enabled) {
this.sprite.enabled = true;
}
var meshInstances = this.sprite.model.meshInstances;
var alpha = pc.math.clamp(dot / this.fadeDropOff, 0, 1);
for(var i = 0; i < meshInstances.length; ++i) {
meshInstances[i].setParameter("material_opacity", alpha);
}
}
};
Hotspot.prototype.doRayCast = function (screenPosition) {
// Only do the raycast if the sprite is showing
if (this.sprite.enabled) {
if( this.cameraEntity===null)
{console.log("HOTSPOT CANT FIND CAMERA");}
else
{console.log("HOTSPOT FOUND CAMERA: "+this.cameraEntity.name);}//Returns fine
if( this.cameraEntity.camera===null)
{console.log("NO COMPONENT ON CAMERA ENTITY");}
else
{console.log("COMPONENT ON CAMERA FOUND : "+this.cameraEntity.camera);}//Returns but its called Undefined
this.cameraEntity=this.app.root.findByName("Camera");//This works if a I put it here
// Initialise the ray and work out the direction of the ray from the a screen position
this.cameraEntity.camera.screenToWorld(screenPosition.x, screenPosition.y, this.cameraEntity.camera.farClip, this.ray.direction);
this.ray.origin.copy(this.cameraEntity.getPosition());
this.ray.direction.sub(this.ray.origin).normalize();
// If the hotspot is clicked on, then send a event to start the 'pulse' effect
if (this.hitArea.intersectsRay(this.ray)) {
this.entity.fire("pulse:start");
}
}
};
Hotspot.prototype.onMouseDown = function(event) {
if (event.button == pc.MOUSEBUTTON_LEFT) {
this.doRayCast(event);
}
};
Hotspot.prototype.onTouchStart = function (event) {
// On perform the raycast logic if the user has one finger on the screen
if (event.touches.length == 1) {
this.doRayCast(event.touches[0]);
// Prevent the default mouse down event from triggering
// https://www.w3.org/TR/touch-events/#h3_list-of-touchevent-types
event.event.preventDefault();
}
};
Iâve tried the script in a completely blank project and it works fine for me after taking out the fixes you made to work around the issue you ran into: https://playcanvas.com/editor/scene/1172121
When I change scenes, Iâm destroying the previous scene hierarchy yes. Surely though this template is only created one the scene itâs in is loaded. The scene its in includes a Camera entity.
Okay, without looking closer at an example project with the same issue, I canât really give a definitive answer here.
My hunch is that when switching scenes, you are creating an instance of these hotspots in an initialise function of a script in the new scene. At this point, you might have both scenes loaded so itâs finding the old sceneâs camera entity.
Once the new scene has fully initialised, the old scene is destroyed and the hotspots have an old reference to effectively an entity with all itâs components destroyed.
A cheat way to work around this is to use a unique name for the camera the hotspots are in or use a unique tag for the camera (eg hotspot-camera) and search by tag instead.
The other way would be to findByName on the scene root and not the app.root.