220 lines
4.3 KiB
Markdown
220 lines
4.3 KiB
Markdown
|
|
# Extra2D API 教程 - 03. 节点系统
|
|||
|
|
|
|||
|
|
## 节点基础
|
|||
|
|
|
|||
|
|
节点(Node)是游戏对象的基本单位,可以包含子节点,形成树形结构。
|
|||
|
|
|
|||
|
|
### 创建节点
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
#include <extra2d/extra2d.h>
|
|||
|
|
|
|||
|
|
using namespace extra2d;
|
|||
|
|
|
|||
|
|
class MyNode : public Node {
|
|||
|
|
public:
|
|||
|
|
MyNode() {
|
|||
|
|
// 设置位置
|
|||
|
|
setPosition(Vec2(100.0f, 200.0f));
|
|||
|
|
|
|||
|
|
// 设置旋转(度)
|
|||
|
|
setRotation(45.0f);
|
|||
|
|
|
|||
|
|
// 设置缩放
|
|||
|
|
setScale(Vec2(2.0f, 2.0f));
|
|||
|
|
|
|||
|
|
// 设置锚点(0-1范围,默认0.5是中心)
|
|||
|
|
setAnchor(0.5f, 0.5f);
|
|||
|
|
|
|||
|
|
// 设置可见性
|
|||
|
|
setVisible(true);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 每帧更新
|
|||
|
|
void onUpdate(float dt) override {
|
|||
|
|
Node::onUpdate(dt);
|
|||
|
|
// 自定义更新逻辑
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 渲染
|
|||
|
|
void onRender(RenderBackend &renderer) override {
|
|||
|
|
Node::onRender(renderer);
|
|||
|
|
// 自定义渲染
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 节点层级
|
|||
|
|
|
|||
|
|
### 添加子节点
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
void onEnter() override {
|
|||
|
|
Scene::onEnter();
|
|||
|
|
|
|||
|
|
// 创建子节点
|
|||
|
|
auto child = makePtr<MyNode>();
|
|||
|
|
|
|||
|
|
// 添加到场景
|
|||
|
|
addChild(child);
|
|||
|
|
|
|||
|
|
// 在指定位置添加
|
|||
|
|
addChild(child, 0); // z-order = 0
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 移除子节点
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 移除指定子节点
|
|||
|
|
removeChild(child);
|
|||
|
|
|
|||
|
|
// 移除所有子节点
|
|||
|
|
removeAllChildren();
|
|||
|
|
|
|||
|
|
// 通过名称移除
|
|||
|
|
removeChildByName("myNode");
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 获取子节点
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 获取子节点数量
|
|||
|
|
size_t count = getChildren().size();
|
|||
|
|
|
|||
|
|
// 通过名称查找
|
|||
|
|
auto node = getChildByName("myNode");
|
|||
|
|
|
|||
|
|
// 遍历子节点
|
|||
|
|
for (auto &child : getChildren()) {
|
|||
|
|
// 处理子节点
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 空间索引
|
|||
|
|
|
|||
|
|
### 启用空间索引
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
class PhysicsNode : public Node {
|
|||
|
|
public:
|
|||
|
|
PhysicsNode() {
|
|||
|
|
// 启用空间索引(用于碰撞检测)
|
|||
|
|
setSpatialIndexed(true);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 必须实现 getBoundingBox()
|
|||
|
|
Rect getBoundingBox() const override {
|
|||
|
|
Vec2 pos = getPosition();
|
|||
|
|
return Rect(pos.x - 25.0f, pos.y - 25.0f, 50.0f, 50.0f);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 边界框
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 获取节点边界框
|
|||
|
|
Rect bounds = node->getBoundingBox();
|
|||
|
|
|
|||
|
|
// 检查点是否在边界框内
|
|||
|
|
if (bounds.contains(Vec2(x, y))) {
|
|||
|
|
// 点在边界框内
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查两个边界框是否相交
|
|||
|
|
if (bounds.intersects(otherBounds)) {
|
|||
|
|
// 边界框相交
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 精灵节点
|
|||
|
|
|
|||
|
|
### 创建精灵
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 加载纹理
|
|||
|
|
auto texture = resources.loadTexture("assets/player.png");
|
|||
|
|
|
|||
|
|
// 创建精灵
|
|||
|
|
auto sprite = Sprite::create(texture);
|
|||
|
|
|
|||
|
|
// 设置位置
|
|||
|
|
sprite->setPosition(Vec2(640.0f, 360.0f));
|
|||
|
|
|
|||
|
|
// 设置锚点(中心)
|
|||
|
|
sprite->setAnchor(0.5f, 0.5f);
|
|||
|
|
|
|||
|
|
// 添加到场景
|
|||
|
|
addChild(sprite);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 精灵动画
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 创建动画
|
|||
|
|
auto animation = Animation::create("walk", 0.1f);
|
|||
|
|
animation->addFrame(resources.loadTexture("assets/walk1.png"));
|
|||
|
|
animation->addFrame(resources.loadTexture("assets/walk2.png"));
|
|||
|
|
animation->addFrame(resources.loadTexture("assets/walk3.png"));
|
|||
|
|
|
|||
|
|
// 播放动画
|
|||
|
|
sprite->playAnimation(animation, true); // true = 循环播放
|
|||
|
|
|
|||
|
|
// 停止动画
|
|||
|
|
sprite->stopAnimation();
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 完整示例
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
class Player : public Node {
|
|||
|
|
public:
|
|||
|
|
Player() {
|
|||
|
|
setSpatialIndexed(true);
|
|||
|
|
|
|||
|
|
// 加载精灵
|
|||
|
|
auto &resources = Application::instance().resources();
|
|||
|
|
auto texture = resources.loadTexture("assets/player.png");
|
|||
|
|
sprite_ = Sprite::create(texture);
|
|||
|
|
sprite_->setAnchor(0.5f, 0.5f);
|
|||
|
|
addChild(sprite_);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void onUpdate(float dt) override {
|
|||
|
|
Node::onUpdate(dt);
|
|||
|
|
|
|||
|
|
// 移动
|
|||
|
|
Vec2 pos = getPosition();
|
|||
|
|
pos = pos + velocity_ * dt;
|
|||
|
|
setPosition(pos);
|
|||
|
|
|
|||
|
|
// 边界检查
|
|||
|
|
auto &app = Application::instance();
|
|||
|
|
float width = static_cast<float>(app.getConfig().width);
|
|||
|
|
float height = static_cast<float>(app.getConfig().height);
|
|||
|
|
|
|||
|
|
if (pos.x < 0 || pos.x > width) {
|
|||
|
|
velocity_.x = -velocity_.x;
|
|||
|
|
}
|
|||
|
|
if (pos.y < 0 || pos.y > height) {
|
|||
|
|
velocity_.y = -velocity_.y;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Rect getBoundingBox() const override {
|
|||
|
|
Vec2 pos = getPosition();
|
|||
|
|
return Rect(pos.x - 25.0f, pos.y - 25.0f, 50.0f, 50.0f);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private:
|
|||
|
|
Ptr<Sprite> sprite_;
|
|||
|
|
Vec2 velocity_{100.0f, 100.0f};
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 下一步
|
|||
|
|
|
|||
|
|
- [04. 资源管理](04_Resource_Management.md)
|
|||
|
|
- [05. 输入处理](05_Input_Handling.md)
|