【OSG学习笔记】Day 13: 事件处理——响应键盘与鼠标

OpenSceneGraph (OSG) 中,事件处理是实现用户交互功能的重要部分。

通过自定义按键事件(如 WASD 键控制模型移动),可以让用户与场景进行互动。

osgGA::GUIEventHandler

osgGA::GUIEventHandlerOpenSceneGraph (OSG) 中用于处理用户输入事件(如键盘、鼠标等)的一个基类。

通过继承 osgGA::GUIEventHandler 并重写其方法,可以实现自定义的事件处理逻辑,以便对用户的交互做出响应。

创建自定义事件处理器

OSG 提供了一个基类 osgGA::GUIEventHandler,可以用来自定义事件处理逻辑。

我们需要继承这个类,并重写其 handle() 方法。

cpp 复制代码
#include <osgViewer/Viewer>
#include <osg/MatrixTransform>
#include <osgGA/GUIEventHandler>
#include <osg/PositionAttitudeTransform>

// 自定义事件处理器
class CustomKeyboardHandler : public osgGA::GUIEventHandler {
public:
    CustomKeyboardHandler(osg::ref_ptr<osg::PositionAttitudeTransform> transform)
        : _transform(transform), _moveSpeed(1.0f) {}

    // 重写 handle() 方法
    bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter&) override {
        switch (ea.getEventType()) {
            case osgGA::GUIEventAdapter::KEYDOWN: {
                float delta = _moveSpeed; // 移动速度
                osg::Vec3d pos = _transform->getPosition(); // 获取当前模型位置

                switch (ea.getKey()) {
                    case 'w': case 'W': // 向前移动
                        pos.z() += delta;
                        break;
                    case 's': case 'S': // 向后移动
                        pos.z() -= delta;
                        break;
                    case 'a': case 'A': // 向左移动
                        pos.x() -= delta;
                        break;
                    case 'd': case 'D': // 向右移动
                        pos.x() += delta;
                        break;
                    default:
                        return false; // 未处理的按键
                }

                _transform->setPosition(pos); // 更新模型位置
                return true; // 表示事件已处理
            }
            default:
                return false; // 其他事件未处理
        }
    }

private:
    osg::ref_ptr<osg::PositionAttitudeTransform> _transform; // 模型变换节点
    float _moveSpeed; // 移动速度
};

设置场景中的可移动模型

为了演示 WASD 控制,我们需要一个模型,并将其附加到 osg::PositionAttitudeTransform 节点上。

这个节点允许我们轻松地调整模型的位置和姿态。

cpp 复制代码
osg::ref_ptr<osg::Node> createModel() {
    // 创建一个简单的几何体作为模型(例如立方体)
    osg::ref_ptr<osg::Geode> geode = new osg::Geode;
    geode->addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(0, 0, 0), 1.0f)));
    return geode.release();
}

整合事件处理器

将自定义事件处理器添加到 OSG 的视图器中,并运行程序。

cpp 复制代码
int main() {
    // 创建视图器
    osgViewer::Viewer viewer;

    // 创建一个 PositionAttitudeTransform 节点用于控制模型位置
    osg::ref_ptr<osg::PositionAttitudeTransform> transform = new osg::PositionAttitudeTransform;
    transform->addChild(createModel()); // 将模型添加到变换节点

    // 将变换节点添加到场景根节点
    osg::ref_ptr<osg::Group> root = new osg::Group;
    root->addChild(transform);

    // 添加自定义事件处理器
    viewer.addEventHandler(new CustomKeyboardHandler(transform));

    // 设置场景数据
    viewer.setSceneData(root);

    // 运行视图器
    return viewer.run();
}

实战

我们来实现让我们的钢铁侠动起来的效果。

代码如下:

cpp 复制代码
#include <osg/Geode>
#include <osg/ShapeDrawable>
#include <osg/MatrixTransform>
#include <osgViewer/Viewer>
#include <osgGA/GUIEventHandler>
#include <osgDB/ReadFile>
#include <iostream>
#include <cstdlib>

// 自定义事件处理类
class KeyHandler : public osgGA::GUIEventHandler
{
public:
    KeyHandler(osg::MatrixTransform* transform) : _transform(transform) {}

    virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
    {
        switch (ea.getEventType())
        {
        case osgGA::GUIEventAdapter::KEYDOWN:
            switch (ea.getKey())
            {
            case 'w':
            case 'W':
                // 向前移动
                translateNode(osg::Vec3(0.0f, 10.0f, 0.0f));
                return true;
            case 'a':
            case 'A':
                // 向左移动
                translateNode(osg::Vec3(-10.0f, 0.0f, 0.0f));
                return true;
            case 's':
            case 'S':
                // 向后移动
                translateNode(osg::Vec3(0.0f, -10.0f, 0.0f));
                return true;
            case 'd':
            case 'D':
                // 向右移动
                translateNode(osg::Vec3(10.0f, 0.0f, 0.0f));
                return true;
            default:
                break;
            }
        default:
            break;
        }
        return false;
    }

private:
    void translateNode(const osg::Vec3& translation)
    {
        // 获取当前矩阵
        osg::Matrix matrix = _transform->getMatrix();
        // 对矩阵进行平移操作
        matrix.preMultTranslate(translation);
        // 设置新的矩阵
        _transform->setMatrix(matrix);
    }

    osg::ref_ptr<osg::MatrixTransform> _transform;
};

int main()
{
    // 创建Viewer对象
    osgViewer::Viewer viewer;

    // 创建一个MatrixTransform节点
    osg::ref_ptr<osg::MatrixTransform> transform = new osg::MatrixTransform;

    // 加载模型(支持.osg/.obj格式)
    osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("../IronMan.osg");  // 替换为你的模型路径

    // 检查模型是否加载成功
    if (!model) {
        std::cerr << "模型加载失败!请检查路径和格式。" << std::endl;
        return 1;
    }

    // 将几何体添加到MatrixTransform节点
    transform->addChild(model);

    // 创建场景图的根节点
    osg::ref_ptr<osg::Group> root = new osg::Group;
    root->addChild(transform);

    // 设置场景数据
    viewer.setSceneData(root);

    // 创建自定义事件处理程序并添加到Viewer
    viewer.addEventHandler(new KeyHandler(transform));

    // 运行Viewer
    return viewer.run();
}

运行效果

距离做游戏又进了一步。_

相关推荐
小陈phd13 分钟前
高级RAG策略学习(四)——上下文窗口增强检索RAG
人工智能·学习·langchain
Jayyih2 小时前
嵌入式系统学习DAY28(网络编程)
网络·学习·tcp/ip
Suckerbin2 小时前
DarkHole: 2靶场渗透
笔记·安全·web安全·网络安全
dbdr09012 小时前
Linux 入门到精通,真的不用背命令!零基础小白靠「场景化学习法」,3 个月拿下运维 offer,第二十六天
linux·运维·服务器·网络·python·学习
一枝小雨2 小时前
【C++】Vector完全指南:动态数组高效使用
开发语言·c++·笔记·vector·学习笔记·std库
大白的编程日记.2 小时前
【Linux学习笔记】信号的深入理解之软件条件产生信号
linux·笔记·学习
buyutang_2 小时前
C/C++ Linux系统编程:线程控制详解,从线程创建到线程终止
linux·c语言·c++·学习
Wilber的技术分享2 小时前
【大模型实战笔记 1】Prompt-Tuning方法
人工智能·笔记·机器学习·大模型·llm·prompt
yzx9910133 小时前
图像去雾:从暗通道先验到可学习融合——一份可跑的 PyTorch 教程
人工智能·pytorch·学习
来生硬件工程师3 小时前
【硬件笔记】负载是如何烧MOS的?
笔记·嵌入式硬件·硬件架构·硬件工程·硬件设计