-
简介
本节课实现了一个填满格子的小游戏,有点类似于小时候玩的吃豆子的游戏。本课并没有使用什么新的技术,但是实现的过程比较繁琐,不喜欢深入细节的读者可以跳过本课。
-
实现
首先我们需要创建文字,下面的代码是其中一些文字的创建方式,其他的与之类似:
osg::Group* createGridCrazyText()
{
osg::Group *fontGroup = new osg::Group;
osg::MatrixTransform *mt = new osg::MatrixTransform;
mt->setMatrix(osg::Matrix::translate(207,24,0));
osg::Geode *font = new osg::Geode;
osgText::Text *text = new osgText::Text;
text->setText("GRID CRAZY");
text->setCharacterSize(20.0f);
text->setFont("fonts/arial.ttf");
text->setColor(osg::Vec4(1.0f,0.5f,1.0f, 1.0f));
font->addDrawable(text);
mt->addChild(font);
fontGroup->addChild(mt);
return fontGroup;
}接着创建玩家的生命显示,在右上角处
osg::Switch* createLives()
{
osg::Switch *livesGroup = new osg::Switch;
g_livesGroup = livesGroup;
for (int i = 0; i < lives-1; ++i)
{
osg::MatrixTransform *transMT = new osg::MatrixTransform;
transMT->setMatrix(osg::Matrix::translate(490+(i*40.0f),40.0f,0.0f));
osg::MatrixTransform *rotMT = new osg::MatrixTransform;
rotMT->addUpdateCallback(new osg::AnimationPathCallback(osg::Vec3(), osg::Z_AXIS, 3.5));
osg::Group *live = new osg::Group;
osg::Geode *liveGeode1 = new osg::Geode;
osg::Geometry *lineGeometry1 = new osg::Geometry;
osg::Vec2Array *verts1 = new osg::Vec2Array;
verts1->push_back(osg::Vec2(-5,-5));
verts1->push_back(osg::Vec2(5,5));
verts1->push_back(osg::Vec2(5,-5));
verts1->push_back(osg::Vec2(-5,5));
osg::Vec3Array *colors1 = new osg::Vec3Array;
colors1->push_back(osg::Vec3(0, 1, 0));
lineGeometry1->setVertexArray(verts1);
lineGeometry1->setColorArray(colors1, osg::Array::BIND_OVERALL);
lineGeometry1->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_STRIP, 0, 2));
lineGeometry1->getOrCreateStateSet()->setMode(GL_LIGHTING, false);
liveGeode1->addDrawable(lineGeometry1);
osg::MatrixTransform *crossMT = new osg::MatrixTransform;
crossMT->addUpdateCallback(new osg::AnimationPathCallback(osg::Vec3(), osg::Z_AXIS, 7.0));
osg::Geode *liveGeode2 = new osg::Geode;
osg::Geometry *lineGeometry2 = new osg::Geometry;
osg::Vec2Array *verts2 = new osg::Vec2Array;
verts2->push_back(osg::Vec2(-7,0));
verts2->push_back(osg::Vec2(7,0));
verts2->push_back(osg::Vec2(0,-7));
verts2->push_back(osg::Vec2(0,7));
osg::Vec3Array *colors2 = new osg::Vec3Array;
colors2->push_back(osg::Vec3(0.0f,0.75f,0.0f));
lineGeometry2->setVertexArray(verts2);
lineGeometry2->setColorArray(colors2, osg::Array::BIND_OVERALL);
lineGeometry2->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_STRIP, 0, 2));
lineGeometry2->getOrCreateStateSet()->setMode(GL_LIGHTING, false);
liveGeode2->addDrawable(lineGeometry2);
live->addChild(liveGeode1);
live->addChild(crossMT);
crossMT->addChild(liveGeode2);
transMT->addChild(rotMT);
rotMT->addChild(live);
livesGroup->addChild(transMT);
}
return livesGroup;
}使用Swith节点的原因是当生命减少的时候我们关掉其中的子节点。
接着要创建网格和填充网格的四边形:
osg::Group* createGrid()
{
osg::Group *group = new osg::Group;
osg::Geode *geode = new osg::Geode;
for (int i = 0; i < 11; i++)
{
for (int j = 0; j < 11; j++)
{
if (i < 10)
{
osg::Geometry *line = new osg::Geometry;
osg::Vec2dArray *vertices = new osg::Vec2dArray;
vertices->push_back(osg::Vec2d(TX(20+i*60), TY(70+j*40)));
vertices->push_back(osg::Vec2d(TX(80+i*60), TY(70+j*40)));
osg::Vec3Array *colors = new osg::Vec3Array;
colors->push_back(osg::Vec3(0.0, 0.5f, 1.0f));
line->setVertexArray(vertices);
line->setColorArray(colors, osg::Array::BIND_OVERALL);
line->getOrCreateStateSet()->setMode(GL_LIGHTING, false);
osg::BlendFunc *blendFunc = new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
line->getOrCreateStateSet()->setAttributeAndModes(blendFunc);
line->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES, 0, 2));
line->setUpdateCallback(new HColorUpdateCallback(i, j));
geode->addDrawable(line);
}
if (j < 10)
{
osg::Geometry *vlineGeometry = new osg::Geometry;
osg::Vec2dArray *vlinevertices = new osg::Vec2dArray;
vlinevertices->push_back(osg::Vec2d(TX(20+i*60), TY(70+j*40)));
vlinevertices->push_back(osg::Vec2d(TX(20+i*60), TY(110+j*40)));
osg::Vec3Array *vlinecolors = new osg::Vec3Array;
vlinecolors->push_back(osg::Vec3(0.0, 0.5f, 1.0f));
vlineGeometry->setVertexArray(vlinevertices);
vlineGeometry->setColorArray(vlinecolors, osg::Array::BIND_OVERALL);
vlineGeometry->getOrCreateStateSet()->setMode(GL_LIGHTING, false);
osg::BlendFunc *blendFunc = new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
vlineGeometry->getOrCreateStateSet()->setAttributeAndModes(blendFunc);
vlineGeometry->setUpdateCallback(new VColorUpdateCallback(i, j));
vlineGeometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES, 0, 2));
geode->addDrawable(vlineGeometry);
}
}
}
group->addChild(geode);
return group;
}NeHe教程定义了这样一个变量struct object
{
int fx, fy;
int x, y;
float spin;
};其中的fx和fy代表了横竖网格的交点,我们玩家角色会一直在交点上出现,实现这个效果需要我们一直调整玩家角色的位置,代码如下:
if (player.fx<player.x*60)
{
player.fx+=steps[adjust];
}
if (player.fx>player.x*60)
{
player.fx-=steps[adjust];
}
if (player.fy<player.y*40)
{
player.fy+=steps[adjust];
}
if (player.fy>player.y*40)
{
player.fy-=steps[adjust];
}这部分代码放在交互的GUIEventHandler的Frame时间中:
case (osgGA::GUIEventAdapter::FRAME):使用上下左右键操作角色的代码如下;
switch(ea.getEventType())
{
case (osgGA::GUIEventAdapter::KEYDOWN):
{
if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Left)
{
if((player.x>0) && (player.fx==player.x*60) && (player.fy==player.y*40))
{
player.x--;
hline[player.x][player.y]=true;
}
}
if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Right)
{
if ((player.x<10) && (player.fx==player.x*60) && (player.fy==player.y*40))
{
hline[player.x][player.y]=true;
player.x++;
}
}
if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Down)
{
if((player.y<10) && (player.fx==player.x*60) && (player.fy==player.y*40))
{
vline[player.x][player.y]=true;
player.y++;
}
}
if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Up)
{
if((player.y>0) && (player.fx==player.x*60) && (player.fy==player.y*40))
{
player.y--;
vline[player.x][player.y]=true;
}
}
if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Space)
{
if (gameover)
{
gameover=false;
filled=true;
level=1;
level2=1;
stage=0;
lives=5;
g_livesGroup->setAllChildrenOn();
g_gameoverSwitch->setAllChildrenOff();
}
}
}
default: break;
}
return false;
}可以看到只有到在帧循环中会不断调整角色的位置至交点处通过交互修改了角色的位置变量,在更新回调中需要更新角色位置,代码如下:
class TranslatePlayerCallback : public osg::NodeCallback
{
public:
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
osg::MatrixTransform *mt = dynamic_cast<osg::MatrixTransform*>(node);
if (!mt)
{
return;
}
mt->setMatrix(osg::Matrix::translate((player.fx+20.0f),(player.fy+70.0f),0.0f));
traverse(node, nv);
}
};
其他更新回调包括字体的闪烁效果:(Game OVer),敌人的位置等都是这样实现的
class FontBlickCallback : public osg::Drawable::UpdateCallback
{
public:
virtual void update(osg::NodeVisitor*, osg::Drawable* drawable)
{
osgText::Text *text = dynamic_cast<osgText::Text*>(drawable);
if (!text)
return;
text->setColor(osg::Vec4(rand() % 10 * 0.1, rand() % 10 * 0.1, rand() % 10 * 0.1, 1.0));
}
};最后把所有这些节点添加到场景中:
osg::Node* buildScene()
{
osg::Group *root = new osg::Group;
root->addChild(createGridCrazyText());
root->addChild(createLevelText());
root->addChild(createStageText());
root->addChild(createGameOverScene());
root->addChild(createLives());
root->addChild(createGrid());
root->addChild(createPlayer());
root->addChild(createHourGlasses());
root->addChild(createEnemy());
root->addChild(createGridQuads());
return root;
}需要注意的是本节课使用的投影是(0,0)点在左上角,使用
camera->setProjectionMatrixAsOrtho(0, traits->width, traits->height, 0, -1.0, 1.0);

本文介绍了一个类似吃豆子的网格小游戏的实现过程,包括创建文字、玩家生命显示、网格及其填充等,通过具体代码展示了如何利用OSG进行游戏开发。
4666

被折叠的 条评论
为什么被折叠?



