基于opengl的2d机器人双人格斗游戏

  1. 2D 项目简介

项目的创意设想、实现功能、项目意义(200字左右)

  作品代表图

 

项目名:机器人双人格斗游戏

 

项目的创意设想:当前ai大火,机器人也是日常生活中的一个热点,自己在小学乃至高中也经常玩格斗游戏。曾经也想过格斗游戏是怎么做的,可是那时候条件有限,而且也没有编程基础。这个想法就一直留到了大学。到大三学习计算机图形学,也有了一定的编程基础,认为这个格斗游戏可以自己完成了。便开始动手,经过几天的努力,终于把这个游戏实现了。

 

实现功能:画了机器人1(乌巢之子),机器人2(关山之月)实现其的技能有:

  1. 上下左右移动:实现了平移
  2. 弹跳:使用了重力加速度实现了起跳落下的算法,
  3. 眼睛发射镭射光线:实现了平移
  4. 肚载大炮发射:实现了平移
  5. 手臂可以伸长:实现了平移
  6. 被炮弹击中会倒退:使用了旋转变换之类函数,实现了旋转
  7. 法天相地(放大):实现了放大 - 经过一段时间也会缩放到原来的大小

 

  1. 计时功能:使用了时间回调函数
  2. 使用了win32API函数,可以插入声音

 

项目意义:自己在开始做这个游戏的时候,也上网百度看是否有类似的格斗游戏,这类型指的是使用opengl库制作的2D无游戏引擎的双人格斗游戏,发现找不到类似的游戏,可以说是网上几乎没人做过类似的2D无游戏引擎的双人格斗游戏。可以说这个游戏填补了当类游戏的空白,也可以给后来者加以参考。

 

作品代表图:

  1. 编程环境(电脑硬件、操作系统和VS平台)说明
  2.  

    电脑硬件:thinkpad x250,

    cpu:i3

    内存:4g

    操作系统:win10

    Vs:vs2017

            

  3. 程序操作说明
  4. 操作如下图:

    编程环境(电脑硬件、操作系统和VS平台)说明

 

电脑硬件:thinkpad x250,

cpu:i3

内存:4g

操作系统:win10

Vs:vs2017

        

  1. 程序操作说明

操作如下图:

按键b:开始游戏

对角色1(乌巢之子)进行操作有:

1、wsad:上下左右移动

2、z:长拳攻击

3、x:从眼部发射镭射光线

4、c:肚载大炮,肚子发射大炮,攻击力强于镭射光线

5、e:法天相地,变大

6、r:弹跳

 

对角色2(关山之月)进行操作有:

  1. ikjl:上下左右移动
  2. n:长拳攻击
  3. m:镭射光线
  4. O:肚载大炮,肚子发射大炮,攻击力强于镭射光线
  5. U:法天相地(变大)
  6. P:弹跳

 

当两者的某一方血条为0时,游戏结束,效果如下:

 

 

  1. 程序实现
  1. 创意详细说明:
    1. 算法类、模拟类: 算法说明,想要实现的效果说明

 

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;

 

  1. 炸弹类的设计:设计炸弹类首先要知道炸弹的坐标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;

}

}

}

}

 

  1. 炸弹爆炸类的设计: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();

}

 

  1. 房子类设计:(功能简单,不做解释)

#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;

}

 

  1. 树类的设计:(功能简单,不做解释)

#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);

}

 

 

 

    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();

}

 

    1. 技术难点、创意特色

技术难点在于设计机器人各技能的使用与是否被击中的事件交互,创意特色则是弥补了基于opengl2d双人格斗无游戏引擎的制作。

 

  1.  程序代码量

      1、主页面:1000行

          2、爆炸类:50行

  3、房子类:280行

  4、炸弹类:80行

.   5、树类:30行

6、机器人1类:480行

       7、机器人2类:520行

8、路类:40行

约:2300行

  1.  项目各种界面和效果截图及其说明

开始-中间-结束

  1.  项目总结   

      项目特色、项目不足、将来改进之处、收获感想、致谢等

 

当前项目是目前为数不多的基于opengl的2D双人无引擎格斗游戏,目前看到网上制作的格斗游戏基本使用引擎制作的,这个可以是作为opengl2D入门游戏制作的参考。项目不足之处在于游戏界面设计的不是很漂亮,机器人设计的不是很逼格,高大上。将来改进之处主要是游戏界面的设计和机器人外貌的改变。通过本次项目的制作,把opengl2D的平移、旋转、变换使用在方方面面,更进一步认识了opengl2D编程,进一步认识了面向对象的编程,这个游戏也是当前自己使用c/c++制作的最多代码量的项目,收益良多。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

猜你喜欢

转载自blog.csdn.net/guanshanyue96/article/details/89037993