iOS SDK construiți un joc de fapte - joc de logică

Bine ați venit în secțiunea a treia și finală a Jocurilor noastre de Facts cu seria tutorial Sprite Kit. Acest tutorial vă va învăța cum să utilizați cadrul Sprite Kit pentru a crea un joc de fapte bazate 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.


Introducere

În acest tutorial, veți programa întreaga logică a jocului, inclusiv viața jucătorului, întrebarea și răspunsul jucătorului. Această serie de tutoriale este împărțită în trei secțiuni: Configurarea proiectului, Interfața faptelor și Logica jocurilor. Dacă nu ați finalizat încă cea de-a doua secțiune, puteți descărca proiectul și preluarea exact acolo unde am rămas. Fiecare parte 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 vă sugerăm să urmați tutorialul pas cu pas. Am inclus, de asemenea, codul sursă pentru fiecare parte separat, oferind astfel o modalitate de a începe tutorialul în orice parte a seriei.

Acesta este aspectul jocului după finalizare:


Ilustrația rezultatului final - fapte

1. Clasa facturilor personalizate

În ultimul tutorial, ați definit un fișier plist pentru întrebări. Fiecare întrebare are patru proprietăți. Pentru a le gestiona, trebuie să creați o clasă personalizată pentru a vă permite să îndepliniți corect această sarcină. Prin urmare, trebuie să formați alta Obiectiv-C clasă. Numeste factObject și să definească NSObject superclasei.

Acum, să editați fișierul antet și să adăugăm cele patru proprietăți plist. Fiecare proprietate plist are propriile caracteristici:

  • Id-ul declarației este un int.
  • Declarația este a NSString.
  • Instrucțiunea isCorrect este o int.
  • Informațiile suplimentare sunt a NSString.

Rezultatul final ar trebui să fie similar cu acesta:

 @property (nonatomic, readwrite) int factID; @property (nonatomic, citiți, rețineți) instrucțiunea NSString *; @property (nonatomic, readwrite) NSInteger este corect; @property (nonatomic, readwrite, reține) NSString * additionalInfo;

Nu este nevoie să utilizați fișierul de implementare (.m). Vom analiza fișierul plist la această clasă personalizată și vom folosi valorile direct din memorie.


2. Facturi Interfață: Inițializare

În ultimul tutorial, ați definit structura de bază a interfeței faptelor. Acum este timpul să o finalizăm cu pașii logici. Pentru a finaliza acest joc, trebuie să creați o etichetă de întrebări, o instrucțiune de fundal personalizată, un buton care solicită o altă întrebare și o interfață adevărată și falsă. Aceste patru afirmații se traduc în cinci proprietăți definite în FactsScene.h fişier. Încă o dată, le puteți numi după cum doriți. Implementarea noastră este:

 @property (nonatomic, reține) UILabel * questionLabel; @property (nonatomic, reține) SKSpriteNode * backgroundStatement; @property (nonatomic, reține) UIButton * nextQuestion; @property (nonatomic, reține) SKSpriteNode * greșit; @property (nonatomic, reține) SKSpriteNode * corect;

Acum îndreptați-vă atenția către FactsScene.m. Trebuie să definiți mai multe obiecte care sunt utilizate intern în clasă:

  • A NSMutableArray pentru a stoca întrebările
  • O valoare aleatorie care reprezintă o întrebare aleatorie
  • Identificatorul întrebării
  • Pragul minim pentru întrebările corecte; acest prag indică răspunsurile corecte minime necesare pentru ca utilizatorul să avanseze la un alt nivel. În acest tutorial, veți folosi valoarea șapte.

Fișierul de implementare ar trebui să arate astfel:

 * Declarații NSMutableArray; int randomQuestion; int questionNumber; int totalRightQuestions; // nevoie de 7 din 10 pentru a trece la nivelul următor

Acum este momentul să alocăm câteva valori și să începem cu logica. În -(id) initWithSize: Dimensiunea (CGSize) în nivelul: Level (NSInteger) cuPlayerLives: (int) lives introduceți metoda questionNumber si totalRightQuestions. Deoarece este prima dată când o utilizați, inițierea este ușoară și poate fi făcută ca:

 questionNumber = 1; totalRightQuestions = 0;

Acum este timpul să folosiți clasa personalizată definită în etapa menționată mai sus. Parsează fișierul plist și folosește magazia de informații în plist pentru a aloca și a popula noi factObject obiecte. Rețineți că vom stoca fiecare factObject obiect într-un obicei NSMutableArray deja definite (declaraţii). Fragmentul complet este mai jos.

 declarații = [[NSMutableArray aloca] init]; 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]; factObject *stat = [factObject new]; stat.factID = [[questions objectForKey:@"id"] intValue]; stat.statement = [questions objectForKey:@"statement"]; stat.isCorrect = [[questions objectForKey:@"isCorrect"] integerValue]; stat.additionalInfo = [questions objectForKey:@"additionalInfo"]; [statements addObject:stat];  

Acest pas elimină codul de analiză mai vechi din -(void) didMoveToView: (SKView *) vizualizare metodă. Puteți să o eliminați, deoarece nu o veți mai folosi.


3. Facturi Interfață: Logică

Acum este timpul să ne concentrăm asupra codului logic în sine. Trebuie să prezentăm întrebarea utilizatorului. Cu toate acestea, întrebarea este întotdeauna o alegere aleatorie. Începeți să definiți un dreptunghi pentru a permite întrebarea și apoi să alocați resursele necesare pentru textul întrebării. Următorul fragment vă va ajuta:

 Eticheta CGRectFrame = CGRectMake (120,300, 530, 100); _questionLabel = [[UILabel alocare] initWithFrame: labelFrame]; randomQuestion = [auto getRandomNumber între: 0 până la: ([numere de declarații] -1)]; NSString * labelText = [declarația objectAtIndex: randomQuestion]; [_questionLabel setText: labelText]; [_questionLabel setTextColor: [UICcolor whiteColor]]; [_quotLabel setFont: [UIFont fontWithName: NULL size: 23]]; [_questionLabel setTextAlignment: NSTextAlignmentCenter]; // Eticheta va folosi un număr nelimitat de linii [_questionLabel setNumberOfLines: 0];

Rețineți că nu veți folosi SKLabelNode peste simplu NSString din cauza a SKLabelNode prescripţie; este vorba numai de un singur rând. Va apărea un avertisment cu privire la getRandomNumber Între: 0 până la: X metodă. Trebuie să declarați și să codificați; obiectivul său este de a returna o valoare aleatorie între două valori. Următorul fragment vă va ajuta:

 -(int) getRandomNumber Între: (int) de la la: (int) la return (int) de la + arc4random ()% (de la + 1); 

Acum, când puteți vedea întrebarea, trebuie să adăugăm câteva funcții la butonul corect și greșit. Schimbați ambele selectori și apelați o nouă metodă numită: presentCorrectWrongMenu.

 [_falseButton addTarget: acțiune auto: @selector (presentCorrectWrongMenu :) forControlEvents: UIControlEventTouchUpInside]; [_trueButton addTarget: acțiune auto: @selector (presentCorrectWrongMenu :) forControlEvents: UIControlEventTouchUpInside];

În plus, definiți o etichetă pentru fiecare buton. Butonul adevărat va fi tag = 1 și eticheta falsă = 0. Aceste etichete vă vor ajuta atunci când apelați -(Void) presentCorrectWrongMenu: (UIButton *) expeditor pentru a determina ce buton a fost apăsat pentru a apela aceeași metodă.

 [_trueButton setTag: 1]; [_falseButton setTag: 0];

Următorul pas este să adăugați -(Void) presentCorrectWrongMenu: (UIButton *) expeditor metodă. Această metodă este complexă și recunoaște ce buton este apăsat, adaugă o interfață personalizată de răspuns și adaugă un buton care cheamă următoarea întrebare. Utilizați următorul fragment pentru a atinge subiectele menționate mai sus:

 -(void) presentCorrectWrongMenu: (UIButton *) expeditor int userData = sender.tag; // background _backgroundStatement = [SKSpriteNode spriteNodeWithImageNamed: @ "background.png"]; _backgroundStatement.position = CGPointMake (CGRectGetMidX (auto.frame), CGRectGetMidY (auto.frame)); _backgroundStatement.size = CGSizeMake (768, 1024); _backgroundStatement.zPosition = 10; _backgroundStatement.alpha = 0.0; [auto addChild: _backgroundStatement]; _nextQuestion = [butonul UIButtonWithType: UIButtonTypeRoundedRect]; _nextQuestion.frame = CGRectMake (CGRectGetMidX (auto.frame) -100, CGRectGetMidY (auto.frame) +90, 200, 70.0); _nextQuestion.backgroundColor = [UICcolor clearColor]; [_nextQuestion setTitleColor: [UICcolor blackColor] pentruState: UIControlStateNormal]; [_nextQuestion setTitle: @ "Apăsați aici pentru a continua" forState: UIControlStateNormal]; [_nextQuestion addTarget: acțiunea proprie: @selector (următorulCertificare) pentruControlEvents: UIControlEventTouchUpInside]; _nextQuestion.alpha = 1.0; [self.view addSubview: _nextQuestion]; [runActionStatementStatement: [SKAction fadeAlphaTo: 1.0f durata: 0.2f]]; _trueButton.alpha = 0.0; _falseButton.alpha = 0.0;

Va apărea un avertisment, dar nu remediați-l imediat. Mai întâi, încheiați declarația metodei. Acum că aveți o interfață personalizată pentru răspuns, trebuie să testați răspunsul jucătorului. Pentru a realiza acest lucru, trebuie să știți ce buton a apucat jucătorul și răspunsul întrebării inerente. Știți deja acest lucru, deci trebuie doar să creați o simplă condiție de testare logică. Pentru a face acest lucru, trebuie să testați dacă răspunsul este corect sau incorect, redați un sunet în consecință și continuați cu actualizarea proprietăților. Următorul fragment vă va ajuta. Rețineți că trebuie să o plasați acolo unde sa terminat ultimul fragment de cod.

 dacă [[[declarațiile objectAtIndex: randomQuestion] isCorrect] == ​​0 && userData == 0) || ([[declarații objectAtIndex: randomQuestion] isCorrect] == ​​1 && userData == 1)) if [[statements objectItIndex : randomQuestion] isCorrect] == ​​0) _questionLabel.text = [[declarații objectAtIndex: randomQuestion] additionalInfo]; _correct = [SKSpriteNode spriteNodeWithImageNamed: @ "corect.png"]; _correct.scale = .6; _correct.zPosition = 10; _correct.position = CGPointMake (CGRectGetMidX (auto.frame), 800); _correct.alpha = 1.0; totalRightQuestions ++; [self touchWillProduceSound: @ "Adevărat"]; [auto addChild: _correct];  altceva if ([[declarații objectAtIndex: randomQuestion] isCorrect] == ​​0) _questionLabel.text = [[declarații objectAtIndex: randomQuestion] additionalInfo]; _wrong = [SKSpriteNode spriteNodeWithImageNamed: @ "greșit.png"]; _wrong.scale = .6; _wrong.zPosition = 10; _wrong.position = CGPointMake (CGRectGetMidX (auto.frame), 800); _wrong.alpha = 1.0; [self removePlayerLife]; [self touchWillProduceSound: @ "Fals"]; [self addChild: _wrong]; 

Îți amintești ultimul avertisment? Acum ar trebui să vedeți mai multe avertismente. Nu vă faceți griji, vă avertizează că lipsesc mai multe metode. Putem corecta asta. Prima metodă de definit este -(Void) nextQuestion. După cum sugerează și numele, aceasta cheamă următoarea întrebare. În afară de prezentarea unei noi întrebări, resetează timer-ul, mărește numărul întrebării, actualizează eticheta de întrebare curentă, înlătură întrebarea prezentată din matrice și testează logica necesară pentru a trece la alt nivel. Codul sursă complet al -(Void) nextQuestion este:

 -(vid) nextQuestion [self resetTimer]; questionNumber ++; _currentLevelLabel.text = [[NSString aliniere] initWithFormat: @ "Nivel:% ld de 10", (lung) questionNumber]; _wrong.alpha = 0,0; _correct.alpha = 0.0; _backgroundStatement.alpha = 0.0; _nextQuestion.alpha = 0.0; [declarații removeObject: [declarații objectAtIndex: randomQuestion]]; // întrebare aleatorie randomQuestion = [auto getRandomNumber între: 0 până la: ([numere de declarații] -1)]; [_questionLabel setText: [declarație objectAtIndex: randomQuestion]]]; _trueButton.alpha = 1.0; _falseButton.alpha = 1.0; dacă (număr de întrebare == 10 && totalRightQuestions> 7) int nexLevel = playerLevel + 2; [setInteger implicit: nexLevel pentruKey: @ "actualPlayerLevel"]; [self removeUIViews]; SK tranziție * tranziție = [SKTransitionation doorwayWithDuration: 2]; NivelSelect * levelSelect = [[NivelSelect alocare] initWithSize: CGSizeMake (CGRectGetMaxX (self.frame), CGRectGetMaxY (auto.frame)); [self.scene.view presentScene: nivel Selectați tranziția: tranziție]; 

Rețineți că ați codificat greu întrebările maxime (10) pentru acest nivel și pragul pentru nivelul următor (7). Încă o dată, va apărea un nou avertisment. Nu resetTimer există o metodă; această metodă resetează numai maximumTime proprietate la 60 de ani și actualizează eticheta în consecință:

 -(void) resetTimer maximumTime = 60; [_timerLevel setText: @ "60"]; 

În ultimul tutorial, ați definit touchWillProduceASound metodă. Cu toate acestea, în acest tutorial, trebuie să îl modificați mai departe. Obiectivul este să-i transmiteți un obiect care reprezintă răspunsul corect sau incorect. Apoi se va reda sunetul corespunzător. Metoda completă este:

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

Trebuie să definiți în continuare -(Void) removePlayerLife metodă. După cum indică numele, acesta testează durata de viață a unui jucător și acționează în consecință. Dacă jucătorul are mai mult de o viață, o viață este redusă și activul inerent este actualizat sau este mutat în ecranul inițial. Metoda completă este de mai jos.

 -(void) removePlayerLife if (playerVizuri> 1) pentru (NSInteger i = 0; i < playerLives; i++) SKSpriteNode* node = [heartArray objectAtIndex:i]; if (i == (playerLives-1)) node.alpha = .1;   playerLives--;  else  [self moveToHome];  

În acest moment, aproape că am terminat. Acum este momentul să actualizați - (Void) updateTimer definite în ultimul tutorial. Această nouă metodă este responsabilă pentru actualizarea valorii cronometrului și testarea duratei de viață a jucătorului. Reacționează automat când cronometrul atinge zero. În acel moment, testează viața jucătorului și acționează corespunzător. Se duce în meniul principal dacă viața jucătorului este mai mică decât una sau altfel o cheamă altfel (scăzând viața jucătorului). Fragmentul complet este mai jos.

 - (void) updateTimer maximumTime--; dacă (maximumTime == 0) if (playerLives < 1) [self touchWillProduceASound:@"False"]; [self moveToHome];  else [self presentCorrectWrongMenu:_trueButton]; [self touchWillProduceASound:@"False"]; [self removePlayerLife];   [_timerLevel setText:[[NSNumber numberWithInt:maximumTime] stringValue]]; 

4. Metode suplimentare

Au fost create două metode suplimentare: -(Void) moveToHome și -(Void) removeUIViews. Trebuie să le definim pentru că le vom folosi de mai multe ori. Este o bună practică să reutilizați codul în loc să îl tastați din nou. -(Void) moveToHome este doar un apel la a SKTransition și Scena mea clasă. Codul este:

 -(void) moveToHome SKTransition * tranziție = [SKTransition fadeWithDuration: 2]; MyScene * myscene = [[MyScene alin] initWithSize: CGSizeMake (CGRectGetMaxX (self.frame), CGRectGetMaxY (auto.frame)); [self.scene.view presentScene: tranziție myscene: tranziție]; 

-(Void) removeUIViews elimină UIKit opinii de la supervizare. Iată cum arată codul:

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

Acum că totul este corect, Alerga proiectul. De fiecare dată când răspundeți corect la o întrebare, veți vedea o interfață asemănătoare cu următoarea imagine:


Ilustrația unui răspuns corect

Pe de altă parte, atunci când răspundeți incorect la o întrebare, veți vedea o interfață care arată astfel:


Ilustrarea unui răspuns incorect

5. Îmbunătățiri finale

Mai avem încă un pas înainte să terminăm. Trebuie să inițializăm corect actualPlayerLevel la selecția nivelului. Schimbați atenția asupra MyScene.m clasa (prima creată de Xcode) și să adăugăm câteva linii de cod. Inițial, adăugați un obiect de tip NSUserDefaults la @implementation secțiune. Următorul fragment vă va ajuta:

 @implementation MyScene // code NSUserDefaults * implicit; 

Acum înăuntru -(void) didMoveToView: (SKView *) vizualizare, adăugați caracterul inerent NSUserDefaults inițializare. Valoarea implicită este una, astfel încât un nou jucător să înceapă întotdeauna un nou joc la nivelul 1. În plus, dacă jucătorul nu a reușit să atingă nivelul minim, acesta începe din nou la același nivel. Rezultatul este mai jos.

 defaults = [NSUserDefaults standardUserDefaults]; [implicit setInteger: 1 pentruKey: @ "actualPlayerLevel"]; // mai mult cod ... 

Mai multe ajustări pot fi făcute în acest joc. Puteți personaliza ratele corecte de răspuns pentru întrebări, animații și tranziții sau pentru a modifica modul peisaj și portret. Credem că sunteți gata pentru o astfel de sarcină. Încercați să le implementați pe cont propriu.


Concluzie

La sfârșitul acestui tutorial Fapte, ar trebui să puteți crea un joc SpriteKit, să creați și să configurați mai multe vederi și acțiuni SpriteKit, să configurați mai multe vizualizări UIKit, să le utilizați în paralel cu SpriteKit și să analizați listele de proprietăți. Nu ezitați să utilizați secțiunea de comentarii de mai jos pentru sugestii sau comentarii. Vă mulțumim pentru lectură!

Cod