Category Archives: Chuẩn hóa LapKan

Thuật toán ngẫu nhiên trong GameplayKit (Phần 1)

I. Thuật toán ngẫu nhiên trong GameplayKit:

Đối với những trò chơi mà xác suất và cơ hội đóng vai trò chính thì thuật toán ngẫu nhiên chính là cốt lõi của cả trò chơi. Khi bạn chơi một trò chơi trên những bàn cờ (board games) thì những điểm của xúc xắc quyết định số nước đi của người chơi hay việc xào bài trong một trò chơi bài bạc như xí dzách, tiến lên. Những điều bất ngờ trong game tạo ra những điểm hấp dẫn dành cho game và thay đổi toàn bộ cách chơi nếu bạn thay đổi xác xuất trong game. Tính ngẫu nhiên có vẻ đơn giản nhưng thực sự để có được sự ngẫu nhiên tốt trong 1 game nó không dễ dàng chút nào.

Việc tạo ra tính ngẫu nhiên trong 1 game kích thích người chơi cố gắng hơn trong việc tiến tới mục tiêu. Việc có được viên kim cương với tỉ lệ 10 % sẽ rất hảo hứng vì nó cho người chơi một mục tiêu cụ thể hơn cho việc hạ gục một con boss trong trò chơi so với việc không biết gì về khả năng có thể có được viên kim cương đó. Tuy nhiên việc đánh hơn 50 lần vẫn không thể có được sẽ làm cho người chơi không còn niềm tin vào tỉ lệ này.

Chính từ những điểm này mà ta cần phải tập trung vào những điểm quan trọng trong việc tạo ra các ngẫu nhiên trong trò chơi.

+ Tính ngẫu nhiên: Một con số hay tỉ lệ ngẫu nhiên nghĩa là bạn không thể đoán ra được (dĩ nhiên là không phải trong tình trạng tỉ lệ 100 %). Tuy nhiên, một thuật toán ngẫu nhiên tốt là gì? Dĩ nhiên là nó khó đoán trước nhưng đó không phải là tất cả những gì có thể có của 1 thuật toán ngẫu nhiên tốt. Thuật toán ngẫu nhiên tốt phải bao gồm cả việc cân bẳng trong giới hạn của nó. Khi bạn ngẫu nhiên 10.000 số trong giới hạn 1-100 thì việc ra các số này phải có tỉ lệ tương đồng với nhau và không có số nào là không xuất hiện. Chu kỳ lặp lại của nó cũng cần phải khó đoán, trong 100 số thì không có sự lặp lại của những khối số.

+ Hiệu suất: Việc tạo ra một đoạn mã thực thi phức tạp để tạo ra tính ngẫu nhiên tốt nhất dĩ nhiên là tốt, tuy nhiên, nó phải đi kèm với hiệu suất tốt. Bạn không thể cho chạy một thuật toán phức tạp chỉ để cho ra 1 số trên mặt xúc xắc trong khi có hàng ngàn thứ khác đang cần xử lý.

+ Định luận: Một trò chơi chất lượng đòi hỏi cần phải được kiểm tra đầy đủ (Test), tuy nhiên với các thuật toán ngẫu nhiên thì việc điểm định hết tất cả các trường hợp trong game gần như là không thể. Một thuật toán ngẫu nhiên tốt sẽ duy trì những tình huống bất ngờ trong game dành cho người chơi. Thêm vào đó, nó cần được quan tâm đúng mức để có thể mở rộng ra khi cần thiết.

+ Tính độc lập: Những con số hay nguồn ngẫu nhiên phải độc lập so với kết quả gần nhất.

+ Tính ngẫu nhiên tương đồng: Những gì được ngẫu nhiên cần phải tương đồng với tất cả các kết quả được đưa ra.

  • Cách sử dụng Randomization trong game:

Toàn bộ những lớp ngẫu nhiên, tính ngẫu nhiên hoá trong GameplayKit đều thông qua giao thức GKRandom. Nó sử dụng thông qua các mô thức tối thiểu trong các giao diện. Chính vì vậy việc đầu tiên bạn cần là chọn ra cách thức ngẫu nhiên phù hợp với game của bạn, tất cả những gì còn lại hãy để cho GameplayKit làm dùm:

+ Trong hầu hết các trường hợp, bạn sẽ cần một con số ngẫu nhiên đồng nhất trong 1 khoản giới hạn. Ví dụ như bạn cần 1 số nằm trong khoản giữa 1 tới 100. Như vậy thứ bạn cần là GKRandomDistribution.

+  Để tuỳ chính tính ngẫu nhiên nhưng vẫn mang tính đồng nhất giữa chúng, bạn có thể chọn những lớp phụ nằm trong GKRandomSource

+ Để có thể tuỳ chỉnh tính ngẫu nhiên (không còn đồng nhất) thì bạn cần phải dùng đến các lớp GKGaussianDistribution hay GKShuffledDistribution

+ Nếu bạn không cần sử dụng đến thuật toán ngẫu nhiên trong một khoảng hay một giới hạn nào đó thì bạn chỉ việc sử dụng trực tiếp GKRandomSource

+ Bạn chỉ việc sử dụng GKRandomSource để ngẫu nhiên một mảng (array) như ngẫu nhiên xào bài.

+ Bạn chỉ việc đưa mảng vào  arrayByShuffelingObjectsInArray:

+ Lệnh sẽ trả về một nhân bản với thứ tự ngẫu nhiên.

Advertisements

Swift Code – Biến và hằng số

Trong lập trình, việc đầu tiên một lập trình viên cần phải làm là bắt tay vào khai báo và định nghĩa các biến, sau đó chúng ta mới lưu trữ và sử dụng nó cho chương trình. Đối với Swift, biến cũng là một thành phần không thể thiếu.

Chúng ta sẽ bắt đầu với việc khai báo một biến trong Swift. Hãy xem một ví dụ:

var lapKanTitle: String = “LapKan Corp.”

Ở trên, ta vừa khai báo một biến tên lapKanTitle kiểu String và có giá trị khởi tạo là “LapKan Corp.”. Đây cũng là cách khai báo biến phổ biến nhất trong Swift!

Tiếp theo là một ví dụ khác:

let lapKanTitle: String = “LapKan Corp.”

Hai câu lệnh khá giống nhau, chỉ khác nhau ở từ khóa var và let. Vậy, sự khác biệt ở đây là gì? Trong câu lệnh thứ nhất, từ khóa var khai báo một biến và biến này có thể thay đổi giá trị, còn ở câu lệnh thứ hai, với từ khóa let ta có thể hiểu biến ở đây như là một biến Constants (hằng số) và giá trị của biến sẽ không thay đổi trong suốt chương trình!

Hãy xem một vài ví dụ khác:

let lapKanTitle: String = “LapKan Corp.”

var lkVersion: Double = 1.0

let lkLevel: Int = 1000

let lkIsAwesome: Bool = true

Ở trên, chúng ta có 3 hằng số và một biến thường!

Cùng với sự ra đời của Swift, Apple cũng cho thêm 1 định nghĩa mới là “Type Inference” (Nói mới là Apple mới ra cho họ thôi, chứ cái này bên C # có lâu rồi, ngôn ngữ khác thì hem biết). Đại khái nó như thế này:

let lapKanTitle = “Swift” // inferred as String

var lkVersion = 1.0 // inferred as Double

let lkLevel = 1000 // inferred as Int

let lkIsAwesome = true // inferred as Bool

Cách khai báo mới này so với cách khai báo trước có gì khác nhau? Dễ dàng nhận thấy các biến đã được lược bỏ các kiểu dữ liệu như String, Double, Int, Bool. Đây chính là Type Inference mà ta đã nhắc tới ở trên. Điều này có ý nghĩa gì? Chúng ta có khả năng khai báo các biến tự do hơn trước nhiều. Nhiều lúc khi sử dụng, Swift sẽ có lựa chọn đúng hơn cho kiểu dữ liệu giúp chúng ta tiết kiệm được bộ nhớ cho việc sử dụng kiểu biến hay nếu chúng ta có thay đổi một kiểu biến ở đâu đó thì chương trình vẫn tối ưu hoá và chạy đúng. Nó khôn chỉ tiết kiệm được thời gian, công sức mà còn rút ngắn được code viết ra.

Thế thì tại sao Swift vẫn hỗ trợ việc khai báo có chứa kiểu dữ liệu như trên, đó là vì có những lúc chúng ta buộc phải sử dụng đúng kiểu dữ liệu để chương trình chạy được. Swift không phải vạn năng để lúc nào cũng hiểu được chính xác lập trình viên nghĩ gì (thật ra có lẽ chính lập trình viên cũng éo hiểu lúc đó nghĩ gì) nên vẫn có những lúc mà chúng ta cần định nghĩa chính xác thứ mà bạn muốn.

Lời khuyên cho lập trình viên là: Hãy sử dụng tối đa lợi thế không cần khai báo biến để giúp cho bạn tiết kiệm thời gian, công sức và dễ dàng bảo trì code trong tương lai.

Hướng dẫn làm game đuổi hình bắt chữ

Game “Đuổi hình bắt chữ” đã trở thành một trào lưu trên các chợ ứng dụng vì cách chơi đơn giản, dễ dàng mà ai cũng chơi được. Nó không những giúp cho người chơi có thể nhớ thêm các từ ngữ tiếng Việt mà còn đem lại những tiếng cười sảng khoái vì đã giải được một câu đố khó, một chữ khó trong game.

Mình đã viết gong một game giống thế và hôm nay sẽ chia sẽ cách làm để các bạn làm quen với việc làm game:

Download game chính

Hôm nay chúng ta sẽ viết một game đuổi hình bắt chữ với Xcode và engine Sprite Kit của Apple. Nói chung thì dùng engine viết game nên cũng không khó lắm.

1. Đầu tiên chúng ta cần hiểu rõ game cần gì?

– Phần hiển thị của game bao gồm:

+ Hình nền của game tạo cảm giác thân thiện cho người dùng

+ 1 hình ảnh hiển thị để người chơi nhìn vào và đoán chữ

+ Dãy đáp án dành cho game

+ Dãy chữ gợi ý để người chơi nhập vào khi tìm kiếm đáp án

– Phần ngầm của game chính là đáp án của game được chúng ta lưu trữ lại trong bộ nhớ để kiểm tra đáp án của game xem có chính xác không.

* Phần quan trọng không kém là những file hình ảnh đi kèm để làm game. Mình kèm theo cho các bạn để quá trình học tập nhanh chóng hơn:

https://drive.google.com/folderview?id=0B38uO5_VDZL4NFV4MDlpLTM4WG8&usp=sharing

* Ngoài ra đây là game đã hoàn chỉnh của mình trên AppStore, có gì các bạn download ủng hộ nhé ^.^

Download game Đuổi hình bắt chữ trên AppStore

2. Sau khi đã biết tất cả những thứ cần thiết dành cho game thì chúng ta có thể bắt đầu rồi:

– Đầu tiên là thêm hình nền vào trong chương trình

SKSpriteNode * lkBackground = [SKSpriteNode spriteNodeWithImageNamed:@”Background”];

lkBackground.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));

[self addChild:lkBackground];

– Thế là xong hình nền, giờ ta thêm vào hình gợi ý ở phía trên hình nền nữa:

* Giờ là lúc chuẩn bị chỗ để lưu câu trả lời dành cho game

Trong file MyScene.h ta để một biến NSString để lưu giữ câu trả lời:

@interface LKMyScene : SKScene

{

NSString * lkAnswer;

}

@end

=> Biến NSString để lưu trữ câu trả lời

3. Loading game:

Bây giờ bắt đầu nạp câu hỏi vào trong game với hàm loadGame

-(void)LoadGame{

SKSpriteNode * lkCurrentPicture = [SKSpriteNode spriteNodeWithImageNamed:@”HinhGoiY”];

lkCurrentPicture.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame) + 220);

[self addChild:lkCurrentPicture];

 

lkAnswer = @”MaNao”;

}

=> Thế là ta có hình ảnh gợi ý và câu trả lời đã được nạp vào game

* Đây là tác phẩm cho tới giờ này: bao gồm hình nền và hình gợi ý

Khởi đầu game

Game đã có hình nền và hình gợi ý

4. Sinh ra thêm các chữ không có trong câu trả lời để làm cho game trở nên khó khăn hơn:

a. Hàm sinh chữ cái ngẫu nhiên:

– Để làm được điều này, ta cần có thêm 1 hàm sinh ra chữ cái ngẫu nhiên và đây chính là nó:

-(NSString*)lkGenRandomChar{

NSMutableString* string = [NSMutableString stringWithCapacity:1];

[string appendFormat:@”%C”, (unichar)(‘a’ + arc4random_uniform(25))];

return string;

}

=> Hàm này sẽ trả về một kí tự ngẫu nhiên trong bảng chữ cái

b. Ta lấy câu trả lời và tuần tự thêm các chữ cái ngẫu nhiên sao cho có đủ 12 chữ cái thì ngừng:

NSMutableString * lkQuestion = [NSMutableString stringWithString:lkAnswer];

lkHintCharArray = [[NSMutableArray alloc] init];

while (lkQuestion.length < 12) {

NSString * charNew = [self lkGenRandomChar];

[lkHintCharArray addObject:charNew];

[lkQuestion appendString:charNew];

}

=> Sử dụng một NSMutableString lưu trữ những kí tự để có thể sinh ra 12 chữ gợi ý cho game

c. Lúc này ta có 12 chữ cái vừa có câu trả lời vừa có những chữ cái thừa. Tuy nhiên câu trả lời lại nằm ngay đầu của chuỗi rất dễ đoán ra nên ta thay đổi vị trí nó một cách ngẫu nhiên bằng những dòng lệnh sau:

for (int i = 0; i < lkQuestion.length; i++) {

int randomNum = arc4random() % lkQuestion.length;

 

NSString * lkCharAtToPost = [lkQuestion substringWithRange:NSMakeRange(randomNum, 1)];

NSString * lkChar = [lkQuestion substringWithRange:NSMakeRange(i, 1)];

[lkQuestion replaceCharactersInRange:NSMakeRange(i, 1) withString:lkCharAtToPost];

[lkQuestion replaceCharactersInRange:NSMakeRange(randomNum, 1) withString:lkChar];

}

=> Thế là các chữ cái được trộn lẫn vào nhau và bây giờ không thể nhận ra được câu trả lời một cách dễ dàng nữa.

 

5. Sinh ra những ô chữ gơi ý trên giao diện:

– Để thể hiện các ô chữ trên giao diện chúng ta cần một đối tượng để có thể bắt hành động của người chơi.

a. Ô chữ gợi ý:

Tạo một đối tượng mới có template là SKSpriteNode và có tên là LKTileChar. Xcode sẽ sinh ra 2 file 1 header (.h) và file code (.m)

* Đây là những gì nên có trong file LKTileChar.h

#import <SpriteKit/SpriteKit.h>

@interface LKTileChar : SKSpriteNode

{

SKLabelNode * lkLabel;

}

-(instancetype) initWithCharacter:(NSString *)lkLetter;

@property NSString * lkCharacter;

@end

=> lkLabel để thể hiện chữ cái chứa trong ô còn lkCharacter là nội dung của nó.

 

* Đây là nội dung tong file LKTileChar.m:

#import “LKTileChar.h”

 

@implementation LKTileChar

-(instancetype) initWithCharacter:(NSString *)lkLetter{

LKTileChar * lkTitleChar = [LKTileChar spriteNodeWithImageNamed:@”Tile”];

lkLabel = [SKLabelNode labelNodeWithFontNamed:@”Arial-BoldItalicMT”];

lkLabel.fontSize = 39;

lkLabel.text = lkLetter;

lkLabel.verticalAlignmentMode = SKLabelVerticalAlignmentModeCenter;

lkLabel.position = CGPointMake(0,0);

 

lkTitleChar.lkCharacter = lkLetter;

[lkTitleChar addChild:lkLabel];

lkTitleChar.userInteractionEnabled = YES;

lkTitleChar.name = @”tileChar”;

return lkTitleChar;

}

@end

=> Chúng ta đã tạo ra mộ đối tượng có nền là hình ảnh của file “Tile.png” và có lkLabel để thể hiện chữ cái của nó.

(Chú ý: userInteractionEnabled cần được chuyển thành YES nếu không đối tượng sẽ không nhận được hành động của người dùng đâu, ngoài ra nó còn phải được nằm trên các đối tượng có enable = NO)

b. Thêm các ô gợi ý vào game:

– Thêm đoạn code sau đây vào phần LoadGame:

for (int j = 0; j < 2;j++) {

for (int i = 0;i < 6;i++) {

NSString* letter = [lkQuestion substringWithRange:NSMakeRange(j*6 + i, 1)];

LKTileChar * myChar = [[LKTileChar alloc] initWithCharacter:[letter uppercaseString]];

myChar.position = CGPointMake(CGRectGetMinX(self.frame) + (i + 1)*100 + 39, CGRectGetMidY(self.frame) – 280 – j*100);

[self addChild:myChar];

}

}

=> Chỉ việc cắt ra từng chữ của lkQuestion mà ta đã tạo và trộn ngẫu nhiên sau đó thể hiện trên màn hình là xong.

Bây giờ bạn đã có 12 chữ cái ngẫu nhiên nằm dưới đáy màn hình rồi đó.

* Đây là hình ảnh sau khi đã tới bước này

Hình được thêm vào các chữ cái gợi ý

Hình đã được thêm vào những chữ cái gợi ý

Hình game mỗi lần chạy đều có gợi ý khác nhau

Hình game được chạy lần 2

6. Sinh ra những ô chữ đáp án trên game:

Các ô chữ đáp án cũng cần được quản lý theo những đối tượng riêng.

Tạo File mới LKTargetSlot là một Object

a. Nội dung file LKTargerSlot.h:

#import <SpriteKit/SpriteKit.h>

@interface LKTargetSlot : SKSpriteNode

{

SKLabelNode * lkLetterNode;

}

-(instancetype)initOnScreen;

@property NSString * lkContent;

@property int lkAnswerNumber;

-(void)lkSetContent:(NSString *)lkContentX;

@end

=> Tương tự lkLetterNode sẽ thể hiện còn lkContent là nội dung mà nó được nhận tuy nhiên còn có lkAnswerNumber để có thể biết được đây là ô chữ thứ bao nhiêu để xét tuần tự.

b. Nội dung file LKTargetSlot.m:

– Chúng ta sẽ đưa file .m như sau:

#import “LKTargetSlot.h”

@implementation LKTargetSlot

-(instancetype) initOnScreen{

LKTargetSlot * targetSlot = [LKTargetSlot spriteNodeWithImageNamed:@”Slot”];

 

targetSlot->lkLetterNode = [SKLabelNode labelNodeWithFontNamed:@”Arial-BoldItalicMT”];

targetSlot->lkLetterNode.fontColor = [UIColor whiteColor];

targetSlot->lkLetterNode.text = @””;

targetSlot->lkLetterNode.verticalAlignmentMode = SKLabelVerticalAlignmentModeCenter;

 

 

targetSlot.lkContent = @””;

targetSlot.userInteractionEnabled = YES;

targetSlot.name = @”targetChar”;

return targetSlot;

}

-(void)lkSetContent:(NSString *)lkContentX

{

[lkLetterNode removeFromParent];

self.lkContent = lkContentX;

lkLetterNode.text = lkContentX;

[self addChild:lkLetterNode];

}

@end

=> Với ô chữ đáp án cũng gần giống với ô chữ gợi ý tuy nhiên khác ở chỗ, ô chữ đáp án có thể hiện hoặc không chữ cái trong game nên chúng ta có thê 1 lệnh để thay đổi chữ cái.

=> Vậy là xong phần chuẩn bị cho việc thể hiện mọi đối tượng trong game

c. Thêm vào trong game các ô chữ đáp án:

– Hàm để thêm vào:

-(void)lkCreateTargetSlot:(int)lkNumber{

int lkNumberX = 0;

if(lkNumber % 2 == 1)

{

for (int i = 0; i < lkNumber; i++) {

LKTargetSlot * myChar = [[LKTargetSlot alloc] initOnScreen];

myChar.lkAnswerNumber = lkNumberX++;

myChar.name = @”targetChar”;

myChar.position = CGPointMake(i * (78 + 20) – (lkNumber/2 * 78 + (lkNumber-1)/2*20) + self.frame.size.width/2, CGRectGetMidY(self.frame) – 80);

[self addChild:myChar];

[lkTargetArray addObject:myChar];

}

} else {

for (int i = 0; i < lkNumber; i++) {

LKTargetSlot * myChar = [[LKTargetSlot alloc] initOnScreen];

myChar.lkAnswerNumber = lkNumberX++;

myChar.name = @”targetChar”;

myChar.position = CGPointMake(i * 100 – (((lkNumber +1)/2*70) + (lkNumber)/2*20) + self.frame.size.width/2 +45, CGRectGetMidY(self.frame) – 80);

[self addChild:myChar];

[lkTargetArray addObject:myChar];

}

}

}

=> Đây là lệnh để thêm các ô đáp án vào đúng vị trí của nó. Trong LoadGame ta thêm vào dòng này là xong:

[self lkCreateTargetSlot:(int)lkAnswer.length];

=> Thế là giao diện game đã hoàn chỉnh tuy nhiên các ô chữ vẫn chưa có phản ứng gì cả. Vậy hãy qua phần tiếp theo để hoàn tất cả game nào

* Bây giờ chúng ta có:

Giao diện game giờ đã hoàn chỉnh

Giao diện game hoàn chỉnh

7. Phản ứng dành cho các ô gợi ý:

Trước hết hãy làm cho các ô gợi ý có phản ứng.

a. Vào trong LKTileChar.m thêm vào dòng sau:

– Hàm sẽ chạy khi người dùng chạm vào đối tượng riêng. Đây là cách quản lý khá hay và đơn giản của Sprite Kit. Bạn có thể truy cập ngược từ cấp con sang các cấp cao hơn 1 cách dễ dàng trong khi bình thường phải dùng đến các delegate rất phức tạp.

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{

[self -> lkLabel removeFromParent];

LKMyScene * myScene = (LKMyScene *)self.scene;

[myScene lkGetTouch:self];

}

=> Đoạn mã này sẽ tự loại bỏ nó ra khỏi màn hình và sau đó truyền chính nó vào trong LKMyScene để chương trình xử lý.

b. Thêm vào file LKMyScene.h function:

-(void)lkGetTouch:(LKTileChar *)lkTileChar;

– Sau đó vào trong LKMyScene.m để hoàn tất nó:

-(void)lkGetTouch:(LKTileChar *)lkTileChar{

BOOL getChar = NO;

for (int i = 0; i < lkTargetArray.count; i++) {

LKTargetSlot * myChar = [lkTargetArray objectAtIndex:i];

if([myChar.lkContent isEqualToString:@””] && (!getChar)){

[myChar lkSetContent:lkTileChar.lkCharacter];

getChar = YES;

}

}

 

if (getChar) {

[lkSelectedArray addObject:lkTileChar];

[lkTileChar removeFromParent];

}

 

[self lkCheckAnswer];

}

=> Thay nội dung vào trong ô chữ đáp án và xem ô chữ đã được giải xong chưa nếu xong rồi thì kết thúc game.

8. Phản ứng dành cho ô đáp án:

– Nếu người chơi lỡ tay nhấn nhầm hoặc đoán không đúng thì sẽ chuyển đáp án ra.

a. Thêm vào trong LKTargetSlot.m những dòng lệnh sau:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{

LKMyScene * myScene = (LKMyScene *)self.scene;

[myScene lkGetBackTileChar:self];

}

=> Gọi ngược về scene khi có chạm vào trong đáp án

b. Trong LKMyScene.h ta thêm interface:

-(void)lkGetBackTileChar:(LKTargetSlot *)lkTargetChar;

Trong file LKMyScene.m, hoàn thành nó:

-(void)lkGetBackTileChar:(LKTargetSlot *)lkTargetChar{

for (int i = 0; i < lkSelectedArray.count; i++) {

LKTileChar * lkTileChar = [lkSelectedArray objectAtIndex:i];

if ([lkTileChar.lkCharacter isEqualToString:lkTargetChar.lkContent]) {

[self addChild:lkTileChar];

[lkSelectedArray removeObject:lkTileChar];

[lkTargetChar lkSetContent:@””];

break;

}

}

}

=> Thế là xong phần phản ứng của game

9. Kiểm tra kết thúc:

Kiếm tra xem câu trả lời của người chơi đã đúng chưa nếu đúng rồi thì kết thúc trò chơi hiện câu thông báo Game đã hoàn thành.

-(BOOL)lkCheckAnswer{

NSMutableString * lkResult = [NSMutableString stringWithString:@””];

 

for (int i = 0; i < lkTargetArray.count; i++) {

LKTargetSlot * lkSlot = [lkTargetArray objectAtIndex:i];

if([lkSlot.lkContent isEqualToString:@””]){

return NO;

} else {

[lkResult appendString:lkSlot.lkContent];

}

}

 

if([[lkResult uppercaseString] isEqualToString:[lkAnswer uppercaseString]])

{

[self lkGameOver];

return YES;

} else

return NO;

}

-(void)lkGameOver{

SKLabelNode * lkGameOverLabel = [SKLabelNode labelNodeWithFontNamed:@”Arial-BoldItalicMT”];

lkGameOverLabel.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));

lkGameOverLabel.text = @”You got this string!! This game is over!!”;

[self addChild:lkGameOverLabel];

}

=> Hoàn tất game

* Hình game khi kết thúc:

Kết thúc game với thông báo

Kết thúc game

 

C. Những thứ cần thiết để phát triển game:

1. Những tài nguyên cần có:

– Mình kèm theo những hình ảnh mà mình sử dụng trong game để các bạn tiện sử dụng và làm cho nhanh.

https://drive.google.com/folderview?id=0B38uO5_VDZL4NFV4MDlpLTM4WG8&usp=sharing

=> Những file này được lưu trên google của mình nên các bạn cứ tự nhiên download về và thực hành nhé.

2. Chương trình mẫu cho những ai không làm được:

– Mình kèm theo chương trình đã được viết hoàn chỉnh để các bạn sử dụng tham khảo luôn

<Các bạn chịu khó vào blog huygamer.wordpress.com để tải nhé>

https://drive.google.com/file/d/0B38uO5_VDZL4RlRPQjBZdGtlNVE/edit?usp=sharing

3. Chương trình hoàn chỉnh trên AppStore:

– Chương trình hoàn chỉnh có quá nhiều thứ nên mình không thể hướng dẫn chi tiết như chương trình mẫu được. Tuy nhiên nếu các lập trình viên IOS Việt Nam muốn có để tham khảo thì có thể liên hệ. Mã nguồn của game này cũng không quá quý giá đến mức mình không chia sẽ được, nên thấy ai hợp thì cùng nhau chia sẽ thôi.

Đây là link để download game trên AppStore, nếu được các bạn úng hộ download cho game Việt nhé. Viết hướng dẫn này mất cả 7 h (nguyên 1 ngày chủ nhật đoá).

Download game on App Store

4. Lời nhắn:

– Đây chỉ là một game nhỏ của mình và mình muốn đưa ra cho mọi người tham khảo, việc viết một hướng dẫn như thế này rất tốn công sức vì vậy, nếu muốn đăng trên các website của các bạn thì làm ơn để lại đường link tới website của mình và để lại 1 dòng comment trên blog huygamer.wordpress.com này để mình biết cái. Mình không muốn giấu những kiến thức này nhưng cũng không muốn những người chỉ đi copy và paste để ra bài chẳng ra 1 cái gì hết chỉ toàn chữ từ trên xuống dưới lại không có bold hay quote nào cả thật là mất thẩm mỹ vô cùng.

– Vì viết quá nhiều có những khúc mình quên mất một số dòng code và vị trí nó ở đâu thì các bạn có thể để lại comment để mình chỉnh sửa lại cũng được. Mình cũng sẽ để lại source code dành cho các bạn, nếu muốn sử dụng cứ pm. Những nước khác đang phát triển game cực thịnh và sẵn sàng chia sẽ kiến thức để phát triển ngành game của họ nhanh hơn còn Việt Nam có được tí code hay kiến thức thì cố gắng giấu vì sợ mọi người biết sẽ giành chén cơm củ mình. Thế thì muôn đời Việt Nam cũng chả bao giờ phát triển nổi.

 

 

 

Thể hiện dữ liệu dưới dạng UICollectionView

*  IOS 6 có rất nhiều cải tiến trong đó hệ thống liệt kê theo danh mục đã có bước chuyển biến mới khi có thêm công cụ giúp cho các lập trình viên có thể nhanh chóng tạo một danh sách cho phép người dùng lựa chọn.

– UICollectionView không những nhanh chóng, tiện lợi lại còn mang rất nhiều chức năng mới cho phép việc lập trình ứng dụng trở nên đơn giản và nhanh chóng hơn rất nhiều lần.

* Khái niệm chung:

– UICollectionView có cách sử dụng khá là giống với UITableView. Tuy nhiên UITableView quản lý và hiển thị dữ liệu trên một cột (1 column, dĩ nhiên là bạn có thể làm được nhiều thứ với 1 column này nhưng thực sự nó rất khó khăn). Với UICollectionVIews bạn có thể tùy biến nó một cách cực kỳ linh động. Tất cả đều phụ thuộc vào bạn, bạn làm nó hình vuông hay hình tròn hay mọi dạng mà bạn có thể nghĩ ra được. Mặc định, SDK IOS cung cấp cho bạn một UICollectionViewFlowLayout để bạn có thể tổ chức quản lý những đối tượng của bạn và một đầu đề, một kết ở cuối mỗi khu vực để bạn có thể thêm vào các ghi chú và tách từng khối với nhau

* Có 3 cách để sử dụng một UICollectionView thể hiện thông tin bạn cần:

+ Dùng Code để sinh ra toàn bộ UICollectionView thì sẽ không đụng tới các Tool của Xcode. Cách này không được trực quan cho lắm tuy nhiên nếu dùng một cách máy móc thì sau này bạn không cần thêm một dòng code nào để thể hiện nó mà chỉ cần truyển database tới mà thôi.

+ Dùng UIColllectionViewController để thể hiện thì bạn sẽ được tạo sẵn hầu như mọi thứ dành cho việc thể hiện dữ liệu. Bạn chỉ cần vài dòng code để đưa database vào mà thôi.

+ Dùng UICollectionView thêm vào trong một ViewController để thể hiện thông tin. Cách này là một nữa của những cách trên.

 

+ Làm việc với UICollectionView bạn sẽ tạo ra được những cách chọn rất tuyệt dành cho game/ ứng dụng của bạn đấy. Cụ thể từng loại thì những bài sau vậy 😀 (Hôm nay viết quá trời roài).

Lập trình trí tuệ nhân tạo dành cho các nhân vật (AI)

Một game sinh ra không phải bao giờ cũng chỉ có những hành động đơn giản được lập trình trước mà còn có thể tuỳ theo tình huống mà các đối tượng có những cách phản ứng hợp lý hơn gây khó khăn và tạo hứng thú cho người chơi. Bạn đâu muốn chơi một trò chơi mà những nhân vật của máy chỉ biết lao đầu vào bạn để chết đúng không?

Sau đây là phương pháp để tạo trí tuệ nhân tạo với Sprite-Kit. 

Đầu tiên bạn phải hiểu rằng, từng nhân vật bạn tạo ra là một đối tượng được điều khiển hoàn toàn riêng biệt trong Sprite-Kit. Bạn có thể add một phần điều khiển vào trong từng đối tượng này để nó có thể tránh né những viên đạn hay đuổi theo người chơi nhằm làm game thực và khó khăn hơn.

Tuy nhiên tạo ra 100 loại quái vật bạn phải ép để viết 100 loại trí tuệ nhân tạo khác nhau là việc làm hết sức nhảm nhí. Chúng ta cần tách riêng giữa nhân vật trong game và trí tuệ nhân tạo của nó, làm như vật bạn không chỉ có thể đưa bất kì trí tuệ nhân tạo (AI) cho các loại quái vật mà còn có thể thay đổi nó một cách dễ dàng. Ngoài ra việc bảo trì và sửa chữa những lỗi trong đó cũng nhẹ nhàng hơn rất nhiều so với việ ép tất cả vào trong cùng một lớp (class) nhân vật.

* Về các bước, bạn cần hiểu nó như sau:

Chúng ta sẽ chia ra làm những lớp sau để quản lý chương trình dễ dàng:

+ LKCharacterNode: là lớp cơ sở nhất của nhân vật, đây là nguồn của mọi nhân vật từ các nhân vật anh hùng (do bạn điều khiển), các loại quái vật (do máy điều khiển) đều bắt đầu từ đây.

+ LKHeroCharacter: là lớp nhân vật do người chơi điều khiển (có thể không chỉ có một vì chúng ta có thể phát triển những game multiplayer có nhiều người chơi) => Từ đây bạn có thể phát triển thành những lớp nhân vật hoàn toàn khác biệt như Chiến binh (Warrior), Pháp sư (Wizard) hay Cung thủ (Ranger) tuỳ vào game của bạn. Đây chính là đối tượng mà các quái vật tìm và tiêu diệt.

+ LKEnemyCharacter: Đây chính là đối tượng cần đến trí tuệ nhân tạo được lập trình bởi các lập trình viên. Đối với những trí tuệ nhân tạo (AI), ta chỉ cần tạo ra sau đó để chúng lên bản đồ lập tức AI sẽ điều khiển những đối tượng này và chúng sẽ tự biết làm những gì để tiêu diệt các anh hùng của chúng ta. Hay đơn giản hơn, trí tuệ nhân tạo sẽ điều khiển các quái vật này đi theo những đường mà người chơi không đoán trước được để làm cho màn chơi trở nên lý thú hơn.

* Ta sẽ đi sâu vào code hơn nữa để hiểu hơn về cách tạo trí tuệ nhân tạo này:

+ LKCharacterNode: sẽ bao gồm những thuộc tính mà bạn cần để chương trình chạy tốt ví dụ như isDead, isStun, lkHealth và những thuộc tính mà bạn cần. Trí tuệ nhân tạo cũng có thể lấy các thuộc tính này ra để xem xét những hành động của đối tượng ví dụ như khi thấy số lượng máu quá thấp nó sẽ tự động chạy trốn hay hành động như thế nào là tuỳ theo ý bạn. Phần này, mỗi người có những thuộc tính cần thiết hay khộng và mỗi game mỗi khác nên tôi sẽ không đi sâu vào nó.

+

+ LKEnemyCharacter: Đây là phần chính của hướng dẫn. Việc đính kèm một trí tuệ nhân tạo sẽ nằm ở đây.

Trong file hedader bạn sẽ cần có những dòng code sau:

#import “LKCharacterNode.h”

@class LKArtificialIntelligence;

@interface LKEnemyCharacter : LKCharacterNode

@property (nonatomic) LKArtificialIntelligence * lkIntelligent;

@end

Bạn kết nối giữa lớp LKArtificialIntelligence với lớp LKEnemyCharacter làm cho 2 lớp này cùng có thể sử dụng và gọi lẫn nhau. Lớp AI giờ là một phần của lớp LKEnemyCharacter và được sử dụng và làm bất cứ thứ gì trong lớp này. Vậy là ta có thể điều khiến chính lớp LKEnenmyCharacter bằng lớp AI này.

Trong file .m ta có những dòng code sau:

#import “LKEnemyCharacter.h”

#import “LKArtificialIntelligence.h”

 

@implementation LKEnemyCharacter

#pragma mark – Loop Update

– (void)updateWithTimeSinceLastUpdate:(CFTimeInterval)interval {

[super updateWithTimeSinceLastUpdate:interval];

[self.lkIntelligent updateWithTimeSinceLastUpdate:interval];

}

}

@end

Đây là phần điều khiển của AI được gắn vào trong đối tượng Enemy. Bạn sẽ gọi lệnh Update này để chương trinh điều khiển. Dòng code super để làm những việc điều khiển bạn đã viết trên LKCharacterNode. Còn dòng intelligent mới thực sự là dòng lệnh bạn cần để AI điều khiển đối tượng.

Thế là chương trình sẽ có phần điều khiển dành cho AI. Giờ thứ bạn cần là một AI để đối tượng hoạt động theo ý muốn của bạn.

* Phần AI: 

– AI là một đối tượng được đính kèm trong LKCharacterNode. Trong đối tượng,

* Đây là phần code tong AI header:

@class LKCharacterNode;

@interface LKArtificialIntelligence : NSObject

@property (nonatomic, weak) LKCharacterNode *character;

@property (nonatomic, weak) LKCharacterNode *target;

– (id)initWithCharacter:(LKCharacterNode *)character target:(LKCharacterNode *)target;

– (void)clearTarget:(LKCharacterNode *)target;

– (void)updateWithTimeSinceLastUpdate:(CFTimeInterval)interval;

@end

+ Như đã giải thích ở trên @class sẽ làm cho lớp này nằm trong một lớp khác và trở thành một phần của lớp kia. AI cần có đối tượng để có thể đưa ra hành động hợp lý, nó cũng cần phải biết đối tượng nó được đính kèm là đối tương nào nên 2 đối LKCharacterNode chính là 2 đối tượng đó. Ngoài ra thì AI không tự khởi tạo mà nó được khởi tạo bới lập trình viên nếu muốn nên nó cũng kèm them một lệnh init. Lệnh update để điều khiển đối tượng đi theo quỹ đạo cần thiết.

 

(Phê rồi từ từ update tiếp, để xem có ai hứng thú không chứ viết bài không ai coi buồn lắm 😀 )

Ưu điểm trong cách sử dụng hàm mới của Objective C

Ngay từ lúc mới chuyển sang sử dụng Xcode và Objective C, điều luôn khiến các lập trình viên bực mình là việc phải từ bỏ một thói quen khi sử dụng “dot”(.) để gọi các hàm trong ngôn ngữ khác như C++ hay .Net. Lúc đó cũng là lúc mình luôn thắc mắc là tại sao Objective không có một lựa chọn gọi hàm theo cách “bình thường” của các ngôn ngữ khác như vậy.

Nguyên nhân nằm ở cơ chế của chính loại ngôn ngữ này.

* Gọi hàm từ bộ nhớ – lkObj.doThis(): (Method Calling)

– Đây là cách gọi hàm “bình thường” của các ngôn ngữ từ trước đến nay. Cơ chế của nó chính là việc thực thi một phương thức hay một hàm bằng cách nhảy đến đia chỉ bộ nhớ và thực hiện một số chỉ định đã được định nghĩa trước (trong lúc dịch thuât – complite)

+ Ưu điểm: Việc thực thi diễn ra rất nhanh do các chỉ định đã được định nghĩa trước.

+ Nhược điểm: Mất đi tính cơ động do các hàm được tạo vào lúc run-time và được cố định vào bộ nhớ không thể thay đổi được. Cho dù bạn có sử dụng dynamic programming thì lúc run-time, hàm cũng phải được tạo ra trước sau đó mới có thể thực thi. Do đó chương trình luôn bị bó buộc vào việc chuẩn bị đầy đủ nguyên liệu trước khi sử dụng.

 

 

* Gửi thông tin thực thi – [lkObj doThis]: (Message Sending)

– Objective C sử dụng một cơ chế hoàn toàn khác. Tất cả các hàm/ phương thức được lưu trữ trong một danh sách được gọi là Runtime Lookup Table) và được quản lý bởi Objective C Runtime. Mỗi đơn vị lưu trữ được định nghĩa với 2 thành phần: Tên của hàm và địa chỉ của hàm trên bộ nhớ.

– Hiểu một cách đơn giản thì khi một hàm được gọi theo cơ chế Message Sending, địa chỉ vùng nhớ của hàm sẽ được dò tìm trên Runtime Lookup Table, dựa vào tên hàm và tham số của nó, sau đó các chỉ thị bên trong hàm mới được gọi thực hiện.

– Do phải dò tìm memory location của hàm trước khi thực hiện, nên việc thực hiện sẽ chậm hơn so với cơ chế Method Calling, nhưng ngược lại, tính linh động cao, vì với một tên hàm & tham số, địa chỉ bộ nhớ của hàm có thể thay đổi bất kỳ lúc nào trên Runtime Lookup Table, điều này mở ra khả năng thay đổi chỉ định đã được định nghĩa sẵn trước đó của hàm là hoàn toàn có thể  với ngôn ngữ Objective-C.

+ Ưu điểm:

Enable something Impossible

Cơ chế này cho phép bạn làm những điều không thể làm trên các ngôn ngữ khác:

– Có thể gọi thực thi một phương thức khi đối tượng gọi thực hiện đang trỏ đến null: [null doThis]

– Gọi thực hiện bất kỳ phương thức nào của một đối tượng (ngay cả những phương thức hiện tại chưa được định nghĩa)

– Khi một phương thức của một đối tượng được gọi, nó có thể chấp nhận thực hiện hoặc từ chối và uỷ nhiệm cho các đối tượng khác thực hiện phương thức đó.

 

Làm quen với hệ thống vật lý trong Sprite Kit!!

Hệ thống engine vật lý trong Sprite Kit sử dụng engine vật lý Box 2D. Tuy nhiên nó được Apple đưa thẳng vào trong framework Sprite Kit nên bạn sẽ không biết hoặc không thể can thiệp bất cứ thứ gì vào trong hệ thống vật lý này.

Điều này cũng có thêm một ý nghĩa khác chính là nếu Apple có thay thế Box2D bằng một engine vật lý khác mạnh mẽ hơn thì bạn cũng không thể nào biết được.

* Đầu tiên ta cần phải hiểu rõ về những điều cơ bản trong hệ thống vật lý này. Đối với Sprite Kit, bạn chỉ cần viết code quy định những thuộc tính của các đối tượng phần còn lại cứ giao cho engine vật lý của Sprite Kit làm việc. Bạn không cần phải làm thêm gì cả để cho hệ thống chạy. Điều này đem lại một ưu điểm là tất cả các game điều có điều bất ngờ mà ngay cả người lập trình ra nó cũng không thể đoán trước được.

– Bạn quy định cần phải làm tất cả những bước như thế này để chiến thắng màn chơi nhưng nhờ tính chất tương tác giữa các đối tượng mà thậm chí người chơi không cần phải làm nhiều đến thế nhưng vẫn đạt được mục đích. Điều đó khiến cho game có nhiều tình huống bất ngờ xảy ra và làm cho game trở nên thú vị hơn.

1. Đầu tiên bạn cần biết về các đối tượng vật lý:

– Các đối tượng muốn bị ảnh hưởng bởi các nguyên tắc vật lý trong môi trường của Sprite Kit cần phải có một cơ thể (physicalBody) thì Sprite Kit mới biết phải làm gì với các đối tượng này.

– Có 2 loại physicalBody:

+ Dynamic Body: là một loại đối tượng vật lý chịu tác động hoàn toàn bởi các luật vật lý trong ứng dụng và có thể có các hình dạng khác nhau tuỳ vào lập trình viên định nghĩa

+ Static Body là đối tượng vật lý không chịu tác động bới các nguyên tắc vật lý tuy nhiên nó lại tương tác với các đối tượng vật lý khác ví dụ như phần bao bên ngoài game để các đối tượng không bị rớt ra khỏi màn hình bởi trọng lực.

2. Các lực trong Sprite Kit:

– Lực trong Sprite Kit có 2 loại:

+ Force: Lực này tác động lên toàn bộ đối tượng và từ từ

+ Impact: Là lực tác động trực tiếp lên đối tượng và có tác động ngay lập tức

 

=> Từ từ viết tiếp hôm nay hơi bị phê roài 🙂 will be continues in next day ^.^