愤怒的小鸟项目设计

在这里插入图片描述

来源:微信公众号「编程学习基地」

淘气鸟项目设计

基于C++面向对象的程序设计方法
微信公众号「编程学习基地」后台回复关键字【淘气鸟】获取完整代码

面向对象三步法
创建对象

淘气鸟项目里定义了四个类

  • Background 背景类
    实现了背景图片加载、利用两张背景图片模拟场景移动
  • Bird 小鸟类
    实现了小鸟图片加载、利用六张背景图片模拟小鸟飞翔
  • Block 障碍物类
    实现了障碍物图片加载、初始化了十个障碍物循环阻碍飞翔
  • Manage 管理类
    将Background、Bird、Block这三个类整合起来,利用用户操作实现小鸟跳跃躲避障碍

定义的四个类中,用Background、Bird、Block组合成管理类实现管理

设置对象

Background、Bird、Block这三个类在这里都可以独自实现某个功能

Background back;		//创建对象
DWORD begin, end, passTime;
begin = GetTickCount();
back.initBackground();
back.SetBkIsMove(true);	//设置对象
while (1)
{
	end = GetTickCount();
	passTime = end - begin;
	begin = end;
	back.drawBackground();
	back.updateBackground(passTime);
}
Bird bird;	 				//创建对象
DWORD begin, end, passTime;
begin = GetTickCount();
bird.initBird();
bird.SetBirdIsStart(true);	//设置对象
while (1)
{
	end = GetTickCount();
	passTime = end - begin;
	begin = end;
	bird.drawBird();
	bird.updateBird(passTime);
}
Block block;				//创建对象
DWORD begin, end, passTime;
begin = GetTickCount();
block.initBlock();
block.SetBlockIsMove(true);//设置对象
while (1)
{
	end = GetTickCount();
	passTime = end - begin;
	begin = end;
	block.drawBlock();
	block.updateBlock(passTime);
}
使用对象
int main()
{
	initgraph(400, 300);
	Manage manage;				//创建对象
	DWORD begin, end, passTime;
	begin = GetTickCount();		
	manage.initGame();
	manage.startUI();	//使用对象
	while (1)
	{
		end = GetTickCount();
		passTime = end - begin;
		begin = end;
		manage.drawGame();			//使用对象
		manage.updateGame(passTime);//使用对象
	}
	closegraph();
	return 0;
}
Background 背景类
//BackGround.h
#pragma once
class Background
{
	IMAGE m_bk;		//背景图片
	DWORD m_PassTime;	//经过的时间
	DWORD m_Dsp;		//帧时间
	int m_iMoveSpeed;	//移动速度
	bool m_isMove;		//是否移动

	int m_x, m_y;	//绘制的位置
public:
	Background();
	~Background();
	void initBackground();	//初始化
	void updateBackground(DWORD PassTime);	//数据更新
	void drawBackground();					//绘制图形
public:
	//设置是否开始移动
	void SetBkIsMove(bool isMove) { m_isMove = isMove; }
};

面向对象编程的优点在于你需要什么就以去实现它,你想开放什么端口就开放什么端口,我这里只开放了是否移动端口,其实还可以加很多端口,像移动速度m_isMove,可以让管理类去设置背景图片的移动速度。

//BackGround.cpp
#include"stafx.h"
#include "Background.h"
Background::Background(){}
Background::~Background(){}
void Background::initBackground()
{
	//初始化
	m_Dsp = 1000 / 12;	//单位是ms
	m_iMoveSpeed = 2;
	m_isMove = false;
	m_PassTime = 0;
	m_x = 0;
	m_y = 0;
	//加载图片
	loadimage(&m_bk, L"imgs/bk/background.bmp");
}
void Background::updateBackground(DWORD PassTime)
{
	if (m_isMove)
	{
		m_PassTime += PassTime;
		if (m_PassTime >= m_Dsp)
		{
			for (int i = 0; i < 2; i++)
			{
				m_x -= m_iMoveSpeed;
				if (m_x <= -WIDTH)
				{
					m_x = 0;
				}
			}
			m_PassTime -= m_Dsp;
		}
	}
}
void Background::drawBackground()
{
	//绘制两张图片模拟场景移动
	putimage(m_x, m_y, &m_bk);
	putimage(m_x + WIDTH, m_y, &m_bk);
}

.cpp就是来实现.h需要实现的功能,这里我就直说下updateBackground中的形参DWORD PassTime,这个是经过的时间,当经过的时间大于你设置的帧时间,那就会进入下一帧,这样背景图片就会移动,造成小鸟飞翔的视觉差错

Bird 小鸟类
#pragma once
class Bird
{
	IMAGE m_bird[4][2];
	int m_index;		//小鸟的状态 平 飞 平 降
	int m_x, m_y;

	float m_Dps;		//帧时间
	float m_PassTime;	//经过的时间
	bool m_isMove;		//是否移动
	bool m_isStart;		//是否开始进入场景
	bool m_isUp;		//是否跳跃
	float m_JumpTimer;	//跳跃的时间

	RECT m_BirdRect;	//小鸟的矩形区域
public:
	Bird();
	~Bird();
	void initBird();	//初始化
	void drawBird();	//绘制图形
	void updateBird(DWORD PassTime);//数据更新
public:
	//获取是否开始状态
	bool GetBirdIsStart() { return m_isStart; }	
	//设置是否开始状态
	void SetBirdIsStart(bool isStart) { m_isStart = isStart; }
	void SetBirdIsUp(bool isUp) { m_isUp = isUp; }
	//获取小鸟所在矩形
	RECT GetBirdRect() { return m_BirdRect; }
};

这里可以开放的端口就比较多了,是否跳跃、跳跃时间、是否移动、是否进入场景、改变帧时间可以改变小鸟震动翅膀的频率,还有就是开放一个端口让管理类能够知道小鸟的位置,m_BirdRect用来和障碍物的m_BlockRect进行匹配判断是否输

#include"stafx.h"
#include "Bird.h"

Bird::Bird(){}
Bird::~Bird(){}
void Bird::initBird()
{
	//初始化游戏数据
	SetRect(&m_BirdRect, 0, 0, 0, 0);
	m_Dps = 1000 / 12;
	m_PassTime = 0;
	m_isMove = false;
	m_isStart = false;
	m_isUp = false;
	m_JumpTimer = 0;
	m_index = 0;
	m_x = m_y = 0;
	//加载图片
	loadimage(&m_bird[0][0], L"imgs/bird/bird1-1.gif");
	loadimage(&m_bird[0][1], L"imgs/bird/bird1-2.gif");
	loadimage(&m_bird[1][0], L"imgs/bird/bird2-1.gif");
	loadimage(&m_bird[1][1], L"imgs/bird/bird2-2.gif");
	loadimage(&m_bird[2][0], L"imgs/bird/bird3-1.gif");
	loadimage(&m_bird[2][1], L"imgs/bird/bird3-2.gif");
	loadimage(&m_bird[3][0], L"imgs/bird/bird4-1.gif");
	loadimage(&m_bird[3][1], L"imgs/bird/bird4-2.gif");
}
void Bird::drawBird()
{
	//setfillcolor(RED);
	//fillrectangle(m_BirdRect.left, m_BirdRect.top, m_BirdRect.right, m_BirdRect.bottom);
	putimage(m_x, m_y, &m_bird[m_index][0], NOTSRCERASE);
	putimage(m_x, m_y, &m_bird[m_index][1], SRCINVERT);

}
void Bird::updateBird(DWORD PassTime)
{
	m_PassTime += PassTime;
	if (m_PassTime >= m_Dps)
	{
		//每隔m_Dps时间 改变小鸟状态index 改变小鸟纵坐标m_y
		m_index = (++m_index) % 3;	
		m_PassTime -= m_Dps;
		//进入场景
		if (m_isStart)
		{
			m_x += 2;
			m_y += 3;
			if (m_x >= 100)
			{
				m_isStart = false;
				m_isMove = true;
			}
		}
		//开始游戏
		if (m_isMove)
		{
			if (m_isUp)//上升
			{
				m_y -= 15;
				m_JumpTimer += PassTime;
				if (m_JumpTimer >= 10)	//跳跃时间为10ms
				{
					m_JumpTimer = 0;
					m_isUp = false;
				}
			}
			else//下降
			{
				m_y += 3;
			}
		}
	}
	//设置小鸟所在矩形
	SetRect(&m_BirdRect, m_x + 2, m_y + 2, m_x + 26, m_y + 23);
}

实现这个小鸟类,需要说明的是每次数据更新之后都要对m_BirdRect进行设置

SetRect(&m_BirdRect, m_x + 2, m_y + 2, m_x + 26, m_y + 23);

Block 障碍物类
#pragma once
class Block
{
	IMAGE m_block;	
	float m_PassTime;	//经过的时间
	float m_Dps;		//帧时间
	int m_iMoveSpeed;	//移动速度
	bool m_isMove;		//是否开始移动
	RECT m_BlockRect[10];
public:
	Block();
	~Block();
	void initBlock();
	void updateBlock(DWORD PassTime);
	void drawBlock();
public:
	RECT* GetBlockRect() { return m_BlockRect; }
	//设置是否开始移动
	void SetBlockIsMove(bool isMove) { m_isMove = isMove; }
	//可另行开放接口
	//......
};

这个障碍物类其实和背景类差不多,就是多了几个矩形区域m_BlockRect[10],这个就是障碍物的区域

#include"stafx.h"
#include "Block.h"

Block::Block(){}
Block::~Block(){}
void Block::initBlock()
{
	//初始化游戏数据
	m_Dps = 1000 / 12;
	m_PassTime = 0;
	m_iMoveSpeed = 2;
	m_isMove = false;
	ZeroMemory(m_BlockRect, sizeof(m_BlockRect));
	//加载图片
	loadimage(&m_block, L"imgs/stone/block.bmp");
	POINT pt[10] = { { 200, -20 },{ 200, 200 },{ 350, -60 },{ 350, 160 },{ 500, -40 },{ 500, 180 },{ 650, 0 },{ 650, 220 },{ 800, -60 },{ 800, 160 }};
	for (int i = 0; i < 10; i++)
	{
		SetRect(&m_BlockRect[i], pt[i].x, pt[i].y, pt[i].x + 32/*这是墙图片的宽度*/, pt[i].y + 153);
	}
}
void Block::updateBlock(DWORD PassTime)
{
	if (m_isMove)
	{
		m_PassTime += PassTime;
		if (m_PassTime >= m_Dps)
		{
			for (int i = 0; i < 10; i++)
			{
				m_BlockRect[i].left -= 2;
				m_BlockRect[i].right -= 2;
			}
			m_PassTime -= m_Dps;
		}
	}
	for (int i = 0; i < 10; i++)
	{
		if (m_BlockRect[i].left < -50)
		{
			//m_BlockRect[i].left += 750;
			m_BlockRect[i].left = m_BlockRect[i].left + rand() % 75 + 675;
			m_BlockRect[i].right = m_BlockRect[i].left + 32;
		}
	}
}
void Block::drawBlock()
{
	for (int i = 0; i < 10; i++)
	{
		putimage(m_BlockRect[i].left, m_BlockRect[i].top, &m_block);
		//setfillcolor(RED);
		//fillrectangle(m_BlockRect[i].left, m_BlockRect[i].top, m_BlockRect[i].right, m_BlockRect[i].bottom);
	}
}

实现障碍物类的时候,这里我就手动的设置了10个障碍物,然后循环造成障碍物无限的假象,没有学过相关算法,所以游戏也没啥难度,就是用来学习面向对象的一个过程

Manage 管理类
#pragma once

class Manage
{
	Bird m_bird;		//小鸟类
	Background m_bk;	//背景类
	Block m_block;		//障碍物
	bool m_isStart;		//是否开始游戏
	DWORD m_passTime;	//经过的时间 用来计算分数
	int m_score;		//分数
public:
	Manage();
	~Manage();
	void initGame();					//初始化
	void updateGame(DWORD PassTime);	//数据更新
	void drawGame();					//绘制游戏
	void startUI();						//开始界面
};

Background、Bird、Block三个类都有了,管理类直接用就行了

#include"stafx.h"
#include"Manage.h"
Manage::Manage(){
	m_isStart = false;
	m_score = 0;
	m_passTime = 0;
}
Manage::~Manage(){}
//初始化
void Manage::initGame()
{
	//初始化其他
	setbkmode(TRANSPARENT);		//设置背景透明
	//初始化对象
	m_bk.initBackground();
	m_bird.initBird();
	m_block.initBlock();
}
//数据更新
void Manage::updateGame(DWORD PassTime)
{
	//暂停或开始游戏
	if (m_isStart)
	{
		m_passTime += PassTime;
		//更新元素
		m_bk.updateBackground(PassTime);
		m_bird.updateBird(PassTime);
		m_block.updateBlock(PassTime);
		//开始游戏之后障碍物和背景移动
		if (!m_bird.GetBirdIsStart())	
		{
			m_block.SetBlockIsMove(true);
			m_bk.SetBkIsMove(true);
			//计算分数
			if (m_passTime >= 2000)
			{
				m_passTime = 0;
				m_score++;
			}
		}
		/***************判断输赢****************/
		RECT BirdRect = m_bird.GetBirdRect();		//获取障碍物所在矩形区域
		RECT* BlockRect = m_block.GetBlockRect();	//获取小鸟所在矩形区域
		for (int i = 0; i < 10; i++)
		{
			//小鸟碰到障碍物
			if (!(BirdRect.right<BlockRect[i].left || BirdRect.left>BlockRect[i].right
				|| BirdRect.top > BlockRect[i].bottom || BirdRect.bottom < BlockRect[i].top))
			{
				//游戏结束	可自行添加结束界面
				this->initGame();
				this->startUI();
				m_isStart = false;
				m_bird.SetBirdIsStart(false);
				m_score = 0;
			}
		}
		//小鸟飞出界面,游戏结束
		if (BirdRect.bottom >= 300 || BirdRect.top <= -44)
		{
			//游戏结束	可自行添加结束界面 这里直接回到开始UI界面
			this->initGame();
			this->startUI();
			m_isStart = false;
			m_bird.SetBirdIsStart(false);
			m_score = 0;
		}
	}
	//玩家操作
	if (kbhit())
	{
		switch (getch())
		{
		case ' ':	//空格
			m_bird.SetBirdIsUp(true);
			break;
		case 'p':
		case 'P':
			if (m_isStart)
				m_isStart = false;
			else
				m_isStart = true;
			break;
		case 13:	//回车
			m_isStart = true;
			m_bird.SetBirdIsStart(true);
			break;
		default:
			break;
		}
	}
}
//绘制游戏
void Manage::drawGame()
{
	if (m_isStart)
	{
		BeginBatchDraw();
		m_bk.drawBackground();
		m_bird.drawBird();
		m_block.drawBlock();
		settextcolor(WHITE);
		settextstyle(24, 0, L"宋体");
		wchar_t str[20];
		wsprintf(str, L"分数:%d", m_score);
		outtextxy(0, 0, str);
		EndBatchDraw();
	}
}
//开始界面
void Manage::startUI()
{
	m_bk.drawBackground();
	settextcolor(RED);
	settextstyle(28, 0, L"宋体");
	outtextxy(120, 50, L"愤怒的小鸟");
	settextcolor(BLACK);
	settextstyle(18, 0, L"宋体");
	outtextxy(135, 90, L"操作说明:");
	outtextxy(120, 120, L"1、按空格键跳跃");
	outtextxy(120, 145, L"2、按P键暂停");
	outtextxy(120, 170, L"3、按回车开始游戏");
	outtextxy(200, 200, L"VX公众号:编程学习基地");
}

别看Manage类挺长的,其实大部分功能Background、Bird、Block三个类都实现了,就添加了判断输赢、统计分数、对玩家操作进行响应

判断输赢

我这里就只说明下判断输赢,我加了个填充矩形,然后就有了这三张图片

玩家正常玩游戏时:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yoVkEC69-1584850974510)(D:\微信公众号\Typora\C++\淘气鸟\1.png)]

在小鸟和障碍物的绘制那里加上填充矩形

void Bird::drawBird()
{
	setfillcolor(RED);
	fillrectangle(m_BirdRect.left, m_BirdRect.top, m_BirdRect.right, m_BirdRect.bottom);
	putimage(m_x, m_y, &m_bird[m_index][0], NOTSRCERASE);
	putimage(m_x, m_y, &m_bird[m_index][1], SRCINVERT);

}
void Block::drawBlock()
{
	for (int i = 0; i < 10; i++)
	{
		setfillcolor(RED);
		fillrectangle(m_BlockRect[i].left, m_BlockRect[i].top, m_BlockRect[i].right, m_BlockRect[i].bottom);
		putimage(m_BlockRect[i].left, m_BlockRect[i].top, &m_block);
	}
}

我们来看下图像

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e4303FW3-1584850974511)(D:\微信公众号\Typora\C++\淘气鸟\2.png)]

好像没什么变化,仔细点看,你会看到小鸟后方好像有个红色填充的矩形

我们将上面的顺序改下,改成下面的

void Bird::drawBird()
{
	putimage(m_x, m_y, &m_bird[m_index][0], NOTSRCERASE);
	putimage(m_x, m_y, &m_bird[m_index][1], SRCINVERT);
	setfillcolor(RED);
	fillrectangle(m_BirdRect.left, m_BirdRect.top, m_BirdRect.right, m_BirdRect.bottom);
}
void Block::drawBlock()
{
	for (int i = 0; i < 10; i++)
	{
		putimage(m_BlockRect[i].left, m_BlockRect[i].top, &m_block);
        setfillcolor(RED);
		fillrectangle(m_BlockRect[i].left, m_BlockRect[i].top, m_BlockRect[i].right, m_BlockRect[i].bottom);
	}
}

有没有看到,其实在逻辑里面没有什么图像不图像的,都是一个个矩形区域代表着某些东西

这样我们就好办了,判断输赢嘛,只要小年所在的矩形区域和障碍物所在的矩形区域没有交集就行了,我的代码大家可能看的有点懵逼

	BirdRect.right<BlockRect[i].left ||
    BirdRect.left>BlockRect[i].right || 
    BirdRect.top > BlockRect[i].bottom ||
    BirdRect.bottom < BlockRect[i].top

这个代码逻辑性还是很强的,一个矩形的最右边和另一个矩形的最左边隔着一段距离,说这两个图像不相交,我们把四个方向都列出来,这个就是不相交的区域,然后取反,就是相交。可能有点绕,这个还是需要自行领悟。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DeRoy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值