源于 Godot,Rust 实现的游戏开发框架 NodeTree

科技   2024-09-07 22:49   广东  

在游戏开发领域,Godot  引擎以其直观易用的节点系统而闻名。如今,Rust  开发者也能体验到类似的便利!NodeTree,一个受 Godot 启发的 Rust 极简框架,为构建大型可扩展程序和游戏提供了全新的思路。本文将深入探讨 NodeTree 的核心概念、使用方法以及其带来的优势。

构建程序的全新方式

NodeTree 框架的核心思想是将程序分解成一个进程树,每个进程都是一个独立的单元,被称为“节点”(Node)。这些节点可以存储自身的状态和数据,并与其他节点进行通信,从而协同完成复杂的任务。

构建你的第一个 NodeTree

使用 NodeTree 非常简单。首先,将 node_tree 添加到你的项目依赖中:

cargo add node_tree

接下来,我们需要创建一个根节点。为了减少样板代码,可以使用 NodeTree 提供的 NodeSys 宏来实现必要的 DynamicNodeAbstract trait。然后,我们只需要手动实现 Node trait 即可。

use node_tree::prelude::*;

#[derive(Debug, Abstract)]
pub struct MyNode {
    base: NodeBase, // 节点必须包含 NodeBase
}

impl MyNode {
    fn new(name: String) -> Self {
        MyNode { base: NodeBase::new(name) }
    }
}

// 实现 Node trait,定义节点行为
impl Node for MyNode {
    // 节点被添加到 NodeTree 时运行一次
    fn ready(&mut self) {
        // 示例:添加子节点
        if self.depth() < 3 {
            let new_depth = self.depth() + 1;
            self.add_child(MyNode::new(format!("{}_Node", new_depth)));
            self.add_child(MyNode::new(format!("{}_Node", new_depth)));
            self.add_child(MyNode::new(format!("{}_Node", new_depth)));
        }

        if self.is_root() {
            println!("{:?}"self.children());
        }
    }

    // 每帧运行一次,提供帧间的时间差(秒)
    fn process(&mut self, delta: f32) {
        // 示例:计算帧率
        println!("{} | {}"self.name(), 1f32 / delta);

        // 使用 NodePath 和 TreePointer 可以引用 NodeTree 中的其他节点
        if self.is_root() {
            if let Some(node) = self.get_node::<MyNode>(NodePath::from_str("1_Node/2_Node1/3_Node2")) {
                println!("{:?}", node);
            }
        }

        // 节点可以被销毁,销毁后其在 NodeTree 中的引用也会被清理
        // 如果根节点被销毁,程序会自动退出
        if self.children().is_empty() {
            self.free(); // 从树梢到树根逐步销毁节点
        }
    }

    // 节点从 NodeTree 中移除时运行一次,无论程序是否终止
    fn terminal(&mut self) {}

    // 返回节点的处理模式
    // 处理模式控制 NodeTree 暂停/运行时 process() 函数的行为
    fn process_mode(&self) -> ProcessMode {
        ProcessMode::Inherit // 默认值,继承父节点的行为
    }
}

最后,我们需要实例化根节点并将其传递给 NodeTree 构造函数:

// ...之前的代码
use node_tree::trees::tree_simple::TreeSimple;

fn main() -> () {
    // 创建树
    let root = MyNode::new("Root".to_string());
    let tree: Box<TreeSimple> = TreeSimple::new(root, LoggerVerbosity::NoDebug);

    // 开始运行
    tree.start();
    loop {
        if tree.process().has_terminated() {
            break;
        }
    }
}

探索更多特性

除了基本的节点管理,NodeTree 还提供了丰富的功能:

  • 灵活的节点通信:  owner()parent()get_child()children()get_node() 等方法使得节点间通信变得轻而易举。
  • 智能指针:  Tp<T>TpDyn  简化了节点引用,减少了语法噪音。
  • 缓存系统:  NodeTree 内置缓存系统,提高了 Tp<T>/TpDyn 的安全性.
  • 动态日志系统:  与节点框架深度集成的日志系统方便调试和错误处理。

总结

NodeTree 将 Godot 引擎中广受好评的节点系统引入 Rust,为开发者提供了一种构建大型可扩展程序的全新思路。其简洁易用的 API、丰富的功能以及对性能的关注,使其成为 Rust 游戏开发和其他领域的有力工具。

文章精选

Tailspin:用 Rust 打造的炫彩日志查看器

Rust: 重塑系统编程的安全壁垒

Youki:用Rust编写的容器运行时,性能超越runc

使用C2Rust将C代码迁移到Rust

Rust语言中如何优雅地应对错误


关注公众号并回复 “NodeTree” 获取项目地址

Rust编程笔记
与你一起在Rust的世界里探索、学习、成长!
 最新文章