唯杰地图 logo唯杰地图
VJMAP
EN

实体Entity

vjmap3d/references/core/entity.md 15.8 KB368 行
原文

实体Entity 是模块的容器,用于连接实体模块EntityModuleBase。它和一个Object3D相关联,通过连接的模块功能,来实现对Object3D的各种操作。

image-20240524093658066

创建新的实体

当直接new一个Entity时,会自动创建一个新的Object3D与之相关联

let entity = new Entity();
entity.addTo(app);

如果往此实体上面加一个子对象时,其对象树状结构如下

let entity = new Entity();
entity.addTo(app);
let child = new Object3D();
entity.addObjects(child);

// └─object3d <-- 实体对应的对象
//    └─child 增加的子对象

可以在创建实体时,直接通过参数node指定相关联的Object3D

let obj = new Object3D(); 
let entity = new Entity({
  node: obj
});
entity.addTo(app);

通过一个或多个object3d对象创建一个实体, 上面的示例也可以用下面的语句代码

let obj = new Object3D(); 
let entity = Entity.fromObject3d(obj);
entity.addTo(app);

如果实体在场景中已存在,可通过attchObject给一个已加入场景中的对象关联一个实体

let obj = mesh; // 场景中的一个实体对象 
let entity = Entity.attchObject(obj); // 不用再调用 addTo(app)了

实体移除

通过remove进行移除

entity.remove();

实体属性

基本属性

通过实体的node对象可获取实体相关联的Object3D对象。

调用实体的position、rotation,quaternion,scale,matrix,matrixWorld,visible属性,等同于调用其相关联实体的相关属性。如

entity.visible = false 执行结果与 entity.node.visible = false 相同

其主要区别是通过实体设置的属性是响应式对象,可获取此属性的响应式对象进行监听位置的变化.

// 监听visible响应式对象变化
vjmap3d.reactivity.watch(entity.visibleRef, (v) => app.logInfo(v ? "可见" : "隐藏", 1000))
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
    container: "map", // 容器id
})
const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
const cube = new THREE.Mesh( geometry, material );
let entity = vjmap3d.Entity.fromObject3d(cube); // 创建一个来源于object3d的实体
entity.addTo(app);// 把实体加进应用中
const ui = await app.getConfigPane(); // 获取配置面板
let cfg = entity.uiConfig; // 获取实体属性配置
if (cfg) {
    ui.appendChild(cfg);
}

pointerEvents

设置实体是否可响应事件

rayIntersectRecursive

拾取时是否遍历子对象 默认为true

RayQueryType

射线查询类型

效果属性

  • selected 是否选中
  • outline 是否描边效果 (与 selected 效果只能二选一)
  • bloom 是否辉光
  • boxHelper 是否显示包围盒
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
    container: "map", // 容器id
})
const geometry = new THREE.ConeGeometry(1, 4);
const material = new THREE.MeshBasicMaterial( { color: 0x00ffff } );
const cube = new THREE.Mesh( geometry, material );
let entity = vjmap3d.Entity.fromObject3d(cube); // 创建一个来源于object3d的实体
entity.addTo(app);// 把实体加进应用中
const ui = await app.getConfigPane();
let cfg = {
    type: 'folder',
    expanded: true,
    label: "实体效果属性",
    children: vjmap3d.generateUiConfig(entity, [
        {
            prop: "selected"
        },
        {
            prop: "bloom"
        },
        {
            prop: "boxHelper"
        },
        {
            prop: "outline"
        }
    ])
}
ui.appendChild(cfg)

如果需修改属性效果的颜色等参数,selected、outline、bloom属于后期处理效果,不能单独设置,需要全局统一设置。 boxHelper可以直接通过属性单独进行设置。

let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
    container: "map", // 容器id
    postProcess: {
        bloomPass: {
            luminanceThreshold: 0.01,
            luminanceSmoothing: 0.1,
            mipmapBlur: true,
            intensity: 3.0,
        },
        outlinePass: {
            edgeStrength: 4.0,
            pulseSpeed: 0.4,
            visibleEdgeColor: 0x00ffff,
            hiddenEdgeColor: 0x634ab0,
        },
        selectedPass: {
            edgeStrength: 8.0,
            pulseSpeed: 1,
            visibleEdgeColor: 0xff0000,
            hiddenEdgeColor: 0xb0634a,
        }
    }
})
const geometry = new THREE.ConeGeometry(1, 4);
const material = new THREE.MeshBasicMaterial( { color: 0x00ffff } );
const cube = new THREE.Mesh( geometry, material );
let entity = vjmap3d.Entity.fromObject3d(cube); // 创建一个来源于object3d的实体
entity.addTo(app);// 把实体加进应用中
entity.selected = true;
entity.bloom = true; 
entity.boxHelper = {
    lineColor: "#ff0",
    fillColor: "#f0f",
    fillOpacity: 0.01
}

设置高亮

  • isHighlight 是否高亮
  • setHighlight 设置高亮
  • clearHighlight 清除高亮

替换材质

  • replaceMaterial 替换材质
  • resetMaterial 恢复材质

⚠️ 坑:setOpacity 内部也走 replaceMaterial 的克隆路径——先 setOpacity(a) 再 resetMaterial() 恢复的是克隆体而非最初材质引用(对象 identity 变了)。要精确还原本引用,用纯净路径 replaceMaterial(new) → resetMaterial(),别混进 setOpacity。

模型边框线

  • addEdges 增加模型边框线
  • clearEdges 移除模型边框线
  • getEdges 获取模型边框线

设置透明度

  • setOpacity 设置透明度

模块

  • addModule 或 add 增加模块
  • getModule 获取模块
  • removeModule 移除模块

通过ResManagerloadModel加载的资源如果模型的动画,会默认加一个动画模块AnimatorModule

let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
    container: "map", // 容器id
     control:{
        initState: { // 相机初始位置 
            cameraTarget: new THREE.Vector3(-15, 0, -10),
            cameraPosition: new THREE.Vector3(-30, 30, -10)
      }
    },
    ui: {
        configPane: {
            title: "设置",
            style: { width: "240px", overflow: "hidden"}
        }
    }
})
let ent = await vjmap3d.ResManager.loadModel(vjmap3d.ResManager.svrUrl("models/Stork.glb"), {
    splitSubEntity: true,
    scale: 0.1,
    anchor: "front-bottom-right"
});
ent.addTo(app);
//
const ui = await app.getConfigPane();
let cfg = [
    {
        type: 'slider',
        label: '透明度',
        value: 1.0,
        bounds: [0, 1],
        onChange: ({ value } )=> {
            ent.setOpacity(value)
        }
    },
    {
        type: 'checkbox',
        label: '开启动画',
        value: false,
        onChange: ({ value } )=> {
            let mod = ent.getModule(vjmap3d.AnimatorModule);
            if (!mod) return
            let animator = mod.getAnimator();
            if (value) { // 开启动画
                animator.play(0)
            } else { // 停止动画
                animator.stop(0)
            }
        }
    },
    {
        type: 'checkbox',
        label: '高亮',
        getValue: () => ent.isHighlight(),
        setValue: v => {
            if (v) { // 设置高亮
                ent.setHighlight("#0ff", 0.2)
            } else { // 清除高亮
                ent.clearHighlight()
            }
        }
    },
    {
        type: 'checkbox',
        label: '替换材质',
        value: false,
        onChange: ({ value } )=> {
            if (value) { // 替换材质
                const material = new THREE.MeshPhongMaterial({
                    color: '#f00'
                });
                ent.replaceMaterial(material)
            } else { // 恢复材质
                ent.resetMaterial()
            }
        }
    },
    {
        type: 'checkbox',
        label: '模型边框线',
        value: false,
        onChange: ({ value } )=> {
            if (value) { // 增加模型边框线
                ent.addEdges({
                    color: 0x00ffff,
                }, true)
            } else { // 移除模型边框线
                ent.clearEdges()
            }
        }
    }
]
cfg.forEach(c => ui.appendChild(c));

常见方法

方法名 描述
create (静态方法)创建一个新的实体对象
fromModule (静态方法)基于模块创建一个新的实体对象
fromObject3d (静态方法)通过一个或多个object3d对象创建一个实体
fromObjectModule (静态方法)通过一个或多个object3d对象和模块创建一个实体
getOwerEntity (静态方法)通过一个obejct获取所属的实体
onEntityAwake 当实体加入应用app时触发,只触发一次
tween 补间动画
addObjects 增加子对象
removeObjects 移除子对象
addChildEntity 增加子实体
removeChildEntity 移除子实体
addTo 增加至应用app
remove 移除
traverse 遍历
traverseVisible 只遍历可见
getObjectByName 通过名称获取对象
getObjectById 通过id获取对象
getObjectByUuid 通过uuid获取对象
getObjectsByName 通过名称获取多个对象
getObjectByProperty 通过属性获取对象
getEntityByName 通过名称获取实体
getEntityById 通过id获取实体
getEntityByUuid 通过uuid获取实体
getMeshEntityByProperty 通过属性获取mesh实体
getMeshEntityByName 通过名称获取mesh实体
getMeshEntityById 通过id获取mesh实体
getMeshEntityByUuid 通过uuid获取mesh实体
getEntitiesByName 通过名称获取多个实体
getEntityByProperty 通过属性获取实体
getEntitiesByProperty 通过属性获取多个实体
getObjectPropertyValueCount 有此属性值的对象个数
getEntitiesByModule 通过模块获取实体
isLikeMeshEntity 判断实体是否是mesh实体或包含mesh实体模块
getMeshEntities 获取所有Mesh实体
getLeafEntities 获取所有叶子实体
getPointerEventsEntities 获取有事件的所有字实体
addEdges 增加模型边框线
clearEdges 移除模型边框线
replaceMaterial 替换材质
resetMaterial 恢复材质
isHighlight 是否高亮
setHighlight 设置高亮
clearHighlight 清除高亮
setOpacity 设置透明度
isFireEventsByIntersect 根据同一个实体上的两个射线的交点信息,来决定是否是不同的事件。默认false不再触发
getBoundingBox 获取物体的外包盒
addAction 增加一个自定义动作脚本
addAnimationAction 增加一个自定义动画动作脚本
removeAction 移除一个自定义动作脚本

⚠️ traverseVisible 的可见性过滤只在传了 filter 时生效:traverseVisible(cb) 无第二参会遍历全部后代(含 visible=false),与"只遍历可见"的字面描述不符;traverseVisible(cb, ()=>true) 才会先按 visible 门控再走回调。要"仅可见且无条件"就传一个恒真 filter。

⚠️ getEntityByName/getEntityById/getEntityByUuid 查的是底层 Object3D 属性:实现是 getEntityByProperty → 遍历 object.name/id/uuid → 反查实体。给实体包装层赋 name 只改实体字段、不传播到其 node 的 name,查不到;要命中需设置 mesh/Object3D 的 .name。同理 getObjectByUuid 匹配的是 object3d.uuid。而 getEntitiesByProperty("name", v) 也走对象属性——实体自身字段要用别的方式筛。

⚠️ 本节 getEntityByXxx/getObjectByXxx 是 Entity 树上的方法,不是 App 上的:App 实例上没有这两个方法(运行时均为 undefined);要全局查找请从场景根实体(或 app.scene 对应的实体入口)调用,查询范围是该实体的后代子树。