基于opengl的2d机器人双人格斗游戏
- 2D 项目简介
项目的创意设想、实现功能、项目意义(200字左右)
作品代表图
项目名:机器人双人格斗游戏
项目的创意设想:当前ai大火,机器人也是日常生活中的一个热点,自己在小学乃至高中也经常玩格斗游戏。曾经也想过格斗游戏是怎么做的,可是那时候条件有限,而且也没有编程基础。这个想法就一直留到了大学。到大三学习计算机图形学,也有了一定的编程基础,认为这个格斗游戏可以自己完成了。便开始动手,经过几天的努力,终于把这个游戏实现了。
实现功能:画了机器人1(乌巢之子),机器人2(关山之月)实现其的技能有:
- 上下左右移动:实现了平移
- 弹跳:使用了重力加速度实现了起跳落下的算法,
- 眼睛发射镭射光线:实现了平移
- 肚载大炮发射:实现了平移
- 手臂可以伸长:实现了平移
- 被炮弹击中会倒退:使用了旋转变换之类函数,实现了旋转
- 法天相地(放大):实现了放大 - 经过一段时间也会缩放到原来的大小
- 计时功能:使用了时间回调函数
- 使用了win32API函数,可以插入声音
项目意义:自己在开始做这个游戏的时候,也上网百度看是否有类似的格斗游戏,这类型指的是使用opengl库制作的2D无游戏引擎的双人格斗游戏,发现找不到类似的游戏,可以说是网上几乎没人做过类似的2D无游戏引擎的双人格斗游戏。可以说这个游戏填补了当类游戏的空白,也可以给后来者加以参考。
作品代表图:
- 编程环境(电脑硬件、操作系统和VS平台)说明
-
电脑硬件:thinkpad x250,
cpu:i3
内存:4g
操作系统:win10
Vs:vs2017
- 程序操作说明
-
操作如下图:
编程环境(电脑硬件、操作系统和VS平台)说明
电脑硬件:thinkpad x250,
cpu:i3
内存:4g
操作系统:win10
Vs:vs2017
- 程序操作说明
操作如下图:
按键b:开始游戏
对角色1(乌巢之子)进行操作有:
1、wsad:上下左右移动
2、z:长拳攻击
3、x:从眼部发射镭射光线
4、c:肚载大炮,肚子发射大炮,攻击力强于镭射光线
5、e:法天相地,变大
6、r:弹跳
对角色2(关山之月)进行操作有:
- ikjl:上下左右移动
- n:长拳攻击
- m:镭射光线
- O:肚载大炮,肚子发射大炮,攻击力强于镭射光线
- U:法天相地(变大)
- P:弹跳
当两者的某一方血条为0时,游戏结束,效果如下:
- 程序实现
- 创意详细说明:
- 算法类、模拟类: 算法说明,想要实现的效果说明
1、注册这几个函数,才可以使窗口不断回画,并计时,也是基于这几个注册函数才仿佛实现了同步,而不必使用多线程。
glutTimerFunc(1000, mytime, 10); //注册闲时函数
glutDisplayFunc(&Display);//在程序运行时是自动调用的,即程序会自动调用display函数重绘窗口
glutMainLoop(); //进入事件处理循环
2、使用了重力加速度公式设计的弹跳-起跳落下的模拟
在机器人1里写了一个弹跳函数(机器人2类似)
类成员:
jumpTime = 0;
jflag = 0;
h = 5.0;
函数
void Robot1::skillr() //跳高
{
jumpTime++;
float g = 0.00005;
if (jflag == 1)
{
float addh = 1.0 / 2.0 * g * jumpTime * jumpTime;
this->y += addh;
this->h -= addh;
if (this->h <= 0)
{
jflag = 2;
jumpTime = 0;
}
}
else if(jflag == 2)
{
float addh = 1.0 / 2.0 * g * jumpTime * jumpTime;
this->y -= addh;
this->h += addh;
if (this->h >= 5.0)
{
jflag = 0;
jumpTime = 0;
}
}
}
主页面交互(键盘点击):
case 'R':
case 'r'://robot1跳高
if (rb1->jflag == 0)
{
rb1->jflag = 1;
rb1->skillr();
}
break;
- 炸弹类的设计:设计炸弹类首先要知道炸弹的坐标x,y,因为这个才可以在界面上绘出炸弹,炸弹的朝向goAhead,有了这个才可以知道炸弹的移动方向,炸弹生命live是否存活,存活才画此炸弹,
Who(谁的炸弹),因为这个炸弹类不止一个人使用,robot1和robot2都使用,zy第三参数,这个参数的作用是记录发送炸弹的机器人的中心坐标
注:在这里说明一下,因为当前游戏是使用2D制作,两个机器人在一条有长有宽的道路上打斗,如果只有长,就只要直接有炸弹的x,y和敌方机器人作边界检测就行了,可是这道路有宽,就要设计看炸弹射出的轨迹是否与敌人在一条轴线上,所以我用zy保存发出炸弹机器人的y坐标,引入了第三个参数,这时就可以把这个界面看出了三维,到时再使用敌方机器人的x,y坐标和己方炸弹的x,zy作边界检测,就可以看炸弹是否击中敌人了。
#pragma once
#pragma once
#include <glut.h>
//robot1子弹类
class myBullet
{
public:
float x;
float y;
bool live;
int goAhead; //子弹朝向
float mBsize; //子弹大小
int who; //谁的子弹
float zy; //第三参数
float zx; //第四参数
myBullet();
~myBullet();
void init(float x,float y,float size,int who, float zx, float zy);
void die();
};
void drawMyBullet(float x, float y, int goAhead, int size, int mBsize)
{
//绘制点
glColor3f(1, 0, 0); //设置蓝色绘制颜色
//glPointSize(2.0);//点的像素大小,默认值为1.0
if (goAhead == 0)
{
glRectf(x - 1 * size, y - 0.05 * size * mBsize, x, y + 0.05 * size * mBsize);
}
else if (goAhead == 1)
{
glRectf(x, y - 0.05 * size * mBsize, x + 1 * size, y + 0.05 * size * mBsize);
}
//glBegin(GL_POINTS);
//glVertex2f(x, y);
//glVertex2f(x+1, y+0.1);
//glEnd();
}
myBullet::myBullet()
{
x = -23;
y = 23;
live = false;
goAhead = -1;
mBsize = 1;
who = -1;
zx = -99;
zy = -99;
}
void myBullet::init(float x, float y, float size,int who,float zx, float zy)
{
this->x = x;
this->y = y;
this->mBsize = size;
live = true;
this->who = who;
this->zx = zx;
this->zy = zy;
}
myBullet::~myBullet()
{
}
void myBullet::die()
{
live = false;
}
炸弹是否击中敌人的边界检测函数:
//子弹边界检测函数
void checkBulletSide()
{
for (int i = 0; i < MAXRB1BULLET; i++)
{
if (mb[i].live == true)
{
if (mb[i].x > -22.0 && mb[i].x < 22.0 && mb[i].y > -12.0 && mb[i].y < 12.0)
{
if (mb[i].who == 1) //机器人1的子弹
{
if (abs(mb[i].x - rb2->x) < 0.6 * rb2->size && abs(mb[i].zy - rb2->y) < 0.7 * rb2->size) //机器人1的子弹击中机器人2
{
for (int j = 0; j < MAXBOMB; j++)
{
if (bomb[j].live == false)
{
bomb[j].init(mb[i].x, mb[i].y, mb[i].mBsize);
rb2->valueOfLife -= 20 * mb[i].mBsize;
if (mb[i].mBsize == 1)
{
PlaySound(L"midb.wav", NULL, SND_FILENAME | SND_ASYNC);
//PlaySound(L"background.wav", NULL, SND_FILENAME | SND_ASYNC);
}
else
{
PlaySound(L"bigb.wav", NULL, SND_FILENAME | SND_ASYNC);
//PlaySound(L"background.wav", NULL, SND_FILENAME | SND_ASYNC);
}
break;
}
}
if (mb[i].goAhead == 0) //子弹朝向左
{
//机器人robot2被左击中
rb2->beated = -1;
}
else if(mb[i].goAhead == 1)//子弹朝向右
{
//机器人robot2被右击中
rb2->beated = 1;
}
mb[i].die();
}
}
else if (mb[i].who == 2)//机器人2的子弹
{
if (abs(mb[i].x - rb1->x) < 0.6 * rb1->size && abs(mb[i].zy - rb1->y) < 0.7 * rb1->size) //机器人2的子弹击中机器人1
{
for (int j = 0; j < MAXBOMB; j++)
{
if (bomb[j].live == false)
{
bomb[j].init(mb[i].x, mb[i].y, mb[i].mBsize);
rb1->valueOfLife -= 20 * mb[i].mBsize;
if (mb[i].mBsize == 1)
{
PlaySound(L"midb.wav", NULL, SND_FILENAME | SND_ASYNC);
//PlaySound(L"background.wav", NULL, SND_FILENAME | SND_ASYNC);
}
else
{
PlaySound(L"bigb.wav", NULL, SND_FILENAME | SND_ASYNC);
//PlaySound(L"background.wav", NULL, SND_FILENAME | SND_ASYNC);
}
break;
}
}
if (mb[i].goAhead == 0) //子弹朝向左
{
//机器人robot1被左击中
rb1->beated = -1;
}
else if (mb[i].goAhead == 1)//子弹朝向右
{
//机器人robot1被右击中
rb1->beated = 1;
}
mb[i].die();
}
}
}
else //子弹已到边界
{
mb[i].live = false;
}
}
}
}
- 炸弹爆炸类的设计:x,y确定爆炸的位置,size确定爆炸的范围,强度,live是否存活,livelong存活的时间
#pragma once
#include <glut.h>
#include <math.h>
#include<stdlib.h>
#define MAXPOINTSIZE 200
class Bomb
{
public:
float x;
float y;
float size;
bool live;
int liveLong;
Bomb()
{
x = -99;
y = -99;
size = 1;
live = false;
liveLong = 40;
}
void init(float x, float y, float size)
{
this->x = x;
this->y = y;
this->size = size;
live = true;
liveLong = 160;
}
void setLiveLong(int liveLong)
{
this->liveLong = liveLong;
}
void die()
{
live = false;
}
};
通过以下的四个函数,第一个是计算炸弹爆炸的存活时间,放置在display函数中,每刷新一次,存活时间减少,达到了可以使爆炸场景在界面停留,检测爆炸场景是否描绘函数不断刷新,当检测到存在爆炸,则调用爆炸场景函数,爆炸场景函数使用rand函数,在一定的范围内画多个红点,起到了爆炸时产生的火花作用。
//将爆炸效果存在时间逐步减少至死亡
void cutBoomLifeToDie()
{
for (int i = 0; i < MAXBOMB; i++)
{
if (bomb[i].live == true)
{
bomb[i].liveLong--;
if (bomb[i].liveLong <= 0)
{
bomb[i].live = false;
}
}
}
}
//检测炸弹场景是否描绘函数
void boomView()
{
for (int i = 0; i < MAXBOMB; i++)
{
if (bomb[i].live == true)
{
drawBomb(bomb[i].x, bomb[i].y, bomb[i].size);
}
}
}
//爆炸场景函数
void drawBomb(float x, float y,float size)
{
for (int i = 0; i < MAXPOINT; i++)
{
float tx;
float ty;
if (i < (MAXPOINT) / 4)
{
tx = rand() % 10 * 0.02 * size + x;
ty = rand() % 10 * 0.02 * size + y;
}
else if(i >= (MAXPOINT) / 4 && i < (MAXPOINT) / 2)
{
tx = rand() % 10 * 0.02 * (-1.0) * size + x;
ty = rand() % 10 * 0.02 * (-1.0) * size + y;
}
else if (i >= (MAXPOINT) / 2 && i < (MAXPOINT) / 4 * 3)
{
tx = rand() % 10 * 0.02 * size + x;
ty = rand() % 10 * 0.02 * size * (-1.0) + y;
}
else
{
tx = rand() % 10 * 0.02 * (-1.0) * size + x;
ty = rand() % 10 * 0.02 * size + y;
}
drawPoint(tx, ty);
}
}
//绘制点函数
void drawPoint(float x, float y)
{
//绘制点
glColor3f(1, 0, 0); //设置红色绘制颜色
glPointSize(2.0);//点的像素大小,默认值为1.0
glBegin(GL_POINTS);
glVertex2f(x, y);
glVertex2f(x + 0.1, y + 0.1);
glEnd();
}
- 房子类设计:(功能简单,不做解释)
#pragma once
#pragma once
//#include "stdafx.h"
#include <glut.h>
#include <math.h>
#define PI 3.14159
//梯形
void Trapezoid(double x, double y, double h, double w1, double w2)
{
glBegin(GL_POLYGON); //开始绘制梯边形
glVertex2f(-w1 / 2 + x, h + y);
glVertex2f(w1 / 2 + x, h + y);
glVertex2f(w2 / 2 + x, y);
glVertex2f(-w2 / 2 + x, y);
glEnd();
}
//长方形
void Rectangle(double x, double y, double h, double w)
{
glBegin(GL_POLYGON); //绘制长方形
glVertex2f(-w / 2 + x, h + y);
glVertex2f(w / 2 + x, h + y);
glVertex2f(w / 2 + x, y);
glVertex2f(-w / 2 + x, y);
glEnd();
}
//长方形,中心点长宽型
void RectangleCen(double x, double y, double h, double w)
{
glBegin(GL_POLYGON); //绘制长方形
glVertex2f(-w / 2 + x, h / 2 + y);
glVertex2f(w / 2 + x, h / 2 + y);
glVertex2f(w / 2 + x, y - h / 2);
glVertex2f(-w / 2 + x, y - h / 2);
glEnd();
}
//圆形
void Circle(double x, double y, double r)
{
glBegin(GL_POLYGON);
int n = 100;
for (int i = 0; i < n; i++)
glVertex2f(x + r * cos(i * 2 * PI / n), y + r * sin(i * 2 * PI / n));
glEnd();
}
class House
{
private:
//所在地图的位置
int x;
int y;
double n;//缩放倍率
public:
House(int x, int y);
House(int x, int y, double n);
void DrawHouse();
void DrawHouse(int x, int y);
void SetPosition(int x, int y);
void drawRoof();
void drawBody();
void drawInterlayer();
void drawSinWindow(int x1, int y1, double h1, double w1);
void drawWindow();
void drawRailing();
void drawDoor(int x, int y);
};
House::House(int x, int y) :x(x), y(y) { n = 0.01; }
House::House(int x, int y, double n) : x(x), y(y), n(n) {}
void House::DrawHouse()
{
glColor3f(1, 1, 1);
//房顶
drawRoof();
//房身
drawBody();
//夹层
drawInterlayer();
//窗户
drawWindow();
//画门
drawDoor(x + 325 * n, y + 160 * n);
//装饰栏杆之类
drawRailing();
//地面
glColor3f(0.51, 0.831, 0.05);
glRectf(x, y + 70 * n, x + 780 * n, y + 100 * n);
glColor3f(0.45, 0.262, 0.207);
glRectf(x, y, x + 780 * n, y + 70 * n);
}
void House::DrawHouse(int x, int y)
{
int x1 = this->x, y1 = this->y;
this->x = x;
this->y = y;
glColor3f(1, 1, 1);
drawRoof();
drawBody();
drawInterlayer();
drawWindow();
drawDoor(x + 325 * n, y + 160 * n);
drawRailing();
glColor3f(0.51, 0.831, 0.05);
glRectf(x, y + 70 * n, x + 780 * n, y + 100 * n);
glColor3f(0.45, 0.262, 0.207);
glRectf(x, y, x + 780 * n, y + 70 * n);
this->x = x1;
this->y = y1;
}
//房顶
void House::drawRoof()
{
//烟囱
glColor3f(0.725, 0.372, 0.372);
glRectf(x + 160 * n, y + 690 * n, x + 240 * n, y + 940 * n);
glColor3f(0.827, 0.552, 0.56);
glRectf(x + 145 * n, y + 940 * n, x + 255 * n, y + 970 * n);
//底顶
glColor3f(0.364, 0.627, 0.741);
glBegin(GL_POLYGON); //开始绘制梯边形
glVertex2f(x + 0, y + 690 * n);
glVertex2f(x + 390 * n, y + 900 * n);
glVertex2f(x + 780 * n, y + 690 * n);
glEnd();
//底顶花纹
glColor3f(0.301, 0.537, 0.639);
for (int i = 690, j = 0; i < 900; i += 42, j += 780 / 5 / 2)
{
//glRectf(x +j, y + i, x + 780-j, y + i+15);
glBegin(GL_POLYGON);
glVertex2f(x + j * n, y + i * n);
glVertex2f(x + 780 * n - j * n, y + i * n);
glVertex2f(x + 780 * n - j * n - 30 * n, y + i * n + 15 * n);
glVertex2f(x + j * n + 30 * n, y + i * n + 15 * n);
glEnd();
}
//圆窗户
/*glColor3f(1, 1, 1);
Circle(x + 390 * n, y + 780 * n, 50 * n);
glColor3f(0.25, 0.4, 0.482);
Circle(x + 390 * n, y + 780 * n, 35 * n);
glColor3f(1, 1, 1);
Rectangle(x + 390 * n, y + 780 * n, 10 * n, 80 * n);
Rectangle(x + 390 * n, y + 780 * n, 80 * n, 10 * n);*/
glColor3f(0.545, 0.27, 0.074); //棕色
glRectf(x + 290 * n, y + 740 * n, x + 490 * n, y + 820 * n);
}
//房身
void House::drawBody()
{
//底色
glColor3f(0.737, 0.49, 0.423);
glRectf(x + 70 * n, y + 160 * n, x + 710 * n, y + 690 * n);
//条纹
glColor3f(0.627, 0.423, 0.36);
for (int i = 690; i >= 510; i -= 60)
{
glRectf(x + 70 * n, y + i * n, x + 710 * n, y + i * n - 20 * n);
}
for (int i = 400; i >= 160; i -= 60)
{
glRectf(x + 70 * n, y + i * n, x + 710 * n, y + i * n - 20 * n);
}
}
//夹层
void House::drawInterlayer()
{
glColor3f(1, 1, 1);
glRectf(x + 35 * n, y + 480 * n, x + 745 * n, y + 510 * n);
glRectf(x, y + 400 * n, x + 780 * n, y + 430 * n);
glColor3f(0.364, 0.627, 0.741);
glBegin(GL_POLYGON); //梯边
glVertex2f(x + 0, y + 430 * n);
glVertex2f(x + 35 * n, y + 480 * n);
glVertex2f(x + 745 * n, y + 480 * n);
glVertex2f(x + 780 * n, y + 430 * n);
glEnd();
//花纹
glColor3f(0.301, 0.537, 0.639);
glBegin(GL_POLYGON);
glVertex2f(x + 35 / 2 * n, y + 430 * n + 50 / 2 * n);
glVertex2f(x + 35 * n, y + 480 * n);
glVertex2f(x + 745 * n, y + 480 * n);
glVertex2f(x + 780 * n - 35 / 2 * n, y + 430 * n + 50 / 2 * n);
glEnd();
}
//单个窗户
void House::drawSinWindow(int x1, int y1, double h1, double w1)
{
glColor3f(1, 1, 1);
glRectf(x1, y1, x1 + w1, y1 + h1);
glColor3f(0.47, 0.321, 0.309);
glRectf(x1 + w1 / 8, y1 + h1 / 9, x1 + w1 - w1 / 8, y1 + h1 / 9 * 4);
glRectf(x1 + w1 / 8, y1 + h1 / 9 * 5, x1 + w1 - w1 / 8, y1 + h1 / 9 * 8);
}
//画四个窗户
void House::drawWindow()
{
drawSinWindow(x + 220 * n, y + 530 * n, 140 * n, 90 * n);
drawSinWindow(x + 460 * n, y + 530 * n, 140 * n, 90 * n);
drawSinWindow(x + 140 * n, y + 220 * n, 160 * n, 110 * n);
drawSinWindow(x + 530 * n, y + 220 * n, 160 * n, 110 * n);
}
//栏杆之类
void House::drawRailing()
{
//扶手
glColor3f(1, 1, 1);
glRectf(x + 15 * n, y + 160 * n, x + 35 * n, y + 400 * n);
glRectf(x + 745 * n, y + 160 * n, x + 765 * n, y + 400 * n);
for (int i = 15; i <= 300; i += 60)
{
glRectf(x + i * n, y + 160 * n, x + i * n + 20 * n, y + 210 * n);
}
for (int i = 505; i <= 745; i += 60)
{
glRectf(x + i * n, y + 160 * n, x + i * n + 20 * n, y + 210 * n);
}
//地基
glRectf(x + 15 * n, y + 100 * n, x + 765 * n, y + 160 * n);
glColor3f(0.75, 0.75, 0.75);
glRectf(x, y + 200 * n, x + 280 * n, y + 220 * n);
glRectf(x + 500 * n, y + 200 * n, x + 780 * n, y + 220 * n);
glRectf(x, y + 140 * n, x + 280 * n, y + 160 * n);
glRectf(x + 500 * n, y + 140 * n, x + 780 * n, y + 160 * n);
glRectf(x + 280 * n, y + 100 * n, x + 505 * n, y + 120 * n);
glRectf(x + 280 * n, y + 120 * n, x + 505 * n, y + 140 * n);
glColor3f(0.6, 0.6, 0.6);
glRectf(x + 280 * n, y + 153 * n, x + 500 * n, y + 160 * n);
glRectf(x + 280 * n, y + 113 * n, x + 505 * n, y + 120 * n);
glRectf(x + 280 * n, y + 133 * n, x + 505 * n, y + 140 * n);
}
//画门
void House::drawDoor(int x, int y)
{
glColor3f(1, 1, 1);
glRectf(x, y, x + 130 * n, y + 220 * n);
glColor3f(0.47, 0.321, 0.309);
glRectf(x + 10 * n, y + 190 * n, x + 120 * n, y + 210 * n);
glRectf(x + 10 * n, y + 100 * n, x + 60 * n, y + 180 * n);
glRectf(x + 70 * n, y + 100 * n, x + 120 * n, y + 180 * n);
glColor3f(0.9, 0.9, 0.9);
glRectf(x + 10 * n, y + 10 * n, x + 60 * n, y + 85 * n);
glRectf(x + 70 * n, y + 10 * n, x + 120 * n, y + 85 * n);
glColor3f(1, 0.803, 0.341);
Circle(x + 10 * n, y + 95 * n, 5 * n);
}
//改位置
void House::SetPosition(int x, int y)
{
this->x += x;
this->y += y;
}
- 树类的设计:(功能简单,不做解释)
#pragma once
#include <glut.h>
class Tree
{
public:
float x;
float y;
float size;
Tree()
{
x = 0;
y = 0;
size = 1;
}
Tree(float x, float y, float size)
{
this->x = x;
this->y = y;
this->size = size;
}
void drawTree()
{
glColor3f(0.545, 0.27, 0.074); //棕色
glRectf(x, y, x + 0.25 * size, y + 1.5 * size);
glColor3f(0, 0.39, 0); //绿色
glRectf(x - 0.4 * size, y + 1.5 * size, x + 0.65 * size, y + 2.5 * size);
}
};
7、路类的设计:功能简单,不做解释
#pragma once
#include <glut.h>
class Road
{
public:
float x;
float y;
float size;
Road()
{
x = 0;
y = 0;
size = 1;
}
Road(float x, float y, float size)
{
this->x = x;
this->y = y;
this->size = size;
}
void drawRoad()
{
float len = 6.0;
glColor3f(1, 1, 1);
glRectf(x, y, x + 44.0*size, y + 0.5 * size);//下边路线
glRectf(x, y + 4.0 * size, x + 44.0*size, y + 4.5 * size);//上边路线
for (int i = 0; i < 10; i++)
{
glRectf(x + i * size * len, y + 2.0 * size, x + (i*len + 4.0)*size, y + 2.5 * size);//下边路线
}
}
};
机器人1类设计(机器人2类基本类似,不做解释):
1、x,y确定机器人的大小,
2、size确定机器人的尺寸,通过size可以达到机器人的放大缩小变换,这对应了机器人的放大缩小技能;
3、live 确定机器人是否存活的函数,不存活,则机器人消失。
4、LeftArmlong和rightArmLong可以通过改变这两个类成员以达到机器人的手伸缩功能。Angle,轮子线框的角度参数,通过改变它可以感觉轮子在移动,
5、goAhead通过这个函数可以知道机器人的朝向,也可以通过它知道机器人伸手攻击和子弹发射的方向。lflag和rflag 对机器人的伸缩手进行了辅助作用,
6、xle,xre,yle,yre确定机器人眼睛的位置,这也是眼镭射光发射的位置。
7、Valueoflife,生命值通过这个参数,可以达到显示血条的多少,也可以判断机器人是否存活。
8、xlefthand、xrighthand,ylefthand、yrighthand这几个成员变量确定手的端点,通过这几个参数来判断是否对别人进行了物理攻击。
9、flag:起到辅助控制放大的作用
10、Biglong成员变量来控制变大的时间。
11、Beated成员变量判断该机器人是否被打击,如果被炸弹击中,设计机器人晃动。
12、Rangle:控制机器人翻滚的角度。
13、Junptime设置挑起的时间,
14、jflag辅助机器人的弹跳控制,
15、h:机器人跳起的高度
#pragma once
#pragma once
#include <math.h>
#include <glut.h>
#include "myBullet.h"
#define PI 3.14159
#define MAXRB1BULLET 250
myBullet* mb = new myBullet[MAXRB1BULLET]; //初始化50个镭射光
class Robot1
{
public:
//机器人一重心中心点
float x;
float y;
float size;
bool live;
float leftArmLong;
float rightArmLong;
int angle;
int goAhead; //0 - 向左,1 - 向右
int lflag;
int rflag;
float xle;
float xre;
float yle;
float yre;
int valueOfLife;
float xLeftHand;//左手端点
float yLeftHand;
float xRightHand;//右手端点
float yRightHand;
int flag;
int bigLong = 10000;
int beated;
float rangle;
int jumpTime;
int jflag;
float h;
Robot1()
{
x = -18;
y = -6;
size = 1;
live = true;
leftArmLong = 1;
rightArmLong = 1;
angle = 170;
goAhead = 1;
lflag = 0;
rflag = 0;
float x1 = this->x;
float y1 = this->y + 1.1 * size;
this->xle = x1 - 0.15 * size;
this->xre = x1 + 0.1 * size;
this->yle = y1;
this->yre = y1;
valueOfLife = 1000;
flag = 0;
beated = 0;
rangle = 0;
jumpTime = 0;
jflag = 0;
h = 5.0;
}
Robot1(float x, float y,int valueOfLife)
{
this->x = x;
this->y = y;
live = true;
leftArmLong = 1;
rightArmLong = 1;
angle = 90;
goAhead = 1;
lflag = 0;
rflag = 0;
this->valueOfLife = valueOfLife;
float x1 = this->x;
float y1 = this->y + 1.1 * size;
this->xle = x1 - 0.15 * size;
this->xre = x1 + 0.1 * size;
this->yle = y1;
this->yre = y1;
flag = 0;
beated = 0;
rangle = 0;
jumpTime = 0;
jflag = 0;
h = 5.0 ;
}
void wheel();//轮子
void body(); //身躯
void head(); //头
void leftArm(); //左臂
void rightArm(); //右臂
void leftHand(); //左手
void rightHand(); //右手
void move(float addx, float addy);//移动函数
void die();
void skillz();
void skillx();
void skillc();
void skille();
void skillq();
void skillr(); //跳跃函数
void checkValueOfLife();
void cutBigLong();
};
void Robot1::skillr() //跳高
{
jumpTime++;
float g = 0.00005;
if (jflag == 1)
{
float addh = 1.0 / 2.0 * g * jumpTime * jumpTime;
this->y += addh;
this->h -= addh;
if (this->h <= 0)
{
jflag = 2;
jumpTime = 0;
}
}
else if(jflag == 2)
{
float addh = 1.0 / 2.0 * g * jumpTime * jumpTime;
this->y -= addh;
this->h += addh;
if (this->h >= 5.0)
{
jflag = 0;
jumpTime = 0;
}
}
}
void Robot1::cutBigLong()
{
if (this->size > 1)
{
this->bigLong--;
if (this->bigLong == 0)
{
this->size = 1;
this->y -= 2.3 * 0.5; //机器人中心点升高
}
}
}
void Robot1::checkValueOfLife()
{
if (this->valueOfLife <= 0)
{
this->live = false;
}
}
void Robot1::move(float addx, float addy)
{
if (this->size == 1)
{
if (this->x + addx <= 20.0 && this->x + addx >= -20.0 && this->y + addy <= -3.0 && this->y + addy >= -8.0)//判断是否在路上
{
this->x += addx;
this->y += addy;
}
}
else if (this->size > 1)
{
if ((this->x + addx <= 20.0 )&& (this->x + addx >= -20.0) && (this->y - 2.3 * 0.5 + addy <= -3.0) && (this->y - 2.3 * 0.5 + addy )>= -8.0)//判断是否在路上
{
this->x += addx;
this->y += addy;
}
}
if (addx > 0)
{
angle -= 30;
if (angle >= 360)
{
angle += 360;
}
goAhead = 1;
}
else if (addx < 0)
{
angle += 30;
if (angle <= 0)
{
angle -= 360;
}
goAhead = 0;
}
float x1 = this->x;
float y1 = this->y + 1.1 * size;
this->xle = x1 - 0.15 * size;
this->xre = x1 + 0.1 * size;
this->yle = y1;
this->yre = y1;
}
void Robot1::die()
{
live = false;
}
void Robot1::skillz() //伸长手攻击
{
if (goAhead == 0) //朝向左
{
if (lflag == 0 && leftArmLong <= 20)
{
leftArmLong += 0.4;
}
if (leftArmLong > 20 || lflag == 1)
{
leftArmLong -= 0.4;
lflag = 1;
if (leftArmLong <= 1)
{
lflag = 0;
}
}
}
else if (goAhead == 1) //朝向右
{
if (rflag == 0 && rightArmLong <= 20)
{
rightArmLong += 0.4;
}
if (rightArmLong > 20 || rflag == 1)
{
rightArmLong -= 0.4;
rflag = 1;
if (rightArmLong <= 1)
{
rflag = 0;
}
}
}
}
void Robot1::skillx()
{
//float x1 = this->x;
//float y1 = this->y + 1.1 * size;
/*xle = x1 - 0.15 * size;
xre = x1 + 0.1 * size;
yle = y1;
yre = y1;*/
for (int i = 0; i < MAXRB1BULLET; i++)
{
if (mb[i].live == false)
{
if (goAhead == 0) //向左
{
mb[i].init(this->xre, this->yre, 1,1,this->x, this->y);
mb[i].live = true;
mb[i].goAhead = 0;
break;
}
else if (goAhead == 1) //向右
{
mb[i].init(this->xle, this->yle, 1,1, this->x, this->y);
mb[i].live = true;
mb[i].goAhead = 1;
break;
}
}
}
}
void Robot1::skillc()
{
for (int i = 0; i < MAXRB1BULLET; i++)
{
if (mb[i].live == false)
{
if (goAhead == 0) //向左
{
mb[i].init(this->x, this->y, 4,1, this->x, this->y);
mb[i].live = true;
mb[i].goAhead = 0;
break;
}
else if (goAhead == 1) //向右
{
mb[i].init(this->x, this->y, 4,1, this->x, this->y);
mb[i].live = true;
mb[i].goAhead = 1;
break;
}
}
}
}
void Robot1::skille()
{
this->size *= 1.5;
this->y += 2.3 * 0.5; //机器人中心点升高
this->valueOfLife += 400;
if (valueOfLife >= 1000)
{
valueOfLife = 1000;
}
}
void Robot1::skillq()
{
}
void Robot1::head() //头
{
float x1 = x;
float y1 = y + 1.1 * size;
float R = 0.08 * size;
int n = 100;
this->xle = x1 - 0.15 * size;
this->xre = x1 + 0.1 * size;
this->yle = y1;
this->yre = y1;
//画矩形
glColor4f(1, 0, 1, 2);
glRectf(x1 - 0.3*size, y1 - 0.3*size, x1 + 0.3*size, y1 + 0.3*size);
//画左眼睛
//画小圆
glColor3f(1, 0, 0);
glBegin(GL_POLYGON);
for (int i = 0; i<n; i++)
{
glVertex2f(xle + R*cos(2 * PI*i / n), yle + R*sin(2 * PI*i / n)); //定义顶点
}
glEnd();
//画右眼睛
//画小圆
glColor3f(1, 0, 0);
glBegin(GL_POLYGON);
for (int i = 0; i<n; i++)
{
glVertex2f(xre + R*cos(2 * PI*i / n), yre + R*sin(2 * PI*i / n)); //定义顶点
}
glEnd();
}
void Robot1::leftArm()//左臂
{
float x1 = x - 0.8 * size;
//float x2 = x + 0.8 * size;
float y1 = y;
//float y2 = y;
//画手臂
glColor3f(1, 1, 1);
glRectf(x1 - 0.3 * leftArmLong * size, y1 - 0.2 * size, x - 0.8 * size, y + 0.2 * size);
}
void Robot1::rightArm() //右臂
{
float x1 = x + 0.8 * size;
//float x2 = x + 0.8 * size;
float y1 = y;
//float y2 = y;
//画手臂
glColor3f(1, 1, 1);
glRectf(x1 + 0.3 * rightArmLong * size, y1 - 0.2 * size, x + 0.8 * size, y + 0.2 * size);
}
void Robot1::leftHand()//左手
{
float x1 = x - (0.8 * size + 0.3 * leftArmLong * size);
float y1 = y;
this->xLeftHand = x1 - 0.5 * size;
this->yLeftHand = y1;
glColor3f(1, 1, 0);
glBegin(GL_TRIANGLES);
glVertex2f(x1, y1 + 0.5 * size);
glVertex2f(x1, y1 - 0.5 * size);
glVertex2f(x1 - 0.5 * size, y1);
glEnd();
}
void Robot1::rightHand()//右手
{
float x1 = x + (0.8 * size + 0.3 * rightArmLong * size);
float y1 = y;
this->xRightHand = x1 + 0.5 * size;
this->yRightHand = y1;
glColor3f(1, 1, 0);
glBegin(GL_TRIANGLES);
glVertex2f(x1, y1 + 0.5 * size);
glVertex2f(x1, y1 - 0.5 * size);
glVertex2f(x1 + 0.5 * size, y1);
glEnd();
}
void Robot1::body()
{
//画矩形
glColor4f(0, 1, 1, 1);
glRectf(x - 0.8*size, y - 0.8*size, x + 0.8*size, y + 0.8*size);
//画矩形
glColor4f(1, 1, 0, 1);
glRectf(x - 0.3*size, y - 0.3*size, x + 0.3*size, y + 0.3*size);
}
void Robot1::wheel()//轮子
{
float xw = x;
float yw = y - 1.5 * size;
float R = 0.7 * size;
float R2 = 0.2 * size;
int n = 100;
//画圆
glColor3f(0, 0, 1);
glBegin(GL_POLYGON);
for (int i = 0; i<n; i++)
{
glVertex2f(xw + R*cos(2 * PI*i / n), yw + R*sin(2 * PI*i / n)); //定义顶点
}
glEnd();
//画直线
glColor3f(1, 1, 0);
GLfloat curSizeLine = 2;
glLineWidth(curSizeLine);
glBegin(GL_LINES);
glVertex3f(xw, yw, 0);
glVertex3f(xw + R*cos(2 * PI * angle / 360), yw + R*sin(2 * PI * angle / 360), 0);
glEnd();
//画车轮支架
glColor3f(0, 1, 1);
glRectf(xw - 0.1 * size, yw, xw, yw + 0.7 * size);
//画小圆
glColor3f(1, 0, 0);
glBegin(GL_POLYGON);
for (int i = 0; i<n; i++)
{
glVertex2f(xw + R2*cos(2 * PI*i / n), yw + R2*sin(2 * PI*i / n)); //定义顶点
}
glEnd();
}
使用valueoflife来达到血条的设置作用
void rb1BroodStrip()
{
float maxBrood = 1000;
float temp = rb1->valueOfLife / maxBrood;
if (temp <= 0)
{
temp = 0;
}
glColor3f(1, 1, 1);
glRectf(-20.0, 10.0, -5.0, 10.2);
glColor3f(1, 0, 0);
glRectf(-20.0, 10.0, -20.0 + 15.0 * temp, 10.2);
}
void rb2BroodStrip()
{
float maxBrood = 1000;
float temp = rb2->valueOfLife / maxBrood;
if (temp <= 0)
{
temp = 0;
}
glColor3f(1, 1, 1);
glRectf(20.0, 10.0, 5.0, 10.2);
glColor3f(1, 0, 0);
glRectf(20.0, 10.0, 20.0 - 15.0 * temp, 10.2);
}
触手击中敌人边界检测函数
//触手击中敌人函数检测函数
void checkTentacleHitEnemy()
{
if (rb1->leftArmLong > 1 && abs(rb1->xLeftHand - rb2->x) < 0.6 * rb2->size && abs(rb1->yLeftHand - rb2->y) < 0.7 * rb2->size) //robot1左手击中敌人
{
for (int j = 0; j < MAXBOMB; j++)
{
if (bomb[j].live == false)
{
bomb[j].init(rb1->xLeftHand, rb1->yLeftHand, rb1->size);
rb2->valueOfLife -= 1 * rb1->size;
break;
}
}
PlaySound(L"smallb.wav", NULL, SND_FILENAME | SND_ASYNC);
}
if (rb1->rightArmLong > 1 && abs(rb1->xRightHand - rb2->x) < 0.6 * rb2->size && abs(rb1->yRightHand - rb2->y) < 0.7 * rb2->size) //robot1右手击中敌人
{
for (int j = 0; j < MAXBOMB; j++)
{
if (bomb[j].live == false)
{
bomb[j].init(rb1-> xRightHand, rb1->yRightHand, rb1->size);
rb2->valueOfLife -= 1 * rb1->size;
break;
}
}
PlaySound(L"smallb.wav", NULL, SND_FILENAME | SND_ASYNC);
}
if (rb2->leftArmLong > 1 && abs(rb2->xLeftUpHand - rb1->x) < 0.6 * rb1->size && abs(rb2->yLeftUpHand - rb1->y) < 0.7 * rb1->size) //robot2左手上角击中敌人
{
for (int j = 0; j < MAXBOMB; j++)
{
if (bomb[j].live == false)
{
bomb[j].init(rb2->xLeftUpHand, rb2->yLeftUpHand, rb2->size);
rb1->valueOfLife -= 1 * rb2->size;
break;
}
}
//PlaySound(L"smallb.wav", NULL, SND_FILENAME | SND_ASYNC);
}
if (rb2->leftArmLong > 1 && abs(rb2->xLeftDownHand - rb1->x) < 0.6 * rb1->size && abs(rb2->yLeftDownHand - rb1->y) < 0.7 * rb1->size) //robot2左手下角击中敌人
{
for (int j = 0; j < MAXBOMB; j++)
{
if (bomb[j].live == false)
{
bomb[j].init(rb2->xLeftDownHand, rb2->yLeftDownHand, rb2->size);
rb1->valueOfLife -= 1 * rb2->size;
break;
}
}
PlaySound(L"smallb.wav", NULL, SND_FILENAME | SND_ASYNC);
}
if (rb2->rightArmLong > 1 && abs(rb2->xRightUpHand - rb1->x) < 0.6 * rb1->size && abs(rb2->yRightUpHand - rb1->y) < 0.7 * rb1->size) //robot2右手上角击中敌人
{
for (int j = 0; j < MAXBOMB; j++)
{
if (bomb[j].live == false)
{
bomb[j].init(rb2->xRightUpHand, rb2->yRightUpHand, rb2->size);
rb1->valueOfLife -= 1 * rb2->size;
break;
}
}
//PlaySound(L"smallb.wav", NULL, SND_FILENAME | SND_ASYNC);
}
if (rb2->rightArmLong > 1 && abs(rb2->xRightDownHand- rb1->x) < 0.6 * rb1->size && abs(rb2->yRightDownHand - rb1->y) < 0.7 * rb1->size) //robot2右手下角击中敌人
{
for (int j = 0; j < MAXBOMB; j++)
{
if (bomb[j].live == false)
{
bomb[j].init(rb2->xRightDownHand, rb2->yRightDownHand, rb2->size);
rb1->valueOfLife -= 1 * rb2->size;
break;
}
}
PlaySound(L"smallb.wav", NULL, SND_FILENAME | SND_ASYNC);
}
}
//设置字体与中文字的函数,引用了老师以前教过的代码
/************************************************************************/
/* 选择字体函数 */
/************************************************************************/
void selectFont(int size, int charset, const char* face)
{
HFONT hFont = CreateFontA(size, 0, 0, 0, FW_MEDIUM, 0, 0, 0,
charset, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, face);
HFONT hOldFont = (HFONT)SelectObject(wglGetCurrentDC(), hFont);
DeleteObject(hOldFont);
}
/************************************************************************/
/* 生成中文字体函数 */
/************************************************************************/
void drawCNString(const char* str)
{
int len, i;
wchar_t* wstring;
HDC hDC = wglGetCurrentDC();
GLuint list = glGenLists(1);
// 计算字符的个数
// 如果是双字节字符的(比如中文字符),两个字节才算一个字符
// 否则一个字节算一个字符
len = 0;
for (i = 0; str[i] != '\0'; ++i)
{
if (IsDBCSLeadByte(str[i]))
++i;
++len;
}
// 将混合字符转化为宽字符
wstring = (wchar_t*)malloc((len + 1) * sizeof(wchar_t));
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, str, -1, wstring, len);
wstring[len] = L'\0';
// 逐个输出字符
for (i = 0; i < len; ++i)
{
wglUseFontBitmapsW(hDC, wstring[i], 1, list);
glCallList(list);
}
// 回收所有临时资源
free(wstring);
glDeleteLists(list, 1);
}
-
- 互动娱乐类:想要实现的效果说明
2)2D 项目实现流程图或类关系图及文字说明
项目更详细的模块之间的流程图或者类关系图 及其这些图的文字说明
3)OpenGL编程技术
(例如鼠标、键盘、图形变换、双缓存、动画、图形构成,举例说明
鼠标点击事件:
void mykeyboard(unsigned char key, int x, int y)
{
if (Menuflag == 0)
{
switch (key)
{
case 'B':
case 'b':// 矩形对角坐标变量修改使得矩形上移
Menuflag = 1;
//fy += 0.3;
break;
}
}
else if (Menuflag == 1)
{
switch (key)
{
case 'W':
case 'w':// 矩形对角坐标变量修改使得矩形上移
rb1->move(0, 0.3);
//fy += 0.3;
break;
case 'S':
case 's'://矩形对角坐标变量修改使得矩形下移
//fy -= 0.3;
rb1->move(0, -0.3);
break;
case 'A':
case 'a'://矩形对角坐标变量修改使得矩形左移
//fx -= 0.3;
rb1->move(-0.3, 0);
break;
case 'D':
case 'd'://矩形对角坐标变量修改使得矩形右移
//fx += 0.3;
rb1->move(0.3, 0);
break;
case 'Z':
case 'z'://伸手攻击
rb1->skillz();
break;
case 'X':
case 'x'://robot1镭射光攻击
rb1->skillx();
break;
case 'C':
case 'c'://robot1肚载炸弹攻击
rb1->skillc();
break;
case 'E':
case 'e'://robot1放大
if (rb1->flag == 0)
{
rb1->skille();
rb1->flag = 1;
}
break;
case 'R':
case 'r'://robot1跳高
if (rb1->jflag == 0)
{
rb1->jflag = 1;
rb1->skillr();
}
break;
case 'I':
case 'i':// 矩形对角坐标变量修改使得矩形上移
rb2->move(0, 0.3);
//fy += 0.3;
break;
case 'K':
case 'k'://矩形对角坐标变量修改使得矩形下移
//fy -= 0.3;
rb2->move(0, -0.3);
break;
case 'J':
case 'j'://矩形对角坐标变量修改使得矩形左移
//fx -= 0.3;
rb2->move(-0.3, 0);
break;
case 'L':
case 'l'://矩形对角坐标变量修改使得矩形右移
//fx += 0.3;
rb2->move(0.3, 0);
break;
case 'U':
case 'u'://放大
if (rb2->flag == 0)
{
rb2->skillu();
rb2->flag = 1;
}
break;
case 'N':
case 'n'://伸手攻击
rb2->skilln();
break;
case 'M':
case 'm'://镭射光
rb2->skillm();
break;
case 'O':
case 'o'://肚载大炮
rb2->skillo();
break;
case 'P':
case 'p'://跳高
if (rb2->jflag == 0)
{
rb2->jflag = 1;
rb2->skillp();
}
break;
}
}
//参数修改后调用重画函数,屏幕图形将发生改变
glutPostRedisplay();
}
-
- 技术难点、创意特色
技术难点在于设计机器人各技能的使用与是否被击中的事件交互,创意特色则是弥补了基于opengl2d双人格斗无游戏引擎的制作。
- 程序代码量
1、主页面:1000行
2、爆炸类:50行
3、房子类:280行
4、炸弹类:80行
. 5、树类:30行
6、机器人1类:480行
7、机器人2类:520行
8、路类:40行
约:2300行
- 项目各种界面和效果截图及其说明
开始-中间-结束
- 项目总结
项目特色、项目不足、将来改进之处、收获感想、致谢等
当前项目是目前为数不多的基于opengl的2D双人无引擎格斗游戏,目前看到网上制作的格斗游戏基本使用引擎制作的,这个可以是作为opengl2D入门游戏制作的参考。项目不足之处在于游戏界面设计的不是很漂亮,机器人设计的不是很逼格,高大上。将来改进之处主要是游戏界面的设计和机器人外貌的改变。通过本次项目的制作,把opengl2D的平移、旋转、变换使用在方方面面,更进一步认识了opengl2D编程,进一步认识了面向对象的编程,这个游戏也是当前自己使用c/c++制作的最多代码量的项目,收益良多。