OpenGL中的蠕虫爬行运动

Worm-Crawling Motion in OpenGL?

本文关键字:爬行 运动 蠕虫 OpenGL      更新时间:2023-10-16

我正在努力找出一种在OpenGL中执行以下行为的有效方法。

我正在制作一个类似蠕虫的家伙的动画(基本上我想要一个有某种可见眼睛的矩形——没有什么复杂的),它应该通过在前面扩展到两倍的长度,然后从后面缩回到原来的大小来移动。蠕虫到达窗口边缘时会反转方向。

我在调整动作方面有很大的困难,在定位眼睛方面也有一些小困难。如果我简单地画两个多边形,一个接一个地画它们的相对位置,我能指望后者画在前者之上吗?我此刻的运动呈现出一个无限循环,我很难从概念上改变为另一种方法。我无法计算出具有加倍/收缩运动和有效接近终止的环路结构。

下面的当前代码:

#define GLUT_DISABLE_ATEXIT_HACK
#include <GL/glut.h>
#include <GL/gl.h>
float wormX = 0;
float wormY = 0;
float wormWidth = 100;
float wormHeight = 75;
bool wormCrawl = true;
void timer( int value )
{
const int w = glutGet(GLUT_WINDOW_WIDTH);
if( wormX + wormWidth >= w / 2 ) //If worm hits edge, reverse direction!
{
    wormX = 2;
}
while( wormCrawl )
{
    wormX += 2; //Move the worm
    wormWidth = 2*wormWidth; //Extend the front
    wormWidth = (1/2)*wormWidth; //Retract the back
}
glutTimerFunc( 16, timer, 0 );
glutPostRedisplay();
}
void display()
{
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
const int w = glutGet(GLUT_WINDOW_WIDTH);
const int h = glutGet(GLUT_WINDOW_HEIGHT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho( -w/2, w/2, -h/2, h/2, -1.0, 1.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
const float leftCorner = wormX;
const float rightCorner = leftCorner + wormWidth;
const float bottomCorner = wormY;
const float topCorner = bottomCorner + wormHeight;
//Worm
glBegin(GL_POLYGON);
    glColor3f(0.0, 1.0, 0.0);
    glVertex2d(leftCorner, topCorner);
    glVertex2d(rightCorner, topCorner);
    glVertex2d(rightCorner, bottomCorner);
    glVertex2d(leftCorner, bottomCorner);
glEnd();
//Worm eye
glBegin(GL_POLYGON);
    glColor3f(0.0, 0.0, 0.0);
    glVertex2d(leftCorner, topCorner);
    glVertex2d(leftCorner + 10, topCorner);
    glVertex2d(leftCorner + 10, bottomCorner);
    glVertex2d(leftCorner, bottomCorner);
glEnd();
glutSwapBuffers();
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize(500, 500);
glutInitWindowPosition(0, 0);
glutCreateWindow ("Inchworm Crawl");
glutDisplayFunc(display);
glutTimerFunc( 0, timer, 0 );
glutMainLoop();
return 0;
}

因此,首先,我们想让生活变得更轻松,您应该尝试将蠕虫抽象为一个类。我想你可能想在之后继续发展,但无论如何,这仍然是一个很好的做法。这也将使我们的抽签更加容易。

好吧,假设我们现在有了Worm类,该类将有自己的draw函数,我们希望在主函数中尽可能简单地保持display()函数中的draw调用。想象一下,如果你想画另一条蠕虫,你将不得不做什么。

所以在你的蠕虫类中,你的draw()方法看起来像这样:

void Worm::draw()
{
    const float leftCorner = wormX;
    const float rightCorner = leftCorner + wormWidth;
    const float bottomCorner = wormY;
    const float topCorner = bottomCorner + wormHeight;
    //Worm
    glBegin(GL_POLYGON);
        glColor3f(0.0, 1.0, 0.0);
        glVertex2d(leftCorner, topCorner);
        glVertex2d(rightCorner, topCorner);
        glVertex2d(rightCorner, bottomCorner);
        glVertex2d(leftCorner, bottomCorner);
    glEnd();
    //Worm eye
    glBegin(GL_POLYGON);
        glColor3f(0.0, 0.0, 0.0);
        glVertex2d(leftCorner, topCorner);
        glVertex2d(leftCorner + 10, topCorner);
        glVertex2d(leftCorner + 10, bottomCorner);
        glVertex2d(leftCorner, bottomCorner);
    glEnd();
}

好吧,这只是它的基础,但我仍然没有回答你的实际问题。我不确定你使用的是哪种版本的免费供过于求,但希望你有最新的。一般来说,如果您计划进行的不仅仅是渲染,则应尽量远离glutmainloop();。它基本上阻止了您在渲染循环之外执行任何逻辑,这实际上意味着程序中的所有内容都将锁定到帧速率。相反,您应该考虑在主函数中编写一个游戏循环,然后每当您想要渲染帧时,它都会调用glutmainloopevent()

因此,现在更多的是移动,一旦你创建了游戏循环,从逻辑上讲,你可以把所有的更新功能都放在这里,在循环结束时,你可以调用glutmainloopevent(),注意这个循环也将是一个半无限循环。当某些标志为真时,它会运行,然后一旦你的蠕虫蠕动足够,你就可以终止它。:P

因此,现在我们可以为蠕虫类添加更多的功能来移动蠕虫并增加其宽度,我们可以将其称为move()。为了简化事情,我们可以给蠕虫添加一个标志,告诉它是应该缩小还是生长。这只是一个在创建蠕虫对象时初始化为false的布尔值。

void Worm::move(bool direction)
{
    if(growing)
    {
        wormWidth += 1;
        if(wormWidth > thresholdWidth)
        {
            growing = false;
        }
    }
    else
    {
        wormWidth += 1;
        if(wormWidth > thresholdWidth)
        {
            growing = false;
        }
    }
    if(direction)
    {
        wormx += 1;
    }
    else
    {
        wormx -= 1;
    }
}

现在我还没有测试过这个可以,但我想它应该能做一些接近你想要的事情。我目前在我的工作电脑上没有过多的设置,也没有openGL。但如果这并不能完全满足你的需求,请给我一些评论,我下班回家后可以看一看。

最后一件需要注意的事情是,你也会在游戏循环中调用你的移动函数,现在对于你的游戏循环,你可能只需要一个while循环来完成这一操作,但最终你可以将其设置为每秒只更新X次,每秒绘制Y次。

需要注意的一点是,因为你每帧画的东西不超过一个,所以这不会有什么不同。你可以继续绘制你的蠕虫,这就是gLClear()函数为你所做的,它正在清除当前颜色缓冲区中的所有内容,如果你正忙于明确使用3D的应用程序,你也需要添加深度缓冲区,否则,事情就不会按照正确的顺序绘制,你可能会遇到z战,或者一些事情被背后的东西所吸引。

希望我没有切中要害,这篇文章对你有一定的价值!

祝你好运!