#include <easyx.h>
#include <time.h>
#include <conio.h>
// 公共基类,可以做高级抽象
struct Cell
{
int x, y;
void __update(int deltaX, int deltaY)
{
x += deltaX;
y += deltaY;
}
};
// 子弹
class Bullet : public Cell
{
typedef Cell Base;
public:
clock_t d; // 记录更新时刻
bool on = false; // 子弹对象是否消亡了
int move = 3; // 移动速度
COLORREF col; // 子弹颜色
// 画出新的位置
// now: 当前这张图像的时刻
virtual void show(clock_t now)
{
setfillcolor(col); //col: 颜色
fillrectangle(x - 5, y - 5, x + 5, y + 5); //使用用上边设置填充颜色填充出来一个方块
}
// 更新子弹位置并进行位置约束,返回是否消亡
// leftX,rightX: 左右限位坐标
virtual bool update(clock_t now, int leftX, int rightX)
{
// 刷新率控制
del();
if (now - d <= 20) return false;
do{
Base::__update(move, 0);
// 边界检测
if (this->x <= leftX) break;
if (this->x >= rightX) break;
// 记录时间
d = now;
return true;
} while (0);
// 死亡了
this->on = false;
return false;
}
private:
//覆盖原来的位置
void del()
{
setfillcolor(0); //0: 黑色
setlinecolor(0); //0: 黑色
fillrectangle(x - 5, y - 5, x + 5, y + 5); //使用用上边设置填充颜色填充出来一个方块
rectangle(x - 5, y - 5, x + 5, y + 5); //使用用上边设置的线颜色画一个方框
}
};
// 物体
class Body : public Cell
{
public:
bool hurting = false; //标志是否受伤了
int hp = 100; //生命
COLORREF clr; //物体颜色
clock_t d_hurt; //受伤的时刻
clock_t mDelay = clock(); //移动的延时
clock_t mDelayAtt = clock();//开火的延时
// 受伤计算
virtual bool hurt(clock_t now, int)
{
d_hurt = now;
hurting = true;
return (hp <= 0);
}
// 发射子弹
virtual void fire(clock_t now, Bullet& but)
{
//放置一个子弹
but.on = true;
but.x = x;
but.y = y;
but.d = now;
}
};
//敌人
class Boss : public Body
{
typedef Body Base;
public:
bool angle = false;//方向
// 移动更新
// upY,downY: Y轴 移动位置限制
// speed: 移动速度(小于0:上移. 大于0:下移.)
virtual void update(clock_t now, int upY, int downY, int speed)
{
del();
if (now - this->mDelay < 50) return;
this->mDelay = now;
//上上下下得移动
if (angle == true)
Base::__update(0, -speed);
else
Base::__update(0, speed);
// 达到边界上就反向移动
if (y >= downY)
angle = true;
if (y <= upY)
angle = false;
}
// 图像显示
virtual void show(clock_t now)
{
//受伤闪烁0.1秒
if (hurting == true){
if (now - d_hurt > 100){
hurting = false;
clr = RGB(150, 180, 210);
}
else
clr = RGB(255, 0, 0);
}
setfillcolor(clr);
fillrectangle(x - 20, y - 40, x + 20, y + 40);
}
virtual void fire(clock_t now, Bullet& but)//攻击
{
Base::fire(now, but);
but.x -= 20;
but.col = RGB(255, 180, 20);
but.move = -3;
}
virtual bool hurt(clock_t now, int repeat) //受伤
{
hp -= 4 * repeat;
setfillcolor(0);
setlinecolor(WHITE);
fillrectangle(160, 485, 560, 510);//更新血条
rectangle(160, 485, 160 + hp * 4, 510);
setfillcolor(RGB(230, 0, 1));
setlinecolor(RGB(255, 255, 255));
fillrectangle(160, 485, 160 + hp * 4, 510);
rectangle(160, 485, 160 + hp * 4, 510);
//返回是否死亡
return Body::hurt(now, repeat);
}
protected:
void del()
{
setfillcolor(0);
setlinecolor(0);
rectangle(x - 20, y - 40, x + 20, y + 40);
fillrectangle(x - 20, y - 40, x + 20, y + 40);
}
};
//玩家类,同上
class Tank : public Body
{
typedef Body Base;
public:
// 更新
virtual void update(clock_t now, int upY, int downY, int speed)
{
//擦除图案
del();
// 更新周期计算
if (now - this->mDelay < 40) return;
this->mDelay = now;
// 移动限位
if (speed == 0) return;
if (speed < 0 && y <= upY) return;
if (speed > 0 && y >= downY) return;
//更新
Base::__update(0, speed);
}
virtual void show(clock_t now)
{
//受伤闪烁0.1秒
if (hurting == true){
if (now - d_hurt > 100){
hurting = false;
clr = RGB(0, 130, 125);
}
else
clr = RGB(0, 255, 0);
}
setfillcolor(clr);
fillrectangle(x - 25, y - 25, x + 25, y + 25);
setfillcolor(RGB(100, 200, 180));
fillrectangle(x, y + 5, x + 40, y - 5);
}
virtual void fire(clock_t now, Bullet& but)
{
Base::fire(now, but);
but.x += 45; // 修正位置
but.col = RGB(150, 180, 210);
but.move = 3;
}
// repeat: 击中了几下
virtual bool hurt(clock_t now, int repeat)
{
hp -= 2* repeat;
setfillcolor(0);
setlinecolor(WHITE);
fillrectangle(160, 515, 560, 540);
rectangle(160, 515, 560, 540);
rectangle(160, 515, 160 + hp * 4, 540);
setfillcolor(RGB(0, 255, 1));
setlinecolor(RGB(255, 255, 255));
fillrectangle(160, 515, 160 + hp * 4, 540);
rectangle(160, 515, 160 + hp * 4, 540);
//返回是否死亡
return Body::hurt(now, repeat);
}
protected:
void del()
{
setfillcolor(0);
setlinecolor(0);
fillrectangle(x - 25, y - 25, x + 25, y + 25);
rectangle(x - 25, y - 25, x + 25, y + 25);
fillrectangle(x, y + 5, x + 40, y - 5);
rectangle(x, y + 5, x + 40, y - 5);
}
};
#define BT_MAX 8
class Battle
{
public:
Boss bo;//敌人
Tank tk;//玩家
Bullet bt[BT_MAX];//玩家的子弹
Bullet ebt[BT_MAX];//敌人的子弹
Battle()
{
tk.x = 30;
tk.y = 30;
bo.x = 580;
bo.y = 240;
bo.mDelay = clock();//初始化延时
bo.mDelayAtt = clock();
bo.clr = RGB(0, 130, 125);
tk.clr = RGB(150, 180, 210);
}
protected:
// 更新子弹位置
// btCnt:表的尺寸
// bt:子弹对象表
void updateBullet(int btCnt, Bullet bt[], clock_t now)
{
//遍历子弹,使子弹刷新
while (btCnt-->0){
if (bt[btCnt].on == false) continue;
// 刷新位置(传递当前时刻,左右边界)
if (bt[btCnt].update(now, 5, 635)) continue;
}
}
// 扫描检查子弹是否集中物体
// btCnt:表的尺寸
// bt:子弹对象表
// area: 需要击中的方块区域
int scanBulletHurt(int btCnt, Bullet bt[], const RECT& area)
{
int c = 0;
//遍历子弹,计算碰撞次数
while (btCnt-->0){
if (bt[btCnt].on == false) continue;
if ((bt[btCnt].x + 5 >= area.left && bt[btCnt].x - 5 <= area.right) &&
(bt[btCnt].y - 5 < area.bottom && bt[btCnt].y + 5 > area.top)){
// 击中了
bt[btCnt].on = false;
++c;
}
}
return c;
}
// 显示子弹
// btCnt:表的尺寸
// bt:子弹对象表
void showBullet(int btCnt, Bullet bt[], clock_t now)
{
//遍历子弹,使子弹刷新
while (btCnt-->0){
if (bt[btCnt].on == false) continue;
// 绘制到新位置
bt[btCnt].show(now);
}
}
public:
// 返回是否死亡结束(0:没死, 1:自己挂, 2:敌人挂)
// tkMoveDirection: 坦克移动方向(-1,0,1)=(上,停,下)
// tkFire:坦克开火
int update(clock_t now, int tkMoveDirection, int tkFire)
{
int ret = 0;
do{// 这个循环结构是用来跳转的,没有循环意义
updateBullet(BT_MAX, bt, now); //自身子弹刷新
updateBullet(BT_MAX, ebt, now); //敌人子弹刷新
tk.update(now, 28, 452, 3*tkMoveDirection); //自己更新(上下边界和移动速度)
bo.update(now, 40, 440, 5); //敌人更新(上下边界和移动速度)
// 检测敌人区域 计算敌人伤害
if (int c = scanBulletHurt(BT_MAX, bt, { bo.x - 20, bo.y - 40, bo.x + 20, bo.y + 40 })){
// 返回是否死亡
if (bo.hurt(now, c)){ ret = 2; break; }
}
// 检测自身区域 计算自身伤害
if (int c = scanBulletHurt(BT_MAX, ebt, { tk.x - 25, tk.y - 25, tk.x + 25, tk.y + 25 })){
// 返回是否死亡
if (tk.hurt(now,c)){ ret = 1; break; }
}
// 坦克开火
if (tkFire && (now - tk.mDelayAtt > 800)){
for (int i = BT_MAX; i--;){
if (bt[i].on) continue;
// 发射子弹
tk.mDelayAtt = now;
tk.fire(now, bt[i]);
break;
}
}
// 敌人自动开火
if (now - bo.mDelayAtt > 700){
for (int i = BT_MAX; i--;){
if (ebt[i].on) continue;
// 发射子弹
bo.mDelayAtt = now;
bo.fire(now, ebt[i]);
break;
}
}
} while (0);
// 显示绘制 敌我子弹
bo.show(now);
tk.show(now);
showBullet(BT_MAX, bt, now);
showBullet(BT_MAX, ebt, now);
return ret;
}
};
int main()
{
// 初始化easyX 图形窗口(尺寸 640 x 550, 4:NOMINIMIZE 没有最小化功能)
initgraph(640, 550, 4);
settextcolor(RGB(0, 254, 0)); // 设置文本颜色
settextstyle(35, 0, _T("黑体"));// 设置文本字体
outtextxy(150, 200, _T("W,S移动,K攻击"));
Sleep(1000);
setlinecolor(0); //0: 黑色
setfillcolor(0);
rectangle(0, 0, 640, 550);
fillrectangle(0, 0, 640, 550);
setlinecolor(RGB(255, 255, 255));
setfillcolor(RGB(255, 255, 255));
line(0, 481, 640, 481);//分割画面与血条
settextstyle(20, 0, _T("黑体"));
outtextxy(10, 485, _T("BOSS的生命值:"));
setfillcolor(RGB(230, 0, 1));
fillrectangle(160, 485, 560, 510);//敌人血条
outtextxy(10, 520, _T("玩家的生命值:"));
setfillcolor(RGB(0, 255, 1));
fillrectangle(160, 515, 560, 540);//玩家血条
// 启动游戏
Battle btl; // 战场控制类对象
int bGameover = 0;
DWORD t, t1 = GetTickCount();
BeginBatchDraw();
while (true){
// 退出程序建检测
if (GetAsyncKeyState('Q') & 0x8000)
break;
// 提取当前时间
t = GetTickCount();
while (t - t1 > 0){ // 时间间隔是否达到50ms
// 输入
int tkMove = 0, tkFire = 0;
if ((GetAsyncKeyState('W') & 0x8000) ||//玩家移动
(GetAsyncKeyState('w') & 0x8000)){
tkMove = -1; //左
}
else if ((GetAsyncKeyState('S') & 0x8000) ||//玩家移动
(GetAsyncKeyState('s') & 0x8000)){
tkMove = 1; //右
}
else if ((GetAsyncKeyState('K') & 0x8000) || //玩家开火
(GetAsyncKeyState('k') & 0x8000)){
tkFire = 1; //开火
}
// 绘制
//cleardevice();
// 更新游戏逻辑
bGameover = btl.update(clock(), tkMove, tkFire);
if (bGameover == 1){ //玩家死了
settextcolor(RGB(254, 0, 0));
settextstyle(35, 0, _T("黑体"));
outtextxy(140, 200, _T("你被boss打败了!"));
break;
}
else if (bGameover == 2){ //敌人死了
settextcolor(RGB(0, 254, 0));
settextstyle(35, 0, _T("黑体"));
outtextxy(150, 200, _T("你打败了boss!你赢了!!"));
break;
}
// 输出缓冲图案
FlushBatchDraw();
t1 = t + 50;
}
// 死亡后结束游戏
if (bGameover) break;
}
//结束绘制
EndBatchDraw();
Sleep(5000);
closegraph();
return 0;
}