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对象,包含相对于屏幕左上角的x和y值。若需实时获取,建议在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平移量。,

回到顶部