iOS SDK construiți un joc de fapte - crearea de interfețe

Acest tutorial vă va învăța cum să utilizați cadrul Sprite Kit pentru a crea un joc bazat pe întrebări. Este conceput atât pentru începători, cât și pentru utilizatori avansați. Pe parcurs, veți aplica nucleul Sprite Kit. Tutorialele Jocurilor de Fapte sunt împărțite în trei părți pentru a acoperi complet fiecare secțiune. După acest tutorial cu trei părți, cititorii vor putea să creeze un simplu joc de întrebări și răspunsuri cu sunete, animații, meniuri, reguli, cronometre și interacțiunea UIKit.


Introducere

Această serie este împărțită în trei părți: configurarea proiectului, crearea interfeței și logica jocurilor. Fiecare parte va produce un rezultat practic, iar suma tuturor pieselor va produce jocul final. În ciuda faptului că fiecare parte poate fi citită independent, pentru o mai bună înțelegere și îndrumare, vă sugerăm ca tutorialul să fie urmat pas cu pas. Am inclus, de asemenea, codul sursă pentru fiecare parte separat. Astfel, oferind o modalitate de a începe tutorialul în orice parte a seriei.

Aceasta este a doua parte a Jocurilor noastre de Facts cu seria tutorial Sprite Kit. În acest tutorial, veți programa selecția nivelului și interfața principală a scenei jocului. Acest tutorial se concentrează pe mai multe aspecte, cum ar fi, personalizat UITableView, inițiative de clasă personalizate, listele de proprietăți și SKActions. Vom explica totul mai departe. Dacă nu ați finalizat încă prima parte a seriei, puteți descărca proiectul și preluarea exact acolo unde am rămas.

Acesta este rezultatul final:


Ilustrarea rezultatului final - fapte

1. Selectați nivelul

Pasul 1

Obiectivul principal al acestui joc este de a crea mai multe întrebări împărțite pe mai multe nivele. În acest fel, trebuie să formați o interfață personalizată pentru a alege nivelul pe care doriți să îl jucați. Pentru a realiza acest lucru, trebuie să adăugați altul Obiectiv-C clasa (Fișier -> Nou -> Fișier). Numeste Selectare nivel și alegeți SKScene ca superclase. Veți vedea două fișiere noi în proiectul dvs..

Pentru selectarea nivelului, veți folosi UITableView vizualizați-l și configurați-l cu mai multe proprietăți. În plus, trebuie să denumiți nivelurile și descrierile acestora. În LevelSelect.h trebuie să adăugați acele proprietăți. Următorul fragment vă va ajuta:

 @property (nonatomic, reține) UIButton * backButton; @property (reține, nonatomic) IBOutlet UITableView * tableView; @property (puternic, nonatomic) NSArray * levelsArray; @property (puternic, nonatomic) NSArray * levelsDescriptionArray;

Fragmentul conține, de asemenea, a UIButton numit backButton. În acest moment, butonul este explicabil; îl ajută pe utilizator să se întoarcă de la interfața de selectare a nivelului la interfața principală.

Pasul 2

Apoi, concentrați-vă pe LevelSelect.m. Primul pas este să adăugați -(Id) initWithSize: (CGSize) dimensiunea metodă. În această clasă, veți configura doar culoarea de fundal. Puteți alege culoarea care vă place cel mai mult.

 -(dimensiune CGSize) if (auto = [super initWithSize: dimensiune]) auto.backgroundColor = [SKColor colorWithRed: 0.25 verde: 0.35 albastru: 0.15 alfa: 1.0];  întoarce-te; 

Din moment ce utilizați UIKit vizualizări, trebuie să adăugați -(void) didMoveToView: (SKView *) vizualizare metodă. Această metodă definește și configurează backButton și tableView, plasează o etichetă de titlu pentru vizualizare, alocă și inițializează levelsArray și levelsDescriptionArray și adaugă tableView la vizualizarea principală. backButton pot fi configurate după cum urmează:

 _backButton = [butonul UIButtonWithType: UIButtonTypeRoundedRect]; _backButton.frame = CGRectMake (CGRectGetMidX (auto.frame) -100, CGRectGetMaxY (auto.frame) -100, 200, 70.0); _backButton.backgroundColor = [UICcolor clearColor]; [_backButton setTitleColor: [UICcolor whiteColor] pentruState: UIControlStateNormal]; Butonul UIImage * buttonExitImageNormal = [UIImage imageNamed: @ "back.png"]; UIImage * strechableButtonExitImageNormal = [butonulExitImageNormal stretchableImageWithLeftCapWidth: 12 topCapHeight: 0]; [_backButton setBackgroundImage: strechableButtonExitImageNormal forState: UIControlStateNormal]; [_backButton addTarget: acțiune auto: @selector (moveToHome) forControlEvents: UIControlEventTouchUpInside]; [self.view addSubview: _backButton];

În plus, trebuie să creați moveToHomeși import MyScene.h.

 -(void) moveToHome MyScene * myScene = [[MyScene aloc] initWithSize: CGSizeMake (CGRectGetMaxX (self.frame), CGRectGetMaxY (self.frame)); [self removeUIViews]; [self.scene.view presentScene: myScene]; 

Deoarece trebuie să eliminați UIKit vedeți în mai multe locuri, să creați o metodă care să facă exact acest lucru. Se numește metoda removeUIViews și este prezentat mai jos:

 -(vid) removeUIViews [_backButton removeFromSuperview]; [_tableView removeFromSuperview]; 

Eticheta la această interfață este foarte simplă. Încercați să programați-vă singur. Dacă aveți probleme, următorul fragment vă va ajuta.

 SKLabelNode * titleLabel = [SKLabelNode labelNodeWithFontNamed: @ "Chalkduster"]; titleLabel.text = @ "Selectați nivelul !!"; titleLabel.fontSize = 60; titleLabel.position = CGPointMake (CGRectGetMidX (auto.frame), CGRectGetMidY (auto.frame) +300); [self addChild: titluLabel];

Pasul 3

tableView configurația este un pic dificilă deoarece trebuie să configuram mai multe proprietăți, cum ar fi dimensiunea și locația cadrului, sursa de date și un delegat.

 _tableView = [[UITableView aliniere] initWithFrame: CGRectMake (CGRectGetMidX (auto.frame) -150, CGRectGetMidY (auto.frame) -250, 300, 400)]; _tableView.dataSource = auto; _tableView.delegate = auto;

A doua și a treia linie a fragmentului menționat anterior necesită un pas suplimentar, deoarece definiți simultan două vizualizări. Sursa de date a UITableView este auto-definit și UITableView are un delegat de acțiune. În LevelSelect.h ar trebui să vă extindeți clasa UITableViewDataSource și UITableViewDelegate. Ta LevelSelect.h ar trebui să arate ca:

 @ interfata LevelSelect: SKScene < UITableViewDataSource, UITableViewDelegate >

Din moment ce vă extindeți UITableViewDataSource și UITableViewDelegate, trebuie implementate metode suplimentare, și anume:

  1. -(NSInteger) tableView: (UITableView *) tabelVizualizare numărOfRowsInSecție: (NSInteger) secțiune
  2. -(UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath

Primul este folosit doar pentru a cunoaște în timp real numărul de rânduri existente în vizualizarea tabelului. A doua metodă este complexă, deoarece este folosită pentru a popula și a configura vizualizarea tabelului. Configurația de vizualizare a tabelului acoperă titlul, descrierea și o imagine rând. Pentru a utiliza descrierea în fiecare rând, trebuie să inițializăm fiecare celulă cu UITableViewCellStyleSubtitle stilul celular.

Această metodă este responsabilă pentru un pas suplimentar: verifică nivelul actual al jucătorului și dezactivează nivelurile din fața lui actualPlayerLevel. Declarația privind metodele complete poate fi văzută mai jos:

 -(UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath NSString * levels = [_levelsArray objectAtIndex: indexPath.row]; NSString * descriptions = [_levelsDescriptionArray objectAtIndex: indexPath.row]; UITableViewCell * celula = [tableView dequeueReusableCellWithIdentifier: @ "Identifier"]; dacă (celula == zero) cell = [[UITableViewCell alin] initWithStyle: UITableViewCellStyleSubtitle reuseIdentifier: @ "Identifier"];  dacă (indexPath.row> = actualPlayerLevel) [cell setUserInteractionEnabled: FALSE]; [cell.textLabel setText: niveluri]; cell.imageView.image = [UIImage imageNamed: @ "appleLogo.png"]; [cell.detailTextLabel setText: descrieri]; celule retur; 
 -(NSInteger) tableView: (UITableView *) tableView numberOfRowsInSection: (NSInteger) sectiunea returnati [_levelsArray Count]; 

Iată două note despre -(UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath metodă. Încă nu ați inițializat proprietățile levelsArray, levelsDescriptionArray și realPlayerLevel. Toate vor fi definite în -(void) didMoveToView: (SKView *) vizualizare metodă. Nu uitați să adăugați actualPlayerLevel proprietate la clasa ta:

 @implementation LevelSelect long actualPlayerLevel; 

Pasul 4

levelsArray este definit cu numele nivelurilor. Puteți să o numiți "Nivelul 1", "Nivelul 2" sau orice alt nume ales de dvs. levelsDescriptionArray urmează același principiu, este o descriere a fiecărui nivel și poate fi definită cu nume alese de dvs. O posibilă implementare este:

 _levelsArray = [[NSArray alocat] initWithObjects: @ "Nivelul 1", "Nivelul 2", "Nivelul 3", "Nivelul 4" , @ "Nivelul 7.", "Nivelul 8", "Nivelul 9", nul]; _levelsDescriptionArray = [[NSArray alloc] initWithObjects: @ "Aventura incepe", @ "Un nou pas.", @ "Achivements ?!", @ "Level 4 description" descriere ", descrierea nivelului 7, descrierea nivelului 8, descrierea nivelului 9, nul;

În cele din urmă, actualPlayerLevel este a lung tipul de valoare care reprezintă nivelul jucătorului. Pentru moment, spuneți că nivelul actual este 1.

 actualPlayerLevel = 1;

-(void) didMoveToView: (SKView *) vizualizare metoda se termină atunci când adăugați ecranul de masă pe ecran:

 [self.view addSubview: _tableView];

În cele din urmă, aveți nevoie de o modificare suplimentară în MyScene.m -(void) moveToGame metodă. Trebuie să numiți această nouă clasă în locul celei vechi. Poate fi ușor realizat prin:

 -(void) moveToGame LevelSelect * factScene = [[NivelSelect alocare] initWithSize: CGSizeMake (CGRectGetMaxX (self.frame), CGRectGetMaxY (self.frame)); // Același cod // ...

În acest moment ar trebui Alerga proiectul și testați noua interfață. Ar trebui să vedeți ceva similar cu următoarea imagine:

Ilustrație a ecranului Selectare nivel

2. Interfața faptelor

Pasul 1

Interfața faptelor este locul în care se produce acțiunea reală. Interfața are mai multe vederi care se traduc direct în proprietăți, cum ar fi numărul de vieți rămase, nivelul curent, un temporizator și butoanele false și false. Mai mult, pentru această clasă veți crea un inițializator personalizat. Avantajul acestui inițializator personalizat este că putem progresa prin joc și valori privind nivelul. De asemenea, viețile sunt transmise clasei, iar clasa reacționează (analizează datele) în consecință.

Încă o dată, vom folosi SKLabelNode, UIButtons, și a NSMutableArray. Complet FactsScene.h este următorul:

 @interface FactsScene: SKScene NSMutableArray * heartArray;  @property (nonatomic, slab) SKLabelNode * currentLevelLabel; @property (nonatomic, slab) SKLabelNode * timerLevel; @property (nonatomic, reține) UIButton * trueButton; @property (nonatomic, reține) UIButton * falseButton; - (id) initWithSize: (CGSize) mărime în nivelul: (NSInteger) nivel cuPlayerLives: (int) vieți;

Acum este momentul să vă deplasați la FactsScene.m și să pună în aplicare câteva obiecte. Aveți nevoie de obiecte suplimentare pentru a prelua și a stoca datele primite de inițializator. În plus, trebuie să stocați timpul maxim pe care un jucător trebuie să-l răspundă la fiecare întrebare. Modificați fișierul de implementare în consecință.

 @implementation FactsScene NSUserDefaults * implicit; NSString * musicPath; Jucator NSInteger; NSInteger playerLevel; int maximumTime; 

Acum, trebuie să scrieți inițializatorul și să stocați valorile acestuia. Initializatorul personalizat este definit ca un inițializator normal și are aceeași structură. Cu toate acestea, are mai multe proprietăți ca parametri. Ar trebui să arate astfel:

 -(id) initWithSize: (CGSize) mărimea nivelului: (NSInteger) cuPlayerLives: (int) trăiește if (self = [super initWithSize: size]) : 1,0]; defaults = [NSUserDefaults standardUserDefaults]; playerLives = vieți; playerLevel = nivel; maximumTime = 30;  întoarce-te; 

Pasul 2

Pentru moment, maximumTime este de 30 de secunde, dar în viitor valoarea se va schimba la 60 de secunde (sau oricare alt moment la alegere). Acum în -(void) didMoveToView: (SKView *) vizualizare adăugați o imagine de fundal, o imagine frontală, o viață a jucătorului, un timer, butoane false și false și întrebările de nivel actual și total. Pentru fundal și față image codul este simplu și ar trebui să puteți să o faceți cu ușurință (folosind SKSpriteNode).

 SKSpriteNode * fundal = [SKSpriteNode spriteNodeWithImageNamed: @ "background.png"]; background.position = CGPointMake (CGRectGetMidX (auto.frame), CGRectGetMidY (auto.frame)); background.size = CGSizeMake (768, 1024); [self addChild: fundal]; SKSpriteNode * frontImage = [SKSpriteNode spriteNodeWithImageNamed: @ "transparentCenterBorder.png"]; frontImage.position = CGPointMake (CGRectGetMidX (auto.frame), CGRectGetMidY (auto.frame)); frontImage.size = CGSizeMake (600, 450); [auto addChild: frontImage];

Viața jucătorului este reprezentată cu o imagine a inimii. Din moment ce veți declara trei vieți, trebuie să puneți trei inimi pe ecran. De asemenea, veți folosi a NSMutableArray deoarece trebuie să-i modificăm în mod dinamic dimensiunea. Următorul fragment vă va ajuta:

 heartArray = [[NSMutableArray aloca] init]; pentru (NSInteger i = 0; i < playerLives; i++) SKSpriteNode* liveImage = [SKSpriteNode spriteNodeWithImageNamed:@"hearth.png"]; liveImage.scale = .6; liveImage.position = CGPointMake(CGRectGetMaxX(self.frame)-40-(i*50),CGRectGetMaxY(self.frame)-40); [heartArray insertObject:liveImage atIndex:i]; [self addChild:liveImage]; 

Butoanele true și false sunt configurate ca:

 _trueButton = [butonul UIButtonWithType: UIButtonTypeRoundedRect]; _trueButton.frame = CGRectMake (CGRectGetMidX (auto.frame) -350, CGRectGetMidY (auto.frame) +300, 335, 106); _trueButton.backgroundColor = [UICcolor clearColor]; [_trueButton setTitleColor: [UICcolor whiteColor] pentruState: UIControlStateNormal]; Butonul UIImage * buttonTrueImageNormal = [UIImage imageNamed: @ "trueBtn.png"]; UIImage * strechableButtonTrueImageNormal = [ButtonTrueImageNormal stretchableImageWithLeftCapWidth: 12 topCapHeight: 0]; [_trueButton setBackgroundImage: strechableButtonTrueImageNormal forState: UIControlStateNormal]; [_trueButton addTarget: acțiune auto: @selector (touchWillProduceASound) forControlEvents: UIControlEventTouchUpInside]; [auto.view addSubview: _trueButton]; _falseButton = [Butonul UIButtonWithType: UIButtonTypeRoundedRect]; _falseButton.frame = CGRectMake (CGRectGetMidX (auto.frame) +10, CGRectGetMidY (auto.frame) +300, 335, 106); _falseButton.backgroundColor = [UICcolor clearColor]; [_falseButton setTitleColor: [UICcolor whiteColor] pentruState: UIControlStateNormal]; Butonul UIImage *FalseImageNormal = [UIImage imageNamed: @ "falseBtn.png"]; UIImage * strechableButtonFalseImageNormal = [butonulFalseImageNormal stretchableImageWithLeftCapWidth: 12 topCapHeight: 0]; [_falseButton setBackgroundImage: strechableButtonFalseImageNormal forState: UIControlStateNormal]; [_falseButton addTarget: acțiune auto: @selector (touchWillProduceASound) forControlEvents: UIControlEventTouchUpInside]; [self.view addSubview: _falseButton];

Rețineți că ambele butoane apelează touchWillProduceASound metodă. Această metodă testează dacă un răspuns dat este corect sau incorect. În această parte, folosim doar un eveniment sonor (cel fals).

 -(void) touchWillProduceASound lung sunetFlag = [implicit integerForKey: @ "sunet"]; NSString * answer = @ "False"; dacă (soundFlag == 1) SKAction * sunet; dacă ([answer esteEqualToString: @ "False"]) sound = [SKAction playSoundFileNamed: @ "wrong.mp3" waitForCompletion: YES]; NSLog (@ "interior");  [auto runAction: sunet]; 

Pasul 3

Cronometrul jocului este a SKLabelNode care se schimbă la fiecare secundă. Cu toate acestea, inițializarea sa se face simplu SKLabelNode:

 _timerLevel = [SKLabelNode labelNodeWithFontNamed: @ "Chalkduster"]; _timerLevel.text = @ "30"; _timerLevel.fontSize = 70; _timerLevel.position = CGPointMake (CGRectGetMidX (auto.frame), CGRectGetMidY (auto.frame) +350); [auto addChild: _timerLevel];

Pentru a actualiza eticheta, trebuie să creați o SKAction care definește un cronometru personalizat pentru a apela o metodă personalizată. Atunci trebuie să creăm un SKAction secvenţă:

 SKAction * așteptați = [Așteaptă așteptare pentru Durata: 1]; SKAction * updateTimer = [Blocare activitate SKAction: ^ [self updateTimer]; ]; SKAction * updateTimerS = [Succesiune: @ [wait, updateTimer]]; [auto runAction: [SKAction repeatActionForever: updateTimerS]];

Veți vedea un avertisment cu privire la - (Void) updateTimer deoarece nu l-ați creat încă. Această metodă face mai multe acțiuni simultan luând în considerare mai multe proprietăți:

  • Verifică dacă sunetul este pornit
  • Actualizează maximumTime proprietate și etichetă, scăzând cu o valoare în fiecare secundă
  • Verifică maximumTime, și dacă valoarea este zero, se va încheia jocul sau se va schimba la o altă întrebare (mai multe despre acest subiect în tutorialul următor)

Vă sfătuim să încercați să scrieți metoda folosind pseudo-codul menționat mai sus. Dar dacă aveți probleme, metoda completă este prezentată mai jos:

 - (void) updateTimer maximumTime--; dacă (maximumTime == 0) lung soundFlag = [implicit integerForKey: @ "sunet"]; dacă (soundFlag == 1) SKAction * sunet; sunet = [SKAction playSoundFileNamed: @ "beep.mp3" waitForCompletion: YES]; [auto runAction: sunet];  în cazul în care (playerLives < 1) SKTransition* transition = [SKTransition fadeWithDuration:2]; MyScene* myscene = [[MyScene alloc] initWithSize:CGSizeMake(CGRectGetMaxX(self.frame), CGRectGetMaxY(self.frame))]; [self removeUIViews]; [self.scene.view presentScene:myscene transition:transition];  else // other   [_timerLevel setText:[[NSNumber numberWithInt:maximumTime] stringValue]]; 

Pasul 4

Încă nu mai există o metodă removeUIViews. Îl îndepărtează UIKit vizualizări din vizualizare atunci când are loc o tranziție.

 -(vid) removeUIViews [_trueButton removeFromSuperview]; [_falseButton removeFromSuperview]; 

Acum este timpul să Alerga proiectul și să vedeți ecranul Fapte!

Ilustrarea ecranului Fapte

3. Liste de proprietăți

Acum este timpul să adăugați unele date în aplicație. Mergi la Fișier -> Nou -> Fișier și alegeți fișierul Listă de proprietăți (plist). Denumiți-o "LevelDescription" și configurați-o după cum urmează:

Ilustrație a listei de proprietăți

Pentru a evita introducerea tuturor datelor în fișierul plist, puteți descărca fișierul direct în fișierul zip (începutul paginii). Acest tutorial nu acoperă în profunzime analiza datelor, astfel încât, pentru a analiza acel fișier, se utilizează următorul fragment. Dacă aveți vreo îndoială, utilizați caseta de comentarii din partea de jos a paginii.

 NSString * plistPath = [[NSBundle mainBundle] pathForResource: @ "LevelDescription" dinType: @ "plist"]; NSMutableDictionary * dicționarul = [[NSMutableDictionary alloc] initWithContentsOfFile: plistPath]; dacă ([dicționar objectForKey: @ "Questions"]! = nil) NSMutableArray * array = [dicționar objectForKey: @ "Questions"]; pentru (int i = 0; i < [array count]; i++) NSMutableDictionary *questions = [array objectAtIndex:i]; NSLog(@"ID %@", [questions objectForKey:@"id"]); NSLog(@"%@", [questions objectForKey:@"statement"]); NSLog(@"%@", [questions objectForKey:@"isCorrect"]); NSLog(@"%@", [questions objectForKey:@"additionalInfo"]);  

Acum, Alerga proiectul și urmăriți jurnalele consolei pe măsură ce introduceți vizualizarea Fapte. Rețineți că fișierul plist poate fi configurat în mai multe moduri diferite. Dacă doriți, puteți modifica Dicţionar, mulțime tipuri și configurații. Rețineți că modificările trebuie declarate în ciclul de analiză. Vă sfătuim cu fermitate să jucați un pic cu fișiere plist și parsarea datelor inerente.


Concluzie

În acest moment ar trebui să puteți utiliza și configura o UITableView, să interacționați între cadrele SpriteKit și UIKit, să creați SKTransitions și SKActions și să creați și să analizați fișierele de proprietate. În secțiunea finală a acestei serii, veți afla despre Joc Logic.

Cod