Skip to content

访问节点和组件

在神岛引擎的组件系统中,了解如何正确访问节点和组件是开发的基础。本文将详细介绍相关操作方法。

访问节点

1. 创建节点

typescript
// 创建节点时绑定实体
const playerNode = new EntityNode(world.querySelector("#实体名称"));

2. 查找已有节点

typescript
import { find } from "@dao3fun/component";

// 通过实体查找对应的节点
const entity = world.querySelector("#NPC");
new EntityNode(entity);

const node = find(entity);
if (node) {
  console.log("找到节点!节点uuid:", node.uuid);
} else {
  console.log("未找到节点!");
}

3. 在组件中访问当前节点

typescript
@apclass("PlayerComponent")
export class PlayerComponent extends Component<GameEntity> {
  start() {
    // 通过 this.node 访问当前组件所属的节点
    console.log("当前节点:", this.node);

    // 访问节点上的实体
    console.log("节点实体:", this.node.entity);
  }
}

访问组件

1. 添加组件

typescript
@apclass("MovementComponent")
export class MovementComponent extends Component<GameEntity> {
  speed = 5;

  start() {
    console.log("移动组件已添加!speed值:", this.speed);
  }
}

// 方式1:通过组件类添加
const node = new EntityNode(null);
node.addComponent(MovementComponent);

// 方式2:通过组件名称添加
node.addComponent("MovementComponent");

// 方式3:添加组件时传入配置
node.addComponent(MovementComponent, {
  speed: 10,
});

2. 获取组件

typescript
@apclass("PlayerController")
export class PlayerController extends Component<GameEntity> {
  start() {
    // 方式1:通过组件类获取
    const movement = this.node.getComponent(MovementComponent);

    // 方式2:通过组件名称获取
    const movement2 = this.node.getComponent("MovementComponent");

    if (movement) {
      movement.speed = 15;
    }
  }
}

3. 获取多个组件

typescript
@apclass("GameManager")
export class GameManager extends Component<GameEntity> {
  start() {
    // 获取节点上的所有组件
    const allComponents = Array.from(this.node.components.values());
    console.log("所有组件:", allComponents);

    // 获取特定类型的所有组件
    const movements = this.node.getComponents(MovementComponent);
    console.log("所有移动组件:", movements);
  }
}

4. 移除组件

typescript
@apclass("ComponentManager")
export class ComponentManager extends Component<GameEntity> {
  start() {
    // 方式1:通过组件类移除
    this.node.removeComponent(MovementComponent);

    // 方式2:通过组件名称移除
    this.node.removeComponent("MovementComponent");
  }
}

实战示例

角色控制系统

typescript
@apclass("MovementComponent")
export class MovementComponent extends Component<GameEntity> {
  speed = 5;

  move(direction: GameVector3) {
    this.node.entity.position = this.node.entity.position.add(
      direction.multiply(this.speed)
    );
  }
}

@apclass("PlayerController")
export class PlayerController extends Component<GameEntity> {
  private movement: MovementComponent | null = null;

  start() {
    // 获取移动组件
    this.movement = this.node.getComponent(MovementComponent);

    // 如果没有移动组件,自动添加一个
    if (!this.movement) {
      this.movement = this.node.addComponent(MovementComponent);
    }

    // 监听按键事件
    world.onPress(({ button }) => {
      if (!this.movement) return;

      switch (button) {
        case "ArrowRight":
          this.movement.move(new GameVector3(1, 0, 0));
          break;
        case "ArrowLeft":
          this.movement.move(new GameVector3(-1, 0, 0));
          break;
      }
    });
  }
}

注意事项

  1. 组件获取检查

    • 总是检查 getComponent 的返回值是否为 null
    • 考虑组件可能不存在的情况
  2. 组件依赖管理

    • 在 start 中获取所需的组件引用
    • 必要时自动添加依赖的组件
  3. 性能优化

    • 缓存经常使用的组件引用
    • 避免在 update 中重复获取组件

常见问题

Q: 为什么获取不到我添加的组件?

A: 检查以下几点:

  • 组件类是否使用了 @apclass 装饰器
  • 组件名称是否正确
  • 组件是否已经被成功添加到节点上

Q: 如何在组件间共享数据?

A: 可以通过以下方式:

  • 通过节点获取其他组件直接访问
  • 使用事件系统进行通信
  • 使用全局状态管理

Q: 组件的获取时机应该是什么时候?

A: 建议在 start 生命周期中获取所需的组件引用,这时所有组件都已完成加载。