【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();
}

运行效果

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

相关推荐
985小水博一枚呀1 小时前
【AI大模型学习路线】第二阶段之RAG基础与架构——第七章(【项目实战】基于RAG的PDF文档助手)技术方案与架构设计?
人工智能·学习·语言模型·架构·大模型
FakeOccupational1 小时前
计算机科技笔记: 容错计算机设计05 n模冗余系统 TMR 三模冗余系统
笔记·科技
shandianchengzi2 小时前
【记录】Windows|竖屏怎么调整分辨率使横竖双屏互动鼠标丝滑
windows·计算机外设·显示器·鼠标·分辨率·双屏
hello1114-2 小时前
Redis学习打卡-Day3-分布式ID生成策略、分布式锁
redis·分布式·学习
小Tomkk2 小时前
2025年PMP 学习二十 第13章 项目相关方管理
学习·pmp·项目pmp
独行soc3 小时前
2025年渗透测试面试题总结-百度面经(题目+回答)
运维·开发语言·经验分享·学习·面试·渗透测试·php
海棠蚀omo3 小时前
C++笔记-红黑树
开发语言·c++·笔记
ysy16480672393 小时前
03算法学习_977、有序数组的平方
学习·算法
FAREWELL000754 小时前
Unity学习总结篇(1)关于各种坐标系
学习·unity·c#·游戏引擎
龙湾开发4 小时前
计算机图形学编程(使用OpenGL和C++)(第2版)学习笔记 12.曲面细分
c++·笔记·学习·3d·图形渲染