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.
Î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:
Î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:
int
.NSString
.int
.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.
Î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ă:
NSMutableArray
pentru a stoca întrebărileFiș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.
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]];
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:
Pe de altă parte, atunci când răspundeți incorect la o întrebare, veți vedea o interfață care arată astfel:
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.
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ă!