当前位置: 首页 > news >正文

用C/C++制作一个简单的俄罗斯方块小游戏

用C/C++制作一个简单的俄罗斯方块小游戏

  • 用C/C++制作一个简单的俄罗斯方块小游戏
    • 0 准备
    • 1 游戏界面设计
      • 1.1 界面布局
      • 1.2 用 EasyX 显示界面
      • 1.3 音乐播放
    • 2 方块设计
      • 2.1 方块显示
      • 2.2 随机生成一个方块
      • 2.3 方块记录
    • 3 方块移动和旋转
      • 3.1 方块的移动
      • 3.2 方块的旋转
      • 3.3 方块的碰撞和消除
        • 3.3.1 碰撞
        • 3.3.2 消除
        • 3.3.3 分数和下落速度
        • 3.3.4 game over
    • 4 制作 exe 文件
    • 5 总结

在这里插入图片描述

0 准备

  • 开发环境:Windows
  • 编程语言:C/C++
  • 开发软件:Visual Studio 2022 (软件安装可参考:链接: Visual Studio 2022 免费版最新版本下载安装教程
  • 图形插件:EasyX

EasyX 的安装非常简单,百度搜索以下即可,但是在安装前一定要先安装 Visual Studio。下面的章节给出如何使用它。

1 游戏界面设计

1.1 界面布局

首先,我们要选择一章图片作为游戏的背景,我们可以在图片网站上下载合适的背景图片。

其次,在背景图片上划分出,游戏区和显示区,一般游戏区在正中间,两边为显示区。游戏区用于控制方块的移动、消除和旋转等;显示区用于速度和分数的展示。下面是我设计的游戏界面:

在这里插入图片描述

用画图工具打开图片,可以看到背景大小为800*600像素,同时在图片中间设置游戏区(虚线框,大小自定义),添加速度和分数的文本框。

1.2 用 EasyX 显示界面

此时,我相信你应该会建立 VS 工程了,并且安装了 EasyX。

建立一个文件夹 imp ,把1.1小节中的图片放在里面。imp 文件夹和 main.cpp 文件同一目录。
显示图形需要调用头文件 graphics.h。我们需要知道显示的图形的大小,这边是800*600,显示的位置为(0,0)

#include <graphics.h>// 绘图窗口初始化initgraph(imp_width, imp_heght);loadimage(&background, _T("img/background.png"));putimage(0, 0, &background);

显示如下:

背景显示

这是一个简单的显示案例,后续方块的显示也是用这种方式。

1.3 音乐播放

先用 酷狗 下载一首好听的音乐,然后将音乐放在和 main.cpp 同一目录下。

我这边用了两首音乐,每次打开都是随机播放

程序:

#include "Windows.h"
#include <time.h>#pragma comment (lib, "winmm.lib")void Music::palyMusic()
{int chFlag;srand((unsigned)time(NULL));chFlag = rand() % 2;if (chFlag == 0){//mciSendString("close 1.mp3", NULL, 0, NULL);mciSendString("open 2.mp3", NULL, 0, NULL);mciSendString("play 2.mp3 repeat", NULL, 0, NULL);mciSendString("setaudio 2.mp3 volume to 100", 0, 0, 0);}else{//mciSendString("close 2.mp3", NULL, 0, NULL);mciSendString("open 1.mp3", NULL, 0, NULL);mciSendString("play 1.mp3 repeat", NULL, 0, NULL);mciSendString("setaudio 1.mp3 volume to 100", 0, 0, 0);}
}

2 方块设计

2.1 方块显示

设计7种方块类型:

const int blocks[7][4] = {1,3,5,7, // I2,4,5,7, // Z 1型3,5,4,6, // Z 2型3,5,4,7, // T2,3,5,7, // L3,5,7,6, // J2,3,4,5, // 田};

方块的表示如下图所示:

方块显示

上面两张图表示在游戏中方块是如何表示的,方块可以由界面的横纵坐标表示,我们可以将下落的初始位置作为横坐标,方块的左边界作为纵坐标。其实就是游戏区的左上角作为坐标。

那么显示的原理知道了,就是操作数组,方块的图形呢?

小方块显示

小方块的显示可以根据这张图,从上图不难看出,小方块的大小为:20*20像素。如果我们要显示第一个小方块,我们可以加载这张图片然后从坐标(0,0)开始,显示长宽为20像素的图片。同意,如果要显示第三个绿色方块,就是从坐标(40,0)开始。

下面是实现的部分程序:

//计算小方块位置
blockType = rand() % 7;
for (int i = 0; i < 4; i++)
{smallBlock[i][0] = blocks[blockType][i] / 2;smallBlock[i][1] = blocks[blockType][i] % 2 + 1;//离左边界一格显示,方便旋转
}//小方块显示函数
void Graph::block()
{IMAGE imgTmp;loadimage(&imgTmp, _T("img/small.png"));SetWorkingImage(&imgTmp);//putimage(0, 0, &imgTmp);for (int i = 0; i < 7; i++) {this->imgs[i] = new IMAGE;getimage(this->imgs[i], i * blocks_size, 0, blocks_size, blocks_size);}SetWorkingImage();
}

初始位置

2.2 随机生成一个方块

随机生成方块的原理就是将记录正在下落方块的数组清空初始化

void Graph::random()
{blockType = rand() % 7;for (int i = 0; i < 4; i++){smallBlock[i][0] = blocks[blockType][i] / 2;smallBlock[i][1] = blocks[blockType][i] % 2 + 1;//离左边界一格显示,方便变形}colBasis = 0;rowBasis = 0;
}

2.3 方块记录

我们不仅需要对当前正在操作的方块进行记录,还需要对已经下落还未被消除的方块进行记录。可以将游戏区看作一个二维数组,开辟一个 29*14 的二维数组进行记录。

	//背景图像大小const unsigned int imp_width = 800;const unsigned int imp_heght = 600;//小方块大小const unsigned int blocks_size = 20;//游戏区边界const unsigned int left_margin = 240;const unsigned int right_margin = 485;const unsigned int down_margin = 570;const unsigned int up_margin = 10;const int rows = 29;const int cols = 14;//记录方块的数组vector<vector<int>> allBlock;

这里有个小技巧,因为方块要显示不同的颜色,因此我们可以用二维数组allBlock的值当作颜色的值

当allBlock[i][j]的值为0时,表示该位置没有方块;
当allBlock[i][j]的值大于0时,表示该位置有方块,显示的颜色用allBlock[i][j]的之表示

//已静止方块显示for (int i = rows-1; i > 3; --i){for (int j = 0; j < cols; ++j){if(allBlock[i][j]!=0)putimage(left_margin + j * blocks_size, up_margin + i * blocks_size, imgs[allBlock[i][j]-1]);}}

3 方块移动和旋转

3.1 方块的移动

方块的移动就是一个核心:方块的移动 = 对数组的操作

方块的下落 = 行坐标+1
方块的左移 = 列坐标-1
方块的右移 = 列坐标+1

前提是需要判断是否出界或者移动的下一个位置是否有方块

程序如下:

void Graph::moveLeft()
{for (int i = 0; i < 4; i++){if (smallBlock[i][1] <= 0 || allBlock[smallBlock[i][0]][smallBlock[i][1] - 1] >= 1)return;}for (int i = 0; i < 4; i++){--smallBlock[i][1];}--colBasis;
}void Graph::moveDown()
{for (int i = 0; i < 4; i++){if (smallBlock[i][0] >= rows)return;}for (int i = 0; i < 4; i++){++smallBlock[i][0];}++rowBasis;
}void Graph::moveRight()
{for (int i = 0; i < 4; i++){if (smallBlock[i][1] >= cols - 1 || allBlock[smallBlock[i][0]][smallBlock[i][1] + 1] >= 1)return;}for (int i = 0; i < 4; i++){++smallBlock[i][1];}++colBasis;
}

当然移动的前提说需要用户按键输入的,所以需要有判断按键输入的函数和读取按键值的函数,我这边使用函数 _kbhit() 来判断是否有按键输入,用函数 _getch() 读取按键值

//控制方块移动if (_kbhit() && graph.startFlag)//如果键盘有输入{graph.keyPlay();}
void Graph::keyPlay()
{int ch = 0;ch = _getch();switch (ch){//WASD键(小写)case 119: changeBlock();//上键break;case 97: moveLeft();//左键break;case 115: moveDown();//下键break;case 100: moveRight();//右键break; //上下左右键case 72: changeBlock();//上键break;case 75: moveLeft();//左键break;case 80: moveDown();//下键break;case 77: moveRight();//右键break;}
}

3.2 方块的旋转

方块旋转

如上图所示,这样可以用几行代码实现了方块的旋转,但是仍然需要注意下面的几个问题:

  • 以什么为中心旋转?
  • 方块是不断下落的,行和列是一直在变化的
  • 在边界处有部分方块是不能旋转的

针对第一个问题,如果想让方块的旋转看起来不那么别捏,以4*4方格的中心旋转是最合适的,即图中的2,3,4,5作为旋转的核心。

针对第二个问题,可以将方块的行列切换至初始位置,再进行上图的公式,然后再切回来,这边可以设置两个变量确定方块离初始位置的距离。

针对第三个问题,将方块的行列号暂存,进行变换,然后再进行检测是否有方块在边界外面,如果有,旋转这步算作废。

旋转的时候初始位置的确定也是非常关键的,因为在边界处有些旋转是做不了的

初始位置

程序实现:

void Graph::changeBlock()
{int temp[4][2] = { 0 };for (int i = 0; i < 4; i++){//配合偏置,进行方块的旋转temp[i][0] = smallBlock[i][1] - colBasis;temp[i][1] = 3 - (smallBlock[i][0] - rowBasis);temp[i][0] += rowBasis;temp[i][1] += colBasis;//检查合法性if (temp[i][1] == 0 || temp[i][1] == cols - 1)return;}for (int i = 0; i < 4; i++)//若合法,实行{smallBlock[i][0] = temp[i][0];smallBlock[i][1] = temp[i][1];}
}

3.3 方块的碰撞和消除

方块的消除需要考虑下面几个问题

  • 碰撞检测
  • 一行的消除算法

3.3.1 碰撞

碰撞检测很容易实现,由于左右移动我已经设置了边界检测,这边只需要对四个方块进行判断,也就是是说判断它们下面是否有方块就行。如果有,就返回 1

int Graph::check()
{int row, col;for (int i = 0; i < 4; i++)//若合法,实行{row = smallBlock[i][0]+1;col = smallBlock[i][1];if (row >= this->rows || allBlock[row][col] >= 1){if (rowBasis == 0)return 2;elsereturn 1;}}return 0;
}

3.3.2 消除

对一行的消除,采用一个二维数组对所有的位置进行记录,如果在(i,j)处有方块,则 allBlock[i][j]=1;在碰撞检测完毕之后,对整个数组进行遍历,对每一行移动的行数进行记录,尽量减少时间复杂度。

int clearRowNum[30] = { 0 };
int num=0;
//unordered_map<int, int>map;
if (check()==1)
{for (int i = 0; i < 4; i++) {int row = smallBlock[i][0];int col = smallBlock[i][1];allBlock[row][col] = blockType+1;}//消除一行for (int i = rows-1; i > 3; --i){for (int j = 0; j < cols; ++j){if (allBlock[i][j] == 0){clearRowNum[i] = num;break;}else if (j == cols-1)//该行需要消除{++num;clearRowNum[i] = 0;}}}for (int i = rows - 2; i > 3; --i){if (clearRowNum[i] != 0){for (int j = 0; j < cols; ++j){allBlock[i + clearRowNum[i]][j] = allBlock[i][j];}}}
}

3.3.3 分数和下落速度

同时,在消除函数中可以添加分数计算,速度计算。大致的逻辑是每消除一行,分数变多;分数越高,下落速度越快;

//设置速度,得分越多,速度越快
score += num * cols;speed = 100+score/10;

3.3.4 game over

当小方块处于初始位置时,它的下方有方块时就可以判断 game over了。

if (row >= this->rows || allBlock[row][col] >= 1)
{if (rowBasis == 0)return 2;elsereturn 1;
}

game over 之后,界面会一直显示game over,直到输入 回车键

//游戏结束if (!graph.startFlag){settextcolor(WHITE);settextstyle(40, 0, "黑体");setbkmode(TRANSPARENT);char s[10] = "Game Over";outtextxy(300, 280, s);if (_kbhit() && _getch() == 13)//如果键盘有输入{graph.init();}}

game over

4 制作 exe 文件

如何用 Visual Studio打包项目程序可以参考:

Visual Studio 怎么将项目程序打包成软件

5 总结

最后我想说的是,对方块的移动和旋转,其根本就是在对数组进行操作。

至于后续的一些最高分数记录,下一个方块提示等功能,都是锦上添花的功能,感兴趣的小伙伴可以尝试添加一下。

程序下载:
俄罗斯方块小游戏程序下载

相关文章:

用C/C++制作一个简单的俄罗斯方块小游戏

用C/C制作一个简单的俄罗斯方块小游戏 用C/C制作一个简单的俄罗斯方块小游戏 0 准备1 游戏界面设计 1.1 界面布局1.2 用 EasyX 显示界面1.3 音乐播放 2 方块设计 2.1 方块显示2.2 随机生成一个方块2.3 方块记录 3 方块移动和旋转 3.1 方块的移动3.2 方块的旋转3.3 方块的碰撞和…...

使用免费负载生成器swingbench对oracle数据库进行压力测试(测试Oracle的功能或评估性能)

1.Swingbench 简介 Swingbench 是一个免费负载生成器&#xff08;和基准测试&#xff09;&#xff0c;旨在对 Oracle 数据库 进行压力测试。目前最新版本 Swingbench 2.6。 SwingBench 由负载生成器&#xff0c;协调器和集群概述组成。该软件可以生成负载 并绘制交易/响应时间…...

【预告】ORACLE Primavera P6 v22.12 虚拟机发布

引言 离ORACLE Primavera P6 EPPM最新系统 v22.12已过去了3个多月&#xff0c;应盆友需要&#xff0c;也为方便大家体验&#xff0c;我近日将构建最新的P6的虚拟环境&#xff0c;届时将分享给大家&#xff0c;最终可通过VMWare vsphere (esxi) / workstation 或Oracle virtua…...

机器学习100天(四十):040 线性支持向量机-公式推导

《机器学习100天》完整目录:目录 机器学习 100 天,今天讲的是:线性支持向量机-公式推导! 首先来看这样一个问题,在二维平面上需要找到一条直线划分正类和负类。 我们找到了 A、B、C 三条直线。这三条直线都能正确分类所有训练样本。但是,哪条直线最好呢?直观上来看,我…...

失败经验之震荡玩家往往死于趋势市场

亏损&#xff0c;是从去年开始的吧。 尤其是去年&#xff0c;仅仅一年&#xff0c;就亏掉了自从交易以来的所有盈利。 现在&#xff0c;我甚至不敢去计算具体的亏损金额。 保守估计&#xff0c;已经亏损100万左右。 现在回想&#xff0c;似乎也是必然。 交易本来就是一个走…...

应用层与传输层~

文章目录应用层自定义应用层协议什么是自定义应用层协议自定义方式运输层运输层概述运输层特点运输层协议UDP协议UDP的特点UDP首部格式校验规则TCP协议TCP的特点TCP协议段格式TCP的性质确认序号超时重传连接管理三次握手四次挥手TCP的状态滑动窗口流量控制拥塞控制延迟应答捎带…...

IO文件操作

认识文件 狭义的文件 存储在硬盘上的数据,以“文件"为单位,进行组织 常见的就是普通的文件 (文本文件,图片, office系列,视频,音频可执行程序…)文件夹也叫做"目录" 也是一种特殊的文件。 广义的文件 操作系统,是要负责管理软硬件资源&#xff0c;操作系统(…...

【构建工具】webpack 3、4 升级指南,摆脱低版本的困扰

一、依赖处理 1.升级通用依赖 借用 ncu 库实现&#xff0c;帮你改写需要升级的package.json 然后再 npm install ncu -u <packages> # 可以指定依赖 ncu # 升级全部依赖大概列了下升级的效果 add-asset-html-webpack-plugin ^2.1.3 → ^5.0.2 clean-webpack-…...

Javaweb第一个项目——实现简单的登陆功能

第一步&#xff1a;打开idea-->文件-->新建 第二步&#xff1a; 在Demo文件夹 点击右键-->添加框架支持-->找到Web应用程序 勾选 第三步&#xff1a;配置Tomcat 第四步&#xff1a;新建一个lib&#xff08;建在web-INF文件夹下&#xff09;文件夹 用于存放jar包…...

OpenKruise 开发者不容错过的带薪实习机会!马上加入 LFX Mentorship 计划

LFX Mentorship 计划由 Linux Foundation 组织发起&#xff0c;为像 OpenKruise 这样的 CNCF 托管项目提供了激励开源贡献、扶植社区发展的优秀土壤。参与其中的开发者不仅有机会在经验丰富的社区 Mentor 指导下贡献开源项目、为职业生涯加分&#xff0c;完成工作后还能获得 $3…...

《c++ primer笔记》第八章 IO库

前言 简单看一下就行 文章目录一、IO类1.1基本概念1.2管理输出缓冲二、文件输入输出2.1文件模式三、string流3.1istringstream3.2ostringstream一、IO类 1.1基本概念 ​ 我们常见的流有istream和ostream&#xff0c;这两个流都是有关输入和输出的&#xff0c;此外&#xff0c…...

web开发 用idea创建一个新项目

这个写着就是给自己当备忘录用的QAQ 这个老师上课一通操作啥也没看清…卑微搞了半天看样子是成功了 记录一下省的以后忘了怎么创建&#xff08;&#xff1f; zufe lxy 2023.3 先行条件是已经自己装好了Tomcat和idea&#xff01;&#xff01;&#xff08;我的idea是申请了教育…...

【FMCW 03】测速

从上一讲 测距 末尾的frame讲起。我们知道一个chirp对应了一个采样后的IF信号&#xff0c;我们将这些采样后的IF信号按chirp的次序排列成一个帧&#xff08;frame&#xff09;&#xff0c;这就得到了我们实际中接收后处理的FMCW信号。 由于chirp的发射返回时间很短&#xff0c;…...

ERP(企业资源管理)概述

&#x1f31f;所属专栏&#xff1a;ERP企业资源管理&#x1f414;作者简介&#xff1a;rchjr——五带信管菜只因一枚&#x1f62e;前言&#xff1a;该系列将持续更新ERP的相关学习笔记&#xff0c;欢迎和我一样的小白订阅&#xff0c;一起学习共同进步~&#x1f449;文章简介&a…...

深入理解java虚拟机精华总结:性能监控和故障处理工具、类加载机制

深入理解java虚拟机精华总结&#xff1a;性能监控和故障处理工具、类加载机制性能监控和故障处理工具、类加载机制jpsjstatjinfojmapjhatjstackVisualVM类加载机制类加载的时机类加载的过程加载验证准备解析初始化类加载器类与类加载器双亲委派模型破坏双亲委派模型往期内容&am…...

推荐系统与推荐算法

文章目录第一章1.1推荐系统意义与价值1.2推荐系统历史与框架1.3推荐算法分类第二章2.1协同过滤的基本思想与分类2.2基于用户的协同过滤2.3基于项目的协同过滤2.4基于邻域的评分预测2.5基于二部图的协同过滤第三章3.1基于关联规则的推荐3.2基于矩阵分解的评分预测3.3概率矩阵分解…...

socket 编程实战(编写客户端程序 )

编写客户端程序 接着上一篇&#xff1a;实战服务端程序 接下来我们再编写一个简单地客户端应用程序&#xff0c;客户端的功能是连接上小节所实现的服务器&#xff0c;连接成功之后向服务器发送数据&#xff0c;发送的数据由用户输入。示例代码如下所示&#xff1a; #include…...

“巨亏成名”的魔鬼交易员,你知道几个?

谁说在期货市场上只有赚大钱才能出名&#xff1f;殊不知还有这样一群特殊的交易员靠着巨额亏损而“一战成名”&#xff0c;亏得是老东家元气大伤&#xff0c;外号“魔鬼交易员”——“不亏不成魔”&#xff01;接下来火象就给大家盘点几位代表性魔鬼交易员&#xff0c;看看他们…...

1380:分糖果(candy)

1380&#xff1a;分糖果(candy) 时间限制: 1000 ms 内存限制: 65536 KB 【题目描述】 童年的我们&#xff0c;将和朋友分享美好的事物作为自己的快乐。这天&#xff0c;C小朋友得到了Plenty of candies&#xff0c;将要把这些糖果分给要好的朋友们。已知糖果从一个人传…...

数据挖掘(2.1)--数据预处理

一、基础知识 1.数据的基本概念 1.1基础知识 数据是数据对象(Data Objects)及其属性(Attributes)的集合。 数据对象(一条记录、一个实体、一个案例、一个样本等)是对一个事物或者物理对象的描述。 数据对象的属性则是这个对象的性质或特征&#xff0c;例如一个人的肤色、眼球…...

【力扣数据库知识手册笔记】索引

索引 索引的优缺点 优点1. 通过创建唯一性索引&#xff0c;可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度&#xff08;创建索引的主要原因&#xff09;。3. 可以加速表和表之间的连接&#xff0c;实现数据的参考完整性。4. 可以在查询过程中&#xff0c;…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩

目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式&#xff1a; 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

Device Mapper 机制

Device Mapper 机制详解 Device Mapper&#xff08;简称 DM&#xff09;是 Linux 内核中的一套通用块设备映射框架&#xff0c;为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程&#xff0c;并配以详细的…...

R语言速释制剂QBD解决方案之三

本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

ZYNQ学习记录FPGA(一)ZYNQ简介

一、知识准备 1.一些术语,缩写和概念&#xff1a; 1&#xff09;ZYNQ全称&#xff1a;ZYNQ7000 All Pgrammable SoC 2&#xff09;SoC:system on chips(片上系统)&#xff0c;对比集成电路的SoB&#xff08;system on board&#xff09; 3&#xff09;ARM&#xff1a;处理器…...

论文阅读:Matting by Generation

今天介绍一篇关于 matting 抠图的文章&#xff0c;抠图也算是计算机视觉里面非常经典的一个任务了。从早期的经典算法到如今的深度学习算法&#xff0c;已经有很多的工作和这个任务相关。这两年 diffusion 模型很火&#xff0c;大家又开始用 diffusion 模型做各种 CV 任务了&am…...

AxureRP-Pro-Beta-Setup_114413.exe (6.0.0.2887)

Name&#xff1a;3ddown Serial&#xff1a;FiCGEezgdGoYILo8U/2MFyCWj0jZoJc/sziRRj2/ENvtEq7w1RH97k5MWctqVHA 注册用户名&#xff1a;Axure 序列号&#xff1a;8t3Yk/zu4cX601/seX6wBZgYRVj/lkC2PICCdO4sFKCCLx8mcCnccoylVb40lP...