SDK iOS Créer un jeu de faits - Logique du jeu

Bienvenue dans la troisième et dernière section de notre série de didacticiels Jeu de faits avec Sprite Kit. Ce tutoriel va vous apprendre à utiliser le framework Sprite Kit pour créer un jeu de faits basé sur des questions. Il est conçu pour les utilisateurs novices et avancés. En chemin, vous appliquerez le noyau du kit Sprite.


introduction

Dans ce didacticiel, vous allez programmer toute la logique du jeu, y compris la vie du joueur, la question et la réponse du joueur. Cette série de didacticiels est divisée en trois sections: Configuration du projet, Interface des faits et Logique du jeu. Si vous n'avez pas encore terminé la deuxième section, vous pouvez télécharger le projet et le ramasser exactement à l'endroit où nous nous sommes arrêtés. Chaque partie produit un résultat pratique et la somme de toutes les parties produira le jeu final. Bien que chaque partie puisse être lue indépendamment, pour une meilleure compréhension, nous suggérons de suivre le tutoriel étape par étape. Nous avons également inclus le code source de chaque partie séparément, offrant ainsi un moyen de démarrer le didacticiel dans n’importe quelle partie de la série..

Voici à quoi ressemblera notre jeu à la fin:


Illustration du résultat final - Faits

1. Classe de faits personnalisés

Dans le dernier tutoriel, vous avez défini un fichier de plist pour les questions. Chaque question a quatre propriétés. Afin de les gérer, vous devez créer une classe personnalisée pour permettre cette tâche correctement. Par conséquent, vous devez former un autre Objectif c classe. Nomme le factObjet et définir le NSObject super classe.

Maintenant, éditons le fichier d’en-tête et ajoutons les quatre propriétés de la pliste. Chaque propriété de plist a ses propres caractéristiques:

  • La déclaration Id est un int.
  • La déclaration est un NSString.
  • La déclaration isCorrect est un int.
  • Les informations supplémentaires sont un NSString.

Le résultat final devrait ressembler à ceci:

 @property (nonatomic, readwrite) int factID; @property (nonatomic, readwrite, keep) instruction NSString *; @property (nonatomic, readwrite) NSInteger isCorrect; @property (nonatomic, readwrite, keep) NSString * additionalInfo;

Vous n'avez pas besoin d'utiliser le fichier d'implémentation (.m). Nous allons analyser le fichier plist dans cette classe personnalisée et utiliser les valeurs directement à partir de la mémoire..


2. Interface des faits: initialisation

Dans le dernier tutoriel, vous avez défini la structure de base de l'interface des faits. Il est maintenant temps de le terminer avec les étapes logiques. Pour terminer ce jeu, nous devons créer une étiquette de question, une déclaration de fond personnalisée, un bouton qui demande une autre question et une interface vraie et fausse. Ces quatre déclarations se traduisent en cinq propriétés définies dans le FactsScene.h fichier. Une fois encore, vous pouvez les nommer comme vous le souhaitez. Notre implémentation est:

 @property (nonatomic, keep) UILabel * questionLabel; @property (nonatomic, keep) SKSpriteNode * backgroundStatement; @property (nonatomic, keep) UIButton * nextQuestion; @property (nonatomic, keep) SKSpriteNode * faux; @property (nonatomic, keep) SKSpriteNode * correct;

Portez maintenant votre attention sur le FactsScene.m. Vous devez définir plusieurs objets utilisés en interne dans la classe:

  • UNE NSMutableArray pour stocker les questions
  • Une valeur aléatoire qui représente une question aléatoire
  • L'identifiant de la question
  • Le seuil minimum pour les bonnes questions; ce seuil indique le nombre minimum de réponses correctes requises pour que l'utilisateur puisse passer à un autre niveau. Dans ce tutoriel, vous utiliserez la valeur sept.

Votre fichier d'implémentation devrait ressembler à ceci:

 NSMutableArray * statement; int randomQuestion; int questionNumber; int totalRightQuestions; // besoin de 7 sur 10 pour passer au niveau suivant

Il est maintenant temps d’attribuer quelques valeurs et de commencer par la logique. dans le -(id) initWithSize: (CGSize) size inLevel: (NSInteger) niveau avecPlayerLives: (int) lives méthode initier le numéro de question et le totalRightQuestions. Comme c'est la première fois que vous l'utilisez, l'initiation est facile et peut se faire comme suit:

 questionNumber = 1; totalRightQuestions = 0;

Il est maintenant temps d'utiliser la classe personnalisée définie à l'étape susmentionnée. Analyser le fichier plist et utiliser la banque d’informations dans la plist pour allouer et peupler de nouvelles factObjet objets. Notez que nous allons stocker chaque factObjet objet dans une coutume NSMutableArray Déjà défini (des déclarations). L'extrait complet est ci-dessous.

 instructions = [[NSMutableArray alloc] init]; NSString * plistPath = [[NSBundle mainBundle] pathForResource: @ "LevelDescription" ofType: @ "plist"]; NSMutableDictionary * dictionary = [[NSMutableDictionary alloc] initWithContentsOfFile: plistPath]; if ([dictionnaire objectForKey: @ "Questions"]! = nil) NSMutableArray * array = [dictionnaire objectForKey: @ "Questions"]; pour (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];  

Cette étape supprime l’ancien code d’analyse de la -(void) didMoveToView: (SKView *) voir méthode. Vous pouvez l'enlever, puisque vous ne l'utiliserez plus.


3. Interface des faits: logique

Il est maintenant temps de se concentrer sur le code logique lui-même. Nous devons montrer la question à l'utilisateur. Cependant, la question est toujours un choix aléatoire. Commencez par définir un rectangle pour répondre à la question, puis allouez les ressources nécessaires au texte de la question. L'extrait suivant vous aidera:

 CGRect labelFrame = CGRectMake (120 300, 530, 100); _questionLabel = [[UILabel alloc]] initWithFrame: labelFrame]; randomQuestion = [self getRandomNumberBetween: 0 to: ([instructions count] -1)]; NSString * labelText = [[instructions objectAtIndex: instruction randomQuestion]]; [_questionLabel setText: labelText]; [_questionLabel setTextColor: [UIColor whiteColor]]; [_questionLabel setFont: [UIFont font fontWithName: taille NULL: 23]]; [_questionLabel setTextAlignment: NSTextAlignmentCenter]; // L'étiquette utilisera un nombre illimité de lignes [_questionLabel setNumberOfLines: 0];

Notez que vous n'utiliserez pas le SKLabelNode sur le simple NSString à cause d'un SKLabelNode limitation; c'est pour le texte d'une seule ligne. Un avertissement apparaîtra concernant la getRandomNumberBetween: 0 to: X méthode. Vous devez le déclarer et le coder. Son objectif est de renvoyer une valeur aléatoire entre deux valeurs. Le prochain extrait vous aidera à:

 -(int) getRandomNumberBetween: (int) de to: (int) to return (int) de + arc4random ()% (to-from + 1); 

Maintenant que vous pouvez voir la question, nous devons ajouter des fonctionnalités aux boutons droit et incorrect. Changez les deux sélecteurs et appelez une nouvelle méthode appelée: presentCorrectWrongMenu.

 [_falseButton addTarget: action automatique: @selector (presentCorrectWrongMenu :) pourControlEvents: UIControlEventTouchUpInside]; [_trueButton addTarget: action propre: @selector (presentCorrectWrongMenu :) pourControlEvents: UIControlEventTouchUpInside];

De plus, définissez une balise pour chaque bouton. Le vrai bouton sera tag = 1 et le faux tag = 0. Ces tags vous aident lorsque vous appelez le -(void) presentCorrectWrongMenu: (UIButton *) expéditeur méthode pour déterminer quel bouton a été utilisé pour appeler cette même méthode.

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

La prochaine étape consiste à ajouter le -(void) presentCorrectWrongMenu: (UIButton *) expéditeur méthode. Cette méthode est complexe et reconnaît le bouton sélectionné, ajoute une interface de réponse personnalisée et ajoute un bouton qui appelle la question suivante. Utilisez l'extrait suivant pour atteindre les rubriques mentionnées ci-dessus:

 -(void) presentCorrectWrongMenu: (UIButton *) sender int userData = sender.tag; // background _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; [auto 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] pourState: UIControlStateNormal]; [_nextQuestion setTitle: @ "Appuyez ici pour continuer" pourState: UIControlStateNormal]; [_nextQuestion addTarget: action propre: @selector (nextQuestion) forControlEvents: UIControlEventTouchUpInside]; _nextQuestion.alpha = 1,0; [auto.view addSubview: _nextQuestion]; [_backgroundStatement runAction: [SKAction fadeAlphaTo: 1.0f duration: 0.2f]]; _trueButton.alpha = 0.0; _falseButton.alpha = 0,0;

Un avertissement apparaîtra, mais ne le corrigez pas tout de suite. Tout d’abord, terminez la déclaration de la méthode. Maintenant que vous avez une interface personnalisée pour la réponse, vous devez tester la réponse du joueur. Pour y parvenir, vous devez savoir sur quel bouton le joueur a tapé et la réponse à la question inhérente. Vous le savez déjà, il vous suffit donc de créer une condition de test logique simple. Pour ce faire, vous devez vérifier si la réponse est correcte ou incorrecte, émettre un son en conséquence et procéder à la mise à jour des propriétés. Le prochain extrait vous aidera. Notez que vous devez le placer à la fin du dernier extrait de code..

 if (([[déclarations objectAtIndex: randomQuestion] isCorrect] == ​​0 && userData == 0) || ([[déclarations objectAtIndex: randomQuestion] isCorrect] == ​​1 && userData == 1)) if ([[déclarations objectAtIndex : randomQuestion] isCorrect] == ​​0) _questionLabel.text = [[instructions 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 ++; [auto touchWillProduceASound: @ "True"]; [auto addChild: _correct];  else if ([[déclarations objectAtIndex: randomQuestion] isCorrect] == ​​0) _questionLabel.text = [[instructions 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; [self removePlayerLife]; [auto touchWillProduceASound: @ "False"]; [auto addChild: _wrong]; 

Vous souvenez-vous du dernier avertissement? Maintenant, vous devriez voir plusieurs avertissements. Ne vous inquiétez pas, ils vous avertissent qu'il manque plusieurs méthodes. Nous pouvons corriger cela. La première méthode à définir est la -(vide) nextQuestion. Comme son nom l'indique, cela appelle la question suivante. En plus de présenter une nouvelle question, il réinitialise le minuteur, incrémente le numéro de la question, met à jour le libellé de la question actuelle, supprime la question présentée du tableau et teste la logique nécessaire pour passer à un autre niveau. Le code source complet de -(vide) nextQuestion est:

 -(void) nextQuestion [auto resetTimer]; questionNumber ++; _currentLevelLabel.text = [[NSString alloc] initWithFormat: @ "Niveau:% ld sur 10", (long) questionNumber]; _wrong.alpha = 0.0; _correct.alpha = 0.0; _backgroundStatement.alpha = 0.0; _nextQuestion.alpha = 0.0; [instructions removeObject: [statement objectAtIndex: randomQuestion]]; // question aléatoire randomQuestion = [self getRandomNumberBetween: 0 to: ([instructions count] -1)]; [_questionLabel setText: [[instructions objectAtIndex: instruction randomQuestion]]]; _trueButton.alpha = 1,0; _falseButton.alpha = 1,0; if (numéro de question == 10 && totalRightQuestions> 7) int nexLevel = playerLevel + 2; [defaults setInteger: nexLevel forKey: @ "actualPlayerLevel"]; [auto removeUIViews]; SKTransition * transition = [SKTransition doorwayWithDuration: 2]; LevelSelect * levelSelect = [[LevelSelect alloc]] initWithSize: CGSizeMake (CGRectGetMaxX (self.frame), CGRectGetMaxY (self.frame))]; [self.scene.view presentScene: levelSelect transition: transition]; 

Notez que vous avez codé en dur les questions maximum (10) pour ce niveau et le seuil pour le niveau suivant (7). Encore une fois, un nouvel avertissement apparaîtra. Non resetTimer méthode existe; cette méthode ne réinitialise que le durée maximale propriété à 60 et met à jour l'étiquette en conséquence:

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

Dans le dernier tutoriel, vous avez défini le touchWillProduceASound méthode. Cependant, dans ce tutoriel, vous devez le modifier davantage. L'objectif est de lui transmettre un objet qui représente la réponse correcte ou incorrecte. Ensuite, le son correspondant sera joué. La méthode complète est:

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

Vous devez encore définir le -(vide) removePlayerLife méthode. Comme son nom l'indique, il teste la vie d'un joueur et agit en conséquence. Si le joueur a plus d'une vie, une vie est réduite et l'actif inhérent est mis à jour ou déplacé vers l'écran d'accueil. La méthode complète est ci-dessous.

 -(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];  

À ce stade, nous avons presque terminé. Il est maintenant temps de mettre à jour le - (void) updateTimer défini dans le dernier tutoriel. Cette nouvelle méthode est chargée de mettre à jour la valeur de la minuterie et de tester la vie du lecteur. Il réagit automatiquement lorsque la minuterie atteint zéro. À ce moment, il teste la vie du joueur et agit en conséquence. Il passe au menu principal si la durée de vie du joueur est inférieure à un ou pose une autre question sinon (diminution de la durée de vie du joueur). L'extrait complet est ci-dessous.

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

4. Méthodes supplémentaires

Deux méthodes supplémentaires ont été créées: -(vide) moveToHome et -(void) removeUIViews. Nous devons les définir car nous les utiliserons plus d'une fois. Il est recommandé de réutiliser le code au lieu de le saisir à nouveau. le -(vide) moveToHome est juste un appel à un SKTransition et Ma scène classe. Le code est:

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

le -(void) removeUIViews supprime le UIKit vues de la superview. Voici à quoi ressemble le code:

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

Maintenant que tout est correct, Courir le projet. Chaque fois que vous répondez correctement à une question, vous verrez une interface similaire à l'image suivante:


Illustration d'une réponse correcte

D'autre part, lorsque vous répondez de manière incorrecte à une question, vous verrez une interface qui ressemble à ceci:


Illustration d'une réponse incorrecte

5. Améliorations finales

Il ne reste plus qu'une étape avant que nous ayons terminé. Nous devons initialiser correctement le actualPlayerLevel au niveau de sélection. Basculez votre attention sur le MyScene.m class (le premier créé par le Xcode) et ajoutons quelques lignes de code. Initialement, ajoutez un objet du type NSUserDefaults au @la mise en oeuvre section. L'extrait suivant vous aidera:

 @implementation MyScene // code NSUserDefaults * defaults; 

Maintenant dans le -(void) didMoveToView: (SKView *) voir, ajouter le inhérent NSUserDefaults initialisation. La valeur par défaut est un, de sorte qu'un nouveau joueur commence toujours une nouvelle partie au niveau 1. De plus, si le joueur ne remplit pas les conditions minimales requises pour passer le niveau, il recommence au même niveau. Le résultat est ci-dessous.

 defaults = [NSUserDefaults standardUserDefaults]; [defaults setInteger: 1 forKey: @ "actualPlayerLevel"]; // plus de code… 

Plusieurs autres ajustements peuvent être apportés à ce jeu. Vous pouvez personnaliser les taux de réponse corrects pour les questions, les animations et les transitions, ou modifier le mode paysage et portrait. Nous pensons que vous êtes prêt pour une telle tâche. Essayez de les mettre en œuvre vous-même.


Conclusion

À la fin de ce didacticiel, vous devriez pouvoir créer un jeu SpriteKit, créer et configurer plusieurs vues et actions SpriteKit, configurer plusieurs vues UIKit, les utiliser en parallèle avec SpriteKit et analyser des listes de propriétés. N'hésitez pas à utiliser la section commentaires ci-dessous pour des suggestions ou des commentaires. Merci d'avoir lu!