I’m working on a scenario in which I’ll have an object fixed on a particular axis (e.g. x axis). I want the user to be able to click and drag the object along that axis.
Obviously if the object was perfectly aligned flat to the screen, I could just use the mouse’s x coordinates to control the x translation of the object. But if the camera is rotated/positioned differently, the object may not be perfectly aligned with the screen. Moving the object along the x axis may be more of a diagonal line as the object moves further away/closer/etc. In that respect, it would no longer be accurate to just look at the mouse’s x movement.
I’m wondering how I could have the mouse movement consider the 3d orientation of the model to correctly calculate how to progress as the user clicks/drags.
Let’s say the axis was the world X axis. In that case, I would:
Create a ray that passes through the view point (the camera’s world position returned by this.entity.getPosition() for the camera entity) and the mouse position in world space (found using the CameraComponent#screenToWorld function).
Intersect that ray with the world X-Z plane.
The x coordinate of the intersection point will be the new x coordinate for the dragged entity.
I’ve just created a project that will be added to the sample list which is more streamlined. In this case, look at using the pc.Plane shape instead of pc.BoundingBox.
Thanks so much, @steven ! That totally helps with understanding the intersect aspect of Raycasting. I’m still having trouble understanding how I could apply this knowledge to allow me to click and drag an object on a single axis in local space.
For example, is it possible to have a scene–like the image below–where no matter how I rotate or zoom the camera, I can predictably drag this object along its local z axis?
So the idea is that you have a plane that is always aligned on the XZ axis of the box (shown in the image below by the grey plane). Once you have that, you can do a raycast on that plane and get the intersection point (shown by the black ‘X’).
This is where it gets a little math heavy. At this point you can project the vector from the position of the box to the intersection point onto the Z axis of the box (shown by the yellow arrow) to get the position on where you should move the box to. (YouTube video on vector projection: https://www.youtube.com/watch?v=fqPiDICPkj8)
If you are still having trouble, drop another message here and if we get some time, we may be able quickly put together an example for you.
Thanks a ton, @steven! That makes a lot more sense. It hadn’t even occurred to me to have a plan solely for the purpose of calculating the movement.
I’ve worked on this a little more, but I’m still a little bit hung up on a couple things. Here’s the PlayCanvas project I’ve been using to experiment (forked from the example you sent me before).
I think my problem is that I’m using the drag object’s position rather than merely the Z axis (as you said, “the Z axis of the box”). But I’m not really sure how to do it only on the Z axis. Here’s the code I’m using:
var Raycast = pc.createScript('raycast');
Raycast.attributes.add('cameraEntity', {type: 'entity', title: 'Camera Entity'});
Raycast.attributes.add('dragEntity', {type: 'entity', title: 'Drag Entity'});
Raycast.attributes.add('projectSurface', {type: 'entity', title: 'Project Surface Entity'});
// initialize code called once per entity
Raycast.prototype.initialize = function() {
// More information about pc.ray: http://developer.playcanvas.com/en/api/pc.Ray.html
this.ray = new pc.Ray();
// 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);
this.app.mouse.on(pc.EVENT_MOUSEUP, this.onMouseUp, this);
this.app.mouse.on(pc.EVENT_MOUSEMOVE, this.onMouseMove, this);
this.hitPosition = new pc.Vec3();
// More information about pc.BoundingBox: http://developer.playcanvas.com/en/api/pc.BoundingBox.html
this.aabbShape = new pc.BoundingBox(this.projectSurface.getPosition().clone(), this.projectSurface.getLocalScale().clone().scale(0.5));
};
Raycast.prototype.doRayCast = function (screenPosition) {
// 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();
var isIntersecting = this.aabbShape.intersectsRay(this.ray, this.hitPosition);
if ( isIntersecting ) {
var projectionPoint = this.hitPosition.project( this.dragEntity.getPosition() );
return projectionPoint;
}
};
Raycast.prototype.onMouseDown = function(event) {
if (event.button == pc.MOUSEBUTTON_LEFT) {
this.state = 'dragging';
}
};
Raycast.prototype.onMouseUp = function(event) {
this.state = 'stationary';
};
Raycast.prototype.onMouseMove = function(event) {
if ( this.state == 'dragging' ) {
var position = this.doRayCast(event);
this.dragEntity.setPosition( position );
}
};
You need to calculate the vector from box to the intersection point and then project that onto the object’s forward (-Z) axis to get the distance to move it by
Hi, I realize it is an old post, but I was wondering if it’s quite the same to drag an entity in 3D space but in 2 axis at the same time so it gives a free move around in XZ for instance ?