当前位置: 首页 > article >正文

三维移动游戏

今天来 用opengl和c++来写,不会opengl的看下一篇。。。

这个示例创建了一个可以通过键盘控制在三维空间中移动的立方体,展示了基本的三维图形渲染以及交互控制的思路。

上代码

#include <GL/glut.h>
#include <iostream>
#include <cmath>

// 全局变量,用于控制物体的位置
float xPos = 0.0f;
float yPos = 0.0f;
float zPos = 0.0f;

// 移动步长
float stepSize = 0.1f;

// 绘制立方体的函数
void drawCube() {
    glBegin(GL_QUADS);

    // 前面
    glColor3f(1.0f, 0.0f, 0.0f);  // 红色
    glVertex3f(-0.5f, -0.5f,  0.5f);
    glVertex3f( 0.5f, -0.5f,  0.5f);
    glVertex3f( 0.5f,  0.5f,  0.5f);
    glVertex3f(-0.5f,  0.5f,  0.5f);

    // 后面
    glColor3f(0.0f, 1.0f, 0.0f);  // 绿色
    glVertex3f(-0.5f, -0.5f, -0.5f);
    glVertex3f( 0.5f, -0.5f, -0.5f);
    glVertex3f( 0.5f,  0.5f, -0.5f);
    glVertex3f(-0.5f,  0.5f, -0.5f);

    // 左面
    glColor3f(0.0f, 0.0f, 1.0f);  // 蓝色
    glVertex3f(-0.5f, -0.5f, -0.5f);
    glVertex3f(-0.5f,  0.5f, -0.5f);
    glVertex3f(-0.5f,  0.5f,  0.5f);
    glVertex3f(-0.5f, -0.5f,  0.5f);

    // 右面
    glColor3f(1.0f, 1.0f, 0.0f);  // 黄色
    glVertex3f(0.5f, -0.5f, -0.5f);
    glVertex3f(0.5f,  0.5f, -0.5f);
    glVertex3f(0.5f,  0.5f,  0.5f);
    glVertex3f(0.5f, -0.5f,  0.5f);

    // 上面
    glColor3f(0.0f, 1.0f, 1.0f);  // 青色
    glVertex3f(-0.5f,  0.5f, -0.5f);
    glVertex3f( 0.5f,  0.5f, -0.5f);
    glVertex3f( 0.5f,  0.5f,  0.5f);
    glVertex3f(-0.5f,  0.5f,  0.5f);

    // 下面
    glColor3f(1.0f, 0.0f, 1.0f);  // 品红色
    glVertex3f(-0.5f, -0.5f, -0.5f);
    glVertex3f( 0.5f, -0.5f, -0.5f);
    glVertex3f( 0.5f, -0.5f,  0.5f);
    glVertex3f(-0.5f, -0.5f,  0.5f);

    glEnd();
}

// 显示回调函数,用于绘制场景
void display() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

    // 设置观察视角
    gluLookAt(0.0, 0.0, 5.0,  // 眼睛位置
              0.0, 0.0, 0.0,  // 观察点位置
              0.0, 1.0, 0.0);  // 上方向向量

    // 平移到物体当前位置
    glTranslatef(xPos, yPos, zPos);

    drawCube();

    glFlush();
    glutSwapBuffers();
}

// 键盘回调函数,处理按键事件来移动物体
void keyboard(unsigned char key, int x, int y) {
    switch (key) {
    case 'w':
        zPos -= stepSize;
        break;
    case 's':
        zPos += stepSize;
        break;
    case 'a':
        xPos -= stepSize;
        break;
    case 'd':
        xPos += stepSize;
        break;
    case 'q':
        yPos += stepSize;
        break;
    case 'e':
        yPos -= stepSize;
        break;
    case 27:  // ESC键,退出程序
        exit(0);
    default:
        break;
    }
    glutPostRedisplay();
}

// 初始化OpenGL相关设置
void init() {
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);  // 设置背景颜色为黑色
    glEnable(GL_DEPTH_TEST);  // 启用深度测试,用于正确处理三维物体的遮挡关系
}

int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(800, 600);
    glutCreateWindow("3D Moving Object");

    init();

    glutDisplayFunc(display);
    glutKeyboardFunc(keyboard);

    glutMainLoop();

    return 0;
}

这个是基础,下面是可直接用的

#include <GL/glut.h>
#include <iostream>
#include <cmath>

// 全局变量,用于控制赛车的位置和朝向
float carX = 0.0f;
float carY = 0.0f;
float carZ = 0.0f;
float carRotationY = 0.0f;  // 绕y轴旋转角度,用于控制赛车朝向

// 移动步长
float moveStep = 0.1f;
// 旋转角度步长
float rotateStep = 1.0f;

// 赛道宽度
const float trackWidth = 10.0f;
// 赛道长度(简单示意,可根据实际调整)
const float trackLength = 100.0f;

// 绘制赛道的函数
void drawTrack() {
    glBegin(GL_QUADS);
    // 赛道地面
    glColor3f(0.2f, 0.2f, 0.2f);  // 灰色
    glVertex3f(-trackWidth / 2, 0.0f, 0.0f);
    glVertex3f(trackWidth / 2, 0.0f, 0.0f);
    glVertex3f(trackWidth / 2, 0.0f, trackLength);
    glVertex3f(-trackWidth / 2, 0.0f, trackLength);

    // 赛道左侧边界(简单示意为高墙)
    glColor3f(0.5f, 0.5f, 0.5f);  // 深灰色
    glVertex3f(-trackWidth / 2, 0.0f, 0.0f);
    glVertex3f(-trackWidth / 2, 5.0f, 0.0f);
    glVertex3f(-trackWidth / 2, 5.0f, trackLength);
    glVertex3f(-trackWidth / 2, 0.0f, trackLength);

    // 赛道右侧边界(简单示意为高墙)
    glColor3f(0.5f, 0.5f, 0.5f);  // 深灰色
    glVertex3f(trackWidth / 2, 0.0f, 0.0f);
    glVertex3f(trackWidth / 2, 5.0f, 0.0f);
    glVertex3f(trackWidth / 2, 5.0f, trackLength);
    glVertex3f(trackWidth / 2, 0.0f, trackLength);
    glEnd();
}

// 绘制赛车的函数(简单示意为长方体代表赛车,可替换为更复杂模型)
void drawCar() {
    glPushMatrix();
    glTranslatef(carX, carY, carZ);
    glRotatef(carRotationY, 0.0f, 1.0f, 0.0f);

    glBegin(GL_QUADS);
    // 赛车车身(简单示意为红色长方体)
    glColor3f(1.0f, 0.0f, 0.0f);
    glVertex3f(-1.0f, 0.0f, -2.0f);
    glVertex3f(1.0f, 0.0f, -2.0f);
    glVertex3f(1.0f, 0.0f, 2.0f);
    glVertex3f(-1.0f, 0.0f, 2.0f);

    // 赛车顶部(简单示意为蓝色长方形)
    glColor3f(0.0f, 0.0f, 1.0f);
    glVertex3f(-1.0f, 1.0f, -2.0f);
    glVertex3f(1.0f, 1.0f, -2.0f);
    glVertex3f(1.0f, 1.0f, 2.0f);
    glVertex3f(-1.0f, 1.0f, 2.0f);

    // 赛车前面(简单示意为绿色长方形)
    glColor3f(0.0f, 1.0f, 0.0f);
    glVertex3f(-1.0f, 0.0f, 2.0f);
    glVertex3f(1.0f, 0.0f, 2.0f);
    glVertex3f(1.0f, 1.0f, 2.0f);
    glVertex3f(-1.0f, 1.0f, 2.0f);

    // 赛车后面(简单示意为黄色长方形)
    glColor3f(1.0f, 1.0f, 0.0f);
    glVertex3f(-1.0f, 0.0f, -2.0f);
    glVertex3f(1.0f, 0.0f, -2.0f);
    glVertex3f(1.0f, 1.0f, -2.0f);
    glVertex3f(-1.0f, 1.0f, -2.0f);

    // 赛车左侧面(简单示意为青色长方形)
    glColor3f(0.0f, 1.0f, 1.0f);
    glVertex3f(-1.0f, 0.0f, -2.0f);
    glVertex3f(-1.0f, 1.0f, -2.0f);
    glVertex3f(-1.0f, 1.0f, 2.0f);
    glVertex3f(-1.0f, 0.0f, 2.0f);

    // 赛车右侧面(简单示意为品红色长方形)
    glColor3f(1.0f, 0.0f, 1.0f);
    glVertex3f(1.0f, 0.0f, -2.0f);
    glVertex3f(1.0f, 1.0f, -2.0f);
    glVertex3f(1.0f, 1.0f, 2.0f);
    glVertex3f(1.0f, 0.0f, 2.0f);
    glEnd();

    glPopMatrix();
}

// 显示回调函数,用于绘制场景
void display() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

    // 设置观察视角
    gluLookAt(0.0, 5.0, 20.0,  // 眼睛位置
              0.0, 0.0, 0.0,  // 观察点位置
              0.0, 1.0, 0.0);  // 上方向向量

    drawTrack();
    drawCar();

    glFlush();
    glutSwapBuffers();
}

// 键盘回调函数,处理按键事件来控制赛车移动和转向
void keyboard(unsigned char key, int x, int y) {
    switch (key) {
    case 'w':
        // 向前移动,根据当前朝向计算移动向量
        carX += moveStep * sin(carRotationY * M_PI / 180.0f);
        carZ -= moveStep * cos(carRotationY * M_PI / 180.0f);
        break;
    case 's':
        // 向后移动,根据当前朝向计算移动向量
        carX -= moveStep * sin(carRotationY * M_PI / 180.0f);
        carZ += moveStep * cos(carRotationY * M_PI / 180.0f);
        break;
    case 'a':
        carRotationY -= rotateStep;
        break;
    case 'd':
        carRotationY += rotateStep;
        break;
    case 27:  // ESC键,退出程序
        exit(0);
    default:
        break;
    }

    // 简单碰撞检测,防止赛车超出赛道边界(这里只检测左右边界)
    if (carX < -trackWidth / 2 + 1.0f) {
        carX = -trackWidth / 2 + 1.0f;
    }
    if (carX > trackWidth / 2 - 1.0f) {
        carX = trackWidth / 2 - 1.0f;
    }

    glutPostRedisplay();
}

// 初始化OpenGL相关设置
void init() {
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);  // 设置背景颜色为黑色
    glEnable(GL_DEPTH_TEST);  // 启用深度测试,用于正确处理三维物体的遮挡关系
}

int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(800, 600);
    glutCreateWindow("Simple Racing Game");

    init();

    glutDisplayFunc(display);
    glutKeyboardFunc(keyboard);

    glutMainLoop();

    return 0;
}

思路:

1. 新增的全局变量

float carX = 0.0f;
float carY = 0.0f;
float carZ = 0.0f;
float carRotationY = 0.0f;

float moveStep = 0.1f;
float rotateStep = 1.0f;

  • carXcarYcarZ 用于控制赛车在三维空间中的位置坐标,初始值都设为 0,表示赛车起始在赛道的原点位置附近(具体根据赛道坐标设定)。carRotationY 用于控制赛车绕 y 轴的旋转角度,也就是控制赛车的朝向,初始化为 0 度,表示朝正前方。
  • moveStep 定义了赛车每次移动的距离步长,rotateStep 定义了赛车每次转向的角度步长,这里分别设置为 0.1 和 1.0,可根据实际游戏手感需求进行调整。

2. drawTrack 函数

void drawTrack() {
    glBegin(GL_QUADS);
    // 赛道地面
    glColor3f(0.2f, 0.2f, 0.2f);  // 灰色
    glVertex3f(-trackWidth / 2, 0.0f, 0.0f);
    glVertex3f(trackWidth / 2, 0.0f, 0.0f);
    glVertex3f(trackWidth / 2, 0.0f, trackLength);
    glVertex3f(-trackWidth / 2, 0.0f, trackLength);
    // 赛道左右边界绘制代码类似,此处省略部分重复注释
    glEnd();
}

这个函数用于绘制简单的赛道,赛道由地面和左右两侧的边界墙组成。通过 glBegin(GL_QUADS) 和 glEnd 来定义一系列四边形来表示不同部分,分别设置不同的颜色和顶点坐标来绘制出赛道地面(灰色)以及左右两侧较高的边界墙(深灰色),这里赛道宽度和长度通过 trackWidth 和 trackLength 两个常量来定义,方便后续调整赛道规模。

3. drawCar 函数

void drawCar() {
    glPushMatrix();
    glTranslatef(carX, carY, carZ);
    glRotatef(carRotationY, 0.0f, 1.0f, 0.0f);
    // 赛车各面绘制代码
    glBegin(GL_QUADS);
    // 车身、顶部、前面、后面、左右侧面等面的绘制代码,此处省略部分重复注释
    glEnd();
    glPopMatrix();
}

这个函数用于绘制赛车模型,这里简单地用多个不同颜色的四边形拼接成一个类似长方体的形状来示意赛车。首先通过 glPushMatrix 和 glPopMatrix 来保存和恢复当前的矩阵状态,确保赛车的变换(平移和旋转)不会影响到其他物体的绘制。然后使用 glTranslatef 根据赛车的当前位置坐标 carXcarYcarZ 进行平移,使赛车出现在对应位置,再通过 glRotatef 根据 carRotationY 绕 y 轴旋转赛车,使其朝向正确的方向,最后绘制赛车各个面的四边形,定义每个面的颜色和顶点坐标来呈现出赛车的外观形状。

4. display 函数

void display() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    gluLookAt(0.0, 5.0, 20.0,  // 眼睛位置
              0.0, 0.0, 0.0,  // 观察点位置
              0.0, 1.0, 0.0);  // 上方向向量
    drawTrack();
    drawCar();
    glFlush();
    glutSwapBuffers();
}

这个函数在原来的基础上,先绘制赛道,再绘制赛车,从而在每帧画面中呈现出完整的游戏场景。视角设置部分通过 gluLookAt 调整了观察位置为 (0, 5, 20),也就是在赛道上方一定距离处朝着原点(赛道起点位置)观察,这样可以较好地看到整个赛道和赛车的情况,其他部分的功能(如清除缓冲区、交换缓冲区等)和之前一样,用于保证画面的正常绘制和显示。

5. keyboard 函数

void keyboard(unsigned char key, int x, int y) {
    switch (key) {
    case 'w':
        carX += moveStep * sin(carRotationY * M_PI / 180.0f);
        carZ -= moveStep * cos(carRotationY * M_PI / 180.0f);
        break;
    case 's':
        carX -= moveStep * sin(carRotationY * M_PI / 180.0f);
        carZ += moveStep * cos(carRotationY * M_PI / 180.0f);
        break;
    case 'a':
        carRotationY -= rotateStep;
        break;
    case 'd':
        carRotationY += rotateStep;
        break;
    case 27:  // ESC键,退出程序
        exit(0);
    default:
        break;
    }
    if (carX < -trackWidth / 2 + 1.0f) {
        carX = -trackWidth / 2 + 1.0f;
    }
    if (carX > trackWidth / 2 - 1.0f) {
        carX = trackWidth / 2 - 1.0f;
    }
    glutPostRedisplay();
}

这个键盘回调函数根据不同按键来控制赛车的移动和转向:

  • 按下 w 键时,根据当前赛车的朝向(通过 carRotationY 角度换算三角函数值)计算在 x 和 z 轴方向的移动分量,从而实现赛车朝着当前朝向向前移动。按下 s 键同理,实现向后移动。
  • 按下 a 键和 d 键分别减少和增加 carRotationY 的值,实现赛车向左和向右转向。
  • 同时添加了简单的碰撞检测逻辑,判断赛车的 x 坐标是否超出赛道左右边界(考虑赛车自身宽度,预留了一定的边界余量,这里是 1.0f),如果超出则将赛车位置限制在边界内,防止其 “驶出” 赛道。最后通过 glutPostRedisplay 触发画面重绘,使得赛车状态的改变能及时显示在屏幕上。

6. init 函数和 main 函数

这两个函数的功能和之前基本一致,init 函数进行 OpenGL 的初始化设置(如背景颜色、深度测试等),main 函数负责初始化 GLUT 库、设置显示模式、创建窗口、注册回调函数并进入主事件循环,让整个游戏程序能够正常运行起来,不断处理各种输入和绘制场景等操作。

当然,这只是一个非常简单的赛车游戏示例,还可以从很多方面进一步完善它,比如:

  • 添加更多赛道元素:如弯道、上坡下坡、障碍物等,这需要更复杂的数学计算和图形绘制逻辑来实现不同形状的赛道部分以及相应的碰撞检测逻辑。
  • 优化赛车模型:使用更复杂逼真的 3D 模型来替换简单的长方体示意,可能需要加载外部的模型文件(如 OBJ 格式等)并进行相应的纹理映射等操作来提升视觉效果。
  • 增加游戏逻辑:例如设置比赛时间、记录圈数、添加对手赛车实现多人竞赛等功能,让游戏更加丰富有趣,有更好的可玩性。


http://www.kler.cn/a/440846.html

相关文章:

  • C++智能指针详解
  • 3D Gaussian Splatting for Real-Time Radiance Field Rendering-简洁版
  • 05、SpringMVC全注解开发
  • 音频声音太小怎么调大?调大音频声音的几种方法
  • STM32-笔记1-点亮led灯
  • sqlilabs靶场二十一关二十五关攻略
  • app的测试范围以及web和app的测试区别
  • 搭建Tomcat(二)--反射的应用
  • 详细描述一下 Elasticsearch 更新和删除文档的过程。
  • 在VScode中对R语言进行环境配置
  • 沈剑-架构师训练营
  • Mongodb 集群搭建
  • 项目二十三:电阻测量(需要简单的外围检测电路,将电阻转换为电压)测量100,1k,4.7k,10k,20k的电阻阻值,由数码管显示。要求测试误差 <10%
  • k8s+rancher配置滚动发布更新时服务不可用
  • STM32--IO引脚复用
  • 留学论文Introduction辅导:论文开头introduction怎么写
  • 19. 【.NET 8 实战--孢子记账--从单体到微服务】--记账模块--收支记录
  • OpenShift 4 - 多云管理(2) - 配置多集群观察功能
  • 【0368】Postgres内核 清除所有旧的 relcache cache files ( 11 )
  • JS进阶-面向对象-搭建网站-HTML与JS交互