IOS SDK Bygg et fakta Game - Game Logic

Velkommen tilbake til den tredje og siste delen av vårt Facts Game med Sprite Kit opplæringsserie. Denne opplæringen vil lære deg hvordan du bruker Sprite Kit-rammeverket for å lage et spørsmålbasert fakta-spill. Den er designet for både nybegynnere og avanserte brukere. Underveis vil du bruke Sprite Kit-kjernen.


Introduksjon

I denne opplæringen vil du programmere hele spilllogikken, inkludert spillerens liv, spørsmålet og spillerens svar. Denne opplæringsserien er oppdelt i tre seksjoner: Prosjektoppsett, Fakta Interface og Game Logic. Hvis du ikke har fullført den andre delen, kan du laste ned prosjektet og henting nøyaktig hvor vi sluttet. Hver del gir et praktisk resultat, og summen av alle deler vil produsere det endelige spillet. Til tross for at hver del kan leses selvstendig, foreslår vi for å få en bedre forståelse å følge veiledningen trinn for trinn. Vi har også inkludert kildekoden for hver del separat, og gir dermed en måte å starte opplæringen i noen del av serien.

Dette er hva vårt spill vil se ut ved ferdigstillelse:


Illustrasjon av endelig resultat - fakta

1. Tilpasset faktaklasse

I den siste opplæringen definerte du en plistfil for spørsmålene. Hvert spørsmål har fire egenskaper. For å kunne håndtere dem må du opprette en tilpasset klasse for å ha råd til oppgaven riktig. Derfor må du danne en annen Objective-C klasse. Navngi det factObject og definer NSObject super.

La oss nå redigere topptekstfilen og legge til de fire plistegenskapene. Hver eiendom har sine egne funksjoner:

  • Erklæringen Id er en int.
  • Erklæringen er a NSString.
  • TheCorrect-setningen er en int.
  • Ytterligere informasjon er en NSString.

Sluttresultatet skal lignes på dette:

 @property (nonatomic, readwrite) int factID; @property (nonatomic, readwrite, behold) NSString * statement; @property (nonatomic, readwrite) NSInteger er Korrekt; @property (nonatomic, readwrite, behold) NSString * additionalInfo;

Du trenger ikke å bruke implementeringsfilen (.m). Vi vil analysere plistfilen til denne egendefinerte klassen og bruke verdiene direkte fra minnet.


2. Fakta Grensesnitt: Initialisering

I den siste opplæringen definerte du den grunnleggende strukturen i faktagrensesnittet. Det er nå på tide å fullføre det med logikkstrinnene. For å fullføre dette spillet må vi lage et spørsmålet, en tilpasset bakgrunnsoppgave, en knapp som ber om et annet spørsmål, og et ekte og falskt grensesnitt. Disse fire setningene oversetter til fem egenskaper definert i FactsScene.h fil. Igjen kan du nevne dem som du vil. Vår implementering er:

 @property (nonatomic, behold) UILabel * questionLabel; @property (nonatomic, behold) SKSpriteNode * backgroundStatement; @property (nonatomic, behold) UIButton * nextQuestion; @property (nonatomic, behold) SKSpriteNode * feil; @property (nonatomic, behold) SKSpriteNode * riktig;

Nå flytt oppmerksomheten til FactsScene.m. Du må definere flere objekter som brukes internt i klassen:

  • EN NSMutableArray å lagre spørsmålene
  • En tilfeldig verdi som representerer et tilfeldig spørsmål
  • Spørsmålidentifikatoren
  • Minimumsgrensen for de riktige spørsmålene; Denne terskelen indikerer de minste nødvendige korrekte svarene for at brukeren skal gå videre til et annet nivå. I denne opplæringen vil du bruke verdien sju.

Implementeringsfilen din skal se slik ut:

 NSMutableArray * uttalelser; int randomquestion; int questionNumber; int totalRightQuestions; // trenger 7 av 10 for å gå videre til neste nivå

Det er nå på tide å tildele noen få verdier og begynne med logikken. I -(id) initWithSize: (CGSize) størrelse inLevel: (NSInteger) nivå medPlayerLives: (int) lives Metode initierer questionNumber og totalRightQuestions. Siden det er første gang du bruker det, er initieringen lett og kan gjøres som:

 questionNumber = 1; totalRightQuestions = 0;

Nå er det på tide å bruke egendefinert klassen definert i ovennevnte trinn. Parse plistfilen og bruk informasjonsforretningen i plist til å tildele og fylle ut nytt factObject objekter. Legg merke til at vi vil lagre hver factObject objekt i en egendefinert NSMutableArray allerede definert (uttalelser). Komplett utdrag er under.

 uttalelser = [[NSMutableArray alloc] init]; NSString * plistPath = [[NSBundle mainBundle] pathForResource: @ "LevelDescription" ofType: @ "plist"]; NSMutableDictionary * ordbok = [[NSMutableDictionary alloc] initWithContentsOfFile: plistPath]; hvis ([ordbok objektForKey: @ "Spørsmål"]! = null) NSMutableArray * array = [ordbok objektForKey: @ "Spørsmål"]; for (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];  

Dette trinnet fjerner den eldre parsingskoden fra -(void) didMoveToView: (SKView *) visning metode. Du kan fjerne den, siden du ikke vil bruke den lenger.


3. Fakta Grensesnitt: Logikk

Nå er det dags å fokusere på selve logikkoden. Vi må vise spørsmålet til brukeren. Men spørsmålet er alltid et tilfeldig valg. Begynn å definere et rektangel for å ha råd til spørsmålet, og tilordne deretter de nødvendige ressursene for spørsmålet. Følgende utdrag vil hjelpe deg:

 CGRect labelFrame = CGRectMake (120,300, 530, 100); _questionLabel = [[UILabel alloc] initWithFrame: labelFrame]; randomQuestion = [self getRandomNumberBetween: 0 til: ([statements count] -1)]; NSString * labelText = [[statements objectAtIndex: randomQuestion] statement]; [_questionLabel setText: labelText]; [_questionLabel setTextColor: [UIColor whiteColor]]; [_questionLabel setFont: [UIFont fontWithName: NULL størrelse: 23]]; [_questionLabel setTextAlignment: NSTextAlignmentCenter]; // Etiketten vil bruke et ubegrenset antall linjer [_questionLabel setNumberOfLines: 0];

Legg merke til at du ikke vil bruke SKLabelNode over det enkle NSString på grunn av a SKLabelNode begrensning; Det er kun for enkeltlinje tekst. En advarsel vil vises angående getRandomNumberBetween: 0 til: X metode. Du må deklarere og kode det Målet er å returnere en tilfeldig verdi mellom to verdier. Neste utdrag vil hjelpe deg:

 -(int) getRandomNumberBetween: (int) fra til: (int) til return (int) fra + arc4random ()% (til-fra + 1); 

Nå som du kan se spørsmålet, må vi legge til noen funksjoner til høyre og feil knapp. Bytt begge velgerne og ring en ny metode kalt: presentCorrectWrongMenu.

 [_falseButton addTarget: self action: @selector (presentCorrectWrongMenu :) forControlEvents: UIControlEventTouchUpInside]; [_trueButton addTarget: selvhandling: @selector (presentCorrectWrongMenu :) forControlEvents: UIControlEventTouchUpInside];

I tillegg definerer du en kode for hver knapp. Den sanne knappen vil være tag = 1 og den falske taggen = 0. Disse kodene hjelper deg når du ringer til -(Void) presentCorrectWrongMenu: (UIButton *) avsender metode for å bestemme hvilken knapp som ble tappet for å ringe samme metode.

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

Det neste trinnet er å legge til -(Void) presentCorrectWrongMenu: (UIButton *) avsender metode. Denne metoden er kompleks og gjenkjenner hvilken knapp som er tappet, legger til et tilpasset svargrensesnitt, og legger til en knapp som kaller det neste spørsmålet. Bruk følgende utdrag for å oppnå de ovennevnte emnene:

 -(void) presentCorrectWrongMenu: (UIButton *) avsender int userData = sender.tag; // bakgrunn _backgroundStatement = [SKSpriteNode spriteNodeWithImageNamed: @ "background.png"]; _backgroundStatement.position = CGPointMake (CGRectGetMidX (self.frame), CGRectGetMidY (self.frame)); _backgroundStatement.size = CGSizeMake (768, 1024); _backgroundStatement.zPosition = 10; _backgroundStatement.alpha = 0.0; [self addChild: _backgroundStatement]; _nextQuestion = [UIButton buttonWithType: UIButtonTypeRoundedRect]; _nextQuestion.frame = CGRectMake (CGRectGetMidX (self.frame) -100, CGRectGetMidY (self.frame) +90, 200, 70,0); _nextQuestion.backgroundColor = [UIColor clearColor]; [_nextQuestion setTitleColor: [UIColor blackColor] forState: UIControlStateNormal]; [_nextQuestion setTitle: @ "Trykk her for å fortsette" forState: UIControlStateNormal]; [_nextQuestion addTarget: self action: @selector (nextQuestion) forControlEvents: UIControlEventTouchUpInside]; _nextQuestion.alpha = 1.0; [self.view addSubview: _nextQuestion]; [_backgroundStatement runAction: [SKAction fadeAlphaTo: 1.0f varighet: 0.2f]]; _trueButton.alpha = 0.0; _falseButton.alpha = 0.0;

En advarsel vises, men gjør det ikke med en gang. Først avslutt metoden deklarasjonen. Nå som du har et tilpasset grensesnitt for svaret, må du teste spillerens svar. For å oppnå det må du vite hvilken knapp spilleren tappet og det inneboende spørsmålet er svaret på. Du vet allerede dette, så du trenger bare å opprette en enkel logisk testtilstand. For å gjøre dette må du teste om svaret er riktig eller feil, spill en lyd tilsvarende, og fortsett til egenskapsoppdateringen. Neste utdrag vil hjelpe deg. Legg merke til at du må plassere den der den siste kodestykket ble avsluttet.

 hvis ([[setninger objectAtIndex: randomQuestion] isCorrect] == ​​0 && userData == 0) || ([[setninger objectAtIndex: randomQuestion] isCorrect] == ​​1 && userData == 1)) if ([[setninger objectAtIndex : randomQuestion] isCorrect] == ​​0) _questionLabel.text = [[setninger objectAtIndex: randomQuestion] additionalInfo]; _correct = [SKSpriteNode spriteNodeWithImageNamed: @ "correct.png"]; _correct.scale = .6; _correct.zPosition = 10; _correct.position = CGPointMake (CGRectGetMidX (self.frame), 800); _correct.alpha = 1.0; totalRightQuestions ++; [self touchWillProduceSound: @ "True"]; [self addChild: _correct];  annet hvis ([[setninger objectAtIndex: randomQuestion] isCorrect] == ​​0) _questionLabel.text = [[setninger objectAtIndex: randomQuestion] additionalInfo]; _wrong = [SKSpriteNode spriteNodeWithImageNamed: @ "wrong.png"]; _wrong.scale = .6; _wrong.zPosition = 10; _wrong.position = CGPointMake (CGRectGetMidX (self.frame), 800); _wrong.alpha = 1.0; [selv fjernePlayerLife]; [self touchWillProduceSound: @ "False"]; [self addChild: _wrong]; 

Husker du den siste advarselen? Nå bør du se flere advarsler. Ikke bekymre deg, de advarer deg om at flere metoder mangler. Vi kan korrigere det. Den første metoden for å definere er -(Void) nextQuestion. Som navnet antyder, kaller det neste spørsmål. I tillegg til å presentere et nytt spørsmål, tilbakestiller den timeren, øker spørsmålet, oppdaterer den aktuelle spørsmålet, fjerner det presenterte spørsmålet fra matrisen, og tester logikken som trengs for å flytte til et annet nivå. Den komplette kildekoden til -(Void) nextQuestion er:

 -(ugyldig) nextQuestion [self resetTimer]; questionNumber ++; _currentLevelLabel.text = [[NSString alloker] initWithFormat: @ "Nivå:% ld av 10", (lang) spørsmålNumber]; _wrong.alpha = 0.0; _correct.alpha = 0.0; _backgroundStatement.alpha = 0.0; _nextQuestion.alpha = 0.0; [uttalelser removeObject: [uttalelser objectAtIndex: randomQuestion]]; // tilfeldig spørsmål randomQuestion = [self getRandomNumberBetween: 0 til: ([statements count] -1)]; [_questionLabel setText: [[statements objectAtIndex: randomQuestion] statement]]; _trueButton.alpha = 1.0; _falseButton.alpha = 1.0; hvis (questionNumber == 10 && totalRightQuestions> 7) int nexLevel = playerLevel + 2; [standardinnstillinger setInteger: nexLevel forKey: @ "actualPlayerLevel"]; [self removeUIViews]; SKTransition * overgang = [SKTransition doorwayWithDuration: 2]; LevelSelect * levelSelect = [[LevelSelect tildeling] initWithSize: CGSizeMake (CGRectGetMaxX (self.frame), CGRectGetMaxY (self.frame))]; [self.scene.view presentScene: levelVelg overgang: overgang]; 

Legg merke til at du har kodet maksimale spørsmålene (10) for dette nivået og terskelen for neste nivå (7). Igjen vises en ny advarsel. Nei resetTimer metode eksisterer; Denne metoden tilbakestiller bare maximumTime eiendom til 60 og oppdaterer etiketten tilsvarende:

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

I den siste opplæringen definerte du touchWillProduceASound metode. Men i denne opplæringen må du endre det videre. Målet er å sende det et objekt som representerer riktig eller feil svar. Deretter spiller den tilsvarende lyden. Den komplette metoden er:

 -(void) touchWillProduceSound: (NSString *) svar long soundFlag = [standard integerForKey: @ "sound"]; if (soundFlag == 1) SKAction * lyd; hvis ([svar isEqualToString: @ "False"]) lyd = [SKAction playSoundFileNamed: @ "wrong.mp3" waitForCompletion: YES];  else sound = [SKAction playSoundFileNamed: @ "right.mp3" waitForCompletion: YES];  [self runAction: lyd]; 

Du må fortsatt definere -(Void) removePlayerLife metode. Som navnet sier, tester det livet til en spiller og handler tilsvarende. Hvis spilleren har mer enn ett liv, reduseres et liv og den inneboende aktiva oppdateres eller flyttes til startskjermbildet. Den komplette metoden er nedenfor.

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

På dette tidspunktet er vi nesten ferdig. Det er nå på tide å oppdatere - (Void) updateTimer definert i den siste opplæringen. Denne nye metoden er ansvarlig for å oppdatere timeren og teste spillernes liv. Det reagerer automatisk når timeren treffer null. På den tiden tester det spillernes liv og handler tilsvarende. Den går til hovedmenyen hvis spillerens liv er mindre enn en eller kaller et annet spørsmål ellers (reduserer spillerens liv). Komplett utdrag er under.

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

4. Ytterligere metoder

To ytterligere metoder ble opprettet: -(Void) moveToHome og -(Void) removeUIViews. Vi må definere dem fordi vi bruker dem mer enn en gang. Det er god praksis å gjenbruke koden i stedet for å skrive alt igjen. De -(Void) moveToHome er bare et anrop til a SKTransition og MyScene klasse. Koden er:

 -(void) moveToHome SKTransition * overgang = [SKTransition fadeWithDuration: 2]; MyScene * myscene = [[MyScene alloker] initWithSize: CGSizeMake (CGRectGetMaxX (self.frame), CGRectGetMaxY (self.frame))]; [self.scene.view presentScene: myscene overgang: overgang]; 

De -(Void) removeUIViews fjerner UIKit visninger fra overvåkingen. Her ser koden ut:

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

Nå som alt er riktig, Løpe prosjektet. Hver gang du svarer riktig på et spørsmål, ser du et grensesnitt som ligner på neste bilde:


Illustrasjon av et riktig svar

På den annen side, når du svarer feil på et spørsmål, ser du et grensesnitt som ser slik ut:


Illustrasjon av feil svar

5. Endelige forbedringer

Det er bare ett skritt før vi er ferdige. Vi må riktig initialisere actualPlayerLevel på nivåvalg. Bytt oppmerksomhet til MyScene.m klasse (den første som er opprettet av Xcode) og la oss legge til noen linjer med kode. Først legger du til et objekt av typen NSUserDefaults til @gjennomføring seksjon. Følgende utdrag vil hjelpe deg:

 @implementation MyScene // kode NSUserDefaults * standard; 

Nå inne i -(void) didMoveToView: (SKView *) visning, legg til det iboende NSUserDefaults initialisering. Standardverdien er en, slik at en ny spiller alltid starter et nytt spill på nivå 1. Videre, hvis spilleren ikke oppnådde minimumskravene til å passere nivået, starter det igjen på samme nivå. Resultatet er under.

 standard = [NSUserDefaults standardUserDefaults]; [standardinnstillinger setInteger: 1 forKey: @ "actualPlayerLevel"]; // mer kode ... 

Flere andre tilpasninger kan gjøres til dette spillet. Du kan tilpasse de riktige svarfrekvensene for spørsmål, animasjoner og overganger, eller endre landskaps- og portrettmodus. Vi tror at du er klar for en slik oppgave. Prøv å implementere dem på egen hånd.


Konklusjon

På slutten av denne veiledningen i fakta bør du kunne lage et SpriteKit-spill, opprette og konfigurere flere SpriteKit-visninger og handlinger, konfigurere flere UIKit-visninger, bruke dem parallelt med SpriteKit, og analyser egenskapslister. Ikke nøl med å bruke kommentarseksjonen nedenfor for forslag eller kommentarer. Takk for at du leste!