俄罗斯方块TC版移植VC

文章详细记录了如何将一个基于TC的16位俄罗斯方块游戏移植到Windows10的VS2019环境中,包括选择使用EasyX库替代图形函数,解决编译错误,调整代码以适应新的库和编程环境,以及在移植后进行的优化工作,如处理文件打开失败、改进延时函数等。
摘要由CSDN通过智能技术生成

前言

以前在TC上编写了一个小游戏--俄罗斯方块(参见以前的文章: ),现在想试玩一下,现在我的电脑是win10系统,发现TC完全运行不起来了。
TC.exe运行不起来,报错:
不支持的16位应用程序:与64位版本的windows不兼容。

可是,我还是想让它能跑起来。
上网查了一下,发现并不能简单的移植,还是需要做一些工作的。
下面记录一下我将这个小游戏移植到VS2019中的经历。
    

一、选择移植方案

    首先是上网进行搜索,看有哪些移植方案。
    在参考了一些网上的文章后,我选择使用 easyx 库来进行画图,这个库的使用方式非常接近原来TC中的画图方式。
        先需要安装库,步骤比较简单,如下:
(1)到 www.easyx.cn 下载 EasyX 库,解压。
(2)执行 Setup 安装,点下一步,可以看到 VC 版本选择界面:
(3)找到您希望安装 EasyX 的 VC 版本,点后面的“安装”。
(4)等待安装完成即可。

二、新建工程

    在VS2019中新建了一个工程,选择控制台程序。
    之所以选择控制台程序,是因为其流程,与TC程序的控制流程基本相同。


三、将原来的C文件,整个拷贝到VC的主要的cpp文件中。

    例如,我就是将原来TC中的 BLOCK.C 中的内容,完全拷贝到新建的工程中的 block.cpp 中。

四、开始编译

    当然遇到一堆的报错,大体分为4类:

1,未定义的标志符:

1)例如图形模式初始化(initgraph())的参数: VGA 
    在easyx中,不需要这样的参数了,只需要指定分辨率就行了。
    原来的初始化:

    initgraph(VGA, VGAHI, "c://tc//egavga.bgi");

    修改为:

    initgraph(640,480);

2)getch()未定义,添加相应头文件:conio.h

3)randomize()、random()未定义,
    修改:
    randomize() -> srand((unsigned)time(NULL))  -》相应添加time()函数需要的头文件 time.h
    random(100)->rand()%100
    
2,有一些函数VS提示为不推荐,修改为使用VS推荐的函数来代替:
    getch()->_getch()
    kbhit()->_kbhit()
    sprintf()->sprintf_s()
    fp = fopen("bloscore.txt", "rb+"); -> fopen_s(&fp, "bloscore.txt", "rb+");

3,缺少类型说明符
    是main()函数,需要返回int类型。
    main() -> int main()
    并且,在最后简单添加返回值:
    return 0;

4,没有与参数列表匹配的重载函数
    在界面上显示文本的时候,我调用的这个函数报错了:

    outtextxy(20, 20, str);

    这个函数有两个定义:

    void outtextxy(int x, int y, LPCTSTR str);
    void outtextxy(int x, int y, TCHAR c);

    由于我是要显示一个字符串,所以希望是上面那个函数,即第三个参数使用 LPCTSTR 。
    继续跟踪,

    typedef LPCWSTR PCTSTR, LPCTSTR;
    typedef _Null_terminated_ WCHAR *NWPSTR, *LPWSTR, *PWSTR;
    typedef wchar_t WCHAR;    // wc,   16-bit UNICODE character

    是16位的UNICODE编码格式的宽字符。
    而我传入的是char*,所以需要做类型转换。
    当然,有另一种解决方法,就是修改工程属性,不使用宽字符:
    配置->属性->高级->字符集 修改为"使用多字节字符集"
    但是官方建议使用宽字符,在VS2013及以后版本中默认使用Unicode字符集了。    这样使程序能够更加国际化,在不同语言设置的计算机上运行且文字显示正常。
    为了通用,我这里是修改为使用宽字符函数。    考虑到这个函数outtextxy()有多处调用,每个地方都做类型的转换,比较麻烦,所以,封装了一个函数,接收char*的输入,转换为wchar*,然后调用输出显示。如下:    

void outtextxy_z(int x, int y, char* str) {
    TCHAR lpszBuf[1000];
    wmemset(lpszBuf, 0, 1000);
    int   nLen = strlen(str);
    int   nwLen = MultiByteToWideChar(CP_ACP, 0, str, nLen, NULL, 0);
    MultiByteToWideChar(CP_ACP, 0, str, nLen, lpszBuf, nwLen);
    outtextxy(x, y, lpszBuf);
}  

 这样,就只是简单的进行函数名称的修改,就解决这个编译错误了,如下:
outtextxy(20, 20, str); -> outtextxy_z(20, 20, str);

至此,blcok.cpp编译通过。

五、运行调试

虽然编译通过了,但是,移植工作并没有结束。
此时,运行程序,确实画出了一个界面,左上角的标题栏是block,正常,但是画面里面一片黑,没有我希望的那些方块出现!
心里有点发慌,为什么?怎么办?

既然是程序有问题,那就开始调试吧。
先理理思路:
首先,回归到起点:我能否画出一个基本的方块来?
还记得之前的文章中说的么,能画出一个方块来,就可以画出多个方块来,进行合适的拼装,就是俄罗斯方块了。

先添加调试手段,可以单步调试,也可以添加输出调试信息。

单步执行并没有问题,但是界面显示不出方块来,很可能是画图的方法存在问题。
参考easyx的例程:
图解在VC里使用graphics.h绘图(类似TC)

可以正常运行。

通过对比例程,发现有4个地方的差异需要修改:

1,颜色取值的差异
在TC的setcolor()函数中,参数是一个0-15的值,代表一种典型颜色。
而在easyx中的setcolor()函数中,参数值是一个RGB的值,取值范围为0-0xffffff。
这里,就需要有一个对应关系:
先弄明白原来在TC中的值的含义:
black 0     drakgray 8
blue 1         lightblue 9
green 2     lightgeen 10
cyan 3         lightcyan 11
red 4         lightred 12
magenta 5     lightmagenta 13
brown 6     yellow 14
lightgray 7 white 15 
一共是0—15

在easy.h中,有对应的颜色常量值定义:

// Color constant
#define    BLACK            0
#define    BLUE            0xAA0000
#define    GREEN            0x00AA00
#define    CYAN            0xAAAA00
#define    RED                0x0000AA
#define    MAGENTA            0xAA00AA
#define    BROWN            0x0055AA
#define    LIGHTGRAY        0xAAAAAA
#define    DARKGRAY        0x555555
#define    LIGHTBLUE        0xFF5555
#define    LIGHTGREEN        0x55FF55
#define    LIGHTCYAN        0xFFFF55
#define    LIGHTRED        0x5555FF
#define    LIGHTMAGENTA    0xFF55FF
#define    YELLOW            0x55FFFF
#define    WHITE            0xFFFFFF

我们只需要一一对应替换即可。

2,设置颜色的方法;
在easyx中,有多个设置颜色的函数
setfillcolor
setlinecolor
settextcolor
setbkcolor
在TC中,就是一个setcolor(),所以,需要根据画图的内容,来选择不同的设置颜色的函数。

3,画图的方法;
在easyx中,画矩形分两个函数:
一个是画矩形的边框:
rectangle
一个是画填充颜色的矩形:
fillrectangle

画圆形也是一样:
circle
This function is used to draw a circle without filling
fillcircle
This function is used to draw a filled circle with a border.
所以,我们要根据画边框还是填充图形,来选择不同的画图函数。

4,设置填充的方式;
setfillstyle()函数的调用方式,参数不同,也需要调整。

举个例子,
以前在TC中是这样画一个矩形:

    setfillstyle(1, 14);
    setcolor(14);
    rectangle(200 , 10 , 215, 25);

现在使用easyx,对于要填充颜色的矩形,要改为这样:

    setfillstyle(BS_SOLID);//设置填充方式时,可以不指定颜色了
    setfillcolor(YELLOW);//设置颜色时,需要指定是填充方式
    fillrectangle(200 , 10 , 215, 25);//调用的函数不同,指明了是要fill的

对于只画边框的,如下改写:

    setlinecolor(YELLOW);//设置颜色时,需要指定是用于线条的颜色
    rectangle(200 , 10 , 215, 25);//调用的函数不同,指明了只是画矩形的边框

改完上面4类差异后,再次运行,就出画面了。初步测试,游戏可以正常进行。
    至此,移植工作可以说是基本完成了。

游戏界面如下:

六、完善程序

     毕竟是很久前写的程序了,运行时发现有点问题,就又做了点优化:

1,save()函数中,对存储分值的文件,添加打开失败的处理;

2,程序中减去几个_getch(),减少操作,使用起来更顺畅;

3,添加结束的提示信息;
    其实就是一个简单的提示,在界面上显示了一句话:

void showGameOver(void)/* 提示游戏结束 */
{
    char a, b = 1;
    settextcolor(YELLOW);
    sprintf_s(str, "%s", "Game over!");
    outtextxy_z(20, 60, str);
}

4,改进结束时的判断;
    结束判断时,原来是只判断了最顶端的一行是否有方块存在,现在改为判断两行,更合理一些。
    在结束判断前,先把新产生的方块画出来,避免还没有看见导致最终结束的方块就结束了游戏。
    
5,改进延时函数dl()的实现,使用sleep来替换dl函数中的for循环,降低cpu占用率;

void dl(int a)/* 进行延时 ,避免完全占用cpu */
{
    //int r, n;
    //for (r = 0; r < a; r++)
    //    for (n = 0; n < 30000; n++)
    //    {
    //        n++;
    //        n--;
    //    }

    int num = 20;
    int min_delay = num * 10;
    int ms = 1;
    if (a < min_delay) {
        ms = 10;
    }
    else {
        ms = a / num;
    }
    Sleep(ms);
}

6,添加一个调试方法,可以在VS的“输出”窗口中输出调试信息:

bool _trace(char* format, ...)
{
    char buf[1000];
    TCHAR lpszBuf[1000];
    va_list argptr;
    va_start(argptr, format);
    vsprintf_s(buf, format, argptr);
    wmemset(lpszBuf, 0, 1000);
    int   nLen = strlen(buf);
    int   nwLen = MultiByteToWideChar(CP_ACP, 0, buf, nLen, NULL, 0);
    MultiByteToWideChar(CP_ACP, 0, buf, nLen, lpszBuf, nwLen);
    va_end(argptr);
    OutputDebugString(lpszBuf);
    return true;
}

移植前后的代码对比图(部分):

 
七、源码地址:

俄罗斯方块移植到VS后的源码:https://download.csdn.net/download/lintax/87419128

后记:
我原来还在TC下编写了一个小游戏,贪吃蛇,也完成了到VS2019的移植,路径如下:https://download.csdn.net/download/lintax/87419133

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值