C++版俄罗斯方块(不是C语言哦)

目录

1. 概述

2. 模块设计

3. 创建项目

 4. 设计Tetris的主要接口

 5. 设计Block的主要接口

6. 启动游戏

7. 实现游戏的主体架构

7.1 实现游戏的主体架构

7.2 补充私有数据成员

7. 3 补充私有成员函数

7.4 完善游戏主体架构

8. 创建新方块

 9. 绘制方块

10. 实现游戏场景

10.1 游戏过程中数据的存储

10.2 数据数据的初始化

10.3 初始化游戏场景

10.4 测试游戏场景

11. 完善方块的渲染

11.1 新方块和预告方块的创建

11.2 渲染方块

12. 俄罗斯方块的降落

 13. 实现俄罗斯方块的左右移动

14. 实现旋转变形(待更新)


本教程配套视频

1. 概述

使用C++面向对象思想开发俄罗斯方块游戏。

2. 模块设计

3. 创建项目

本教程配套视频 

添加类Block和Tetris

 4. 设计Tetris的主要接口

本教程配套视频 

Tetris.h 

class Tetris
{
public:
	Tetris(int rows, int cols, int left, int top, int blockSize);
	void init();
	void play();
};

 5. 设计Block的主要接口

Block.h

#include <graphics.h>
class Block
{
public:
	Block();
	void drop();
	void moveLeftRight(int offset);
	void retate(); //旋转
	void draw(int leftMargin, int topMargin);
};

6. 启动游戏

main.cpp

#include "Tetris.h"

int main() {
	Tetris game(20, 10, 56, 58, 36);
	game.play();
	return 0;
}

7. 实现游戏的主体架构

7.1 实现游戏的主体架构

Tetris.cpp

void Tetris::play()
{
    init();

    int timer = 0;
    while (1) {
        keyEvent(); //待定义

        timer += getDelay();   //待定义
        if (timer > delay) {   //delay待定义
            timer = 0;
            drop();            //待定义
            update = true;     //待定义
        }

        if (update) {
            update = false;
            updateWindow();  //待定义
            clearLine();     //待定义
        }
    }
}

7.2 补充私有数据成员

Tetris.h

private:
	int delay;
	bool update;

7. 3 补充私有成员函数

Tetris.cpp

private:
	void keyEvent();
	int getDelay();
	void drop();
	void updateWindow();
	void clearLine();

7.4 完善游戏主体架构

游戏开始时,需要创建新方块,以及下一个方块的预告。

添加新的数据成员

Tetris.h

	Block* curBlock;
	Block* nextBlock; //方块预告

以上两个数据成员都是private权限,并需要补充头文件Block.h

Tetris.h

#include "Block.h"

 完善游戏主题架构

void Tetris::play()
{
    init();

    nextBlock = new Block;
    curBlock = nextBlock;
    nextBlock = new Block;
   
    // ...
}

8. 创建新方块

在调用 new Block 时,会自动调用Block的默认构造函数,所以我们需要在这个构造函数里面完成新方块的创建。

俄罗斯方块的表示方法有很多,最常见的是使用一个二维数组,表示一种俄罗斯方块的某种形态,也就是说,一个俄罗斯方块,需要使用4个二维数组来表示各个形态(4个方向)。我们这里使用一个更灵巧的方式:

Block.h

struct Point {
	int row;
	int col;
};

class Block
{
public:
	Block();
	void drop();
	void moveLeftRight(int offset);
	void retate(); //旋转
	void draw(int leftMargin, int topMargin);

	int getBlockType();

private:
	int x;
	int y;
	int blockType;
	Point smallBlocks[4];
	IMAGE* img;

private:
	static int size;
	static IMAGE* imgs[7];
};

 注意,在这里,我们把所有的方块图像,定义为Block类的static数据成员.

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;
			getimage(imgs[i], i * size, 0, size, size);
		}
		SetWorkingImage();
		srand(time(NULL));
	}

	// 以下,对每个新创建的方块,都要执行:
	blockType = 1 + rand() % 7;
	img = imgs[blockType - 1];

	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, // 田
	};
	for (int i = 0; i < 4; i++) {
		smallBlocks[i].row = blocks[blockType - 1][i] / 2;
		smallBlocks[i].col = blocks[blockType - 1][i] % 2;
	}
}

同时把项目属性的字符集,修改为多字节字符集。

 9. 绘制方块

绘制正在降落过程中的方块。

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

10. 实现游戏场景

10.1 游戏过程中数据的存储

添加以下数据成员,用来表示游戏的状态数据,用一个二维数组来表示各个位置的状态。

	int rows;
	int cols;
	int leftMargin;
	int topMargin;
	int blockSize;
	IMAGE imgBg;
	vector<vector<int>> map;

10.2 数据数据的初始化

在Tetris类的构造函数中,对游戏数据进行初始化。

Tetris::Tetris(int rows, int cols, int left, int top, int blockSize)
{
    this->rows = rows;
    this->cols = cols;
    this->leftMargin = left;
    this->topMargin = top;
    this->blockSize = blockSize;

    for (int i = 0; i < rows; i++) {
        vector<int> row;
        for (int j = 0; j < cols; j++) {
            row.push_back(0);
        }
        map.push_back(row);
    }
}

10.3 初始化游戏场景

const int SPEED_NORMAL = 500; //普通速度 
const int SPEED_QUICK = 50;   //快速降落速度

void Tetris::init()
{
    initgraph(640, 832);
    loadimage(&imgBg, "res/bg.jpg");
    
    delay = SPEED_NORMAL;
  
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            map[i][j] = 0;
        }
    }
}

10.4 测试游戏场景

已经定义很多成员函数,但是大部分都还没有做具体的实现,现在把这些成员函数都补充一个空的函数体,以便测试,后面再详细实现各个函数接口。

修改main函数,添加测试代码:

int main() {
	Tetris game(20, 10, 56, 58, 36);

	//game.play();

	game.init();
	Block block;
	block.draw(56, 58);
	system("pause");

	return 0;
}

执行效果如下:

11. 完善方块的渲染

11.1 新方块和预告方块的创建

在7.4中已经实现了新方块和预告方块的定义和创建。

11.2 渲染方块

在Block类中添加接口getImages, 以获取各种方块的图形纹理。

//Block.h
class Block {
public:
    static IMAGE** getImages();
    ......
}

 添加getImages的实现:

//Block.cpp
IMAGE** Block::getImages()
{
	return imgs;
}

渲染俄罗斯方块:

void Tetris::updateWindow()
{
	BeginBatchDraw();

	putimage(0, 0, &imgBg);

	IMAGE** imgs = Block::getImages();
	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();
}

12. 俄罗斯方块的降落

在Tetris类中添加数据成员,用来备份当前正在降落的俄罗斯方块,以便让俄罗斯方块进入非法位置后进行还原。

Block bakBlock;

实现俄罗斯方块的降落操作:

void Tetris::drop()
{
	bakBlock = *curBlock;
	curBlock->drop();


	if (!curBlock->blockInMap(map)) {
		bakBlock.solidify(map);
		delete curBlock;
		//curBlock = new Block;
		curBlock = nextBlock;
		nextBlock = new Block;
	}

	delay = SPEED_NORMAL; //每下将一次,就把降落速度还原成普通速度
}

补充实现Block的赋值构造函数:

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

补充实现Block的固化功能:

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

测试效果:

 13. 实现俄罗斯方块的左右移动

在之前的按键事件处理中,实现左右移动:

void Tetris::keyEvent()
{
	int dx = 0;
	bool rotateFlag = false;
	unsigned char ch = 0;

	while (_kbhit()) {
		unsigned char ch = _getch();
		if (ch == 224) {
			ch = _getch();
			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;
	}
}

添加内部成员函数moveLeftRight:

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


	if (!curBlock->blockInMap(map)) {
		*curBlock = bakBlock;
	}
}

实现Block类的moveLeftRight

void Block::moveLeftRight(int offset)
{
	for (int i = 0; i < 4; i++) {
		smallBlocks[i].col += offset;
	}
}

测试效果:

14. 后续实现见 本教程配套视频​​​​​​​

  • 65
    点赞
  • 272
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
1. 游戏需求 随机给出不同的形状(长条形、Z字形、反Z形、田字形、7字形、反7形、T字型)下落填充给定的区域,若填满一条便消掉,记分,当达到一定的分数时,过关,设置几个关卡,每关方块下落的速度不同,若在游戏中各形状填满了给定区域,为输者。 2.游戏界面需求: 良好的用户界面,有关数显示和分数显示,以及下一个方块显示。让方块在一定的区域内运动和变形,该区域用一种颜色表明 。还需用另一种颜色把该区域围起来,宽度适中,要实现美感。 3.游戏形状(方块)需求: 良好的方块形状设计,绘制七种常见的基本图形(长条形、Z字形、反Z形、田字形、7字形、反7形、T字型),各个方块要能实现它的变形,可设为顺时针或逆 时针变形,一般为逆时针。 4.输入与输出: 用户根据需要输入上(输出旋转效果)、左右(输出移动效果)、下(输出加速下沉效果)、空格(沉底)、P(暂停)、ESC(退出)。 2.游戏界面需求: 良好的用户界面,有关数显示和分数显示,以及下一个方块显示。让方块在一定的区域内运动和变形,该区域用一种颜色表明 。还需用另一种颜色把该区域围起来,宽度适中,要实现美感。 3.游戏形状(方块)需求: 良好的方块形状设计,绘制七种常见的基本图形(长条形、Z字形、反Z形、田字形、7字形、反7形、T字型),各个方块要能实现它的变形,可设为顺时针或逆 时针变形,一般为逆时针。 4.输入与输出: 用户根据需要输入上(输出旋转效果)、左右(输出移动效果)、下(输出加速下沉效果)、空格(沉底)、P(暂停)、ESC(退出)。
### 回答1: 在使用Devc进行俄罗斯方块制作前,我们需要先了解一下俄罗斯方块的游戏规则和操作方式。俄罗斯方块是一款经典的益智游戏,玩家需要控制下落方块的运动轨迹,让它们落到正确的位置上以组成一行或多行完整的方块,被消去后就可以得到积分。 制作俄罗斯方块的过程可以分为如下几个步骤: 1. 设计游戏界面,包括游戏背景、方块的样式、方块落下的速度和方向等。 2. 编写游戏逻辑,主要包括方块的运动控制、碰撞检测、消行处理等,需要用到C语言中的一些基本语法和算法知识。 3. 实现用户操作,可以通过键盘输入来控制下落方块的方向和移动速度,使得游戏更加 interactive。 4. 添加音效和动画效果,使得游戏变得更加生动有趣。 将以上步骤整合在一起,我们便可以使用Devc来进行俄罗斯方块的制作了。在编写代码时,需要注意逻辑清晰、语法规范,有助于提高程序的可读性和可维护性。同时,需要反复测试,解决程序中可能存在的bug,保证游戏质量和用户体验。 总之,在使用Devc进行俄罗斯方块制作的过程中,需要掌握一定的C语言编程基础,并且注重游戏设计和用户体验,这样才能制作出一款精致、有趣、富有挑战性的俄罗斯方块游戏。 ### 回答2: 俄罗斯方块是一款经典的休闲益智游戏,可在Dev-C++中制作。Dev-C++是一款C++编程软件,为了实现俄罗斯方块游戏,我们需要采取以下步骤: 首先,我们需要考虑游戏的逻辑。俄罗斯方块由七种方块积木组成,每种方块由四个小方块组成。游戏开始时,积木块从上方逐渐下落,玩家需要使用方向键控制积木块的移动和旋转,在底部固定后,积木块将形成一行并消除得分。如果积木块达到顶部则游戏结束。 其次,我们需要创建一个游戏界面,包括积木块、得分和游戏状态的显示。使用Dev-C++提供的图形库可以轻松实现这些功能。 接着,我们需要实现程序的基本框架,包括游戏的初始化、主循环和事件处理等。通过编写相应的函数,可以确保游戏逻辑正确运行。 最后,我们需要优化游戏的性能和可玩性,例如增加游戏难度、音效和高分榜等功能。 总之,利用Dev-C++可以轻松制作出一款经典的俄罗斯方块游戏,不仅可以满足编程爱好者的需求,也可以为玩家提供趣味益智的游戏体验。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

程序员Rock

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

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

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

打赏作者

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

抵扣说明:

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

余额充值