用JAVA写的俄罗斯方块

 

业务需求->业务对象模型(对象关系)->数据建模->类的设计->概要编码->详细功能设计


基本规则

1)俄罗斯方块都是由一个个Cell构成的:
     行宽:10,列高:20,以每个小正方形为单位
2)其次,所有的俄罗斯方块都是一组由4个小型正方形组成的规则图形Tetromino
    分别为:S、Z、L、J、I、O、T这几种 


数据结构设计:


Cell     格子
|--     int row 行
|--     int col 列
|--     Image image 贴图

Tetromino     四格方块,有7个子类
|--     Cell[] cells     包含4个格子

Tetris     俄罗斯方块
|--     int score     分数
|--     int lines      行数
|--     Cell[][] wall = new Cell[20][10]     20*10的墙
|--     Tetromino tetromino     正在下落的方块

|--     Trtromino nextOne        下一个下落方块 

 

其次是功能:

下落移动功能
旋转流程
定时自动下落
暂停
继续
结束游戏

算法设计
 4格方块的初始形态:I S Z J L T O 
  就在初始数据的数值状态设计

四格方块的下落计算: 就是将每个格子的row+1
四格子方块的左右移动计算:就是将每个格子的col+1或col-1

下落流程控制:控制方块下落与墙之间的控制关系
   1) 如果"能够下落", 就下落一步
   2) 否则就"着陆到墙里面"
   3) 着落以后, "销毁充满的行", 并且记分
   4) 检查游戏是否结束
   5) 如果没有结束,还能玩,就生成下一个方块

分数计算

界面的绘制

键盘事件控制
  
旋转流程控制

加速下降流程控制

记分

开始流程控制(Timer)
  主刷新(Timer)频率: 1/100 秒 
0.5秒执行一次下落(是主刷新的50倍)

利用定时器定时的调用 下落流程
如果暂停时候, 就不执行下落流程来


实现过程:
1) 在Tetris类中添加属性 Timer, 用于启动主刷新频率
2) 在Tetris类中添加属性 level 是下落的间隔次数
   如: 50 表示50次刷新以后,执行一次 下落, 如果减少, 
   加快下落
3)  在Tetris类中添加属性 stepIndex 是下落计数 
  每当 stepIndex % level == 0 时候 执行一次 下落
4) 在Tetris类中添加属性  pause 暂停执行状态控制
5) 在action() 方法添加主刷新Timer的启动
   在主刷新中 添加代码 控制下落
   
绘制背景图片
1) 在Tetris类中声明静态变量 background 
2) 使用静态代码块 加载磁盘文件到内存对象
    将使用到 图片读取API: ImageIO 
3) 在paint 方法中绘制背景图片

  
绘制 正在下落的方块
1) 先在action方法中生产 正在下落的方块和下一个方块
2) 在paint方法中调用 paintTetromino()方法
3) 在Tetris类中 增加paintTetromino()方法
    将正在下落的4格方块的每个格子逐一绘制出来

使方块移动
1) 处理键盘事件(API), 获得用户何时按下 ->  <-
2) 当按下 -> 按键时候, 执行当前4格方块 向右移动 方法
3) 向右移动 方法 会改变当前4格方块的每个格子的列坐标
4) 调用继承于JPanel类的 repaint(), 这个方法会尽快的
  调用 paint()
5) paint() 会根据当前数据(已经被右移动改变的数据)
  绘制全部面板效果




以下是源码:

Cell.java(格子类)

 1 package com.timmy.tetris;
 2 import java.awt.Image;
 3 public class Cell {
 4     /**
 5      *一个格子
 6      */
 7         private int row;
 8         private int col;
 9         private Image image;
10         public Cell(int row,int col,Image image){
11             this.row = row;
12             this.col = col;
13             this.image = image;
14         }
15         
16         //toString打印行列,方便调试
17         public String toString(){
18             return "["+row+","+col+"]";
19         }
20         
21         public void drop(){
22             row++;
23         }
24         
25         public void moveLeft(){
26             col--;
27         }
28         
29         public void moveRight(){
30             col++;
31         }
32         
33         
34         public void setRow(int row){
35             this.row = row;
36         }
37         
38         public int getRow(){
39             return row;
40         }
41         public int getCol() {
42             return col;
43         }
44         public void setCol(int col) {
45             this.col = col;
46         }
47         public Image getImage() {
48             return image;
49         }
50         public void setImage(Image image) {
51             this.image = image;
52         }
53 }

 

 

Tetromino.java(四格方块类)

  1 package com.timmy.tetris;
  2 import java.util.Random;
  3 //抽象类
  4 public abstract class Tetromino {
  5     /**
  6      * 四格方块类
  7      */
  8     //是留给子类的,所以用保护
  9     protected Cell[] cells = new Cell[4];
 10     protected State[] states;//旋转状态
 11     
 12     /**私有构造器,不会有子类*/
 13     private Tetromino(){
 14     }
 15     
 16     /**内部类、旋转*/
 17     protected class State{
 18         int row0,col0,row1,col1,row2,col2,row3,col3;
 19         public State(int row0, int col0, int row1, int col1, int row2,
 20                 int col2, int row3, int col3) {
 21             super();
 22             this.row0 = row0;
 23             this.col0 = col0;
 24             this.row1 = row1;
 25             this.col1 = col1;
 26             this.row2 = row2;
 27             this.col2 = col2;
 28             this.row3 = row3;
 29             this.col3 = col3;
 30         }
 31     }
 32     
 33     /**旋转下标*/
 34     private int index = 10000;
 35     
 36     /**向右转*/
 37     public void rotateRight(){
 38         index++;    //10001
 39         State s = states[index%states.length];//取余是[1]
 40         //s = s1
 41         Cell o = cells[0];//找到0号格子作为旋转轴
 42         int row = o.getRow();
 43         int col = o.getCol();
 44         cells[1].setRow(row+s.row1);
 45         cells[1].setCol(col+s.col1);
 46         cells[2].setRow(row+s.row2);
 47         cells[2].setCol(col+s.col2);
 48         cells[3].setRow(row+s.row3);
 49         cells[3].setCol(col+s.col3);
 50     }
 51     
 52     /**向左转*/
 53     public void rotateLeft(){
 54         index--;    
 55         State s = states[index%states.length];
 56         Cell o = cells[0];
 57         int row = o.getRow();
 58         int col = o.getCol();
 59         cells[1].setRow(row+s.row1);
 60         cells[1].setCol(col+s.col1);
 61         cells[2].setRow(row+s.row2);
 62         cells[2].setCol(col+s.col2);
 63         cells[3].setRow(row+s.row3);
 64         cells[3].setCol(col+s.col3);
 65     }
 66     
 67     /**下落一步*/
 68     public void softDrop(){
 69         for(int i=0;i<cells.length;i++){
 70             Cell cell = cells[i];
 71             cell.drop();
 72         }
 73     }
 74     public void moveLeft(){
 75         for(int i=0;i<cells.length;i++){
 76             Cell cell = cells[i];
 77             cell.moveLeft();
 78         }   
 79     }
 80     public void moveRight(){
 81         for(int i=0;i<cells.length;i++){
 82             Cell cell = cells[i];
 83             cell.moveRight();
 84         }
 85             
 86     }
 87     
 88     //简单工厂方法模式:
 89     public static Tetromino randomOne(){
 90         Random r = new Random();
 91         int type = r.nextInt(7);
 92         switch(type){
 93         case 0: return new T();
 94         case 1: return new I();
 95         case 2: return new S();
 96         case 3: return new Z();
 97         case 4: return new L();
 98         case 5: return new J();
 99         case 6: return new O();
100         }
101         return null;
102     }
103     
104     //利用私有内部类,封装了子类实现的细节,永远只有7种子类
105     //静态会造成用类名直接访问,外部不知道,别人不能new
106     private static class T extends Tetromino{
107         public T(){
108             cells[0] = new Cell(0,4,Tetris.T);
109             cells[1] = new Cell(0,3,Tetris.T);
110             cells[2] = new Cell(0,5,Tetris.T);
111             cells[3] = new Cell(1,4,Tetris.T);
112             states = new State[4];
113             states[0] = new State(0,0,0,-1,0,1,1,0);
114             states[1] =  new State(0,0,-1,0,1,0,0,-1);
115             states[2] = new State(0,0,0,1,0,-1,-1,0);
116             states[3] = new State(0,0,1,0,-1,0,0,1);
117         }
118     }
119     private static class I extends Tetromino{
120         public I(){
121             cells[0] = new Cell(0,4,Tetris.I);
122             cells[1] = new Cell(0,3,Tetris.I);
123             cells[2] = new Cell(0,5,Tetris.I);
124             cells[3] = new Cell(0,6,Tetris.I);
125             states = new State[2];
126             states[0] = new State(0,0,0,-1,0,1,0,2);
127             states[1] = new State(0,0,-1,0,1,0,2,0);
128         }
129             
130     }
131     private static class S extends Tetromino{
132         public S(){
133             cells[0] = new Cell(0,4,Tetris.S);
134             cells[1] = new Cell(0,5,Tetris.S);
135             cells[2] = new Cell(1,3,Tetris.S);
136             cells[3] = new Cell(1,4,Tetris.S);
137             states = new State[2];
138             states[0] = new State(0,0,1,0,-1,-1,0,-1);
139             states[1] = new State(0,0,0,1,1,-1,1,0);
140             
141         }
142         
143     }
144     private static class Z extends Tetromino{
145         public Z(){
146             cells[0] = new Cell(1,4,Tetris.Z);
147             cells[1] = new Cell(0,3,Tetris.Z);
148             cells[2] = new Cell(0,4,Tetris.Z);
149             cells[3] = new Cell(1,5,Tetris.Z);
150             states = new State[2];
151             states[0] = new State(0,0,-1,1,0,1,1,0);
152             states[1] = new State(0,0,-1,-1,-1,0,0,1);
153         }
154         
155     }
156     private static class L extends Tetromino{
157         public L(){
158             cells[0] = new Cell(0,4,Tetris.L);
159             cells[1] = new Cell(0,3,Tetris.L);
160             cells[2] = new Cell(0,5,Tetris.L);
161             cells[3] = new Cell(1,3,Tetris.L);
162             states = new State[4];
163             states[0] = new State(0,0,-1,0,1,0,-1,-1);
164             states[1] = new State(0,0,0,1,0,-1,-1,1);
165             states[2] = new State(0,0,1,0,-1,0,1,1);
166             states[3] = new State(0,0,0,-1,0,1,1,-1);
167         }
168         
169     }
170     private static class J extends Tetromino{
171         public J(){
172             cells[0] = new Cell(0,4,Tetris.J);
173             cells[1] = new Cell(0,3,Tetris.J);
174             cells[2] = new Cell(0,5,Tetris.J);
175             cells[3] = new Cell(1,5,Tetris.J);
176             states = new State[4];
177             states[0] = new State(0,0,-1,0,1,0,1,-1);
178             states[1] = new State(0,0,0,1,0,-1,-1,-1);
179             states[2] = new State(0,0,1,0,-1,0,-1,1);
180             states[3] = new State(0,0,0,-1,0,1,1,1);
181         }
182         
183     }
184     private static class O extends Tetromino{
185         public O(){
186             cells[0] = new Cell(0,4,Tetris.O);
187             cells[1] = new Cell(0,5,Tetris.O);
188             cells[2] = new Cell(1,4,Tetris.O);
189             cells[3] = new Cell(1,5,Tetris.O);
190             states = new State[1];
191             states[0] = new State(0,0,0,1,1,0,1,1);
192         }
193         
194     }
195 }

 

 

Tetris.java(俄罗斯方块类【主类】)

  1 package com.timmy.tetris;
  2 import java.awt.Color;
  3 import java.awt.Font;
  4 import java.awt.Graphics;
  5 import java.awt.Image;
  6 import java.awt.event.KeyAdapter;
  7 import java.awt.event.KeyEvent;
  8 import java.awt.event.KeyListener;
  9 import java.util.Arrays;
 10 import java.util.Timer;
 11 import java.util.TimerTask;
 12 import javax.imageio.ImageIO;
 13 import javax.swing.JPanel;
 14 import javax.swing.JFrame;
 15 //JPanel 图形界面上能够显示的空白面板(空白矩形区域)
 16 //扩展了面板为 俄罗斯方块,扩展出    分数   和  正在下落的
 17 //方块,以及下一个下落的方块
 18 public class Tetris extends JPanel{
 19     /**
 20      * 俄罗斯方块类,继承于JPanel
 21      */
 22     public static final int ROWS = 20;
 23     public static final int COLS = 10;
 24     //格子的绘制大小
 25     public static final int CELL_SIZE = 26;
 26     //行数
 27     private int lines;
 28     //分数
 29     private int score;
 30     //字体颜色
 31     public static final int FONT_COLOR =0x667799;
 32     public static final int FONT_SIZE = 30;
 33     
 34     private boolean pause;
 35     private boolean gameOver;
 36     private Timer timer;
 37     //时间间隔
 38     private int inteval = 500;
 39     //
 40     private Cell[][] wall = new Cell[20][10];
 41     //正在下落的方块
 42     private Tetromino tetromino;
 43     //下一个方块
 44     private Tetromino nextOne;
 45     //分数表,用于针对一次性消除不同行数,给不同分数
 46     private int[] scoreTable= {0,1,5,10,20};
 47     //利用静态代码块静态加载图片资源
 48     //将磁盘上的图片文件,加载到内存中的图片对象
 49     public static Image background;
 50     public static Image gameOverImg;
 51     public static Image I;
 52     public static Image T;
 53     public static Image S;
 54     public static Image Z;
 55     public static Image L;
 56     public static Image J;
 57     public static Image O;
 58     
 59     static {//静态代码块,只执行一次
 60         //Class类提供了方法getResource()可以定位
 61         //package中的文件位置
 62         //图片文件到内存中的对象
 63         //tetris.png 文件与Tetris.class 在同一个包中
 64         try{
 65             Class cls = Tetris.class;
 66             background = ImageIO.read(cls.getResource("tetris.png"));
 67             gameOverImg = ImageIO.read(cls.getResource("game-over.png"));
 68             I = ImageIO.read(cls.getResource("I.png"));
 69             L = ImageIO.read(cls.getResource("L.png"));
 70             J = ImageIO.read(cls.getResource("J.png"));
 71             O = ImageIO.read(cls.getResource("O.png"));
 72             S = ImageIO.read(cls.getResource("S.png"));
 73             T = ImageIO.read(cls.getResource("T.png"));
 74             Z = ImageIO.read(cls.getResource("Z.png"));
 75             }catch(Exception e){
 76                 e.printStackTrace();
 77             }
 78         }
 79     
 80     /**画界面*/
 81     //重写父类JPanel 类的 paint方法(绘图方法)
 82     //重写之后修改了父类的paint方法,目的是实现自定义绘制
 83     //Graphics  理解为一个绑定到当前面板的画笔
 84     public void paint(Graphics g){
 85         g.drawImage(background, 0, 0, null);
 86         //坐标系平移
 87         g.translate(15,15);
 88         //画墙
 89         paintWall(g);
 90         //画下落方块
 91         paintTetromino(g);
 92         //画下一个下落的方块
 93         paintnextOne(g);
 94         //画分数
 95         paintScore(g);
 96         if(gameOver){
 97             g.translate(-5, -5);
 98             g.drawImage(gameOverImg,0,0,null);
 99         }
100     }
101     
102     /**绘制分数*/
103     private void paintScore(Graphics g) {
104         
105         int x = 289;
106         int y = 165;
107         g.setColor(new Color(FONT_COLOR));
108         Font font = getFont();//获得系统字体
109         font = new Font(font.getFontName(),font.BOLD,FONT_SIZE);
110         g.setFont(font);
111         String str = "SCORE:" + score;
112         g.drawString(str, x, y);
113         y+= 54;
114         str = "LINES:" + lines;
115         g.drawString(str, x, y);
116         y+= 54;
117         str = "[P]Pause";
118         if(pause){
119             str = "[C]CONTINUE";
120         }
121         if(gameOver){
122             str = "[S]RESTART";
123         }
124         g.drawString(str, x, y);
125     }
126     
127     /**绘制墙,就是将Wall数组的内容绘制到界面*/
128     private void paintWall(Graphics g){
129         for(int row=0;row<wall.length;row++){
130             //row是0~19
131             Cell[] line = wall[row];
132             for(int col=0;col<line.length;col++){
133                 //col 是 0~9
134                 Cell cell = line[col];
135                 int x = col*CELL_SIZE;
136                 int y = row*CELL_SIZE;
137                 if(cell==null){
138                     g.setColor(new Color(0));//黑色
139                     //画方块
140                     g.drawRect(x, y,CELL_SIZE, CELL_SIZE);
141                 }else{
142                     g.drawImage(cell.getImage(), x-1, y-1, null);
143                 }
144             }
145         }
146     }
147     
148     /**启动软件*/
149     public void action(){
150         startAction();
151         repaint();//JPanel 中的重绘方法,会尽快调用paint
152         //键盘按键监听器,KeyListener是KeyAdapter的父类
153         //利用匿名内部类
154         KeyListener l = new KeyAdapter() {
155         //如果有按键按下,就会执行
156             public void keyPressed(KeyEvent e){
157                 int key = e.getKeyCode();
158                 if(key == KeyEvent.VK_Q){
159                     System.exit(0);//结束程序
160                 }
161                 if(gameOver){
162                     if(key == KeyEvent.VK_S){
163                         startAction();
164                         repaint();
165                     }
166                     return;
167                 }
168                 if(pause){
169                     if(key == KeyEvent.VK_C){
170                         continueAction();
171                     }
172                     return;     //提前结束方法,不再处理后续事件
173                 }
174                 switch(key){
175                 case KeyEvent.VK_DOWN:softDropAction();
176                     break;
177                 case KeyEvent.VK_RIGHT:moveRightAction();
178                     break;
179                 case KeyEvent.VK_LEFT:moveLeftAction();
180                     break;
181                 case KeyEvent.VK_UP:rotateRightAction();
182                     break;
183                 case KeyEvent.VK_SPACE:hardDropAction();
184                     break;
185                 case KeyEvent.VK_P:pauseAction();
186                     break;
187                 }
188                 repaint();//重绘
189             }
190         };
191         //将键盘监听器对象,添加到面板上
192         this.addKeyListener(l);//this 代表当前俄罗斯方块面板 
193         this.requestFocus();//获得焦点
194     }
195     
196     /**方块旋转*/
197     private void rotateRightAction() {
198         tetromino.rotateRight();
199         if(outOfBounds() || coincide()){
200             tetromino.rotateLeft();
201         }   
202     }
203     
204     /**画正在下落块*/
205     public void paintTetromino(Graphics g){
206         //如果没有正在下落的方块,就不绘制
207         if(tetromino==null){
208             return;
209         }
210         Cell[] cells = tetromino.cells;
211         for(int i=0;i<cells.length;i++){
212             Cell cell = cells[i];
213             int x = cell.getCol()*CELL_SIZE;
214             int y = cell.getRow()*CELL_SIZE;
215             g.drawImage(cell.getImage(),x-1,y-1,null);
216         }
217     }
218     
219     /**画下一个方块*/
220     public void paintnextOne(Graphics g){
221         //如果没有正在下落的方块,就不绘制
222         if(tetromino==null){
223             return;
224         }
225         Cell[] cells = nextOne.cells;
226         for(int i=0;i<cells.length;i++){
227             Cell cell = cells[i];
228             int x = (cell.getCol()+10)*CELL_SIZE;
229             int y = (cell.getRow()+1)*CELL_SIZE;
230             g.drawImage(cell.getImage(),x-1,y-1,null);
231         }
232     }
233     
234     /**下落方法*/
235     private void softDropAction(){
236         if(canDrop()){
237             tetromino.softDrop();
238         }else{
239             landToWall();
240             destroyLines();
241             checkGameOver();
242             tetromino = nextOne;
243             nextOne = Tetromino.randomOne();
244         }
245     }
246     
247     /**检查当前方块是否能下落*/
248     private boolean canDrop(){
249         Cell[] cells = tetromino.cells;
250         //检查当前方块是否到达底部
251         for(Cell cell: cells){
252             int row = cell.getRow();
253             if(row==ROWS-1){
254                 return false;
255             }
256         }
257         //检查是否到墙
258         for(Cell cell:cells){
259             int row = cell.getRow();
260             int col = cell.getCol();
261             if(wall[row+1][col]!=null){
262                 return false;
263             }
264         }
265         return true;
266     }
267     /**着陆到墙*/
268     private void landToWall(){
269         Cell[] cells = tetromino.cells;
270         for(Cell cell:cells){
271             int row = cell.getRow();
272             int col = cell.getCol();
273             wall[row][col] = cell;
274         }
275     }
276     
277     /**硬下落*/
278     public void hardDropAction(){
279         while(canDrop()){
280             tetromino.softDrop();
281         }
282         landToWall();
283         destroyLines();
284         checkGameOver();
285         tetromino = nextOne;
286         nextOne = Tetromino.randomOne();
287     }
288     
289     /**消除行*/
290     private void destroyLines(){
291         int lines = 0;
292         for(int row=0;row<ROWS;row++){
293             if(fullCells(row)){
294                 deleteLine(row);
295                 lines++;
296             }
297         }
298         this.lines += lines;
299         this.score += scoreTable[lines];
300     }
301     
302     /**检查是否这行是否满*/
303     private boolean fullCells(int row){
304         Cell[] line = wall[row];
305         for(Cell cell:line){
306             if(cell==null){
307                 return false;
308             }
309         }
310         return true;
311     }
312     
313     /**删除行*/
314     private void deleteLine(int row){
315         for(int i=row;i>=1;i--){
316             //wall[i-1]->wall[i]
317             System.arraycopy(wall[i-1], 0, wall[i], 0, COLS);
318         }
319         Arrays.fill(wall[0], null);//第零行归零
320     }
321     
322     /**检查是否游戏结束*/
323     private void checkGameOver(){
324         if(wall[0][4] != null){
325             gameOver = true;
326             timer.cancel();
327             repaint();
328         }
329     }
330     
331     /**向左移动*/
332     private void moveLeftAction(){
333         tetromino.moveLeft();
334         if(outOfBounds() || coincide()){
335             tetromino.moveRight();
336         }
337         repaint();
338     }
339     
340     /**向右移动*/
341     private void moveRightAction(){
342         tetromino.moveRight();
343         //检查顺序一定是先检查是否出界,再检查是否重合
344         if(outOfBounds() || coincide()){
345             tetromino.moveLeft();
346         }
347         repaint();
348     }
349     
350     /**检查出界*/
351     private boolean outOfBounds(){
352         Cell[] cells = tetromino.cells;
353         for(Cell cell : cells){
354             int col = cell.getCol();
355             if(col<0 || col>=COLS){
356                 return true;
357             }
358         }
359         return false;
360     }
361     
362     /**检查格子是否会重合*/
363     private boolean coincide(){
364         Cell[] cells = tetromino.cells;
365         for(Cell cell : cells){
366             int row = cell.getRow();
367             int col = cell.getCol();
368             if(row>=0 && row<ROWS && col>=0 && col<COLS && wall[row][col]!=null){
369                 return true;
370             }
371         }
372         return false;
373     }
374     
375     /**开始*/
376     public void startAction(){
377         pause = false;
378         gameOver = false;
379         score = 0;
380         lines = 0;
381         for(Cell[] line : wall){
382             Arrays.fill(line, null);
383         }
384         tetromino = Tetromino.randomOne();
385         nextOne = Tetromino.randomOne();
386         TimerTask task = new TimerTask() {
387             public void run(){
388                 softDropAction();
389                 repaint();
390             }
391         };
392         timer = new Timer();
393         timer.schedule(task, inteval, inteval);
394     }
395     
396     /**暂停*/
397     public void pauseAction(){
398         timer.cancel();
399         pause = true;
400     }
401     
402     /**继续*/
403     public void continueAction(){
404         pause = false;
405         timer = new Timer();
406         timer.schedule(new TimerTask(){
407             public void run(){
408                 softDropAction();
409                 repaint();
410             }
411         },inteval,inteval);
412         pause = false;
413     }
414     
415     /**main 方法*/  
416     
417     public static void main(String[] args) {
418         JFrame frame = new JFrame("俄罗斯方块");//窗口框
419         Tetris tetris = new Tetris();//Tetris 继承了JPanel
420         //Tetris 也是面板,面板可以放到frame中显示
421         
422         frame.add(tetris);
423         //去处窗口装饰
424         frame.setUndecorated(true);
425         frame.setSize(530,580);
426         //设置窗口的默认关闭操作是退出程序
427         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
428         //设置窗口可见,frame在显示的时候会尽快的调用 paint方法
429         frame.setVisible(true);
430         //设置窗口居中
431         frame.setLocationRelativeTo(null);
432         //开始动作
433         tetris.action();
434     }
435 }

转载于:https://www.cnblogs.com/STill-SB/p/4817809.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值