一,游戲怎么玩奔脐?
好吧俄周,我又回來了,之前利用SpriteKit游戲引擎做過一個十字消除的游戲髓迎。對于不會其它引擎的人來說峦朗,SpriteKit的優(yōu)點就是比較簡單迅速,快速開發(fā)些有趣的小游戲排龄。前些天甚垦,玩了一個很好玩的在線游戲Hex Frvr,是使用Html5做的涣雕,在AppStore上有可以下載玩到艰亮,在線游戲的地址點這里。
從這里下載完整的Demo工程挣郭,在Xcode里面直接打開運行吧迄埃。玩起來的效果是這樣的:
Ok,東西就是這么個東東兑障,本文就詳細描述下demo里面的內(nèi)容侄非,用SpriteKit來實現(xiàn)吧。
二流译,開始制作游戲之旅逞怨。
1,素材的準備福澡。
好吧叠赦,之前做十字消游戲的時候,使用程序生成方塊的素材革砸。這次素材是六邊形除秀,程序這塊本想研究下怎么生成的,不過學習使人進步算利,我最近研究了下Mac OS下的一個設計神器“Sketch”册踩,然我由此走上了設計師的路(尼瑪,老板你怎么不請美工靶谩)暂吉。
ok胖秒,簡單使用下Sketch,畫個六邊形慕的。如果不想畫的哥們扒怖,請直接拿Demo工程里面的素材,不謝不謝业稼!
Sketch繪制六邊形的方法:
(1)繪制六邊形盗痒。
a,打開Sketch低散,新建一個畫布(A)
b俯邓,插入多邊形,默認是5邊形熔号,先畫一個稽鞭。在右邊檢查器里選6,增加一個點引镊,就生成了6變形朦蕴。
注意:按著Shift鍵拉動,調(diào)整成正六邊形弟头。
編輯多變形點個數(shù)
(2)上顏色加增加立體效果吩抓。
a,上顏色赴恨。
在右側檢查器的Fill欄疹娶,選擇喜歡顏色,然后Blending模式選擇Normal伦连。就有基本的底色了雨饺。
b,增加圖像的立體感惑淳。
增加增加線性漸變:Fill欄里额港,添加線性漸變,Blending模式選擇Overlay歧焦。
圓形漸變:Fill欄里移斩,添加圓形漸變,Blending模式選擇Overlay倚舀。
效果特效自己可以隨便設置叹哭,我都是以最上面那個點為漸變起點忍宋,最底下的點為漸變終點痕貌,強調(diào)一下立體的感覺。
最終效果如圖所示:
線性漸變和圓形漸變
c糠排,導出素材
點擊export舵稠,按照提示到處png格式圖片。
2,游戲玩法和規(guī)則哺徊。
游戲規(guī)則(百度詞條):
“游戲的主界面是一個大六邊形棋盤[2] 室琢,六邊形的每一邊又由5個小六邊形組成,構成共有61格的棋盤空間落追。玩家拖動系統(tǒng)自動輸出的各種六邊形組合置于大六邊形的空白處盈滴,使之排列成完整的一行或多行并且消除得分,而連續(xù)消除會有額外Combo加分轿钠。直到棋盤上再也無法擺下任意一個六邊形組合的時候巢钓,游戲就會失敗×贫猓”
OK症汹,歸納成編寫程序的輸入,我們要實現(xiàn)下面的東東:
(1)61個小六邊形組成的蜂巢狀棋盤贷腕。
如圖的游戲主界面所示背镇,主界面是如游戲名Hex所代表的蜂窩狀六邊形組合成的棋盤,共有61格泽裳,為了顯示方便瞒斩,我都邊上了號碼。
(2)24種有4個小六邊形組成的不同形狀+1個單六邊形涮总。
如圖2.6所示济瓢,4個小六邊形所排列組合成的不同形狀,就是我們要填入到棋盤格子里面的圖形和形狀妹卿。
(3)將(2)中的不同形狀填入(1)中的棋盤旺矾,在橫向,左斜方向和右斜方向有占滿的六邊形格子就消除夺克。
(4)每回合有三個(2)中的形狀箕宙,填入一個補充一個。如果三個形狀都無法再填入61格子的棋盤中铺纽,游戲結束柬帕。
其游戲過程可以參見文章開頭的動圖顯示。
3狡门,游戲實現(xiàn)數(shù)據(jù)結構及算法說明陷寝。
(1)要有方向。
一個六邊形有六個方向其馏,因此如果要在棋盤中進行比較和消除凤跑,必須比較每一個小六邊形單元六個方向的情況。很顯然叛复,我們將六邊形的六個方向做一個編號:
//LeftTop:0
//RightTop:1
//Right:2
//RightBottom:3
//LeftBottom:4
//Left:5
typedef enum : NSInteger {
SUDNone = -1,
SUDTopLeft = 0,
SUDTopRight,
SUDRight,
SUDBottomRight,
SUDBottomLeft,
SUDLeft,
} ShapeUnitDirector;
其方向示意如圖所示:
(2)要有順序仔引。
如何檢查2.(2)中的25種形狀是否可以放入2.(1)中的棋盤呢扔仓?嗯,好了咖耘,計算機要一個一個的比較翘簇,沒有你聰明。不過好在它的速度飛快儿倒!
但是你要告訴電腦怎么弄版保。首先比較是有順序的,對比較的不同的形狀夫否,必須規(guī)定一個比較順序找筝。為其中每一個單元的六邊形編一個序號,表示比較的順序慷吊。這個順序要是一個連續(xù)的袖裕,可達的路徑,計算機比較的時候能有來有回溉瓶。路徑可以用上面規(guī)定的方向來表示急鳄。如圖選了兩個形狀,來說明他們的比較順序堰酿。
“一”和“二”的兩個圖形里每個小六邊形都有編號疾宏,其編號就是其比較順序,1非常重要触创,是比較的起始點坎藐。
“一”圖形里,1是起始比較點哼绑,比較1后岩馍,就要比較2,2位于1的LeftBottom方位抖韩,所以要將LeftBottom記住蛀恩。3位于2的RightBottom位置,4位于3的RightTop方位茂浮。所以按照1到4的順序双谆,比較路徑就是[LeftBottom,RightBottom席揽,RightTop]顽馋,紅色箭頭所示。
同理幌羞,“二”的圖形里寸谜,比較路徑就是[Right,RightBottom新翎,LeftBottom]程帕。
注意:這個形狀比較順序非常重要住练,要理解清楚地啰。
(3)要會比較愁拭。
a,第一種比較是亏吝,將圖形放置到棋盤上后岭埠,看圖形里的每一個六邊形是否能放置到棋盤上去。在程序里面其實算法很簡單蔚鸥,就是遍歷圖形里每一個小六邊形惜论,看其所在位置下的棋盤格子是否是空的,如果全都是空的就可以放上去了止喷。
b馆类,每一次成功放置了形狀后,都會補充一個新的形狀弹谁。此時乾巧,要判定游戲失敗條件,即游戲是否可以進行下去预愤。將現(xiàn)有的沒有放進棋盤的三個形狀沟于,迭代的對棋盤里的每一個位置進行一下比較,看是否能放得進去植康。如果三個形狀都放不進棋盤旷太,那么游戲結束了。
注意销睁,這里的比較供璧,就要使用到剛才定義的比較順序了,如“一”里面的[LeftBottom冻记,RightBottom嗜傅,RightTop],因為你知道棋盤格每一個的位置檩赢,棋盤六個方向的位置也可以通過數(shù)據(jù)結構來記錄吕嘀,但是你需要知道放置的圖形,它的比較路徑和位置信息贞瞒,才能夠很好的便利偶房,所以才會定義比較路徑和比較起始點,有了這兩個元素军浆,比較才能進行棕洋。
PS:其實還有別的方法,僅需要圖形的起始點乒融,把起始點移到棋盤的每一格掰盘,按照比較a里的位置判斷方法比較摄悯。好了,其實比較簡單啦愧捕!理解下就好奢驯。
4,編程實現(xiàn)次绘。
SpriteKit的用法和Cocos2dx比較像瘪阁,也是將實體精靈Sprite以樹形結構組織,你來規(guī)定Sprite節(jié)點的交互和動畫邮偎,達到游戲的結果管跺。在iOS9系統(tǒng)中加入了很多的新功能,實在值得好好研究禾进,不過這里我們用的比較簡單豁跑。大家也可以看看我之前寫的《使用SpriteKit游戲引擎,做一個十字消游戲》泻云。里面有SpriteKit的普及和基礎知識艇拍,我們在這里就不對SpriteKit引擎進行過多的講解。
游戲界面:
(1)設計游戲界面布局壶愤。
第一淑倾,由于是蜂巢狀的六邊形,每一行的個數(shù)先是增加征椒,后來又遞減娇哆,而且位置又不太相同。所以需要記錄棋盤每行的單元格個數(shù)勃救。計算出橫向和縱向的距離碍讨,再進行添加。
第二蒙秒,需要記錄蜂巢狀六邊形的六個方向的單元格編號勃黍,方便比較的時候搜索。我在這里使用了一個JSON文件晕讲,將每一個單元格的信息寫入里面覆获,初始化的時候讀入,生成相關的信息數(shù)據(jù)結構瓢省。其結構如下弄息,serialNum號就是單元格編號,如圖2.5所示勤婚。adjacent就是鄰接的單元點編號摹量,-1代表該方向沒有單元格。
"unitInfos" : [
{
"x" : 0,
"y" : 0,
"serialNum" : 0,
"adjacent" : "-1 -1 1 6 5 -1"
},
添加棋盤的代碼參見Demo代碼里的GameScene.m的如下代碼:
- (void)addPlayground
{
// 1 初始化相關數(shù)據(jù)結構
SKSpriteNode *node;
self.unitNodeArray = [[NSMutableArray alloc] init];
self.unitTexture = [SKTexture textureWithImageNamed:@"6kuai_gray.png"];
self.unitWidth = self.unitTexture.size.width;
self.unitHight= self.unitTexture.size.height;
//2 生成每行的單元格個數(shù),并設置起始點缨称。
NSArray *arrayNumber = @[@5,@6,@7,@8,@9,@8,@7,@6,@5];
CGPoint startPoint = CGPointMake(CGRectGetMidX(self.frame) -2*self.unitWidth, CGRectGetHeight(self.frame)-150 );
// 3 兩層循環(huán)凝果,擺放棋盤單元格,并填入從JSON中讀取的信息睦尽,放入userdata字段器净。
int index = 0;
int nodeCount = 0;
for (NSNumber *lineNumber in arrayNumber) {
int count = lineNumber.intValue;
for (int i = 0; i < count; i++) {
//3.1 生成單元格節(jié)點
node = [SKSpriteNode spriteNodeWithTexture:self.unitTexture];
//3.2 擺放位置
if (index <= 4) {
[node setPosition:CGPointMake(startPoint.x-XDISTANCE*index +i*self.unitWidth, startPoint.y-YDISTANCE*index)];
}
else
{
[node setPosition:CGPointMake(startPoint.x - XDISTANCE*((PLAYGROUNDLINE-1)-index) + i*self.unitWidth, startPoint.y - YDISTANCE*index)];
}
// 3.3 讀取單元格信息,并填入userData
ShapeUnitInfo *unitInfo = [_unitInfoArray objectAtIndex:nodeCount];
unitInfo.unitPosition = node.position;
node.userData = [[NSMutableDictionary alloc] init];
[node.userData setValue:unitInfo forKey:@"unitInfo"];
[node setName:@"unitShape"];
// 3.4 加入數(shù)字標簽
SKLabelNode *label = [SKLabelNode labelNodeWithText:[NSString stringWithFormat:@"%d",nodeCount]];
label.position = CGPointMake(0, 0);
label.fontColor = [UIColor blackColor];
label.fontSize = 18;
label.zPosition = 2;
[node addChild:label];
//3.5 添加節(jié)點入GameScene
[self addChild:node];
[self.unitNodeArray addObject:node];
nodeCount++;
}
index++;
}
}
- (void)addPlayground
{
// 1 初始化相關數(shù)據(jù)結構
SKSpriteNode *node;
self.unitNodeArray = [[NSMutableArray alloc] init];
self.unitTexture = [SKTexture textureWithImageNamed:@"6kuai_gray.png"];
self.unitWidth = self.unitTexture.size.width;
self.unitHight= self.unitTexture.size.height;
//2 生成每行的單元格個數(shù)骂删,并設置起始點掌动。
NSArray *arrayNumber = @[@5,@6,@7,@8,@9,@8,@7,@6,@5];
CGPoint startPoint = CGPointMake(CGRectGetMidX(self.frame) -2*self.unitWidth, CGRectGetHeight(self.frame)-150 );
// 3 兩層循環(huán)四啰,擺放棋盤單元格宁玫,并填入從JSON中讀取的信息,放入userdata字段柑晒。
int index = 0;
int nodeCount = 0;
for (NSNumber *lineNumber in arrayNumber) {
int count = lineNumber.intValue;
for (int i = 0; i < count; i++) {
//3.1 生成單元格節(jié)點
node = [SKSpriteNode spriteNodeWithTexture:self.unitTexture];
//3.2 擺放位置
if (index <= 4) {
[node setPosition:CGPointMake(startPoint.x-XDISTANCE*index +i*self.unitWidth, startPoint.y-YDISTANCE*index)];
}
else
{
[node setPosition:CGPointMake(startPoint.x - XDISTANCE*((PLAYGROUNDLINE-1)-index) + i*self.unitWidth, startPoint.y - YDISTANCE*index)];
}
// 3.3 讀取單元格信息欧瘪,并填入userData
ShapeUnitInfo *unitInfo = [_unitInfoArray objectAtIndex:nodeCount];
unitInfo.unitPosition = node.position;
node.userData = [[NSMutableDictionary alloc] init];
[node.userData setValue:unitInfo forKey:@"unitInfo"];
[node setName:@"unitShape"];
// 3.4 加入數(shù)字標簽
SKLabelNode *label = [SKLabelNode labelNodeWithText:[NSString stringWithFormat:@"%d",nodeCount]];
label.position = CGPointMake(0, 0);
label.fontColor = [UIColor blackColor];
label.fontSize = 18;
label.zPosition = 2;
[node addChild:label];
//3.5 添加節(jié)點入GameScene
[self addChild:node];
[self.unitNodeArray addObject:node];
nodeCount++;
}
index++;
}
}
(2)設置游戲相關初始化數(shù)據(jù)。
這里就是讀入JSON文件匙赞,并將分數(shù)置0佛掖。
- (void)unitInfoInit
{
// 1 初始化信息存入的數(shù)據(jù)容器,一個NSArray
if (_unitNodeArray != nil) {
return;
}
_unitInfoArray = [[NSMutableArray alloc] init];
// 2 讀入JSON文件
NSString *bundleDir = [[NSBundle mainBundle] bundlePath];
NSString *path = [bundleDir stringByAppendingPathComponent:@"unitInfo.json"];
NSURL *url = [NSURL fileURLWithPath:path];
NSData *data = [NSData dataWithContentsOfURL:url];
NSError *error = nil;
// 3 解析JSON文件涌庭,并存入Arrary容器
NSDictionary *jsonDic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
NSArray *unitInfos = [jsonDic objectForKey:@"unitInfos"];
if (unitInfos != nil) {
for (NSDictionary *unitInfoDic in unitInfos) {
ShapeUnitInfo *unitInfo = [[ShapeUnitInfo alloc] init];
int x = ((NSNumber *)[unitInfoDic objectForKey:@"x"]).intValue;
int y = ((NSNumber *)[unitInfoDic objectForKey:@"y"]).intValue;
int sn = ((NSNumber *)[unitInfoDic objectForKey:@"serialNum"]).intValue;
NSString *adjacentString = (NSString *)[unitInfoDic objectForKey:@"adjacent"];
NSArray *adjacents = [adjacentString componentsSeparatedByString:@" "];
unitInfo.unitLocation = CGPointMake(x, y);
unitInfo.serialNumber = sn;
[unitInfo.adjacentArray addObjectsFromArray:adjacents];
[_unitInfoArray addObject:unitInfo];
}
}
}
(3)三個備選容器添加.
在棋盤下面的位置添加三個備選容器芥被,如圖2.5種的2所標示的位置。
- (void)addShapeFrame
{
//1 初始化存儲容器
SKSpriteNode *node;
_shapePosArray = [[NSMutableArray alloc] initWithCapacity:3];
_shapeArray = [[NSMutableArray alloc] initWithCapacity:3];
// 2 生成被選位置節(jié)點坐榆,并加入到Scene中去
for (int i = 0; i < 3; i++) {
node = [[SKSpriteNode alloc] init];
node.size = CGSizeMake(100, 100);
node.position= CGPointMake(CGRectGetMidX(self.frame) + (i - 1)*120, 220);
node.name = [NSString stringWithFormat:@"shapeFrame_%d",i];
[self addChild:node];
[_shapePosArray addObject:[NSValue valueWithCGPoint:node.position]];
}
// 3 調(diào)用生成被選圖形的接口拴魄,填充入這些位置節(jié)點。
[self shapeFill];
}
(4)隨機生成填充形狀席镀。
上面(3)中代碼的最后一步匹中,在GameScene里面調(diào)用shapeFill方法來,填充三個備選容器豪诲。實際上是調(diào)用RandomShapeMgr.h中的RandomShapeMgr的單例對象顶捷,生成如圖2.6所示的25種不同的待填入形狀。重要的是將其編號屎篱,和比較隊列寫好服赎,放入一個隊列對象。RandomShapeMgr里的代碼里面的posInfoInit方法可以研究下交播,節(jié)點的位置隊列和比較隊列如何生成好保存重虑。
游戲交互
實際上SpriteKit里面的交互和iOS應用里的交互一脈相承。由于有點擊堪侯,拖動圖形嚎尤,放下圖形等操作,所以使用如下幾個方法:
a伍宦,-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event芽死;
b乏梁,- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event;
c关贵,- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event遇骑;
分別在點擊開始,中途和結束時進行程序處理完成主要核心交互揖曾,如下三個功能:
(1)點擊形狀落萎,識別形狀,并移動炭剪。
點擊形狀练链,調(diào)用方法a。在方法a中,根據(jù)節(jié)點Name值,判斷是否時需要保存處理的形狀節(jié)點霸妹。如是的話,使用 _handleNode來持有绿鸣。因為屏幕大小限制暂氯,平時待選的圖形僅僅只有單元格大小的1/2,因此點擊持有形狀后痴施,會執(zhí)行動畫,將待選形狀擴大一倍晾剖。處理邏輯:
if ([node.name isEqual:@"shape"]) {
// NSLog(@"shape");
_handleNode = node;
[_handleNode runAction:[SKAction scaleTo:2 duration:0.4] completion:^{
}];
break;
}
移動持有圖形,調(diào)用方法b齿尽,如果_handleNode有值的話,實時更新_handleNode的位置Position值循头。
(2)放置形狀,并判斷是否能夠放置入棋盤卡骂。
在方法c中,判定是否能夠放入棋盤全跨。就是判斷圖形里的節(jié)點是否能否放在它下面的那個單元格中。處理邏輯:
if (_handleNode != nil) {
// 1 獲取放入形狀的所有小六邊形,并獲取其顏色紋理texuture
NSArray *handleShapeNodes = [_handleNode children];
SKTexture *texture = [(SKSpriteNode *)[[_handleNode children] firstObject] texture];;
// 2 遍歷所有的小六邊形渺杉,獲取其位置,并察看該位置下是否存在Unit單元格是越,如果存在單元格,察看是否是被占狀態(tài)倚评。如果所有的小六邊形下,都有未被占用的單元格天梧,那么就可以放置在棋盤上了盔性;反之,返回形狀待選區(qū)域腿倚。
NSUInteger index = 0;
NSUInteger ocuppiedCount = 0;
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
// 2.1 遍歷小六邊形
for (SKSpriteNode *child in handleShapeNodes) {
index++;
// 2.2 獲取小六邊形位置
CGPoint childLocation = CGPointMake(child.position.x*2 +_handleNode.position.x, child.position.y*2+_handleNode.position.y);
// 2.3 獲取該位置下的所有節(jié)點
NSArray *shapeNodes = [self nodesAtPoint:childLocation];
// 2.4 看該節(jié)點下纯出,是否存在違背占用的單元格
for (SKNode *shapeNode in shapeNodes) {
if ([self isShapeUnit:(SKSpriteNode *)shapeNode] && ![self isUnitOcuppied:(SKSpriteNode *)shapeNode]) {
ocuppiedCount++;
[tempArray addObject:shapeNode];
}
}
}
// 2.5 如果所有六邊形都用空白的Unit可以占蚯妇,那么就可以放入敷燎。
if ( index == ocuppiedCount ) {
// 2.6 執(zhí)行占用,并調(diào)用shapeFill補充shape
for (SKSpriteNode *unitNode in tempArray) {
[unitNode setTexture:texture];
ShapeUnitInfo *unitInfo = [unitNode.userData objectForKey:@"unitInfo"];
unitInfo.occupy = YES;
// NSLog(@"set occupy");
}
[_shapeArray removeObject:_handleNode];
[_handleNode removeFromParent];
[self shapeFill];
}
else {
// 2.7 否則就將shape移動回待選區(qū)域箩言。
NSUInteger index = [_shapeArray indexOfObject:_handleNode];
CGPoint location = [(NSValue *)[_shapePosArray objectAtIndex:index] CGPointValue];
SKAction *scale = [SKAction scaleTo:1 duration:0.3];
SKAction *move = [SKAction moveTo:location duration:0.3];
SKAction *group = [SKAction group:@[scale,move]];
group.timingMode = SKActionTimingEaseOut;
[_handleNode runAction:group];
}
_handleNode = nil;
(3)消除判斷硬贯,消除積分增加。
在方法c中陨收,還需要進行消除判斷饭豹,填入形狀后,是否會在橫务漩,左斜和右斜方向存在填滿一行的情況拄衰,如果有就需要進行消除,并積分饵骨。
// 檢查消除并積分
[self resultDealElimination];
使用數(shù)組記錄下Top和Bottom行的單元格編號翘悉,并記錄每一行開頭的單元格編號:
// 1 每一行開頭的單元格編號
NSArray *compareIndexRow = @[@0,@5,@11,@18,@26,@35,@43,@50,@56];
// 2 Top行所有元素的編號
NSArray *compareIndexTopSlash = @[@0,@1,@2,@3,@4];
// 3 Bottom行所有元素的編號
NSArray *compareIndexBottomSlash = @[@56,@57,@58,@59,@60];
涉及單元格如圖:
比較方向如下圖所示,1是橫向居触,2是Top斜妖混,3是Bottom斜:
比較方向按六邊形方向定義比較,具體見代碼轮洋。
(4)游戲結束判斷
將三個待填入的圖形分別比較放在棋盤里進行
// 調(diào)用檢查是否能Continue
[self checkContinue];
比較方案如上述所描述制市,按照填入圖形的比較序列,逐個對每個單元格進行比對弊予,如果還存在可以填入的位置祥楣,游戲就可以繼續(xù),如果不存在,游戲就結束误褪。核心比較代碼:
- (BOOL)isOccupByShape:(SKSpriteNode *)shapeNode atUnit:(SKSpriteNode *)unitNode
{
// 1 獲取該形狀Shape的比較序列
NSArray *comSeqArray = (NSArray *)[shapeNode.userData objectForKey:@"shapeCompOrder"];
// 2 以讀入的單元格為起始比較單元格床未,按照比較序列進行比較。
SKSpriteNode *tempNode = unitNode;
ShapeUnitInfo *nodeInfo = (ShapeUnitInfo *)[tempNode.userData objectForKey:@"unitInfo"];
if ([nodeInfo isOccupied]) {
return YES;
}
for (NSNumber *index in comSeqArray) {
NSInteger nodeIndex = [(NSNumber *)[nodeInfo.adjacentArray objectAtIndex:[index unsignedIntegerValue]] integerValue];
if(-1 == nodeIndex) {
return YES;
}
tempNode = (SKSpriteNode *)[_unitNodeArray objectAtIndex:nodeIndex];
nodeInfo = (ShapeUnitInfo *)[tempNode.userData objectForKey:@"unitInfo"];
if ([nodeInfo isOccupied]) {
return YES;
}
}
return NO;
}
三薇搁,游戲效果渡八,何去何從。
好了屎鳍,從這里下載完整的Demo工程逮壁,在Xcode里面運行打開吧。執(zhí)行效果文章開始所示卖宠。
好了忧饭,其實還有很多的功能可以添加和細化词裤。比如添加很多的動畫效果,增加積分機制和加入社交化分享逆航,廣告條因俐。很多功能可以添加赖瞒,有興趣的哥們,就在GitHub的Frvr項目吧兔,這個工程里面境蔼,好好加油,我頂你哦箍土!