HarmonyOS鸿蒙Next中如何获取node节点的世界坐标

HarmonyOS鸿蒙Next中如何获取node节点的世界坐标 这是官方智能大师给的

import { Scene, Node, Mat4, Vec3 } from '@kit.ArkGraphics3D';

// 假设已加载场景并获取到 scene 对象

let scene: Promise<Scene> = Scene.load($rawfile("模型文件.glb"));

scene.then((result: Scene) => { if (result && result.root) {

// 通过路径查找节点,例如 "scene/box"

let targetNode: Node | null = result.root.getNodeByPath("scene/box");

if (targetNode) {

// 获取节点的世界变换矩阵

let worldMat: Mat4 = targetNode.worldTransform;

// 从矩阵中提取位置(矩阵的第四列,索引为12、13、14的元素)

let worldPosition: Vec3 = new Vec3(worldMat, worldMat, worldMat);

console.info(`World position of node: x=${worldPosition.x}, y=${worldPosition.y}, z=${worldPosition.z}`); } } });

可是property ‘worldTransform’ does not exist on type ‘Node’. <ArkTSCheck>?


更多关于HarmonyOS鸿蒙Next中如何获取node节点的世界坐标的实战教程也可以访问 https://www.itying.com/category-93-b0.html

4 回复

尊敬的开发者,您好,node节点位置坐标也可以代表世界坐标节点坐标,详细参考:node

Node

3D场景由树状层次结构的节点组成,其中每个节点都实现了Node接口。继承自SceneResource

属性

系统能力: SystemCapability.ArkUi.Graphics3D

名称 类型 只读 可选 说明
position Position3 节点位置,单位为世界坐标系下的场景单位(比如cm、m、km等)。
rotation Quaternion 节点旋转角度。
scale Scale3 节点缩放。
visible boolean 节点是否可见。true表示该节点可见,false表示不可见。
nodeType NodeType 节点类型。
layerMask LayerMask 节点的图层掩码。
path string 节点路径。
parent Node null
children Container<Node> 节点的子节点,不存在则为空值。为只读属性,表示不能替换整个children容器,但可以通过容器方法操作子节点(如append()insertAfter()remove()clear())。如果append或insertAfter的节点已存在于容器中,容器会先移除该节点再插入,因此数量不会增加,看似“无效”;添加新节点才会真正增加子节点数量。

示例代码如下:

import {
  Image,
  Shader,
  MaterialType,
  Material,
  ShaderMaterial,
  Animation,
  Environment,
  Container,
  SceneNodeParameters,
  LightType,
  Light,
  Camera,
  SceneResourceParameters,
  SceneResourceFactory,
  Scene,
  Node,
  Vec3
} from '@kit.ArkGraphics3D';
import { LengthMetrics } from '@kit.ArkUI';

@Entry
@Component
 struct Index {
  scene: Scene | null = null;
  @State sceneOpt: SceneOptions | null = null;
  cam: Camera | null = null;
  @State x: number = 0
  @State y: number = 0
  @State z: number = 0

  aboutToAppear(): void {
    this.Init();
  }

  Init(): void {
    if (this.scene == null) {
      // 加载模型,将gltf文件放置到相关路径,加载时以实际路径为准
      Scene.load($rawfile("box.glb"))
        .then(async (result: Scene) => {
          this.scene = result;
          let rf: SceneResourceFactory = this.scene.getResourceFactory();
          // 创建相机
          this.cam = await rf.createCamera({ "name": "Camera" });
          // 设置合适的相机参数
          this.cam.enabled = true;
          this.sceneOpt = { scene: this.scene, modelType: ModelType.SURFACE } as SceneOptions;

          if (result && result.root) {
            let targetNode = result.root.getNodeByPath(`Scene/Cube`)
            if (targetNode) {
              console.info(`节点的世界坐标为: x=${targetNode.position.x}, y=${targetNode.position.y}, z=${targetNode.position.z}`);

              let worldPos: Vec3 = targetNode.position;

              // 2. 向上追溯父节点,累加其坐标得到绝对世界坐标
              let parent = targetNode.parent;
              while (parent && parent !== result.root) {
                worldPos.x += parent.position.x;
                worldPos.y += parent.position.y;
                worldPos.z += parent.position.z;
                parent = parent.parent;
              }

              console.info(`节点的世界坐标为: x=${worldPos.x}, y=${worldPos.y}, z=${worldPos.z}`);
            }
          }
        })
        .catch((reason: string) => {
          console.log(reason);
        });
    }
  }

  build() {
    Flex({ direction: FlexDirection.Column }) {
      if (this.sceneOpt) {
        // 通过Component3D呈现3D场景
        Component3D(this.sceneOpt)
          .flexGrow(1).flexBasis(1).flexShrink(1)

        Flex({ space: { main: LengthMetrics.vp(5), cross: LengthMetrics.vp(5) } }) {
          Column() {
            Row() {
              Text(`x:${this.x.toFixed(1)}`)
              Slider({
                value: this.x,
                min: -50,
                max: 50,
                step: 0.1,
                style: SliderStyle.OutSet
              }).onChange((value: number) => {
                if (this.cam) {
                  this.x = this.cam.position.x = value
                }
              }).showSteps(true)
            }

            Row() {
              Text(`y:${this.y.toFixed(1)}`)
              Slider({
                value: this.y,
                min: -50,
                max: 50,
                step: 0.1,
                style: SliderStyle.OutSet
              }).onChange((value: number) => {
                if (this.cam) {
                  this.y = this.cam.position.y = value
                }
              }).showSteps(true)
            }

            Row() {
              Text(`z:${this.z.toFixed(1)}`)
              Slider({
                value: this.z,
                min: -50,
                max: 50,
                step: 0.1,
                style: SliderStyle.OutSet
              }).onChange((value: number) => {
                if (this.cam) {
                  this.z = this.cam.position.z = value
                }
              }).showSteps(true)
            }
          }
        }

      } else {
        Text("loading ...")
      }
    }.width('100%').height('100%')
  }
}

更多关于HarmonyOS鸿蒙Next中如何获取node节点的世界坐标的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


这个报错是正常的:当前 ArkGraphics3D 的 Node 接口没有 worldTransform 属性,智能生成的这段代码不能直接用。官方 SceneNode 文档里 Node 暴露的是 position、rotation、scale、path、parent、children 等属性;getNodeByPath() 返回的是 Node | null,所以获取“世界坐标”要沿 parent 链把本地坐标逐级变换到根节点坐标系。

可以先封装一个工具方法,节点自身原点的世界坐标用 getWorldPosition(targetNode):

import { Node, Vec3, Quaternion } from '@kit.ArkGraphics3D';

function add(a: Vec3, b: Vec3): Vec3 {
  return { x: a.x + b.x, y: a.y + b.y, z: a.z + b.z };
}

function rotate(v: Vec3, q: Quaternion): Vec3 {
  const ix = q.w * v.x + q.y * v.z - q.z * v.y;
  const iy = q.w * v.y + q.z * v.x - q.x * v.z;
  const iz = q.w * v.z + q.x * v.y - q.y * v.x;
  const iw = -q.x * v.x - q.y * v.y - q.z * v.z;
  return {
    x: ix * q.w + iw * -q.x + iy * -q.z - iz * -q.y,
    y: iy * q.w + iw * -q.y + iz * -q.x - ix * -q.z,
    z: iz * q.w + iw * -q.z + ix * -q.y - iy * -q.x
  };
}

function applyParent(p: Vec3, parent: Node): Vec3 {
  const s = parent.scale;
  const scaled: Vec3 = { x: p.x * s.x, y: p.y * s.y, z: p.z * s.z };
  return add(rotate(scaled, parent.rotation), parent.position);
}

function getWorldPosition(node: Node): Vec3 {
  let p: Vec3 = { ...node.position };
  let cur: Node | null = node.parent;
  while (cur) {
    p = applyParent(p, cur);
    cur = cur.parent;
  }
  return p;
}

如果模型层级没有父节点旋转/缩放,简化版就是沿 parent 累加 position;如果要算某个顶点或包围盒中心的世界坐标,则先把该点按节点自身的 scale/rotation/position 转到父坐标系,再继续向上套父节点变换。

来源: SceneNode API: https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-inner-scene-nodes SceneType API: https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-inner-scene-types#position3

在HarmonyOS Next中,获取Node节点的世界坐标可通过组件提供的getGlobalOffset()方法实现。调用this.getGlobalOffset()返回一个Offset对象,包含相对于屏幕左上角的xy值。若需实时获取,建议在onPageShow或布局完成后的回调中调用,确保坐标已更新。

在HarmonyOS Next的ArkGraphics3D中,Node 类并没有直接暴露 worldTransform 属性,需要通过 getWorldTransform 方法获取世界变换矩阵。提取坐标时,应使用矩阵元素的正确索引。修正后的代码如下:

import { Scene, Node, Mat4, Vec3 } from '@kit.ArkGraphics3D';

let scene: Promise<Scene> = Scene.load($rawfile("模型文件.glb"));
scene.then((result: Scene) => {
  if (result && result.root) {
    let targetNode: Node | null = result.root.getNodeByPath("scene/box");
    if (targetNode) {
      // 通过方法获取世界变换矩阵
      let worldMat: Mat4 = targetNode.getWorldTransform();
      // 从矩阵提取平移分量(第12、13、14个元素,索引从0开始)
      let worldPosition: Vec3 = new Vec3(
        worldMat.get(12),
        worldMat.get(13),
        worldMat.get(14)
      );
      console.info(`World position: x=${worldPosition.x}, y=${worldPosition.y}, z=${worldPosition.z}`);
    }
  }
});

getWorldTransform() 返回 Mat4 对象,其 get(index) 方法按列主序访问元素,索引12-14对应世界坐标的xyz平移量。,

回到顶部