
在OpenSceneGraph (OSG)
中,事件处理是实现用户交互功能的重要部分。
通过自定义按键事件(如 WASD 键控制模型移动),可以让用户与场景进行互动。
osgGA::GUIEventHandler
osgGA::GUIEventHandler
是 OpenSceneGraph (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();
}
运行效果

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