Interfejsy API gier dla systemu iOS na platformie Xamarin.iOS

W tym artykule omówiono nowe ulepszenia gier udostępniane przez system iOS 9, które mogą służyć do ulepszania funkcji graficznych i audio gry Xamarin.iOS.

Firma Apple wprowadziła kilka ulepszeń technologicznych interfejsów API gier w systemie iOS 9, które ułatwiają implementowanie grafiki i dźwięku gier w aplikacji platformy Xamarin.iOS. Obejmują one zarówno łatwość programowania dzięki platformom wysokiego poziomu, jak i wykorzystanie mocy procesora GPU urządzenia z systemem iOS w celu zwiększenia szybkości i możliwości graficznych.

An example of an app running flocking

Obejmuje to programy GameplayKit, ReplayKit, Model I/O, MetalKit i Metal Performance Shaders oraz nowe, ulepszone funkcje systemów Metal, SceneKit i SpriteKit.

W tym artykule przedstawiono wszystkie sposoby ulepszania gry platformy Xamarin.iOS przy użyciu nowych ulepszeń gier systemu iOS 9:

Wprowadzenie do zestawu GameplayKit

Nowa struktura GameplayKit firmy Apple udostępnia zestaw technologii, które ułatwiają tworzenie gier dla urządzeń z systemem iOS przez zmniejszenie ilości powtarzalnego, wspólnego kodu wymaganego do implementacji. Zestaw Rozgrywkowy udostępnia narzędzia do tworzenia mechaniki gier, które można następnie łatwo połączyć z aparatem graficznym (takim jak SceneKit lub SpriteKit), aby szybko dostarczyć ukończoną grę.

Zestaw rozgrywkowy zawiera kilka typowych algorytmów gry, takich jak:

  • Oparta na zachowaniu symulacja agenta, która umożliwia definiowanie ruchów i celów, które sztuczna inteligencja będzie realizować automatycznie.
  • Minimalna sztuczna inteligencja do gry opartej na kolei.
  • System reguł dla logiki gry opartej na danych z rozmytym rozumowaniem w celu zapewnienia pojawiających się zachowań.

Ponadto zestaw GameplayKit stosuje podejście bloków konstrukcyjnych do tworzenia gier przy użyciu modularnej architektury, która zapewnia następujące funkcje:

  • Maszyna stanowa do obsługi złożonych systemów opartych na kodzie proceduralnym w grze.
  • Narzędzia do zapewniania losowej gry i nieprzewidywalności bez powodowania problemów z debugowaniem.
  • Architektura oparta na jednostkach wielokrotnego użytku.

Aby dowiedzieć się więcej na temat zestawu GameplayKit, zobacz Podręcznik programowania zestawów rozgrywkowych firmy Apple i Podręcznik struktury RozgrywkaKit.

Przykłady zestawu Rozgrywka

Przyjrzyjmy się szybkiej implementacji prostej mechaniki gry w aplikacji platformy Xamarin.iOS przy użyciu zestawu do gry.

Obliczanie ścieżki

Pathfinding to możliwość znalezienia elementu sztucznej inteligencji w grze, aby znaleźć swoją drogę wokół planszy gry. Na przykład wróg 2D znajduje drogę przez labirynt lub postać 3D przez teren świata strzelca pierwszej osoby.

Rozważmy następującą mapę:

An example pathfinding map

Przy użyciu metody pathfinding ten kod języka C# może znaleźć drogę przez mapę:

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));

Klasyczny system ekspertów

Poniższy fragment kodu w języku C# pokazuje, jak zestaw GameplayKit może służyć do implementowania klasycznego systemu ekspertów:

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;
}

Na podstawie danego zestawu reguł (GKRule) i znanego zestawu danych wejściowych system ekspertów (GKRuleSystem) utworzy przewidywalne dane wyjściowe (fizzbuzz na przykład powyżej).

Uciekają

Stado pozwala grupie kontrolowanych jednostek gry sztucznej inteligencji zachowywać się jako stado, gdzie grupa reaguje na ruchy i działania jednostki ołowiu, takie jak stado ptaków w locie lub szkoła pływania ryb.

Poniższy fragment kodu w języku C# implementuje zachowanie stada przy użyciu zestawu GameplayKit i zestawu SpriteKit na potrzeby wyświetlania grafiki:

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}");
        }
    }
}

Następnie zaimplementuj tę scenę na kontrolerze widoku:

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);
    }
}

Po uruchomieniu mały animowany "Boids" będzie stada wokół naszych naciśnięciów palcami:

The little animated Boids will flock around the finger taps

Inne przykłady firmy Apple

Oprócz przedstawionych powyżej przykładów firma Apple udostępniła następujące przykładowe aplikacje, które mogą być transkodowane w językach C# i Xamarin.iOS:

Metal

W systemie iOS 9 firma Apple wprowadziła kilka zmian i dodatków do systemu Metal w celu zapewnienia niskiego obciążenia dostępu do procesora GPU. Za pomocą systemu Metal można zmaksymalizować potencjał grafiki i obliczeń aplikacji systemu iOS.

Struktura Metal zawiera następujące nowe funkcje:

  • Nowe tekstury wzornika prywatnego i głębokości dla systemu OS X.
  • Ulepszona jakość cienia z zaciskami głębokości i oddzielnymi wartościami wzornika przedniego i tylnego.
  • Ulepszenia biblioteki Metal Shading Language i Metal Standard Library.
  • Cieniowania obliczeniowe obsługują szerszy zakres formatów pikseli.

Struktura MetalKit

Platforma MetalKit udostępnia zestaw klas narzędzi i funkcji, które zmniejszają ilość pracy wymaganej do użycia systemu Metal w aplikacji systemu iOS. Zestaw MetalKit zapewnia obsługę w trzech kluczowych obszarach:

  1. Asynchroniczne ładowanie tekstury z różnych źródeł, w tym typowe formaty, takie jak PNG, JPEG, KTX i PVR.
  2. Łatwy dostęp do zasobów opartych na we/wy modelu na potrzeby obsługi modeli specyficznych dla systemu Metal. Te funkcje zostały wysoce zoptymalizowane w celu zapewnienia wydajnego transferu danych między siatkami we/wy modelu i buforami metalowymi.
  3. Wstępnie zdefiniowane widoki metali i zarządzanie widokami, które znacznie zmniejszają ilość kodu wymaganego do wyświetlania renderowania grafiki w aplikacji systemu iOS.

Aby dowiedzieć się więcej na temat zestawu MetalKit, zapoznaj się z dokumentacją platformy MetalKit firmy Apple, podręcznikem programowania metalowego, dokumentacją struktury metali i podręcznikem języka cieniowania metali.

Struktura cieniowania wydajności metali

Struktura Cieniowania wydajności metali udostępnia wysoce zoptymalizowany zestaw cieniowania grafiki i obliczeń do użycia w aplikacjach systemu iOS opartych na systemie iOS. Każdy moduł cieniowania w strukturze Cieniowania wydajności metali został specjalnie dostrojony w celu zapewnienia wysokiej wydajności na procesorach GPU z systemem iOS obsługiwanych przez metal.

Korzystając z klas cieniowania wydajności metali, można osiągnąć najwyższą wydajność dla każdego konkretnego procesora GPU systemu iOS bez konieczności określania celu i konserwacji poszczególnych baz kodu. Cieniowanie wydajności metali może być używane z dowolnym zasobem metalowym, takim jak tekstury i bufory.

Struktura Cieniowania wydajności systemu operacyjnego udostępnia zestaw typowych cieniowania, takich jak:

  • Gaussian Blur (MPSImageGaussianBlur)
  • Wykrywanie przeglądarki Sobel Edge (MPSImageSobel)
  • Histogram obrazu (MPSImageHistogram)

Aby uzyskać więcej informacji, zobacz Podręcznik języka cieniowania metali firmy Apple.

Wprowadzenie do operacji we/wy modelu

Struktura we/wy modelu firmy Apple zapewnia głębokie zrozumienie zasobów 3D (takich jak modele i powiązane zasoby). Model We/Wy udostępnia gry z systemem iOS z materiałami fizycznymi, modelami i oświetleniem, które mogą być używane z Zestawami Rozgrywki, Metal i SceneKit.

W przypadku operacji we/wy modelu można obsługiwać następujące typy zadań:

  • Importuj oświetlenie, materiały, dane siatki, ustawienia aparatu i inne informacje oparte na scenie z różnych popularnych formatów oprogramowania i aparatu gry.
  • Przetwarzaj lub generuj informacje oparte na scenie, takie jak tworzenie proceduralnie teksturowanych kopuł nieba lub oświetlenie pieca w siatkę.
  • Współpracuje z systemami MetalKit, SceneKit i GLKit w celu wydajnego ładowania zasobów gier do buforów procesora GPU na potrzeby renderowania.
  • Eksportowanie informacji opartych na scenie do różnych popularnych formatów oprogramowania i gier.

Aby dowiedzieć się więcej o we/wy modelu, zobacz Dokumentacja struktury we/wy modelu firmy Apple

Wprowadzenie do zestawu ReplayKit

Nowa struktura ReplayKit firmy Apple umożliwia łatwe dodawanie nagrywania gry do gry z systemem iOS i umożliwia użytkownikowi szybkie i łatwe edytowanie i udostępnianie tego wideo z poziomu aplikacji.

Aby uzyskać więcej informacji, zobacz film wideo Firmy Apple Going Social with ReplayKit and Game Center i ich DemoBots: Building a Cross Platform Game with SpriteKit and GameplayKit sample app (Tworzenie międzyplatformowej gry za pomocą zestawu SpriteKit i GameKit).

SceneKit

Zestaw Sceny to interfejs API grafu sceny 3D, który upraszcza pracę z grafiką 3D. Po raz pierwszy wprowadzono go w systemie OS X 10.8 i teraz przyszedł do systemu iOS 8. Dzięki zestawowi Scen Kit tworzenie immersyjnych wizualizacji 3D i zwykłych gier 3D nie wymaga wiedzy w zakresie technologii OpenGL. Opierając się na typowych pojęciach dotyczących grafu sceny, zestaw Scene Kit abstrahuje od złożoności bibliotek OpenGL i OpenGL ES, co ułatwia dodawanie zawartości 3D do aplikacji. Jednak jeśli jesteś ekspertem OpenGL, Zestaw Sceny ma doskonałe wsparcie dla wiązania bezpośrednio z OpenGL, jak również. Zawiera również wiele funkcji, które uzupełniają grafikę 3D, taką jak fizyka, i integrują się bardzo dobrze z kilkoma innymi strukturami firmy Apple, takimi jak Core Animation, Core Image i Sprite Kit.

Aby uzyskać więcej informacji, zobacz dokumentację zestawu SceneKit .

Zmiany zestawu scen

Firma Apple dodała następujące nowe funkcje do zestawu SceneKit dla systemu iOS 9:

  • Program Xcode udostępnia teraz edytor scen, który umożliwia szybkie tworzenie gier i interaktywnych aplikacji 3D przez edytowanie scen bezpośrednio z poziomu środowiska Xcode.
  • Klasy SCNView i SCNSceneRenderer mogą służyć do włączania renderowania metali (na obsługiwanych urządzeniach z systemem iOS).
  • Klasy SCNAudioPlayer i SCNNode mogą służyć do dodawania efektów dźwiękowych przestrzennych, które automatycznie śledzą pozycję odtwarzacza w aplikacji systemu iOS.

Aby uzyskać więcej informacji, zobacz naszą dokumentację zestawu ScenKit i dokumentację platformy SceneKit firmy Apple oraz Fox: Building a SceneKit Game with the Xcode Scene Editor sample project (Dokumentacja platformy SceneKit firmy Apple) i Fox: Building a SceneKit Game with the Xcode Scene Editor sample project (Kompilowanie gry SceneKit za pomocą przykładowego projektu edytora scen Xcode).

SpriteKit

Sprite Kit, struktura gier 2D firmy Apple, ma kilka ciekawych nowych funkcji w systemach iOS 8 i OS X Yosemite. Obejmują one integrację z zestawem Scene Kit, obsługą cieniowania, oświetleniem, cieniami, ograniczeniami, normalnym generowaniem map i ulepszeniami fizyki. W szczególności nowe funkcje fizyki sprawiają, że bardzo łatwo jest dodać realistyczne efekty do gry.

Aby uzyskać więcej informacji, zobacz dokumentację zestawu SpriteKit .

Zmiany zestawu SpriteKit

Firma Apple dodała następujące nowe funkcje do zestawu SpriteKit dla systemu iOS 9:

  • Efekt dźwięku przestrzennego, który automatycznie śledzi położenie odtwarzacza z klasą SKAudioNode .
  • Program Xcode zawiera teraz edytor scen i edytor akcji, aby ułatwić tworzenie gier 2D i aplikacji.
  • Łatwa obsługa przewijania gier za pomocą nowych obiektów Aparat Nodes (SKCameraNode).
  • Na urządzeniach z systemem iOS obsługujących system Metal zestaw SpriteKit będzie automatycznie używać go do renderowania, nawet jeśli używasz już niestandardowych cieniowania OpenGL ES.

Aby uzyskać więcej informacji, zobacz dokumentację spritekit firmy Apple Dotyczącą struktury SpriteKit i ich demobotów: tworzenie gry międzyplatformowej za pomocą przykładowej aplikacji SpriteKit i GameplayKit .

Podsumowanie

W tym artykule omówiono nowe funkcje gier dostępne dla systemu iOS 9 dla aplikacji platformy Xamarin.iOS. Wprowadzono Zestaw Rozgrywkowy i Model We/Wy; główne ulepszenia systemu Metal; oraz nowe funkcje platformy SceneKit i SpriteKit.