基于MFC的俄罗斯方块小游戏(二)

C++ vs2019 基于MFC实现俄罗斯小方块游戏 –文件可自取,提取码 xhhs– 添加链接描述 #主要框架来自于杭电李卫明老师的教学视频(如果真要写的话可以先看他的视频,本文主要记录一些拓展功能的实现) 添加链接描述 1. 绘制自定义的图形 在4*4(当然也可以随便)的方格(看成二维数组)里把需要的地方随便赋值,画个南瓜饼(把除了四个角都赋值)什么的都行。 2.得分与显示 在game类加上一个数据成员表示这局游戏的分数,消行的时候增加分数(连续消行也可以加的更多)。 显示,绘制一个edit控件,然后对它右键选择创建变量,变量类型设为“值”,类型为int 阅读详情

上次讲到一些基本的类、操作函数、还有一些乱七八糟的变量的定义,这次我们来完成操作函数的实现!

1、将png贴图透明
void TransparentPNG(CImage *png)  
{  
    for(int i = 0; i <png->GetWidth(); i++)  
    {  
        for(int j = 0; j <png->GetHeight(); j++)  
        {  
            unsigned char* pucColor = reinterpret_cast<unsigned char *>(png->GetPixelAddress(i , j));  
            pucColor[0] = pucColor[0] * pucColor[3] / 255;  
            pucColor[1] = pucColor[1] * pucColor[3] / 255;  
            pucColor[2] = pucColor[2] * pucColor[3] / 255;  
        }  
    }  
}  
2、随机产生方块的颜色
void CChildView::DrawBlock(int flag)
{

    for(int c=0;c<4;c++)
    {
        int i=rand()%colorKind;  //颜色挑一种
        block.eachPane[c].imageKind=i;

    }
}
3、随机产生方块的形状
void CChildView::GenerateBlock(struct Block &blk)
{
    int sel=rand()%7;
    switch(sel)
    {
    case 0://田
        blk.eachPane[0].x=4;
        blk.eachPane[0].y=0;
        blk.eachPane[1].x=5;
        blk.eachPane[1].y=0;
        blk.eachPane[2].x=4;
        blk.eachPane[2].y=1;
        blk.eachPane[3].x=5;
        blk.eachPane[3].y=1;
        blk.x1=blk.eachPane[0].x;
        blk.x2=blk.eachPane[3].x;
        blk.y1=blk.eachPane[0].y;
        blk.y2=blk.eachPane[3].y;
        break;
    case 1://一
        blk.eachPane[0].x=3;
        blk.eachPane[0].y=0;
        blk.eachPane[1].x=4;
        blk.eachPane[1].y=0;
        blk.eachPane[2].x=5;
        blk.eachPane[2].y=0;
        blk.eachPane[3].x=6;
        blk.eachPane[3].y=0;
        blk.x1=blk.eachPane[0].x;
        blk.x2=blk.eachPane[3].x;
        blk.y1=blk.eachPane[0].y;
        blk.y2=blk.eachPane[3].y;
        break;
    case 2://L
        blk.eachPane[0].x=4;
        blk.eachPane[0].y=0;
        blk.eachPane[1].x=4;
        blk.eachPane[1].y=1;
        blk.eachPane[2].x=4;
        blk.eachPane[2].y=2;
        blk.eachPane[3].x=5;
        blk.eachPane[3].y=2;
        blk.x1=blk.eachPane[0].x;
        blk.x2=blk.eachPane[3].x;
        blk.y1=blk.eachPane[0].y;
        blk.y2=blk.eachPane[3].y;
        break;
    case 3://J
        blk.eachPane[0].x=4;
        blk.eachPane[0].y=0;
        blk.eachPane[1].x=4;
        blk.eachPane[1].y=1;
        blk.eachPane[2].x=5;
        blk.eachPane[2].y=1;
        blk.eachPane[3].x=6;
        blk.eachPane[3].y=1;
        blk.x1=blk.eachPane[0].x;
        blk.x2=blk.eachPane[3].x;
        blk.y1=blk.eachPane[0].y;
        blk.y2=blk.eachPane[3].y;
        break;
    case 4://Z
        blk.eachPane[0].x=4;
        blk.eachPane[0].y=0;
        blk.eachPane[1].x=5;
        blk.eachPane[1].y=0;
        blk.eachPane[2].x=5;
        blk.eachPane[2].y=1;
        blk.eachPane[3].x=6;
        blk.eachPane[3].y=1;
        blk.x1=blk.eachPane[0].x;
        blk.x2=blk.eachPane[3].x;
        blk.y1=blk.eachPane[0].y;
        blk.y2=blk.eachPane[3].y;
        break;
    case 5://反z
        blk.eachPane[0].x=4;
        blk.eachPane[0].y=0;
        blk.eachPane[1].x=4;
        blk.eachPane[1].y=1;
        blk.eachPane[2].x=5;
        blk.eachPane[2].y=1;
        blk.eachPane[3].x=5;
        blk.eachPane[3].y=2;
        blk.x1=blk.eachPane[0].x;
        blk.x2=blk.eachPane[3].x;
        blk.y1=blk.eachPane[0].y;
        blk.y2=blk.eachPane[3].y;
        break;
    case 6://T
        blk.eachPane[0].x=4;
        blk.eachPane[0].y=1;
        blk.eachPane[1].x=5;
        blk.eachPane[1].y=1;
        blk.eachPane[2].x=5;
        blk.eachPane[2].y=0;
        blk.eachPane[3].x=6;
        blk.eachPane[3].y=1;
        blk.x1=blk.eachPane[0].x;
        blk.x2=blk.eachPane[3].x;
        blk.y1=blk.eachPane[2].y;
        blk.y2=blk.eachPane[3].y;
        break;
    }
}
4、更新方块block的上下左右边界
void CChildView::UpdateLimit(struct Block &blk)
{
    blk.x1 = blk.eachPane[0].x;
    blk.x2 = blk.eachPane[0].x;
    blk.y1 = blk.eachPane[0].y;
    blk.y2 = blk.eachPane[0].y;
    for (int i = 1; i < 4; i++)
    {
        if (blk.x1 > blk.eachPane[i].x)
        {
            blk.x1 = blk.eachPane[i].x;
        }
        if (blk.x2 < blk.eachPane[i].x)
        {
           blk.x2 = blk.eachPane[i].x;
        }
        if (blk.y1 > blk.eachPane[i].y)
        {
            blk.y1 = blk.eachPane[i].y;
        }
        if (blk.y2 < blk.eachPane[i].y)
        {
            blk.y2 = blk.eachPane[i].y;
        }
    }
}
5、对方块block进行变形
void CChildView::TransBlockForm()
{
    //struct Block COPY=block;
    int x1 = block.x1;
    int x2 = block.x2;
    int y1 = block.y1;
    int y2 = block.y2;

    if(x2-x1==y2-y1)  //方块为正方形
        return;

     // 变形实现,对4个小方格依次判断重新设定位置
    const int centx = (x1+x2)/2;
    const int centy = (y1+y2)/2;
    for(int i=0;i<4;i++)
    {
        int &x=block.eachPane[i].x;
        int &y=block.eachPane[i].y;
        if(x==centx&&y==centy) continue;  // 中心方块不旋转
        int detx=x-centx;
        int dety=y-centy;
        if(detx>0)  //在中心方块右边
        {
            if(dety==0)//正右方---正下方
            {
                y = centy+detx;
                x = centx;
            }
            else if(dety<0) //右上方--->右下方,列不变
            {
                y = centy-dety;
            }
            else       //右下方--->左下方
            {
                x = centx-detx;
            }
        }
        else if(detx<0)//在中心方块右边
        {
            if(dety==0)//正左方--->正上方
            {
                y = centy+detx;
                x = centx;
            }
            else if(dety<0) //左上方--->右上方,行不变
            {
                x = centx-detx;
            }
            else //左下方--->左上方,列不变
            {
                y = centy-dety;
            }
        }
        else  //与中心点在同列
        {
            y = centy-detx;
            x = centx-dety;
        }
        if(x<0||x>=10||(y>=0)&&gameAreas[y][x].bSet)//变形失败
        {
            //block=COPY;
            TRACE("变形失败\n");
            return;
        }
    }
    TimesTransform++;
    if (TimesTransform == 2) // 连续变形了两次
    {
        Move(RIGHT); // 右移一个单位
        TimesTransform = 0;
    }
    UpdateLimit(block);  // 更新方块的边界
    return;
}
6、对block进行移动
//对block进行移动
void CChildView::Move(int direction)
{
    switch(direction)
    {
    case LEFT:
    case RIGHT:
        if(block.x1+direction<0||block.x2+direction>9) break;
        if(gameAreas[block.y1][block.x1+direction].bSet||gameAreas[block.y1][block.x2+direction].bSet||gameAreas[block.y2][block.x1+direction].bSet||gameAreas[block.y2][block.x2+direction].bSet) break;
        for(int i =0;i<4;i++)
        {   
            block.eachPane[i].x+=direction;
        }
        block.x1+=direction;
        block.x2+=direction;
        break;

    }
}
7、block下降
void CChildView::MoveDown()
{
    if(IsBorder())//到底了或是下方有物体
    {
        SetBlockFlag();
        IsLine();        //是否满足消去条件
        if(block.y1==0) 
            GameOver();
        DrawBlock(flag);
        GenerateBlock(block);
        TimesTransform=0;
        Invalidate();
        return;
    }
    for(int c=0;c<4;c++)
    {
        block.eachPane[c].y+=1;
    }
    block.y1+=1;
    block.y2+=1;
    Invalidate();
    return;
}
8、判断是否满足消去条件
bool CChildView::IsLine()
{
    int u=block.y1;
    //int tupLine = AreasTopLine;
    for(int e = block.y2;e >= u;)
    {
        int k = 0;
        for(k;k < 10; k++)
        {
            if(!gameAreas[e][k].bSet)
                break;
        }
        if(k==10)
        {
            for(int t = e;t >= AreasTopLine;t--)
            {
                for(int r = 0;r <10;r++)
                {
                    gameAreas[t][r].bSet = gameAreas[t-1][r].bSet;
                    gameAreas[t][r].blockkind = gameAreas[t-1][r].blockkind;
                }
            }
            for(e = 0;e < 10;e++)
            {
                gameAreas[AreasTopLine][e].bSet = FALSE;
            }
            score += 100;
            AreasTopLine++;
            u++;
        }
        else
            e--;
    }
    return FALSE;
}
9、判断是否到底
bool CChildView::IsBorder()
{
    if(block.y2>=17)  //到底
        return TRUE;
    for(int c=0;c<4;c++) //左、右、下方是否有物体
    {
        int i= block.eachPane[c].x;
        int j= block.eachPane[c].y;
        if(i>=0&&j>=0&&gameAreas[j+1][i].bSet)
            return TRUE;
    }
    return FALSE;
}
10、标记gameAreas
void CChildView::SetBlockFlag()
{
    TRACE("---------------------SETBLICKFLAG------------------\n");
    if(AreasTopLine > block.y1)
    {

        AreasTopLine=block.y1; //记录最高一行

        TRACE("记录最高一行:topline=%d\n",AreasTopLine);
    }

    for(int c=0;c<4;c++)
    {
        int m=block.eachPane[c].x;
        int n=block.eachPane[c].y;
        int i=block.eachPane[c].imageKind;
        gameAreas[n][m].bSet=TRUE; // 设置此处已经被占据
        gameAreas[n][m].blockkind=i;

    }
    return;
}
10、初始化
void CChildView::Initialize()
{
    srand((unsigned)time(0)+clock());
    background.Load(_T("res\\background.png"));

    GenerateBlock(block);
    DrawBlock(flag);
    for(int c=0;c<4;c++)
    {
        block.eachPane[c].blockPNG.Load(_T("res\\BLOCK.png"));
        TransparentPNG(&block.eachPane[c].blockPNG);
    }
    flag=1;
    score=0;
    AreasTopLine=17;  //初始高度为1
    for(int i=0;i<18;i++)
    {
        for(int j=0;j<10;j++)
        {
            gameAreas[i][j].bSet=FALSE;
            gameAreas[i][j].blockPNG.Load(_T("res\\BLOCK.png"));
            TransparentPNG(&gameAreas[i][j].blockPNG);
        }
    }
    return;
}
11、游戏结束
void CChildView::GameOver()
{
    KillTimer(TIMER_MOVE);
}
12、键盘响应
void CChildView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    switch (nChar)
    {
    case VK_UP:
        TransBlockForm();//改变形状
        Invalidate();
         break;
    case VK_LEFT:
         Move(LEFT);//左移
         Invalidate();
         break;
    case VK_RIGHT:
         Move(RIGHT);//右移
         Invalidate();
         break;
    case VK_DOWN:
         MoveDown();//下降
         Invalidate();
         break;
    }
    return;
}
13、定时器
void CChildView::OnTimer(UINT_PTR nIDEvent)
{
     switch(nIDEvent)  
    {  
    case TIMER_MOVE:
        MoveDown();
        break;
    }  

    //CWnd::OnTimer(nIDEvent);
}
基于MFC的单机对战游戏 我们知道,图像是由一个个像素点组成,如果像画出一个图像也就是把图像的每个点都画出来,但那种画法不能一次性将完整的图像呈现给你,所以我们就采用双缓冲技术画图,代码如下:【画图代码必须加在OnDraw()代码里】《算法原理》这堂课要结课了,这是通过这个课第一次接触C++,所以就用MFC设计一个小游戏(游戏可玩性还可以更丰富,主要是考试多加没时间,就没有加那么多,有兴趣的可以在此基础上丰富我的代码)if (HeroAttack.frame == 4) //到最后了再重头开始。 阅读详情

相关推荐

VC/MFC 俄罗斯方块双人版(基于MFC单文档)

游戏最终界面如图:题目要求参考VC++程序设计实验指导书,将上次编写的dos版双人俄罗斯方块改写成VC界面版。功能需求①实现双人俄罗斯方块 ②隐藏菜单栏、工具栏、状态栏 ③实现难度可以选择 ④实现下一个砖块预测功能 ⑤实现总分统计功能,和每步消除所得分数显示 ⑥实现下、左、右、旋转、暂停功能 ⑦实现背景音乐播放功能 ⑧添加游戏帮助菜单 ⑨实现砖块三维化 附加:实现双缓冲避免屏幕闪烁

机器学习 5782

MFC俄罗斯方块

MFC俄罗斯方块设计报告 第一章项目描述 1.1功能描述 使用MFC设计一款俄罗斯方块。实现方块下落,方块移动,方块叠加,方块变形,消行等功能。 1.2所需技术 MFC双缓冲绘图,方块移动,下落,叠加,变形,消行处理。 第二章总体设计 2.1运行流程 如附录图2-1俄罗斯方块运行流程图所示。 第三章详细设计 3.1CTetrisGame类的成员函数与变量

Have a nice day. 5957

[原创]MFC实现的俄罗斯方块

用MFC实现的俄罗斯方块,上学期期末复习着无聊的时候做出来的,每天做一点,最后居然顺利做出来.做了之后才发现,要做成这个小游戏,值得考虑的地方还是很多的.想了想就有以下的一些细节,总结起来有以下几点: 1.   关于方块结构的确定,最初考虑的是用4*4的数组来表示一个方块,但这就意味着每个方块会造成远大于自身的存储空间的浪费,而且按照这种思路,方块每切换一次形状,就要对一个4*4的数组数据

jingyucustom的专栏 2206

项目二:使用MFC实现c++贪吃蛇小游戏--预习报告

其中,在绘制时,要判断蛇是否吃到了食物,如果没有吃到,则向移动的方向画一格,并删除尾节点,如果吃到,则不用删除尾节点。现在,一个简单的贪吃蛇小游戏就制作完成了,上述全部代码仅为部分重要代码展示,即伪代码,并不能直接运行,需要进行补充。在对话框中依次添加picture control控件,显示难度和分数的输出窗口,并新建菜单。绘制食物的代码和绘制蛇的代码类似,但绘制食物时要注意:不能将食物绘制在蛇身上,每次蛇吃到食物,难度就会增加,并且随着难度的增加,每次吃到食物得分也增加。首先,做出贪吃蛇的ui。

qq_59241751的博客 573

c#实现简单的俄罗斯方块

      由于最近刚进出c#,想快速上手,所以决定写几个小游戏(后面会陆续更新)来熟悉语言,并且记录学习winform的一些相关问题和解决方法。      首先给我的感觉winform和c++中的界面设计MFC还是非常类似的(很可惜当时MFC深入浅出没吃透彻),下面我们就从最基础简单的来实现俄罗斯方块:1首先弄出一个窗体:我是在pictureBox控件上面实现画图的(因为没找到图片资源,所以我的...

c_living的博客 1万+

基于MFC实现的俄罗斯方块游戏

默认方向向下,当方块向下移动时,遍历方块数组依次判断每个值为1的方块的正下方方块是否有方块阻挡,以及判断是否碰到底界,如果遇到阻挡或碰到底,则将当前方块数组附加到背景数组,并返回结束本次移动,否则Now.pt.x++方块起点的X值加1。当前方块图形绘制,由于Now.pt.x表示所在行数,Now.pt.y表示所在列数。正好与默认坐标映射模式相反,所以在BitBlt()函数的前2个参数的X,Y坐标时,调换了行和列的坐标值。Attch()函数的实现,遍历当前方块数组,值为1的坐标赋值给背景数组对应的点。

神仙别闹的自留地 687

基于PierreDellacherie算法MFC俄罗斯方块

文章目录一、 系统分析二、 主要函数代码说明三、程序运行界面截图四、程序调试心得体会参考文档: 一、 系统分析 简要概括: 基于对话框实现俄罗斯方块小游戏。可以玩家亲自下,也可以由算法控制自动下。并且能够连接数据库,记录玩家的得分情况以及查看得分记录。 类图: 主要由五个类组成 图1 项目结构图 CGJXDlg类 图 2 GJXDlg类图 表1 CGJXDlg类成员变量 变量类型 变量名 功能描述 Game game; Game类成员变量,用于控制游戏的进行以及调用Game类方法。实现游戏的可视化

MrGong123的博客 3577

MFC实现的经典俄罗斯方块游戏

俄罗斯方块(Tetris)是一款经典的电子游戏,自1984年由苏联程序员阿列克谢·帕基特诺夫发明以来,吸引了世界各地的玩家。游戏的目标是通过旋转和移动不断下落的方块,使它们在游戏区域底部形成完整的一行,从而消除这一行并获得分数。随着游戏的进行,方块下落的速度逐渐加快,游戏难度也逐渐增加。MFC(Microsoft Foundation Classes)是微软公司提供的一套面向对象的C++封装库,用于在Windows平台上快速开发应用程序。

weixin_29317963的博客 928

MFC 实现 俄罗斯方块

 很经典的游戏.简单又让人着迷.今天就用mfc来实现个自己的俄罗斯方块.运行截图如下:看起来还行吧..网上有很多版本的实现代码.但是看了几个都是用一个很大的数组记录这些下落的物体的形状.然后写一个很大的switch case来实现变形.感觉真麻烦.今天提出一个容易的实现方法.下面切入正题思路是这样的:一:随机生成物体二:控制    1.变形        围绕一个方块向

秋城 1万+

基于mfc的俄罗斯方块(源代码)

基于mfc的俄罗斯方块小游戏,开发工具vs2012.

俄罗斯方块游戏 mfc

是用mfc工程创建游戏,里面有实现俄罗斯方块功能的源代码,对于有想做游戏的,可以拿来借鉴。

VC6.0 MFC 单文档 俄罗斯方块游戏

俄罗斯方块游戏 一、详细步骤 1、新建一个MFC单文档应用程序,如下图所示。 2、新建一个类,如下图所示。 3、添加成员变量并初始化,如下图所示。 4、添加成员函数Draw,并在里面调用画游戏边界和画方块函数(后面会用到),如下图所示。 代码如下: void CRussiaBlock::Draw(CDC *p) { CString str; CFont ft; pDC = p; DrawArea(); DrawBlock(); str.Format("得分:%d 分",m_scor

青哥 2582

基于MFC的俄罗斯方块游戏的设计与实现

(MFC)俄罗斯方块游的设计与实现

简单的MFC俄罗斯方块

MFC俄罗斯方块说明游戏功能介绍游戏界面游戏内数据如何改变文本的样式插入链接与图片游戏功能介绍生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入 说明 这是使用 Visual Studio 2013的MFC单文档 开发的一个 简单的 俄罗斯方块游戏。适合初学者,源码中包含 大量注释 ,附件中包含 XMind思维导图 辅助理解。

Mirrors406的博客 1826

基于MFC对话框的俄罗斯方块小游戏

基于MFC对话框,多线程播放背景音乐,虽然比较简单,但还是能够比较好的实习基本功能。存在一个小bug,但不影响功能。得分记录是通过数据库实现的。

基于VC++6.0 MFC下的俄罗斯方块游戏

最初的版本因为是用c语言编写的纯控制台版本 所以在mfc重写的时候没有重写整体代码结构(没有充分使用面向对象思路) 只是使用了MFC的GDI图形库 整体的思路仍然是C语言的面向过程思路 见谅 这个程序是我08年上大学的时候在课余阶段写的 后来又陆陆续续的修正bug 因为当时是初学者 所以写的不好的地方请大家见谅 整体程序大概1.8k行左右 需要交流的朋友请加我QQ185261784~

俄罗斯方块 c++ MFC

一、实验实习目的及要求 加强对C++语言的深入理解,达到提高学生分析问题,解决综合问题的能力。要求:分析俄罗斯方块游戏的类及层次图;实现俄罗斯方块游戏的基本功能;实现界面:Windows界面,利用对话框应用程序形式。 二、实验实习设备(环境)及要求(软硬件条件) 设备:PC机 操作系统:Windows 编译软件:Visual Studio 2019 三、实验实习内容简介: 系统应实现的主要功能: 游戏方块移动,游戏方块的碰撞检测,游戏方块旋转,满行消除,分数以及等级计算,开始、结束以及重新开始按钮,游戏方

qq_43527345的博客 3016

【PDN仿真笔记9-使用Sigrity PowerDC进行IR Drop仿真的方法】

使用Sigrity PowerDC进行IR Drop进行仿真的方法

qq_42682826的博客 8008

MFC实现俄罗斯方块二

1>生成物体         大家想的都过于复杂了.生成物体只是为几个小方块生成几个 行列值,当然这个行列值不是随意的.而是有限制的.假如我们让第一个方块的行列值为 然后依据这个方块在它的上下左右四个方向随机生成第二个方块,然后再根据第二个生成第三....根据第三个生成第四个.每个方块都是上一个方块的上下左右四个方向中非上一个方块的地方,那这个生成的物体正是我们要的!!!不信在纸上画一下! 根据

秋城 6427

从原理图到PCB:手把手教你绘制DRV8301驱动板(附完整代码与避坑指南)

本文详细介绍了如何从原理图设计到PCB布局,手把手教你绘制DRV8301驱动板,特别适用于FOC(磁场定向控制)系统开发。内容涵盖元器件选型、电源树架构设计、栅极驱动电路、PCB布局技巧、焊接调试及代码配置,并附有完整代码与常见问题避坑指南,帮助工程师快速掌握DRV8301驱动板的设计与优化。

weixin_33716941的博客 397

从业务角色到工作台:一步步创建 SAP Fiori Launchpad Space 与 Page

SAP S/4HANA Launchpad 配置优化指南:从业务角色出发构建高效工作台 本文针对S/4HANA项目中用户普遍反映的"入口难找"问题,提出基于Spaces和Pages的系统化解决方案。通过"以工作岗位为中心"的设计理念,将Launchpad信息架构重构为:一个业务角色对应一个Space,每个Space包含1-5个Page,Page内按任务划分2-5个Section,每个Section放置3-7个Tiles。文章详细阐述了从Business Role入手配

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 231
上一篇: 基于MFC的俄罗斯方块小游戏(一)
下一篇: Bootstrap基本框架
HY_Chan
博客等级 码龄10年 54粉丝 60原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值