#include<iostream>
#include<string>
#include<ctime>
#include<conio.h>
#include<Windows.h>
#include<cstdlib>
using namespace std;
#define Ht 20
#define Wh 10
string Map[Ht][Wh];
void Print_map()
{
for (int i = 0; i < Ht; i++) {
for (int j = 0; j < Wh; j++) {
cout << Map[i][j];
}
cout << endl;
}
}
class Coord
{
public:
int m_X;
int m_Y;
Coord(int x,int y) {
m_X = x;
m_Y = y;
}
bool operator==(Coord C) {
return C.m_X == m_X && C.m_Y == m_Y;
}
};
class Assets
{
public:
int m_X[4];
int m_Y[4];
Assets(){}
Assets(int* s1) {
srand((unsigned int)time(NULL));
for (int i = 0; i < 4; i++) {
m_X[i] = s1[i];
}
m_Y[0] = 5;
}
};
Assets Choice[6];
//废弃不用的决策号
int a0_x[4] = { 0,0,0,0 };
Assets a0(a0_x);
/////////////
// ■ //
// ■ //
// ■■ //
// //
/////////////
int a1_x[4] = { 0,1,2,2 };
Assets a1(a1_x);
/////////////
// ■ //
// ■ //
// ■ //
// ■ //
/////////////
int a2_x[4] = { 0,1,2,3 };
Assets a2(a2_x);
/////////////
// ■ //
// ■■ //
// ■ //
// //
/////////////
int a3_x[4] = { 0,1,1,2 };
Assets a3(a3_x);
/////////////
// ■■ //
// ■■ //
// //
// //
/////////////
int a4_x[4] = { 0,0,1,1 };
Assets a4(a4_x);
/////////////
// ■■ //
// ■■ //
// //
// //
/////////////
int a5_x[4] = { 0,0,1,1 };
Assets a5(a5_x);
//////////////////////////////////////////////////////////////////////////////////////
//输入一个Assets块将其添加到Map中(在打印层面上)
void assign_ast(Assets A)
{
for (int i = 0; i < 4; i++) {
Map[A.m_X[i]][A.m_Y[i]] = "□";
}
Sleep(100);
}
//输入一个Assets块将其从Map中删除(在打印层面上)
void delete_ast(Assets A)
{
for (int i = 0; i < 4; i++) {
Map[A.m_X[i]][A.m_Y[i]] = " ";
}
}
//////////////////////////////////////////////////////////////////////////////////////
void create_ast()
{
srand((unsigned int)time(NULL));
int judge = rand() % 5 + 1;
assign_ast(Choice[3]);
}
//禁止触碰的位点(在触碰之后需要更新)
Coord Pbt_area[Wh - 2] = { {18,1},{18, 2},{18, 3},{18, 4},{18, 5},{18, 6},{18, 7},{18, 8} };
//检查是否被触碰(若位点被赋值"□",那么函数将返回是)ps:这里只输入当前正在操控的temp
bool check_if(Assets temp)
{
bool exit = 0;
Coord coord_0(temp.m_X[0], temp.m_Y[0]);
Coord coord_1(temp.m_X[1], temp.m_Y[1]);
Coord coord_2(temp.m_X[2], temp.m_Y[2]);
Coord coord_3(temp.m_X[3], temp.m_Y[3]);
Coord ALL[4] = { coord_0 ,coord_1 ,coord_2 ,coord_3 };
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 8; j++) {
exit = exit || (ALL[i] == Pbt_area[j]);
}
}
return exit;
}
int main()
{
srand((unsigned int)time(NULL));
// 画面的初始化 && 添加边框
for (int i = 0; i < Ht; i++) {
for (int j = 0; j < Wh; j++) {
Map[i][j] = " ";
}
}
for (int i = 0; i < Ht; i++) {
for (int j = 0; j < Wh; j++) {
if (j == 0 || j == Wh - 1 || i == 19) {
Map[i][j] = "■";
}
}
}
//////////////////////////////////////////////////////////////////////////////////////
//Assets块的设计
a1.m_Y[1] = a1.m_Y[0];
a1.m_Y[2] = a1.m_Y[0];
a1.m_Y[3] = a1.m_Y[0] - 1;
a2.m_Y[1] = a2.m_Y[0];
a2.m_Y[2] = a2.m_Y[0];
a2.m_Y[3] = a2.m_Y[0];
a3.m_Y[1] = a3.m_Y[0];
a3.m_Y[2] = a3.m_Y[0] - 1;
a3.m_Y[3] = a3.m_Y[0];
a4.m_Y[1] = a4.m_Y[0] - 1;
a4.m_Y[2] = a4.m_Y[0] - 1;
a4.m_Y[3] = a4.m_Y[0];
a5.m_Y[1] = a5.m_Y[0] - 1;
a5.m_Y[2] = a5.m_Y[0] - 1;
a5.m_Y[3] = a5.m_Y[0] - 2;
Assets Choice[6] = { a0,a1,a2,a3,a4,a5, };
Assets temp;
int judge;
//游戏进行循环
while (true)
{
//随机创建一个Assets块
judge = rand() % 5 + 1;
assign_ast(Choice[judge]);
temp = Choice[judge];
while (true)
{
Print_map();
if (_kbhit()) {
int key = _getch();
switch (key)
{
//输入a(代表顺时针旋转)
case 97:
break;
//输入d(代表逆时针旋转)
case 100:
break;
default:
break;
}
}
//刷新正在操控的Assets块,使其向下
delete_ast(temp);
for (int i = 0; i < 4; i++) {
temp.m_X[i]++;
}
assign_ast(temp);
//程序的出口(若正在操控的Assets块触碰到了违法位点,函数将输出1)
if (check_if(temp)) {
for (int i = 0; i <= 18; i++) {
for (int j = 1; j <= 8; j++) {
if (Map[i][j] == "□") {
Map[i][j] = "■";
}
}
}
system("cls");
break;
}
//违法位点的更新
int i;
for (int j = 1; j <= 8; j++) {
i = 0;
while (Map[i][j] != "■"){
i++;
}
Pbt_area[j].m_X = i - 1;
Pbt_area[j].m_Y = j;
}
system("cls");
}
//检查是否有行被填满
for (int i = 18; i >= 0; i--) {
for (int j = 18; j >= 0; j--) {
}
}
}
system("pause");
return 0;
}
感觉代码暂时没什么问题,我运行起来也没有发现游戏框右移的情况。你可以自己的命令行字体是不是出现了一些问题。
或者注释掉第264行,并把263行的j换成j-1。
就是不知道为什么,一把违法位点的更新的注释取消,就在打印游戏边框的时候,第一行偏移了
你那个旋转和左右移动的都还没写呢,
在assign_ast函数加个延迟可以使其慢慢降落。
为什么偏移了
参考地址:
https://zhuanlan.zhihu.com/p/121152511
https://www.bilibili.com/video/BV1P7411677z?from=search&seid=8395073364390701112
Github源码:https://github.com/RainbowRoad1/Cgame
#include <iostream>
#include <conio.h>
#include <windows.h>
#include <ctime>
using namespace std;
int map[250]; //地图数组
int X = 4, Y = 1; //方块组分别距左墙、上墙的距离
class Square //方块组类
{
private:
int node[28][4] = //节点数组,规律存储各方块组的形状
{//每四个数存储一个形状的方块组在地图中对应的信息
//其含义可以看作在平面直角坐标系中,但不存在可以随机访问的y轴,而最大X轴为10
//要访问特定y轴的数,就需要对x轴进行加减,x+n大于10的部分会在y+1轴重新计算,反之在y-1
{ -1,0,1,-11 },{ 10,0,-10,-9 },{ 11,-1,0,1 },{ 9,10,0,-10 }, { -1,0,1,-9 },{ 10,11,0,-10 },{ 9,-1,0,1 },
{ 0,10,-10,-11 }, { 9,10,0,1 },{ 11,0,1,-10 },{ 9,10,0,1 },{ 11,0,1,-10 }, { 10,11,-1,0 },{ 10,0,1,-9 },
{ 10,11,-1,0 },{ 10,0,1,-9 },{ -1,0,1,-10 },{ 10,0,1,-10 },{ 10,-1,0,1 },{ 10,-1,0,-10 },{ 20,10,0,-10 },
{ -1,0,1,2 }, { 20,10,0,-10 }, { -1,0,1,2 }, { 10,11,0,1 }, { 10,11,0,1 }, { 10,11,0,1 }, { 10,11,0,1 }
};
//方块组形状,循环
int form = rand() % 1;
bool sign = false; //判断是否执行了方块组下方为边界或其他方块的标记
public:
int& getform() //获取和修改当前方块组的形状编号
{
return form;
}
bool move(int& Z, int L)
{
Z += L; //形参1 += 形参2
for (int i = 0; i < 4; i++)
{
int j = (node[form][i] + 11) % 10 - 1 + X; //计算出小方块所在地图的列数
//如果当前方块组修改之后有方块的x轴小于0或x轴大于9
//或有方块的列数大于24
//或更改方块组Y坐标后方块所在的位置上已经有其他方块
//则撤回移动并返回1
if (j < 0 || j > 9 || ((Y * 10 + X +node[form][i]) / 10) > 24 || map[Y * 10 + X +node[form][i]])
{
//如果方块组移动后纵坐标>24或下方有其他方块为真,则设置标记为true,否则标记为false
(((Y * 10 + X +node[form][i]) / 10) > 24 || map[Y * 10 + X +node[form][i]])
?
sign = true : sign = false;
Z -= L; //因为撤回方块组移动会更改方块组的数值,
//而判断方块组下方边界和其他方块需要的时方块组移动之后的数据
//所以撤回撤回移动写在标记赋值下边
return false;
}
}
//i从后向前遍历地图数组,k用来累加地图一行中为1值的个数,为10则执行消行
for (int i = 249, k = 1; i > 9; i--)
{
if (i % 10 == 0)
k = 0;
if (map[i] == 1)
k++;
if (k == 10)
for (int j = i / 10 * 10 + 9; j >= 0; j--)
j - 10 >= 0 ? map[j] = map[j - 10] : map[j] = 0;
}
return true; //正常移动返回true
}
void setmap() //将方块组对应的地图位置赋值为-1
{
for (int i = 0; i < 4; i++)
map[Y * 10 + X +node[form][i]] = -1;
}
void drop() //下落
{
if (move(Y, 1)||(!sign)) //正常下落或方块组碰左右墙直接返回
return;
//方块组成功落地,执行地图对应位置赋值并重置下落方块组的操作
for (int i = 0; i < 4; i++) //将当前方块组在地图中对应的位置赋为1
map[Y * 10 + X + node[form][i]] = 1;
X = 4, Y = 1, form = rand() % 28; //将方块组的位置和形状编号进行重置
for (int i = 0; i < 4; i++)
map[Y * 10 + X + node[form][i]] = -1;
}
};
void HideCursor() //光标隐藏
{
CONSOLE_CURSOR_INFO cursor_info = { 1, 0 };
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}
int main()
{
system("mode con:cols=20 lines=25"); //设置控制台宽高,且因为需要用到控制台自动换行,控制台必为10宽,且因为map原因必为25高
HideCursor(); //光标隐藏
srand((unsigned)time(NULL)); //随鸡种子
Square square; //方块组类
//游戏循环
while (true)
{
if (_kbhit()) //键盘按下时
{
char key = _getch();
switch (key)
{
case 'a': //左移
square.move(X,-1); break;
case 'd': //右移
square.move(X, 1); break;
case 's': //下落
if (Y < 2) break; //方块重置时不可连按下落,否则有时会出现bug,原因不明
square.drop(); break;
case ' ': //暂停
_getch(); break;
case 'w': //旋转
//旋转的本质是改变方块组的形状,将方块组编号传入move中修改,可对修改后的方块组进行越界检测
//修改后方块组如果与方块1、下、左、右墙重合则会被取消改变
int &tempf = square.getform();
square.move(tempf, (tempf % 4) < 3 ? 1 : -3);//将(form % 4) < 3 ? 1 : -3)与form相加即可得到节点数组中与形状相对应的旋转形状,其中玄机,还请自悟
}
}
square.setmap(); //将方块组对应在地图中的位置修改为-1
//遍历地图数组,非0的位置输出■代表方块
for (auto i = 0; i < 250; i++)
{
cout << (map[i] ? "■" : " ");
if(map[i] == -1) //必须加if(map[i]==-1)的原因为:在方块组成功下落后,
//地图对应位置会变为1来表示已经下落的方块,如果不加判断会将已经下落的方块重新置为0
map[i] = 0; //输出完方块后将当前地图值重新赋值为0
}
square.drop(); //方块组下落
Sleep(150);
}
return 0;
}
看半天,就是你数组越界了,改到Map第一个格子的内存实心方块变成了\b,导致左上角第一个不显示方块并且第一行整体左移了一点。
Pbt_area[j-1].m_X = i - 1;
Pbt_area[j-1].m_Y = j;
这里改一下,你的循环从1开始的当j=8的时候就越界了。
建议:数组的循环和定义的长度保持一致Coord Pbt_area[PBT_LEN],不要只用写死数字for (int j = 1; j <= PBT_LEN + 1; j++) {
【如果解决了你的问题,顺手点个采纳呗(>^ω^<)】