lufylegend引擎俄罗斯方块的实现
来源:互联网 发布:淘宝店铺更改所在地 编辑:程序博客网 时间:2024/06/05 12:00
lufylegend引擎俄罗斯方块的实现
- 旋转坐标的实现
俄罗斯方块的实现第一步就是旋转坐标的获得,下面将会详细写出推导过程,首先必须要分配好方块的构成,这里需要用4个精灵来形成基本的block,如长方形,L形等,之后就是选择原点坐标和各点的相对坐标,这里先把选转的坐标公式推导一下,如下图:
旋转前的坐标为x,y,旋转前的角度为ζ度,旋转后的坐标为x1,y1,旋转后的角度为(ζ+β)度,因为旋转后,该点到原点的距离并不会发生改变,所以我们需要先用已只条件得到该点到原点的距离h=y/sinζ=x/cosζ
那么对应的旋转后距离就是h1=y1/sin(ζ+β)=x1/cos(ζ+β);因为h1=h,所以可以推导出y1/sin(ζ+β)=x1/cos(ζ+β)=y/sinζ=x/cosζ;
接下来要用到和角公式
sin(a+b)=sinacosb+sinbcosa
cos(a+b)=cosacosb-sinasinb
y1=y*sin(ζ+β)/sinζ=y(sinζcosβ+sinβcosζ)/sinζ=ycosβ+ycosζ/sinζ*sinβ;
又因为y/x=sinζ/cosζ,所以x=ycosζ/sinζ,所以y1=ycosβ+xsinβ
以此类推x1=xcos(ζ+β)/cosζ=x(cosζcosβ-sinζsinβ)/cosζ=xcosβ-xsinζ/cosζ*sinβ=xcosβ-ysinβ,即x1=xcosβ-ysinβ
这样第一步旋转公式的推导就ok了,接下来是应用了,这里选择将第三个方块的起始x,y作为坐标原点,如下图以L形方块为例:
可以看到第一个为初始L形,此时第三个方块的坐标看做0,0,那么剩余方块坐标为1号方块(-1,1),2号方块为(-1,0),4号方块为(1,0),则根据之前的旋转公式可以得到,x1=xcosβ-ysinβ,y1=ycosβ+xsinβ,此中p为90度,则实际x1=-y,y1=x,所以旋转后坐标1号方块为(-1,-1),2号方块为(0,-1),4号方块为(0,1),以此类推可以求出第三次旋转,第四次旋转的坐标。
所以实现的代码为
javascriptfor(var i=0;i<4;i++){ //求得相对坐标 var x=sprite[i].x-sprite[2].x; var y=-(sprite[i].y-sprite[2].y); //求出相对坐标后在换算成实际坐标 sprite[i].x=-y+sprite[2].x; sprite[i].y=-x+sprite[2].y; }
因为实际lufylegend的引擎坐标系是以左上角为原点的,所以这里做了一下换算,将坐标系反过来了
这样旋转后的方块就可以很方便的画出来了
- 方块的最下方的判断
对于俄罗斯方块来说都有着最低层,并且之后检测碰撞的方便都会用到方块的最低点的坐标,所以接下来的工作就是实现这个目的,以L形为例子,初始的最低点为,第一个方块的Y坐标就是方块的最低点,所以在初始化方块的时候可以事先将maxY赋值成1,之后在方块的下落过程中,方块的坐标是在不断变动的,以及方块旋转后其最低点的坐标也是会发生变化的。这里需要考虑到这两个条件,所以接下来就要修改上面的旋转函数和方块的下落函数,并在其中求最大的Y坐标:
javascript//旋转函数for(var i=0;i<4;i++){ var x=sprite[i].x-sprite[2].x; var y=-(sprite[i].y-sprite[2].y); sprite[i].x=-y+sprite[2].x; sprite[i].y=-x+sprite[2].y; //求最大X的坐标 if(sprite[maxX].x<sprite[i].x) maxX=i; //求最大Y的坐标 if(sprite[maxY].y<sprite[i].y) maxY=i; }
javascriptif(s.childList[maxY].y<290){ for(var i=0;i<s.childList.length;i++){ s.childList[i].y=s.childList[i].y+10; //更新最大的Y坐标 if(s.childList[maxY].y<=s.childList[i].y){ maxY=i; } } }else{ s.activemode=false; }
这样方块的最下方的判断就完成了
3.方块的生成
俄罗斯方块中是由方块的掉落来进行游戏的,所以需要对方块进行生成,如果是写定的方块的话容易产生一种厌烦感,所以这里需要对方块进行随机生成,首先需要确定是第一个方块的位置,这个很容易,因为Y坐标必定是0,所以只需要对x进行随机就行了
var x=Math.round(Math.random()*13)*10;
var y=0;
这样就可以生成一个0,130的随机数,需要注意一下math.random()是产生0到1的随机数,如果不用math.round的话,产生的x值并不是10的整数倍,所以需要在这里先用math.round得到一个整数,之后再乘以10,获得就是0,10,20,30.。。130的随机数
而对于下一个方块来说,坐标取值只有4种,[-10,0],[10,0],[0,10],[0,-10],也就是上下左右4个领接位置,但是当方块坐标为0,或者130时为了使方块不跃出边界,就只能选择3方向的方块,0:[10,0],[0,10],[0,-10],130:[-10,0],[0,10],[0,-10],这样就确定好了3种情况所能随机的方块位置了,还需要注意一下的就是之前的方块位置,避免两个方块差生在同一个位置,所以源代码如下:
LBlock.prototype.onshow=function(){ var s=this; var x=Math.round(Math.random()*13)*10; //var x=200; var y=0; var i; var a=new Array(); var save=new Array(); a[0]=[x,y]; save=[[x,y],[-1,-1],[-1,-1],[-1,-1]]; for(i=1;i<4;i++){ if(a[i-1][0]==130){ var b=[[-10,0],[0,10],[0,-10]] }else if(a[i-1][0]==10){ var b=[[10,0],[0,10],[0,-10]] }else{ var b=[[-10,0],[10,0],[0,10],[0,-10]]; } var select=Math.round(Math.random()*(b.length-1)); var m=a[i-1][0]+b[select][0]; var n=a[i-1][1]+b[select][1]; if(find([m,n],save)){ a[i]=[m,n] save[i]=[m,n] }else{ b.splice(select,1); select=Math.round(Math.random()*(b.length-1)); m=a[i-1][0]+b[select][0]; n=a[i-1][1]+b[select][1]; a[i]=[m,n] } } var sprite=[]; for(i=0;i<4;i++){ sprite[i]=new LSprite(); sprite[i].x=a[i][0]; sprite[i].y=a[i][1]; sprite[i].graphics.drawRect(2,"blue",[0,0,10,10],true,"green"); s.addChild(sprite[i]); }}
这样就可以随机生成不同坐标的不一样形状的方块了
4.方块的碰撞检测
俄罗斯方块游戏中的重点,方块与方块,方块与边界的碰撞,在整个游戏中需要注意以下这几种情况1.旋转时的碰撞检测,如果旋转后的坐标上有方块或边界就不能进行旋转 2.左右移动时的碰撞检测,3.向下移动时的碰撞检测
首先需要定义一个用来存放方块的容器,这里采用高200,宽120的大小容器,定义为一个20,14的数组
var mapData=[[0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0]
]
初始值全为0,当方块停止后,就将对应位置的数组值置为1
1.向下移动时的碰撞检测
因为方块需要一起进行移动,所以要优先判断所有方块是否由碰撞,如有就不移动方块,否则会出现一部分方块停止后,另一部分依然正常运动的情况,所以这里采用先对所有方块的碰撞进行判断,之后再移动方块
LBlock.prototype.onframe=function(){
var s=this;
var flag=true;
if(s.childList[maxY].y<190){
for(var i=0;i
flag=true; for(var i=0;i<4;i++){ var x=sprite[i].x-10; var y=sprite[i].y; var m=190-y; var n=x; if(y>=0){ if((mapData[Math.floor(m/10)][Math.floor(n/10)]==1)||(n>130)||(n<0)||(m>190)){ flag=false; } } } if(flag){ for(var i=0;i<4;i++){ sprite[i].x=sprite[i].x-10; if(sprite[maxX].x<sprite[i].x) maxX=i; if(sprite[maxY].y<sprite[i].y) maxY=i; } }
3.旋转判定
if(button==65){ //以sprite2为坐标原点 flag=true; for(var i=0;i<4;i++){ var x=sprite[i].x-sprite[2].x; var y=-(sprite[i].y-sprite[2].y); var m=-y+sprite[2].x; var n=190-(-x+sprite[2].y); if((mapData[Math.floor(n/10)][Math.floor(m/10)]==1)||(m>130)||(m<0)||(n>190)){ flag=false; } } if(flag){ for(var i=0;i<4;i++){ var x=sprite[i].x-sprite[2].x; var y=-(sprite[i].y-sprite[2].y); sprite[i].x=-y+sprite[2].x; sprite[i].y=-x+sprite[2].y; if(sprite[maxX].x<sprite[i].x) maxX=i; if(sprite[maxY].y<sprite[i].y) maxY=i; } }
5.消行
上述工作完成后,对于俄罗斯方块而言,仅缺少的就是消行,因为之前已经定义了mapData这个容器数组,所以只需要在onframe函数中,检测mapData中一横行的数值是否全为,如果全为1既消去整一行,并删掉改行的方块,为了实现这个,首先需要对s.result这个在方块停止的函数进行改动,因为对于blocklayer来讲下属的精灵类非常不好进行查询,所以这里在停止时会删掉这个部分,并将这部分的值赋给一个事先定义好的mapsprite数组,这样定义的话,数组就是有序的,方便进行删除,下面就是s.result函数的修改后的代码,在将坐标获得后直接拿掉改对象,改用mapsprite进行绘制
LBlock.prototype.result=function(){ var s=this; var b=[]; for(var i=0;i<s.childList.length;i++){ var x=s.childList[i].x; var y=190-s.childList[i].y; //求得数组坐标 var m=Math.round(x/10); var n=Math.round(y/10); mapData[n][m]=1; mapsprite[n][m]=s.childList[i]; mapsprite[n][m].graphics.drawRect(2,"blue",[0,0,10,10],true,"green"); //mapLayer.addChild(mapsprite[n][m]); b[i]=[n,m]; } for(var i=0;i<b.length;i++){ mapLayer.addChild(mapsprite[b[i][0]][b[i][1]]); } s.remove();}
这一步完成后游戏中就可以获得行对应行,列对应列的有序数组了,这样删除起来就方便多了,接下来是删除函数,对于俄罗斯方块的消行来说,需要完成两个步骤,第一个步骤就是将mapData后一行的值赋给前一行,并将最后一行的所有值置为0,第二个步骤就是将mapsprite绘制的方块全部下移一行,这两个步骤完成后,消行也就完成了,所以代码需要这样写:
function canclemap(n){ for(var i=0;i<mapsprite[n].length;i++){ mapsprite[n][i].remove(); } for(var i=n;i<mapData.length;i++){ if(i<mapData.length-1){ mapData[i]=mapData[i+1]; for(var j=0;j<mapsprite[n].length;j++){ mapsprite[i+1][j].y=mapsprite[i+1][j].y+10; mapsprite[i]=mapsprite[i+1]; } }else{ mapData[i]=[0,0,0,0,0,0,0,0,0,0,0,0,0,0]; for(var j=0;j<mapsprite[n].length;j++){ mapsprite[i][j].remove(); } } } }
源代码:
index.php
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>LTetris</title> <script type="text/javascript" src="lufylegend.js-lufylegend-1.9.7/lufylegend-1.9.7.min.js"></script> <script src="jquery/jquery-1.8.3.js"></script> <script src="tetrisblock.js"></script> <script> LInit(30,"legend",450,320,gameInit); LGlobal.setDebug(true); var backLayer,loadingLayer,blockLayer,mapLayer; var block;//方块 var point;//分数 var next; var nextLayer,nextblockLayer;//下一个方块 //整个方框的大小,长20,宽14,即长200,宽140 var mapData=[[0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0] ] var mapsprite=new Array(); var totalnum=1;//同时只有一个方块 /*var loadData = [ {name:"cr",path:"./images/charc.jpg"}, {name:"map",path:"./images/map.jpg"}, {name:"money",path:"./images/bird.png"}, {name:"photo1",path:"./images/66RPG_002_f.png"}, {name:"photo2",path:"./images/66RPG_005_f.png"} ]; function main(){ //加入进度条 loadingLayer = new LoadingSample3(); addChild(loadingLayer); //加载图片并显示进度 LLoadManage.load( loadData, function(progress){ loadingLayer.setProgress(progress); }, gameInit ); } */ function gameInit(){ initmap(); backLayer = new LSprite(); backLayer.graphics.drawRect(2,"green",[0, 0, 140,200],true,"white"); addChild(backLayer); blockLayer=new LSprite(); backLayer.addChild(blockLayer); mapLayer=new LSprite(); backLayer.addChild(mapLayer); //添加分数区 //添加下一个方块的提示区 //block=new LBlock(); //blockLayer.addChild(block); //加入动画和按键事件监听 backLayer.addEventListener(LEvent.ENTER_FRAME,onframe); LEvent.addEventListener(LGlobal.window, LKeyboardEvent.KEY_DOWN,move); LEvent.addEventListener(LGlobal.window, LKeyboardEvent.KEY_UP,stop); } function addblock(){ if(totalnum==1){ block=new LBlock(); blockLayer.addChild(block); totalnum--; } } function onframe(){ addblock(); if(block.mode!="die"){ block.onframe(); }else{ totalnum++; } for(var i=0;i<mapData.length;i++){ if(mapData[i].toString()==[1,1,1,1,1,1,1,1,1,1,1,1,1,1].toString()){ canclemap(i); } } checkEnd(); } function initmap(){ for(var i=0;i<20;i++){ mapsprite[i]=new Array(); for(var j=0;j<14;j++){ mapsprite[i][j]=new LSprite(); } } } function move(e){ block.onbutton(e.keyCode); } function canclemap(n){ for(var i=0;i<mapsprite[n].length;i++){ mapsprite[n][i].remove(); } for(var i=n;i<mapData.length;i++){ if(i<mapData.length-1){ mapData[i]=mapData[i+1]; for(var j=0;j<mapsprite[n].length;j++){ mapsprite[i+1][j].y=mapsprite[i+1][j].y+10; mapsprite[i]=mapsprite[i+1]; } }else{ mapData[i]=[0,0,0,0,0,0,0,0,0,0,0,0,0,0]; for(var j=0;j<mapsprite[n].length;j++){ mapsprite[i][j].remove(); } } } } function checkEnd(){ for(var j=0;j<14;j++){ if(mapData[19][j]==1){ alert("game over"); } } } </script> </head> <body> <div id="legend"></div> </body> </html>
tetrisblock.js
var maxX=0;var maxY=0;var speed=1;//初始为5function LBlock(){ var s=this; base(s,LSprite,[]); s.i=Math.round(Math.random()*6+1); //s.i=7; //1为竖形,2为L形,3位反L形,4为正方形,5为楼梯形,6为反楼梯形,7为T形 s.status=1;//1为默认,2为 s.activemode=true;//true可操作,false不可 s.mode="live"; s.onshow();}LBlock.prototype.setspeed=function(speedc){ speed=speedc;}LBlock.prototype.getvalue=function (){ var s=this; return s;}LBlock.prototype.onframe=function(){ var s=this; var flag=true; if(s.childList[maxY].y<190){ for(var i=0;i<s.childList.length;i++){ if(s.childList[i].y>=0){ var m=Math.floor((190-(s.childList[i].y+speed))/10); var n=Math.floor(s.childList[i].x/10); if(mapData[m][n]==1){ s.activemode=false; s.mode="die"; s.result(); flag=false; } } } if(flag){ for(var i=0;i<s.childList.length;i++){ s.childList[i].y=s.childList[i].y+speed; if(s.childList[maxY].y<=s.childList[i].y){ maxY=i; } } } }else{ s.activemode=false; s.mode="die"; s.result(); }}LBlock.prototype.result=function(){ var s=this; var b=[]; for(var i=0;i<s.childList.length;i++){ var x=s.childList[i].x; var y=190-s.childList[i].y; //求得数组坐标 var m=Math.round(x/10); var n=Math.round(y/10); mapData[n][m]=1; mapsprite[n][m]=s.childList[i]; mapsprite[n][m].graphics.drawRect(2,"blue",[0,0,10,10],true,"green"); //mapLayer.addChild(mapsprite[n][m]); b[i]=[n,m]; } for(var i=0;i<b.length;i++){ mapLayer.addChild(mapsprite[b[i][0]][b[i][1]]); } s.remove();}LBlock.prototype.onbutton=function(button){ var s=this; var flag=true; if(s.activemode){ var sprite=[]; for(i=0;i<4;i++){ sprite[i]=new LSprite(); sprite[i]=s.childList[i]; } //旋转 if(button==65){ //以sprite2为坐标原点 flag=true; for(var i=0;i<4;i++){ var x=sprite[i].x-sprite[2].x; var y=-(sprite[i].y-sprite[2].y); var m=-y+sprite[2].x; var n=190-(-x+sprite[2].y); if((mapData[Math.floor(n/10)][Math.floor(m/10)]==1)||(m>130)||(m<0)||(n>190)){ flag=false; } } if(flag){ for(var i=0;i<4;i++){ var x=sprite[i].x-sprite[2].x; var y=-(sprite[i].y-sprite[2].y); sprite[i].x=-y+sprite[2].x; sprite[i].y=-x+sprite[2].y; if(sprite[maxX].x<sprite[i].x) maxX=i; if(sprite[maxY].y<sprite[i].y) maxY=i; } } }else if(button==37){ //左移右移 flag=true; for(var i=0;i<4;i++){ var x=sprite[i].x-10; var y=sprite[i].y; var m=190-y; var n=x; if(y>=0){ if((mapData[Math.floor(m/10)][Math.floor(n/10)]==1)||(n>130)||(n<0)||(m>190)){ flag=false; } } } if(flag){ for(var i=0;i<4;i++){ sprite[i].x=sprite[i].x-10; if(sprite[maxX].x<sprite[i].x) maxX=i; if(sprite[maxY].y<sprite[i].y) maxY=i; } } }else if(button==39){ //左移右移 flag=true; for(var i=0;i<4;i++){ var x=sprite[i].x+10; var y=sprite[i].y; var m=190-y; var n=x; if(y>=0){ if((mapData[Math.floor(m/10)][Math.floor(n/10)]==1)||(n>130)||(n<0)||(m>190)){ flag=false; } } } if(flag){ for(var i=0;i<4;i++){ sprite[i].x=sprite[i].x+10; if(sprite[maxX].x<sprite[i].x) maxX=i; if(sprite[maxY].y<sprite[i].y) maxY=i; } } }else if(button==40){ //左移右移 flag=true; for(var i=0;i<4;i++){ var x=sprite[i].x; var y=sprite[i].y+20; var m=190-y; var n=x; if(y>=0){ if((mapData[Math.floor(m/10)][Math.floor(n/10)]==1)||(n>130)||(n<0)||(m>190)){ flag=false; } } } if(flag){ for(var i=0;i<4;i++){ sprite[i].y=sprite[i].y+20; if(sprite[maxX].x<sprite[i].x) maxX=i; if(sprite[maxY].y<sprite[i].y) maxY=i; } } } }}function find(f,a){ for(var i=0;i<a.length;i++){ if((a[i][1]==f[1])&&(a[i][0]==f[0])){ return false; } } return true;}LBlock.prototype.onshow=function(){ var s=this; var x=Math.floor(Math.random()*13)*10; //var x=200; var y=0; var i; var a=new Array(); var save=new Array(); a[0]=[x,y]; save=[[x,y],[-1,-1],[-1,-1],[-1,-1]]; for(i=1;i<4;i++){ if(a[i-1][0]==130){ var b=[[-10,0],[0,10],[0,-10]] }else if(a[i-1][0]==0){ var b=[[10,0],[0,10],[0,-10]] }else{ var b=[[-10,0],[10,0],[0,10],[0,-10]]; } var select=Math.round(Math.random()*(b.length-1)); var m=a[i-1][0]+b[select][0]; var n=a[i-1][1]+b[select][1]; if(find([m,n],save)){ a[i]=[m,n] save[i]=[m,n] }else{ b.splice(select,1); select=Math.round(Math.random()*(b.length-1)); m=a[i-1][0]+b[select][0]; n=a[i-1][1]+b[select][1]; a[i]=[m,n]; } } var sprite=[]; for(i=0;i<4;i++){ sprite[i]=new LSprite(); sprite[i].x=a[i][0]; sprite[i].y=a[i][1]; sprite[i].graphics.drawRect(2,"blue",[0,0,10,10],true,"green"); s.addChild(sprite[i]); }}
- lufylegend引擎俄罗斯方块的实现
- lufylegend引擎俄罗斯方块js代码!
- lufylegend引擎 实现2D RPG地图的切换
- 使用lufylegend引擎实现图片变形
- 利用开源HTML5引擎lufylegend.js结合javascript实现的五子棋人机对弈
- lufylegend HTML5游戏引擎
- lufylegend引擎 rpg开发之2D地图的碰撞检测
- 俄罗斯方块的接口实现
- 俄罗斯方块的简单实现
- MFC实现的俄罗斯方块
- C# 俄罗斯方块的实现
- C实现的俄罗斯方块
- 俄罗斯方块的实现
- 俄罗斯方块java的实现
- 俄罗斯方块的源码实现
- 俄罗斯方块的 C++实现
- 俄罗斯方块的源码实现
- 实现俄罗斯方块的下落
- tomcat-5.0.28在https模式下IE8无法下载文件问题解决
- 喜羊羊系列之【设备 - 驱动 编入内核】
- 任意模型的折纸效果 Folding effect
- 解决同一IP不同端口访问的站点iframe应用session丢失的问题
- OpenCL异构计算资料收集
- lufylegend引擎俄罗斯方块的实现
- javascript轮播技术
- Making a Kali Bootable USB Drive
- poj 3083
- mysql 时间日期查询
- ceph pg(placement group)状态总结
- 黑马程序员-java之网络编程
- OC视频笔记5.4(协议的定义与使用)(代理设计模式)(代理设计模式思想)
- 各类移动安全竞赛题/部分writeup收集与整理