API de jeu iOS dans Xamarin.iOS

Cet article décrit les nouvelles améliorations de jeu fournies par iOS 9 qui peuvent être utilisées pour améliorer les fonctionnalités graphiques et audio de votre jeu Xamarin.iOS.

Apple a apporté plusieurs améliorations technologiques aux API de jeu dans iOS 9, ce qui facilite l’implémentation de graphiques de jeu et d’audio dans une application Xamarin.iOS. Celles-ci incluent la facilité de développement via des frameworks de haut niveau et l’utilisation de la puissance du GPU de l’appareil iOS pour améliorer la vitesse et les capacités graphiques.

An example of an app running flocking

Cela inclut GameplayKit, ReplayKit, Model I/O, MetalKit et Metal Performance Shaders, ainsi que de nouvelles fonctionnalités améliorées de Metal, SceneKit et SpriteKit.

Cet article présente toutes les façons d’améliorer votre jeu Xamarin.iOS avec les nouvelles améliorations de jeu d’iOS 9 :

Présentation du GameplayKit

La nouvelle infrastructure GameplayKit d’Apple fournit un ensemble de technologies qui facilitent la création de jeux pour les appareils iOS en réduisant la quantité de code répétitif et commun requis pour l’implémentation. GameplayKit fournit des outils pour développer les mécanismes de jeu qui peuvent ensuite être facilement combinés avec un moteur graphique (tel que SceneKit ou SpriteKit) pour fournir rapidement un jeu terminé.

GameplayKit comprend plusieurs algorithmes de jeu courants, tels que :

  • Simulation basée sur un comportement, agent qui vous permet de définir des mouvements et des objectifs que l’IA poursuivra automatiquement.
  • Une intelligence artificielle minmax pour le jeu à tour.
  • Système de règles pour la logique de jeu pilotée par les données avec un raisonnement flou pour fournir un comportement émergent.

En outre, GameplayKit adopte une approche de bloc de construction pour le développement de jeux à l’aide d’une architecture modulaire qui fournit les fonctionnalités suivantes :

  • Machine d’état pour la gestion des systèmes complexes basés sur le code procédural dans le jeu.
  • Outils permettant de fournir un jeu aléatoire et une prévisibilité sans provoquer de problèmes de débogage.
  • Architecture basée sur des entités réutilisables et composantées.

Pour en savoir plus sur GameplayKit, consultez le Guide de programmation de Gameplaykit d’Apple et la référence de l’infrastructure GameplayKit.

Exemples gameplayKit

Examinons rapidement l’implémentation de mécanismes de jeu simples dans une application Xamarin.iOS à l’aide du kit de jeu.

Pathfinding

Pathfinding est la possibilité pour un élément IA d’un jeu de trouver son chemin autour du tableau de jeu. Par exemple, un ennemi 2D trouvant son chemin dans un labyrinthe ou un personnage 3D par le biais d’un terrain mondial de première personne.

Considérez la carte suivante :

An example pathfinding map

L’utilisation du chemin d’accès à ce code C# peut trouver un moyen de parcourir la carte :

var a = GKGraphNode2D.FromPoint (new Vector2 (0, 5));
var b = GKGraphNode2D.FromPoint (new Vector2 (3, 0));
var c = GKGraphNode2D.FromPoint (new Vector2 (2, 6));
var d = GKGraphNode2D.FromPoint (new Vector2 (4, 6));
var e = GKGraphNode2D.FromPoint (new Vector2 (6, 5));
var f = GKGraphNode2D.FromPoint (new Vector2 (6, 0));

a.AddConnections (new [] { b, c }, false);
b.AddConnections (new [] { e, f }, false);
c.AddConnections (new [] { d }, false);
d.AddConnections (new [] { e, f }, false);

var graph = GKGraph.FromNodes(new [] { a, b, c, d, e, f });

var a2e = graph.FindPath (a, e); // [ a, c, d, e ]
var a2f = graph.FindPath (a, f); // [ a, b, f ]

Console.WriteLine(String.Join ("->", (object[]) a2e));
Console.WriteLine(String.Join ("->", (object[]) a2f));

Système d’experts classique

L’extrait de code C# suivant montre comment le GameplayKit peut être utilisé pour implémenter un système d’expert classique :

string output = "";
bool reset = false;
int input = 15;

public override void ViewDidLoad ()
{
    base.ViewDidLoad ();

    /*
    If reset is true, clear the output and set reset to false
    */
    var clearRule = GKRule.FromPredicate ((rules) => reset, rules => {
        output = "";
        reset = false;
    });
    clearRule.Salience = 1;

    var fizzRule = GKRule.FromPredicate (mod (3), rules => {
        output += "fizz";
    });
    fizzRule.Salience = 2;

    var buzzRule = GKRule.FromPredicate (mod (5), rules => {
        output += "buzz";
    });
    buzzRule.Salience = 2;

    /*
    This *always* evaluates to true, but is higher Salience, so evaluates after lower-salience items
    (which is counter-intuitive). Print the output, and reset (thus triggering "ResetRule" next time)
    */
    var outputRule = GKRule.FromPredicate (rules => true, rules => {
        System.Console.WriteLine(output == "" ? input.ToString() : output);
        reset = true;
    });
    outputRule.Salience = 3;

    var rs = new GKRuleSystem ();
    rs.AddRules (new [] {
        clearRule,
        fizzRule,
        buzzRule,
        outputRule
    });

    for (input = 1; input < 16; input++) {
        rs.Evaluate ();
        rs.Reset ();
    }
}

protected Func<GKRuleSystem, bool> mod(int m)
{
    Func<GKRuleSystem,bool> partiallyApplied = (rs) => input % m == 0;
    return partiallyApplied;
}

En fonction d’un ensemble donné de règles (GKRule) et d’un ensemble connu d’entrées, le système expert (GKRuleSystem) crée une sortie prévisible (fizzbuzz pour notre exemple ci-dessus).

Flocage

Le troupeau permet à un groupe d’entités de jeu contrôlées par l’IA de se comporter comme un troupeau, où le groupe répond aux mouvements et actions d’une entité de plomb comme un troupeau d’oiseaux en vol ou une école de natation de poisson.

L’extrait de code C# suivant implémente le comportement de troupeau à l’aide du GameplayKit et du SpriteKit pour l’affichage graphique :

using System;
using SpriteKit;
using CoreGraphics;
using UIKit;
using GameplayKit;
using Foundation;
using System.Collections.Generic;
using System.Linq;
using OpenTK;

namespace FieldBehaviorExplorer
{
    public static class FlockRandom
    {
        private static GKARC4RandomSource rand = new GKARC4RandomSource ();

        static FlockRandom ()
        {
            rand.DropValues (769);
        }

        public static float NextUniform ()
        {
            return rand.GetNextUniform ();
        }
    }

    public class FlockingScene : SKScene
    {
        List<Boid> boids = new List<Boid> ();
        GKComponentSystem componentSystem;
        GKAgent2D trackingAgent; //Tracks finger on screen
        double lastUpdateTime = Double.NaN;
        //Hold on to behavior so it doesn't get GC'ed
        static GKBehavior flockingBehavior;
        static GKGoal seekGoal;

        public FlockingScene (CGSize size) : base (size)
        {
            AddRandomBoids (20);

            var scale = 0.4f;
            //Flocking system
            componentSystem = new GKComponentSystem (typeof(GKAgent2D));
            var behavior = DefineFlockingBehavior (boids.Select (boid => boid.Agent).ToArray<GKAgent2D>(), scale);
            boids.ForEach (boid => {
                boid.Agent.Behavior = behavior;
                componentSystem.AddComponent(boid.Agent);
            });

            trackingAgent = new GKAgent2D ();
            trackingAgent.Position = new Vector2 ((float) size.Width / 2.0f, (float) size.Height / 2.0f);
            seekGoal = GKGoal.GetGoalToSeekAgent (trackingAgent);
        }

        public override void TouchesBegan (NSSet touches, UIEvent evt)
        {
            boids.ForEach(boid => boid.Agent.Behavior.SetWeight(1.0f, seekGoal));
        }

        public override void TouchesEnded (NSSet touches, UIEvent evt)
        {
            boids.ForEach (boid => boid.Agent.Behavior.SetWeight (0.0f, seekGoal));
        }

        public override void TouchesMoved (NSSet touches, UIEvent evt)
        {
            var touch = (UITouch) touches.First();
            var loc = touch.LocationInNode (this);
            trackingAgent.Position = new Vector2((float) loc.X, (float) loc.Y);
        }

        private void AddRandomBoids (int count)
        {
            var scale = 0.4f;
            for (var i = 0; i < count; i++) {
                var b = new Boid (UIColor.Red, this.Size, scale);
                boids.Add (b);
                this.AddChild (b);
            }
        }

        internal static GKBehavior DefineFlockingBehavior(GKAgent2D[] boidBrains, float scale)
        {
            if (flockingBehavior == null) {
                var flockingGoals = new GKGoal[3];
                flockingGoals [0] = GKGoal.GetGoalToSeparate (boidBrains, 100.0f * scale, (float)Math.PI * 8.0f);
                flockingGoals [1] = GKGoal.GetGoalToAlign (boidBrains, 40.0f * scale, (float)Math.PI * 8.0f);
                flockingGoals [2] = GKGoal.GetGoalToCohere (boidBrains, 40.0f * scale, (float)Math.PI * 8.0f);

                flockingBehavior = new GKBehavior ();
                flockingBehavior.SetWeight (25.0f, flockingGoals [0]);
                flockingBehavior.SetWeight (10.0f, flockingGoals [1]);
                flockingBehavior.SetWeight (10.0f, flockingGoals [2]);
            }
            return flockingBehavior;
        }

        public override void Update (double currentTime)
        {
            base.Update (currentTime);
            if (Double.IsNaN(lastUpdateTime)) {
                lastUpdateTime = currentTime;
            }
            var delta = currentTime - lastUpdateTime;
            componentSystem.Update (delta);
        }
    }

    public class Boid : SKNode, IGKAgentDelegate
    {
        public GKAgent2D Agent { get { return brains; } }
        public SKShapeNode Sprite { get { return sprite; } }

        class BoidSprite : SKShapeNode
        {
            public BoidSprite (UIColor color, float scale)
            {
                var rot = CGAffineTransform.MakeRotation((float) (Math.PI / 2.0f));
                var path = new CGPath ();
                path.MoveToPoint (rot, new CGPoint (10.0, 0.0));
                path.AddLineToPoint (rot, new CGPoint (0.0, 30.0));
                path.AddLineToPoint (rot, new CGPoint (10.0, 20.0));
                path.AddLineToPoint (rot, new CGPoint (20.0, 30.0));
                path.AddLineToPoint (rot, new CGPoint (10.0, 0.0));
                path.CloseSubpath ();

                this.SetScale (scale);
                this.Path = path;
                this.FillColor = color;
                this.StrokeColor = UIColor.White;

            }
        }

        private GKAgent2D brains;
        private BoidSprite sprite;
        private static int boidId = 0;

        public Boid (UIColor color, CGSize size, float scale)
        {
            brains = BoidBrains (size, scale);
            sprite = new BoidSprite (color, scale);
            sprite.Position = new CGPoint(brains.Position.X, brains.Position.Y);
            sprite.ZRotation = brains.Rotation;
            sprite.Name = boidId++.ToString ();

            brains.Delegate = this;

            this.AddChild (sprite);
        }

        private GKAgent2D BoidBrains(CGSize size, float scale)
        {
            var brains = new GKAgent2D ();
            var x = (float) (FlockRandom.NextUniform () * size.Width);
            var y = (float) (FlockRandom.NextUniform () * size.Height);
            brains.Position = new Vector2 (x, y);

            brains.Rotation = (float)(FlockRandom.NextUniform () * Math.PI * 2.0);
            brains.Radius = 30.0f * scale;
            brains.MaxSpeed = 0.5f;
            return brains;
        }

        [Export ("agentDidUpdate:")]
        public void AgentDidUpdate (GameplayKit.GKAgent agent)
        {
        }

        [Export ("agentWillUpdate:")]
        public void AgentWillUpdate (GameplayKit.GKAgent agent)
        {
            var brainsIn = (GKAgent2D) agent;
            sprite.Position = new CGPoint(brainsIn.Position.X, brainsIn.Position.Y);
            sprite.ZRotation = brainsIn.Rotation;
            Console.WriteLine ($"{sprite.Name} -> [{sprite.Position}], {sprite.ZRotation}");
        }
    }
}

Ensuite, implémentez cette scène dans un contrôleur d’affichage :

public override void ViewDidLoad ()
{
    base.ViewDidLoad ();
        // Perform any additional setup after loading the view, typically from a nib.
        this.View = new SKView {
        ShowsFPS = true,
        ShowsNodeCount = true,
        ShowsDrawCount = true
    };
}

public override void ViewWillLayoutSubviews ()
{
    base.ViewWillLayoutSubviews ();

    var v = (SKView)View;
    if (v.Scene == null) {
        var scene = new FlockingScene (View.Bounds.Size);
        scene.ScaleMode = SKSceneScaleMode.AspectFill;
        v.PresentScene (scene);
    }
}

Lors de la course, le petit « Boids » animé va se déplacer autour de nos touches de doigt :

The little animated Boids will flock around the finger taps

Autres exemples Apple

En plus des exemples présentés ci-dessus, Apple a fourni les exemples d’applications suivants qui peuvent être transcodés en C# et Xamarin.iOS :

Métal

Dans iOS 9, Apple a apporté plusieurs modifications et ajouts à Metal pour fournir un accès à faible surcharge au GPU. À l’aide de Metal, vous pouvez optimiser le potentiel graphique et informatique de vos applications iOS.

Le framework Metal inclut les nouvelles fonctionnalités suivantes :

  • Nouvelles textures de gabarit privées et de profondeur pour OS X.
  • Amélioration de la qualité de l’ombre avec une pince de profondeur et des valeurs de gabarit avant et arrière distinctes.
  • Améliorations apportées à la bibliothèque Metal Shading Language et Metal Standard Library.
  • Les nuanceurs de calcul prennent en charge un large éventail de formats de pixels.

L’infrastructure MetalKit

Le framework MetalKit fournit un ensemble de classes et de fonctionnalités utilitaires qui réduisent la quantité de travail requise pour utiliser Metal dans une application iOS. MetalKit fournit une prise en charge dans trois domaines clés :

  1. Chargement de texture asynchrone à partir d’une variété de sources, notamment des formats courants tels que PNG, JPEG, KTX et PVR.
  2. Accès facile des ressources basées sur les E/S de modèle pour la gestion de modèle spécifique à Metal. Ces fonctionnalités ont été hautement optimisées pour fournir un transfert de données efficace entre les maillages d’E/S de modèle et les mémoires tampons metal.
  3. Vues metal prédéfinies et gestion des vues qui réduisent considérablement la quantité de code requise pour afficher les rendus graphiques dans une application iOS.

Pour en savoir plus sur MetalKit, consultez la référence du framework MetalKit d’Apple, le Guide de programmation metal, la référence du framework metal et le guide du langage metal shading.

Metal Performance Shaders Framework

Le framework Metal Performance Shader fournit un ensemble hautement optimisé de graphiques et de nuanceurs basés sur le calcul pour une utilisation dans vos applications iOS basées sur Metal. Chaque nuanceur du framework Metal Performance Shader a été spécifiquement paramétré pour fournir des performances élevées sur les GPU iOS pris en charge par Metal.

En utilisant des classes Metal Performance Shader, vous pouvez obtenir les performances les plus élevées possibles sur chaque GPU iOS spécifique sans avoir à cibler et à gérer des bases de code individuelles. Les nuanceurs de performances métalliques peuvent être utilisés avec n’importe quelle ressource metal, comme les textures et les mémoires tampons.

Le framework metal Performance Shader fournit un ensemble de nuanceurs courants tels que :

  • Gaussian Blur (MPSImageGaussianBlur)
  • Détection de périphérie sobel (MPSImageSobel)
  • Histogramme d’image (MPSImageHistogram)

Pour plus d’informations, consultez le Guide linguistique de l’ombrage métallique d’Apple.

Présentation des E/S de modèle

L’infrastructure d’E/S de modèle d’Apple fournit une compréhension approfondie des ressources 3D (telles que les modèles et leurs ressources associées). Les E/S de modèle fournissent à vos jeux iOS des matériaux, modèles et éclairages physiques qui peuvent être utilisés avec GameplayKit, Metal et SceneKit.

Avec les E/S de modèle, vous pouvez prendre en charge les types de tâches suivants :

  • Importez des éclairages, des matériaux, des données de maillage, des paramètres de caméra et d’autres informations basées sur des scènes à partir de divers formats de logiciels et de moteurs de jeu populaires.
  • Traitez ou générez des informations basées sur des scènes, telles que créer des dômes de ciel texturés de manière procédurale ou faire cuire l’éclairage dans un maillage.
  • Fonctionne avec MetalKit, SceneKit et GLKit pour charger efficacement des ressources de jeu dans des mémoires tampons GPU pour le rendu.
  • Exportez des informations basées sur des scènes vers divers formats de logiciels et de moteurs de jeu populaires.

Pour en savoir plus sur les E/S de modèle, consultez la référence du framework d’E /S de modèle d’Apple

Présentation de ReplayKit

La nouvelle infrastructure ReplayKit d’Apple vous permet d’ajouter facilement l’enregistrement du jeu à votre jeu iOS et de permettre à l’utilisateur de modifier et de partager rapidement et facilement cette vidéo à partir de l’application.

Pour plus d’informations, consultez la vidéo d’Apple « Going Social with ReplayKit and Game Center » et leurs DemoBots : Building a Cross Platform Game with SpriteKit and GameplayKit sample app.

SceneKit

Scene Kit est une API graphique de scène 3D qui simplifie l’utilisation de graphiques 3D. Il a été introduit pour la première fois dans OS X 10.8 et est maintenant venu sur iOS 8. Avec Scene Kit, la création de visualisations 3D immersives et de jeux 3D occasionnels ne nécessite pas d’expertise dans OpenGL. En s’appuyant sur des concepts courants de graphique de scène, Scene Kit extrait les complexités d’OpenGL et OpenGL ES, ce qui facilite l’ajout de contenu 3D à une application. Toutefois, si vous êtes un expert OpenGL, Scene Kit a un excellent support pour lier directement avec OpenGL. Il comprend également de nombreuses fonctionnalités qui complètent des graphiques 3D, tels que la physique, et s’intègre très bien à plusieurs autres frameworks Apple, tels que Core Animation, Core Image et Sprite Kit.

Pour plus d’informations, consultez notre documentation SceneKit .

Modifications de SceneKit

Apple a ajouté les nouvelles fonctionnalités suivantes à SceneKit pour iOS 9 :

  • Xcode fournit désormais un éditeur de scène qui vous permet de créer rapidement des jeux et des applications 3D interactives en modifiant des scènes directement à partir de Xcode.
  • Les SCNView classes et SCNSceneRenderer les classes peuvent être utilisées pour activer le rendu Metal (sur les appareils iOS pris en charge).
  • Les SCNAudioPlayer classes et SCNNode les classes peuvent être utilisées pour ajouter des effets audio spatiaux qui suivent automatiquement la position d’un lecteur à une application iOS.

Pour plus d’informations, consultez notre documentation SceneKit et la référence de l’infrastructure SceneKit d’Apple et Fox : Building a SceneKit Game with the Xcode Scene Editor sample project.

SpriteKit

Sprite Kit, l’infrastructure de jeu 2D d’Apple, a quelques nouvelles fonctionnalités intéressantes dans iOS 8 et OS X Yosemite. Il s’agit notamment de l’intégration à Scene Kit, à la prise en charge du nuanceur, à l’éclairage, aux ombres, aux contraintes, à la génération normale de la carte et aux améliorations de la physique. En particulier, les nouvelles fonctionnalités physiques facilitent l’ajout d’effets réalistes à un jeu.

Pour plus d’informations, consultez notre documentation SpriteKit .

Modifications spriteKit

Apple a ajouté les nouvelles fonctionnalités suivantes à SpriteKit pour iOS 9 :

  • Effet audio spatial qui suit automatiquement la position du lecteur avec la SKAudioNode classe.
  • Xcode propose désormais un éditeur de scène et un éditeur d’action pour faciliter la création de jeux et d’applications 2D.
  • Prise en charge facile du jeu de défilement avec les nouveaux objets Nœuds de caméra (SKCameraNode).
  • Sur les appareils iOS qui prennent en charge Metal, SpriteKit l’utilise automatiquement pour le rendu, même si vous utilisiez déjà des nuanceurs OpenGL ES personnalisés.

Pour plus d’informations, consultez notre documentation SpriteKit Sur la référence spriteKit Framework d’Apple et leurs DemoBots : création d’un jeu multiplateforme avec spriteKit et exemple d’application GameplayKit.

Résumé

Cet article a abordé les nouvelles fonctionnalités de jeu fournies par iOS 9 pour vos applications Xamarin.iOS. Il a introduit gameplayKit et E/S de modèle ; les principales améliorations apportées à Metal ; et les nouvelles fonctionnalités de SceneKit et spriteKit.