当前位置:网站首页>用OpenGL绘制winXP版扫雷的笑脸表情
用OpenGL绘制winXP版扫雷的笑脸表情
2022-08-04 08:36:00 【zorchp】
tags: OpenGL Game
写在前面
最近看看OpenGL的东西, 学了一些基础的语法, 所以用来做点东西, 用基本的点线多边形等来绘制一个Windows XP中经典的游戏扫雷里面的标志性笑脸, 如下图:

思路
这个笑脸有四种形态,分别是初始状态的笑脸, 点击时候的o型嘴, 赢的时候的墨镜脸和输了时候的伤心脸, 下面通过简单的直线和圆来绘制, 都是简单图形, 本质上其实都是点的绘制, 这里我封装了一下函数, 调用会方便一些, 分别是通过vector<GLint>表示的点, 这样用initializer_list时候会方便很多.
其次就是一些小细节, 例如圆的绘制中的角度问题(这里就是嘴的绘制), 还有键鼠事件的交互, 我觉得熟悉了这个流程之后, 主要的思路就都放在点的坐标上了, 这里我建议可以通过Geogebra这款软件进行草图的绘制, 找到待绘制的点的坐标之后会方便很多.
代码(C++)
下面是代码, 这里分成几个部分分别说:
首先是导言区, 导入相应的模块和常量.
#include <GL/glut.h>
#include <vector>
//#include <iostream>
#include <cmath>
using namespace std;
const int w = 400, h = 400;
const GLfloat pi = 3.1415926536f;
void init() {
glClearColor(1, 0, 1, 0);//黑色背景
glMatrixMode(GL_PROJECTION);//正投影
glLoadIdentity();
gluOrtho2D(-w, w, -h, h);
}
然后是一些自定义的函数, 分别是画圆画直线和画多边形(这里是四边形)的, 用起来比直接读取点要方便一些.
void drawCircle(GLint x, GLint y, GLint r,
vector<GLfloat> rgb = {
0, 0, 0},
GLint cir = 2, int mode = GL_POLYGON,
GLfloat lw = 1,
const int n = 10000) {
glColor3f(rgb[0], rgb[1], rgb[2]);
glLineWidth(lw);
glBegin(mode);
for (int i = 0; i < n; i++) {
glVertex2i(x + r * cos(cir * pi / n * i),
y + r * sin(cir * pi / n * i));
}
glEnd();
}
void drawLine(vector<GLint> p1, vector<GLint> p2, GLfloat lw = 1,
vector<GLfloat> rgb = {
0, 0, 0},//default:black
int mode = GL_LINE_STRIP) {
glColor3f(rgb[0], rgb[1], rgb[2]);
glLineWidth(lw);
glBegin(mode);
glVertex2i(p1[0], p1[1]);
glVertex2i(p2[0], p2[1]);
glEnd();
}
void drawPolygon(vector<GLint> p1, vector<GLint> p2,
vector<GLint> p3, vector<GLint> p4,
vector<GLfloat> rgb = {
0, 0, 0},//default:black
int mode = GL_POLYGON,
GLfloat lw = 1) {
glColor3f(rgb[0], rgb[1], rgb[2]);
glLineWidth(lw);
glBegin(mode);
glVertex2i(p1[0], p1[1]);
glVertex2i(p2[0], p2[1]);
glVertex2i(p3[0], p3[1]);
glVertex2i(p4[0], p4[1]);
glEnd();
}
最后就是主要的绘制了,
void display() {
// 绘制基本的笑脸
drawCircle(0, 0, 50, {
1, 1, 0});//face
drawCircle(-20, 15, 6);//left eye
drawCircle(20, 15, 6);//right eye
drawCircle(0, -10, 20, {
0, 0, 0}, -1, GL_LINE_STRIP, 3);//mouth
glFlush();
}
void openMouth() {
//点击时候的张嘴
drawCircle(-20, 15, 8);//left eye
drawCircle(20, 15, 8);//right eye
drawCircle(0, -10, 20, {
1, 1, 0}, -1, GL_LINE_STRIP, 3);//cover old mouth
drawCircle(0, -18, 10, {
0, 0, 0}, 2, GL_LINE_STRIP, 3);//mouth
glFlush();
}
void coolFace() {
//赢时候的墨镜表情
drawPolygon({
-40, 25}, {
-10, 25}, {
-10, 8}, {
-40, 8});//left eye
drawPolygon({
40, 25}, {
10, 25}, {
10, 8}, {
40, 8});//right eye
drawLine({
-10, 23}, {
10, 23}, 3);//glasses middle
drawLine({
-45, 20}, {
-40, 25}, 3);//glasses left
drawLine({
45, 20}, {
40, 25}, 3);//glasses right
glFlush();
}
void dieFace() {
//输了时候的伤心脸
drawCircle(0, 0, 50, {
1, 1, 0});//face
drawLine({
-12, 7}, {
-28, 23}, 3);
drawLine({
-12, 23}, {
-28, 7}, 3);//left eye
drawLine({
12, 7}, {
28, 23}, 3);
drawLine({
12, 23}, {
28, 7}, 3);//right eye
drawCircle(0, -10, 20, {
0, 0, 0}, 1, GL_LINE_STRIP, 3);//mouth
glFlush();
}
// 这里是键盘交互, 通过按下q或者esc键实现退出, 按d实现伤心脸, 按w实现墨镜脸
void Key(unsigned char key, int x, int y) {
switch (key) {
case 27:
case 'q':
exit(0);
case 'd':
dieFace();
break;
case 'w':
coolFace();
break;
}
}
// 鼠标事件, 点击笑脸变形为O型嘴
void Mouse(int btn, int state, int x, int y) {
// 如果在圆内, 点击生效
if (x * x + y * y - w * x - w * y + (w / 2) * w < 50 * 50) {
if (btn == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
// cout << x << " " << y << endl;
openMouth();
} else {
display();
}
}
}
int main(int argc, char **argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
glutInitWindowPosition(200, 200);
glutInitWindowSize(w, h);
glutCreateWindow("MineSweeper Smiling Face");
init();
glutDisplayFunc(display);
glutKeyboardFunc(Key);
glutMouseFunc(Mouse);
glutMainLoop();
return 0;
}
最后实现结果:

顺便附带一下cmake文件:(我的测试环境是Mac, 在Windows中需要配置一下路径)
cmake_minimum_required(VERSION 3.21)
project(minesweeper-smile)
set(CMAKE_CXX_STANDARD 17)
set(program_SOURCES main.cpp)
link_directories("/opt/homebrew/lib")
include_directories("/opt/homebrew/include")
find_library(Cocoa_Library Cocoa)
find_library(OpenGl_Library OpenGL)
find_library(GLUT_Library glut)
add_executable(${
PROJECT_NAME} ${
program_SOURCES})
target_link_libraries(${
PROJECT_NAME}
${
Cocoa_Library}
${
OpenGl_Library}
${
GLUT_Library}
)
边栏推荐
猜你喜欢

RT-Thread Studio学习(十一)IIC

从零开始C语言精讲篇6:结构体

沃尔玛、阿里国际该如何做测评自养号?

Cross-species regulatory sequence activity prediction

图的基本概念

Recommend several methods that can directly translate PDF English documents

智汇华云 | 华云软件定义网络 DCI介绍

csdn图片去水印 | 其他方法无效时的解决方案

inject() can only be used inside setup() or functional components.

inject() can only be used inside setup() or functional components.
随机推荐
智能健身动作识别:PP-TinyPose打造AI虚拟健身教练!
Detailed explanation of TCP protocol
阿里云的数据库系统怎么升级更新的www.zgysffm.com怎么加快访问速度?
RT-Thread Studio学习(十二)W25Q128(SPI)的读写
通过GBase 8c Platform安装数据库集群时报错
华为设备配置VRRP与路由联动监视上行链路
线程的状态
【论文笔记】Dynamic Convolution: Attention over Convolution Kernels
金仓数据库KingbaseES客户端编程接口指南-JDBC(9. JDBC 读写分离)
【高并发基石】多线程、守护线程、线程安全、线程同步、互斥锁
js-第一个出现两次的字母
技术实现 | 图像检索及其在淘宝的应用
Recommend several methods that can directly translate PDF English documents
金仓数据库KingbaseES客户端编程接口指南-JDBC(6. JDBC 大对象数据处理)
recursive thinking
图的基本概念
【JS 逆向百例】某网站加速乐 Cookie 混淆逆向详解
Yolov5 replaces the backbone network of "Megvii Lightweight Convolutional Neural Network ShuffleNetv2"
金仓数据库 KDTS 迁移工具使用指南 (6. 注意事项)
力扣 剑指 Offer 04. 二维数组中的查找