C++简易俄罗斯方块游戏5

实现方块旋转

Tetris.h

#pragma once
#include <vector>
#include <graphics.h>
#include "Block.h"  
using namespace std;

class Tetris
{
public://构造函数
	Tetris(int rows,int cols,int left,int top,int blockSize);
	void init(); //初始化
	void play(); //开始游戏

private:  //封装
	void keyEvent();  //内部使用
	void updateWindow();
	//返回距离上一次调用该函数,间隔了多长时间,单位毫秒
	//第一次调用该函数,返回0
	int getDelay();  
	void drop();
	void clearLine();
	void moveLeftRight(int offset);
	void rotate();   //旋转

private:
	int delay;
	bool update; //是否更新

	//int map[20][10];写死不利于修改
	//0:空白,没有任何方块
	//5:是第5种俄罗斯方块
	vector<vector<int>> map;  //表示二维数组
	int rows;  //总行数
	int cols;  //总列数
	int leftMargin;
	int topMargin;
	int blockSize;
	IMAGE imgBg;  //背景图片

	Block* curBlock;  //当前方块
	Block* nextBlock;  //预告方块
	Block bakBlock;  //备用方块,当前方块坠落过程中,备份上一个合法位置

};

Tetris.cpp

#include "Tetris.h"
#include <stdlib.h>
#include <time.h>
#include "Block.h"
#include <graphics.h>
#include <conio.h>

const int SPEED_NORMAL = 500; //普通速度    (不同关卡不同速度)
const int SPEED_QUICK = 50;   //快速降落速度


Tetris::Tetris(int rows, int cols, int left, int top, int blockSize)
{
	this->rows = rows;  //当形参和成员变量同名时,可用this指针来区分
	this->cols = cols;
	this->leftMargin = left;
	this->topMargin = top;
	this->blockSize = blockSize;

	//二维数组添加元素
	for (int i = 0; i < rows; i++) {
		vector<int> mapRow;   //空行
		for (int j = 0; j < cols; j++) {
			mapRow.push_back(0);   //在数组最后面加一个0
		}
		map.push_back(mapRow);  //添加行
	}


}

void Tetris::init()
{
	delay = SPEED_NORMAL;

	//配置随机种子
	srand(time(NULL));

	initgraph(938,896);  //创建游戏窗口,496,448
	 
	loadimage(&imgBg, "res/bg2.png"); //加载背景图片

	//初始化游戏区中的数据
	char data[20][10];
	for (int i = 0; i < rows; i++) {
		for (int j = 0; j < cols; j++) {
			map[i][j] = 0;
		}
	}


}

void Tetris::play()
{
	init(); //初始化

	nextBlock = new Block;  //预告方块
	curBlock = nextBlock;  //预告方块的下一个方块
	nextBlock = new Block;   //预告方块进入游戏后的预告方块



	//渲染画面CPU高消耗,处理方法,增加延时	
	int timer = 0;
	while (1)
	{
		//接受用户输入
		keyEvent();

		//距离上一次循环间隔多长时间,这样对系统消耗并不大
		timer += getDelay();//统计输入
		//判断时间到没到
		if (timer > delay) {
			timer = 0;//保证下次循环也间隔相同毫秒(清零)
			drop();   //进入游戏的动作,下降
			//渲染游戏画面
			update = true;  //更新游戏标记
			
		}
		//检查标记
		if (update) {
			update = false;
			updateWindow();

			//更新游戏数据
			clearLine();
		}
	}
}

void Tetris::keyEvent()
{
	int dx = 0;  //偏移量
	bool rotateFlag = false;   //旋转标记
	unsigned char ch = 0;
	//按键输入
	while (_kbhit()) {
		unsigned char ch = _getch();  //接受输入
		if (ch == 224) {
			ch = _getch();
			//如果按下方向建会自动返回两个字符
			//如果按向上方向键,会先返回224,在返回72
			switch (ch) {
			case 72:
				rotateFlag = true;
				break;
			case 80:  //下
				delay = SPEED_QUICK; //快速降落
				break;
			case 75:   //左
				dx = -1;
				break;
			case 77:  //右
				dx = 1;
				break;
			default:
				break;
			}
		}
	}

	if (dx != 0) {
		//左右移动
		moveLeftRight(dx);
		update = true;  //更新游戏画面
	}

	if (rotateFlag) {
		rotate();
		update = true;

	}
}

void Tetris::updateWindow()
{
	putimage(0, 0, &imgBg);  //在x零坐标y零坐标把图片显示出来

	//测试
	//Block block;
	//block.draw(leftMargin,topMargin);  //画方块,先画边界方块

	//底部方块出现,可以通过判断,大于零说明已经落到底部,在绘制
	IMAGE** imgs = Block::getImages();

	BeginBatchDraw();   //开始绘制

	for (int i = 0; i < rows; i++) {
		for (int j = 0; j < cols; j++) {
			if (map[i][j] == 0) continue;     //等于零说明没方块
			//确定位置
			int x = j * blockSize + leftMargin;  //列乘方块宽度加左侧边界
			int y = i * blockSize + topMargin;   
			putimage(x, y, imgs[map[i][j] - 1]);  //绘制方块
		}
	}


	//出现方块
	curBlock->draw(leftMargin,topMargin);   //出现在游戏中
	nextBlock->draw(689,150);  //需要计算偏移量,出现在右上角

	EndBatchDraw();  //结束绘制
}

//第一次调用返回0
//之后调用需要返回距离上一次调用间隔多少毫秒
int Tetris::getDelay()
{
	static unsigned long long lastTime = 0;
	unsigned long long currentTime = GetTickCount();

	if (lastTime == 0) {    //等于零说明为第一次调用
		lastTime = currentTime;   //将当前时钟数赋值给lastTime
		return 0;
	}
	else {
		int ret = currentTime - lastTime;  //当前时间减去上一次lastTime说明间隔时间
		lastTime = currentTime;
		return ret;
	}
}

void Tetris::drop()
{
	bakBlock = *curBlock;
	curBlock->drop();  //方块坠落

	//检查方块有没有到底部
	if (curBlock->blockInMap(map)==false) {
		//方块到底部不动
		bakBlock.solidify(map);
		delete curBlock;    //释放方块
		//curBlock = new Block;
		curBlock = nextBlock;
		nextBlock = new Block;   //当前方块生成新方块
	}
	delay = SPEED_NORMAL;  //还原到正常速度
}

void Tetris::clearLine()
{
}

void Tetris::moveLeftRight(int offset)
{
	bakBlock = *curBlock;  //备份
	curBlock->moveLeftRight(offset);

	//判断左右移动有没有出界
	if (!curBlock->blockInMap(map)) {
		*curBlock = bakBlock;  //非法位置还原方块
	}

}

void Tetris::rotate()
{
	if (curBlock->getBlockType() == 7) return;

	bakBlock = *curBlock;  //备份
	curBlock->rotate();


	if (!curBlock->blockInMap(map)) {
		*curBlock = bakBlock;  //非法位置还原方块
	}
}

Block.h

#pragma once
#include <graphics.h>
#include <vector>
using namespace std;

struct Point
{
	int row;
	int col;
};

class Block
{
public:
	Block();
	void drop(); //下降
	void moveLeftRight(int offset);  //移动
	void rotate();  //旋转
	void draw(int leftMargin,int topMargin);   //绘制
	static IMAGE** getImages();
	Block& operator=(const Block& other);

	//Point* getSmallBlocks();
	bool blockInMap(const vector<vector<int>>&map);  //方块是否在一个地图
	void solidify(vector<vector<int>>& map);  //固化方块
	int getBlockType();

private:
	int x;
	int y;
	int blockType;
	Point smallBlocks[4];//结构体中存储四种小方块
	IMAGE* img;  //定义变量 图片表示小方块,指针指向图片

private:
	static int size;
	static IMAGE* imgs[7];  //静态变量,里面有七个图片数组
	

};

Block.cpp

#include "Block.h"
#include <stdlib.h>

//初始化
IMAGE* Block::imgs[7] = { NULL, };
int Block::size = 36;   //方块大小

Block::Block()
{
	// 仅初始化一次
	if (imgs[0] == NULL) {
		//切割图片,放入内存
		IMAGE imgTmp;
		loadimage(&imgTmp, "res/tiles.png");//爆红 项目属性改为多节字符集
		SetWorkingImage(&imgTmp);
		for (int i = 0; i < 7; i++) {
			imgs[i] = new IMAGE;           //切第几刀放第几个,分配块内存
			//第一个参数指向内存放哪,第二,三个是切割点 从哪切,是x y坐标。四五是长宽
			getimage(imgs[i], i * size, 0, size, size);  
		}
		SetWorkingImage();   //恢复工作区
		//srand(time(NULL));
	}


	int blocks[7][4] = {
		1,3,5,7, // I
		2,4,5,7, // Z 1型
		3,5,4,6, // Z 2型
		3,5,4,7, // T
		2,3,5,7, // L
		3,5,7,6, // J
		2,3,4,5, // 田
	};

	//随机生成一种俄罗斯方块,新方块类型
	blockType= 1+rand() % 7;//这样就是1--7
	//初始化,小方块位置几行几列,坐标
	for (int i = 0; i < 4; i++) {
		smallBlocks[i].row = blocks[blockType - 1][i] / 2;
		smallBlocks[i].col = blocks[blockType - 1][i] % 2;

	}
	//新方块图片配置
	img = imgs[blockType - 1];
}

void Block::drop()
{

	//一种for(inti=0;i<4;i++){ smallBlocks[il.row++; }  改变结构体成员行标
	//第二种
	for (auto& block : smallBlocks)
	{
		block.row++;
	}

}

void Block::moveLeftRight(int offset)
{
	for (int i = 0; i < 4; i++) {
		smallBlocks[i].col += offset;   //列坐标加偏移量
	}

}

void Block::rotate()
{
	Point p = smallBlocks[1];
	for (int i=0; i < 4; i++) {
		Point tmp = smallBlocks[i];
		smallBlocks[i].col = p.col - tmp.row + p.row; //旋转中心的列减去当前行标加上旋转中心行标
		smallBlocks[i].row = p.row + tmp.col - p.col;  //旋转中心行标加上原来位置列减去旋转中心列标
	}
}

void Block::draw(int leftMargin, int topMargin)
{
	//绘制正在降落过程的方块
	for (int i = 0; i < 4; i++) {
		int x = smallBlocks[i].col * size + leftMargin;  //左侧边界加上第几列宽度
		int y = smallBlocks[i].row * size + topMargin;    //顶部
		putimage(x, y, img);
	}

}

IMAGE** Block::getImages()
{
	return imgs;     //直接返回数组
}

Block& Block::operator=(const Block& other)
{
	if (this == &other) return *this;   //如果是当前对象本身,直接返回

	this->blockType = other.blockType;
	for (int i = 0; i < 4; i++) {
		this->smallBlocks[i] = other.smallBlocks[i];
	}

	return *this;

}

bool Block::blockInMap(const vector<vector<int>>& map)
{
	//判断当前方块在此位置是否合法
	int rows = map.size(); 
	int cols = map[0].size();
	for(int i = 0; i < 4; i++) {
		//行标太小或太大,列标太小或太大,当前位置是否有小方块
		if (smallBlocks[i].col < 0 || smallBlocks[i].col >= cols
			|| smallBlocks[i].row < 0 || smallBlocks[i].row >= rows
			|| map[smallBlocks[i].row][smallBlocks[i].col] != 0) {

			return false;
		}
	}
	return true;
 
}

void Block::solidify(vector<vector<int>>& map)
{
	for (int i = 0; i < 4; i++) {
		// 设置标记,“固化”对应位置
		map[smallBlocks[i].row][smallBlocks[i].col] = blockType;
	}

}

int Block::getBlockType()
{
	return blockType;
}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值