[0]LayaAir3.0 Scene3D场景下,实现鼠标拖动3D物体

1:鼠标单击物体后,鼠标在不抬起的时候,物体跟随鼠标移动。
2:物体在跟随鼠标移动时,能触发其他有碰撞体的3D物体,触发对应的方
3:鼠标松开时,物体能落在有碰撞体的物体上。比如:放在桌子上,落在地板上
 
3D物体在上下左右远近上都有变化,XYZ轴上的值都变化。
 
抱拳了
已邀请:

186*****109

赞同来自:

实现这个效果具体功能如下:
1.给需要拖动的3D物体添加鼠标事件监听器,监听鼠标按下、鼠标抬起和鼠标移动事件。
// 给需要拖动的物体添加事件监听器
dragObj.on(Laya.Event.MOUSE_DOWN, this, onMouseDown);
dragObj.on(Laya.Event.MOUSE_UP, this, onMouseUp);
dragObj.on(Laya.Event.MOUSE_MOVE, this, onMouseMove);
2.在鼠标按下事件的回调函数中记录鼠标位置和物体位置的偏移量,并设置物体的鼠标捕捉属性为true。
function onMouseDown() {
// 记录鼠标按下时的位置
lastMousePos.x = Laya.stage.mouseX;
lastMousePos.y = Laya.stage.mouseY;
// 记录物体位置与鼠标位置的偏移量
offset.x = dragObj.transform.position.x - lastMousePos.x;
offset.y = dragObj.transform.position.y - lastMousePos.y;
// 设置鼠标捕捉
dragObj.inputEnabled = true;
}
3.在鼠标移动事件的回调函数中更新物体的位置,同时检测是否与其他物体发生碰撞,并触发对应的方法。
function onMouseMove() {
if (dragObj.inputEnabled) {
// 计算物体新的位置
var newPos = new Laya.Vector3(
Laya.stage.mouseX + offset.x,
Laya.stage.mouseY + offset.y,
dragObj.transform.position.z
);
dragObj.transform.position = newPos;
// 检测碰撞
var hits = Laya.Physics.rayCastAll(
dragObj.transform.position,
new Laya.Vector3(0, 0, -1),
10
);
for (var i = 0; i < hits.length; i++) {
var hit = hits[i];
var otherObj = hit.collider.owner;
// 触发对应的方法
if (otherObj && otherObj.onCollide) {
otherObj.onCollide(dragObj);
}
}
}
}
4.在鼠标抬起事件的回调函数中取消物体的鼠标捕捉属性。
function onMouseUp() {
dragObj.inputEnabled = false;
}
需要注意的是,上述代码中的dragObj是需要拖动的3D物体,其他物体需要自行添加碰撞体和碰撞检测逻辑。此外,还需要在场景初始化时启用物理引擎,并设置物理引擎的重力和碰撞检测模式。

rabbit

赞同来自:

import Scene3D = Laya.Scene3D;
import Camera = Laya.Camera;
import Vector3 = Laya.Vector3;
import Point = Laya.Point;
import Browser = Laya.Browser;
import Handler = Laya.Handler;
import Sprite3D = Laya.Sprite3D;

// 加载场景和摄像机
const scene: Scene3D = Laya.stage.addChild(new Scene3D()) as Scene3D;
const camera: Camera = (scene.addChild(new Camera(0, 0.1, 100))) as Camera;
camera.transform.translate(new Vector3(0, 3, 5));
camera.transform.rotate(new Vector3(-15, 0, 0), true, false);

// 加载3D模型并添加到场景
const model: Sprite3D = scene.addChild(Laya.Sprite3D.load("model.lh")) as Sprite3D;

// 定义鼠标事件相关变量
let lastMouseX: number = 0;
let lastMouseY: number = 0;
let isMouseDown: boolean = false;

// 监听鼠标事件
Laya.stage.on(Laya.Event.MOUSE_DOWN, this, function(event: Laya.Event) {
lastMouseX = event.stageX;
lastMouseY = event.stageY;
isMouseDown = true;
});

Laya.stage.on(Laya.Event.MOUSE_MOVE, this, function(event: Laya.Event) {
if (isMouseDown) {
const offsetX: number = event.stageX - lastMouseX;
const offsetY: number = event.stageY - lastMouseY;
lastMouseX = event.stageX;
lastMouseY = event.stageY;
// 将鼠标移动距离转换为3D世界坐标系中的偏移量
const point: Point = new Point(offsetX, offsetY);
const scenePoint: Point = camera.viewport.toScene(point);
const worldOffset: Vector3 = new Vector3(scenePoint.x, scenePoint.y, 0);
camera.convertScreenCoordToOrthographicCoord(point, worldOffset);
// 将偏移量应用到模型上
model.transform.translate(worldOffset, false);
}
});

Laya.stage.on(Laya.Event.MOUSE_UP, this, function(event: Laya.Event) {
isMouseDown = false;
});

 以上代码仅为示例参考,提供一种编写思路。

在该示例代码中,首先加载了场景和摄像机,并加载了一个3D模型并添加到场景中。

接着,监听了鼠标事件,并在鼠标移动时判断是否处于拖动状态。如果是,则计算出鼠标移动的距离,并将其转换为3D世界坐标系中的偏移量。然后,将偏移量应用到模型的transform属性中,实现了拖动模型的效果。

需要注意的是,在实际的项目中,还需要根据实际需求对该示例代码进行修改和优化。例如,根据不同的模型尺寸和相机位置调整拖动的灵敏度和限制范围等。同时,在加载和处理3D模型时,还需要注意优化模型的性能和质量,例如合并网格、使用LOD等技术,避免模型过于复杂导致渲染性能下降。此外,在实际的项目中,还需要考虑拖动物体的碰撞检测、多物体拖动、触控设备支持等问题。

需要注意的是,在使用Scene3D场景和3D物体时,还需要进行相关的初始化和配置。例如,需要设置使用WebGL渲染、开启抗锯齿、设置背景颜色、设置相机视口等属性。此外,在使用3D物体时,还需要对其材质、纹理、网格等进行配置和优化,以提高渲染性能和质量。

总之,在使用LayaAir3D引擎开发3D应用时,需要综合考虑多个方面的因素,例如场景、相机、物体、光照、材质、渲染性能等,才能实现高质量的3D应用效果。

rabbit

赞同来自:

再提供一种脚本编写的示例:
以下是基于组件的示例代码,通过继承Laya.Script3D,编写逻辑,并将脚本添加到3D物体上,实现鼠标拖动3D物体的应用示例:
import Scene3D = Laya.Scene3D;
import Camera = Laya.Camera;
import Vector3 = Laya.Vector3;
import Point = Laya.Point;
import Browser = Laya.Browser;
import Handler = Laya.Handler;
import Sprite3D = Laya.Sprite3D;
import Script3D = Laya.Script3D;

export default class DragScript extends Script3D {
// 保存上一次鼠标的位置
private lastMouseX: number = 0;
private lastMouseY: number = 0;
// 是否处于拖动状态
private isMouseDown: boolean = false;

// 重写onAwake函数,在组件添加到物体上时调用
public onAwake(): void {
// 监听鼠标事件
Laya.stage.on(Laya.Event.MOUSE_DOWN, this, this.onMouseDown);
Laya.stage.on(Laya.Event.MOUSE_MOVE, this, this.onMouseMove);
Laya.stage.on(Laya.Event.MOUSE_UP, this, this.onMouseUp);
}

// 重写onDestroy函数,在组件从物体上移除时调用
public onDestroy(): void {
// 移除鼠标事件监听
Laya.stage.off(Laya.Event.MOUSE_DOWN, this, this.onMouseDown);
Laya.stage.off(Laya.Event.MOUSE_MOVE, this, this.onMouseMove);
Laya.stage.off(Laya.Event.MOUSE_UP, this, this.onMouseUp);
}

// 鼠标按下事件处理函数
private onMouseDown(event: Laya.Event): void {
this.lastMouseX = event.stageX;
this.lastMouseY = event.stageY;
this.isMouseDown = true;
}

// 鼠标移动事件处理函数
private onMouseMove(event: Laya.Event): void {
if (this.isMouseDown) {
const offsetX: number = event.stageX - this.lastMouseX;
const offsetY: number = event.stageY - this.lastMouseY;
this.lastMouseX = event.stageX;
this.lastMouseY = event.stageY;
// 将鼠标移动距离转换为3D世界坐标系中的偏移量
const point: Point = new Point(offsetX, offsetY);
const scenePoint: Point = this.owner.scene._cameraPool[0].viewport.toScene(point);
const worldOffset: Vector3 = new Vector3(scenePoint.x, scenePoint.y, 0);
this.owner.scene._cameraPool[0].convertScreenCoordToOrthographicCoord(point, worldOffset);
// 将偏移量应用到物体上
this.owner.transform.translate(worldOffset, false);
}
}

// 鼠标松开事件处理函数
private onMouseUp(event: Laya.Event): void {
this.isMouseDown = false;
}
}
在该示例代码中,定义了一个名为DragScript的脚本类,并继承了Laya.Script3D。该脚本类重写了onAwake和onDestroy函数,在组件添加到物体上和从物体上移除时分别调用相应的函数进行处理。

同时,该脚本类还定义了三个事件处理函数onMouseDown、onMouseMove和onMouseUp,用于处理鼠标的按下、移动和松开事件。在这些事件处理函数中,根据鼠标移动的距离计算出物体需要移动的偏移量,并将其应用到物体的transform属性上,实现了拖动物体的效果。

需要注意的是,在使用该脚本类时,需要将其添加到需要拖动的3D物体的组件上。例如,在创建3D物体时,可以使用下面的代码将DragScript脚本添加到物体的组件上:
const box: Sprite3D = scene.addChild(new Sprite3D()) as Sprite3D;
const mesh: Mesh = new Mesh();
const boxFilter: MeshFilter = box.addComponent(MeshFilter) as MeshFilter;
boxFilter.mesh = mesh;
const boxRenderer: MeshRenderer = box.addComponent(MeshRenderer) as MeshRenderer;
boxRenderer.material = new UnlitMaterial();
const dragScript: DragScript = box.addComponent(DragScript) as DragScript;
在以上示例代码中,首先创建了一个Sprite3D对象,并添加了MeshFilter和MeshRenderer组件,然后将UnlitMaterial材质赋值给MeshRenderer组件,最后将DragScript脚本添加到物体上。这样,在运行程序时,就可以使用鼠标拖动该物体了。

需要注意的是,在使用组件时,还需要根据实际需求对组件的属性和逻辑进行调整和优化。例如,根据不同的物体尺寸和场景情况调整拖动的灵敏度和限制范围等。同时,在使用组件时,还需要考虑多物体拖动、触控设备支持等问题。
 
总之,在使用LayaAir3D引擎开发3D应用时,组件是一个非常重要的概念和工具。通过编写组件,可以将特定的逻辑和功能封装起来,使得代码更加清晰、可读性更强、可维护性更高。同时,组件还可以方便地复用和扩展,提高开发效率和代码质量。

需要注意的是,在使用组件时,还需要充分了解LayaAir3D引擎的组件系统和相关API。例如,在定义组件时,需要继承Laya.Script3D类,并重写其中的相应函数;在使用组件时,需要使用addComponent函数将组件添加到物体上,并在需要时使用getComponent函数获取组件对象。同时,在组件内部还可以使用owner属性获取组件所属的物体对象,并使用transform属性获取物体的变换属性。
 

rabbit

赞同来自:

再次补充示例如下:
 
import Scene3D = Laya.Scene3D;
import Camera = Laya.Camera;
import Vector3 = Laya.Vector3;
import Point = Laya.Point;
import Browser = Laya.Browser;
import Handler = Laya.Handler;
import Sprite3D = Laya.Sprite3D;
import Script3D = Laya.Script3D;
import Event = Laya.Event;
import PhysicsCollider = Laya.PhysicsCollider;
import HitResult = Laya.HitResult;
import RigidBody3D = Laya.RigidBody3D;
import PhysicsComponent = Laya.PhysicsComponent;

export default class DragScript extends Script3D {
// 保存上一次鼠标的位置
private lastMouseX: number = 0;
private lastMouseY: number = 0;
// 是否处于拖动状态
private isMouseDown: boolean = false;
// 是否已经落在碰撞体上
private isLanded: boolean = false;
// 物体所在的场景
private scene: Scene3D;
// 物体的碰撞体
private physicsCollider: PhysicsCollider;

// 重写onAwake函数,在组件添加到物体上时调用
public onAwake(): void {
// 获取物体所在的场景和碰撞体
this.scene = this.owner.scene;
this.physicsCollider = this.owner.getComponent(PhysicsCollider) as PhysicsCollider;

// 监听鼠标事件
Laya.stage.on(Event.MOUSE_DOWN, this, this.onMouseDown);
Laya.stage.on(Event.MOUSE_MOVE, this, this.onMouseMove);
Laya.stage.on(Event.MOUSE_UP, this, this.onMouseUp);
}

// 重写onDestroy函数,在组件从物体上移除时调用
public onDestroy(): void {
// 移除鼠标事件监听
Laya.stage.off(Event.MOUSE_DOWN, this, this.onMouseDown);
Laya.stage.off(Event.MOUSE_MOVE, this, this.onMouseMove);
Laya.stage.off(Event.MOUSE_UP, this, this.onMouseUp);
}

// 鼠标按下事件处理函数
private onMouseDown(event: Event): void {
this.lastMouseX = event.stageX;
this.lastMouseY = event.stageY;
this.isMouseDown = true;
}

// 鼠标移动事件处理函数
private onMouseMove(event: Event): void {
if (this.isMouseDown) {
const offsetX: number = event.stageX - this.lastMouseX;
const offsetY: number = event.stageY - this.lastMouseY;
this.lastMouseX = event.stageX;
this.lastMouseY = event.stageY;
// 将鼠标移动距离转换为3D世界坐标系中的偏移量
const point: Point = new Point(offsetX, offsetY);
const scenePoint: Point = this.scene._cameraPool[0].viewport.toScene(point);
const worldOffset: Vector3 = new Vector3(scenePoint.x, scenePoint.y, 0);
this.scene._cameraPool[0].convertScreenCoordToOrthographicCoord(point, worldOffset);
// 将偏移量应用到物体上
this.owner.transform.translate(worldOffset, false);

// 判断物体是否已经落在碰撞体上
if (this.isLanded) {
// 如果已经落在碰撞体上,检测物体是否还在碰撞体上
const hitResult: HitResult = new HitResult();
const isHit: boolean = this.physicsCollider.raycastFromTo(this.owner.transform.position, new Vector3(this.owner.transform.position.x, this.owner.transform.position.y - 1, this.owner.transform.position.z), hitResult);
if (!isHit) {
this.isLanded = false;
}
} else {
// 如果没有落在碰撞体上,检测物体是否与碰撞体相交
const hitResult: HitResult = new HitResult();
const isHit: boolean = this.physicsCollider.raycastFromTo(this.owner.transform.position, new Vector3(this.owner.transform.position.x, this.owner.transform.position.y - 1, this.owner.transform.position.z), hitResult);
if (isHit) {
// 如果相交,将物体位置调整到碰撞体的表面
const rigidBody: RigidBody3D = hitResult.collider.owner.getComponent(RigidBody3D) as RigidBody3D;
if (rigidBody && rigidBody.isKinematic) {
this.owner.transform.position = hitResult.point;
this.isLanded = true;
}
}
}
}
}

// 鼠标松开事件处理函数
private onMouseUp(event: Event): void {
this.isMouseDown = false;
}
}

在以上代码中,我们首先使用raycastFromTo函数检测物体当前是否与碰撞体相交,如果相交,则说明物体正在落在碰撞体上,此时我们需要将物体位置调整到碰撞体的表面,并将isLanded设置为true。

如果物体已经落在碰撞体上,则需要检测物体是否还在碰撞体上。此时我们可以使用raycastFromTo函数检测物体与碰撞体之间的射线,如果射线不再与碰撞体相交,则说明物体已经从碰撞体上掉落,此时需要将isLanded设置为false。

需要注意的是,在使用该脚本类时,需要将其添加到需要拖动的3D物体的组件上,并且需要给该物体添加碰撞体组件,并设置碰撞体的属性和形状。同时,需要给其他需要被物体触发的3D物体添加刚体组件,并设置其属性和形状,以使其能够与物体发生碰撞,并触发相应的事件处理函数。

要回复问题请先

商务合作
商务合作