Používání .NET 4.x v Unity

Technologie c# a .NET, které jsou základem skriptování Unity, pokračovaly v přijímání aktualizací od původního vydání Microsoftu v roce 2002. Vývojáři Unity ale nemusí vědět o stabilním proudu nových funkcí přidaných do jazyka C# a rozhraní .NET Framework, protože před Unity 2017.1 unity používala ekvivalentní skriptovací modul runtime .NET 3.5, chybějící roky aktualizací.

Ve verzi Unity 2017.1 zavedla Unity experimentální verzi skriptovacího modulu runtime upgradovanou na verzi kompatibilní s .NET 4.6, C# 6.0. V Unity 2018.1 se ekvivalentní modul runtime .NET 4.x už nepovažuje za experimentální, zatímco starší ekvivalentní modul runtime .NET 3.5 se nyní považuje za starší verzi. Ve verzi Unity 2018.3 se Unity chystá provést upgradovaný skriptovací modul runtime jako výchozí výběr a aktualizovat ještě dál na C# 7. Další informace a nejnovější aktualizace na této roadmapě najdete v blogovém příspěvku Unity nebo navštivte fórum Experimentální skriptování ve verzi Preview. Mezitím si projděte následující části, kde najdete další informace o nových funkcích, které jsou teď k dispozici pomocí modulu runtime skriptování .NET 4.x.

Požadavky

Povolení modulu runtime skriptování .NET 4.x v Unity

Pokud chcete povolit skriptovací modul runtime .NET 4.x, proveďte následující kroky:

  1. Otevřete přehrávač Nastavení v nástroji Unity Inspector výběrem možnosti Upravit > projekt Nastavení > Přehrávač > Jiné Nastavení.

  2. V části Konfigurace klikněte na rozevírací seznam Úroveň kompatibility rozhraní API a vyberte rozhraní .NET Framework. Zobrazí se výzva k restartování Unity.

Screenshot showing the Select .NET 4.x equivalent.

Volba mezi profily .NET 4.x a .NET Standard 2.1

Jakmile přepnete na ekvivalentní skriptovací modul runtime .NET 4.x, můžete pomocí rozevírací nabídky v přehrávači zadat úroveň kompatibility rozhraní API Nastavení (Upravit > project Nastavení > Player). Existují dvě možnosti:

  • .NET Standard 2.1. Tento profil odpovídá profilu .NET Standard 2.1 publikovanému platformou .NET Foundation. Unity doporučuje .NET Standard 2.1 pro nové projekty. Je menší než .NET 4.x, což je výhodné pro platformy s omezenými velikostmi. Unity se také zavázala podporovat tento profil napříč všemi platformami, které Unity podporuje.

  • .NET Framework. Tento profil poskytuje přístup k nejnovějšímu rozhraní .NET 4 API. Zahrnuje také veškerý kód dostupný v knihovnách tříd rozhraní .NET Framework a podporuje také profily .NET Standard 2.1. Profil .NET 4.x použijte, pokud váš projekt vyžaduje část rozhraní API, která není součástí profilu .NET Standard 2.0. Některé části tohoto rozhraní API ale nemusí být podporované na všech platformách Unity.

Další informace o těchto možnostech najdete v blogovém příspěvku Unity.

Přidání odkazů na sestavení při použití úrovně kompatibility rozhraní API .NET 4.x

Při použití nastavení .NET Standard 2.1 v rozevíracím seznamu Úroveň kompatibility rozhraní API jsou všechna sestavení v profilu rozhraní API odkazována a použitelná. Při použití většího profilu .NET 4.x se ale některá sestavení dodávaná s Unity ve výchozím nastavení neodkazují. Pokud chcete tato rozhraní API používat, musíte ručně přidat odkaz na sestavení. Sestavení Unity se dají zobrazit v adresáři MonoBleedingEdge/lib/mono instalace editoru Unity:

Screenshot showing the MonoBleedingEdge directory.

Pokud například používáte profil .NET 4.x a chcete použít HttpClient, musíte přidat odkaz na sestavení pro System.Net.Http.dll. Bez něj si kompilátor stěžuje, že chybí odkaz na sestavení:

Screenshot showing the missing assembly reference.

Visual Studio znovu vygeneruje soubory .csproj a .sln pro projekty Unity při každém otevření. V důsledku toho nemůžete přidat odkazy na sestavení přímo v sadě Visual Studio, protože při opětovném otevření projektu dojde ke ztrátě. Místo toho je nutné použít speciální textový soubor s názvem csc.rsp :

  1. V kořenovém adresáři assetů projektu Unity vytvořte nový textový soubor s názvem csc.rsp.

  2. Na prvním řádku prázdného textového souboru zadejte: -r:System.Net.Http.dll a pak soubor uložte. Můžete nahradit "System.Net.Http.dll" za jakékoli zahrnuté sestavení, které může chybět odkaz.

  3. Restartujte editor Unity.

Využití výhod kompatibility .NET

Kromě nových funkcí syntaxe a jazyka C# poskytuje skriptovací modul runtime .NET 4.x uživatelům Unity přístup k obrovské knihovně balíčků .NET, které nejsou kompatibilní se starší verzí skriptovacího modulu runtime .NET 3.5.

Přidání balíčků z NuGetu do projektu Unity

NuGet je správce balíčků pro .NET. NuGet je integrovaný do sady Visual Studio. Projekty Unity však vyžadují speciální proces pro přidání balíčků NuGet, protože při otevření projektu v Unity se jeho soubory projektu sady Visual Studio znovu vygenerují a vrátí zpět potřebné konfigurace. Pokud chcete přidat balíček z NuGetu, do projektu Unity:

  1. Vyhledejte kompatibilní balíček NuGet, který chcete přidat (.NET Standard 2.0 nebo .NET 4.x). Tento příklad předvede přidání Json.NET, oblíbeného balíčku pro práci s JSON do projektu .NET Standard 2.0.

  2. Klikněte na tlačítko Stáhnout :

    Screenshot showing the download button.

  3. Vyhledejte stažený soubor a změňte příponu z .nupkg na .zip.

  4. V souboru ZIP přejděte do adresáře lib/netstandard2.0 a zkopírujte soubor Newtonsoft.Json.dll .

  5. Ve složce kořenových assetů projektu Unity vytvořte novou složku s názvem Plugins. Moduly plug-in jsou speciálním názvem složky v Unity. Další informace najdete v dokumentaci k Unity.

  6. Vložte soubor Newtonsoft.Json.dll do adresáře modulů plug-in projektu Unity.

  7. V adresáři Assets projektu Unity vytvořte soubor s názvem link.xml a přidejte následující KÓD XML, abyste zajistili, že proces odstraňování bajtového kódu Unity neodebere potřebná data při exportu do platformy IL2CPP. I když je tento krok specifický pro tuto knihovnu, můžete narazit na problémy s jinými knihovnami, které používají Reflexe ion podobným způsobem. Další informace najdete v dokumentaci Unity k tomuto článku.

    <linker>
      <assembly fullname="System.Core">
        <type fullname="System.Linq.Expressions.Interpreter.LightLambda" preserve="all" />
      </assembly>
    </linker>
    

S veškerým obsahem teď můžete balíček Json.NET použít.

using Newtonsoft.Json;
using UnityEngine;

public class JSONTest : MonoBehaviour
{
    class Enemy
    {
        public string Name { get; set; }
        public int AttackDamage { get; set; }
        public int MaxHealth { get; set; }
    }
    private void Start()
    {
        string json = @"{
            'Name': 'Ninja',
            'AttackDamage': '40'
            }";

        var enemy = JsonConvert.DeserializeObject<Enemy>(json);

        Debug.Log($"{enemy.Name} deals {enemy.AttackDamage} damage.");
        // Output:
        // Ninja deals 40 damage.
    }
}

Toto je jednoduchý příklad použití knihovny, která nemá žádné závislosti. Pokud balíčky NuGet spoléhají na jiné balíčky NuGet, budete je muset stáhnout ručně a přidat je do projektu stejným způsobem.

Nové funkce syntaxe a jazyka

Použití aktualizovaného skriptovacího modulu runtime poskytuje vývojářům Unity přístup k C# 8 a k hostiteli nových jazykových funkcí a syntaxe.

Inicializátory automatických vlastností

V modulu runtime skriptování .NET 3.5 Unity usnadňuje syntaxe automatických vlastností rychlé definování neinicializovaných vlastností, ale inicializace se musí provést jinde ve vašem skriptu. Teď s modulem runtime .NET 4.x je možné inicializovat automatické vlastnosti na stejném řádku:

// .NET 3.5
public int Health { get; set; } // Health has to be initialized somewhere else, like Start()

// .NET 4.x
public int Health { get; set; } = 100;

Interpolace řetězců

Se starším modulem runtime .NET 3.5 vyžadovalo zřetězení řetězcůwkward syntaxi. Díky modulu runtime $ .NET 4.x teď funkce interpolace řetězců umožňuje vkládání výrazů do řetězců v přímojší a čitelnější syntaxi:

// .NET 3.5
Debug.Log(String.Format("Player health: {0}", Health)); // or
Debug.Log("Player health: " + Health);

// .NET 4.x
Debug.Log($"Player health: {Health}");

Členové tvoření výrazy

S novější syntaxí jazyka C# dostupnou v modulu runtime .NET 4.x můžou výrazy lambda nahradit tělo funkcí, aby byly stručnější:

// .NET 3.5
private int TakeDamage(int amount)
{
    return Health -= amount;
}

// .NET 4.x
private int TakeDamage(int amount) => Health -= amount;

Ve vlastnostech jen pro čtení můžete také použít členy s body výrazu:

// .NET 4.x
public string PlayerHealthUiText => $"Player health: {Health}";

Asynchronní vzor založený na úlohách (TAP)

Asynchronní programování umožňuje provádět časově náročné operace bez toho, aby vaše aplikace nereagovala. Tato funkce také umožňuje kódu čekat na dokončení časově náročných operací, než bude pokračovat v kódu, který závisí na výsledcích těchto operací. Můžete například počkat na načtení souboru nebo dokončení síťové operace.

V Unity se asynchronní programování obvykle provádí s koruteny. Od jazyka C# 5 je však upřednostňovanou metodou asynchronního programování ve vývoji .NET asynchronní vzor založený na úlohách (TAP), který používá async klíčová await slova a používá system.Threading.Task. Stručně řečeno, ve async funkci můžete await dokončit úlohu bez blokování zbývající části aplikace v aktualizaci:

// Unity coroutine
using UnityEngine;
public class UnityCoroutineExample : MonoBehaviour
{
    private void Start()
    {
        StartCoroutine(WaitOneSecond());
        DoMoreStuff(); // This executes without waiting for WaitOneSecond
    }
    private IEnumerator WaitOneSecond()
    {
        yield return new WaitForSeconds(1.0f);
        Debug.Log("Finished waiting.");
    }
}
// .NET 4.x async-await
using UnityEngine;
using System.Threading.Tasks;
public class AsyncAwaitExample : MonoBehaviour
{
    private async void Start()
    {
        Debug.Log("Wait.");
        await WaitOneSecondAsync();
        DoMoreStuff(); // Will not execute until WaitOneSecond has completed
    }
    private async Task WaitOneSecondAsync()
    {
        await Task.Delay(TimeSpan.FromSeconds(1));
        Debug.Log("Finished waiting.");
    }
}

TAP je složitý předmět, přičemž vývojáři, kteří by měli zvážit nuance specifické pro Unity, by měli zvážit. V důsledku toho tap není univerzální náhradou korutin v Unity; je to ale jiný nástroj, který se má použít. Rozsah této funkce je nad rámec tohoto článku, ale níže najdete některé obecné osvědčené postupy a tipy.

Úvodní referenční informace pro TAP s Unity

Tyto tipy vám můžou pomoct začít s TAP v Unity:

  • Asynchronní funkce, které mají být očekávána, by měly mít návratový typ Task nebo Task<TResult>.
  • Asynchronní funkce, které vracejí úlohu, by měly mít k jejich názvům připojenou příponu "Async" . Přípona "Async" pomáhá indikovat, že funkce by měla být vždy očekávána.
  • Návratový async void typ použijte pouze pro funkce, které aktivuje asynchronní funkce z tradičního synchronního kódu. Tyto funkce se nedají očekávat a neměly by mít příponu "Async" v jejich názvech.
  • Unity používá UnitySynchronizationContext k zajištění, aby se asynchronní funkce spouštěly ve výchozím nastavení v hlavním vlákně. Rozhraní Unity API není přístupné mimo hlavní vlákno.
  • Je možné spouštět úlohy na podprocesech na pozadí pomocí metod jako Task.Run a Task.ConfigureAwait(false). Tato technika je užitečná pro snižování zátěže drahých operací z hlavního vlákna za účelem zvýšení výkonu. Použití vláken na pozadí však může vést k problémům, které se obtížně ladí, jako jsou podmínky časování.
  • Rozhraní Unity API není přístupné mimo hlavní vlákno.
  • Úlohy, které používají vlákna, nejsou podporovány v buildech Unity WebGL.

Rozdíly mezi korutinami a TAP

Mezi koruty a TAP / async-await existují některé důležité rozdíly:

  • Coroutiny nemůžou vracet hodnoty, ale Task<TResult> můžou.
  • Do příkazu try-catch nelze vložit yield příkaz try-catch, což znesnadňuje zpracování chyb s koruty. Try-catch ale funguje s TAP.
  • Coroutinová funkce Unity není k dispozici ve třídách, které nejsou odvozené od MonoBehaviour. TAP je skvělý pro asynchronní programování v takových třídách.
  • V tomto okamžiku Unity nenavrhuje TAP jako velkoobchodní náhradu korutin. Profilace je jediný způsob, jak zjistit konkrétní výsledky jednoho přístupu oproti druhému pro každý daný projekt.

nameof – operátor

Operátor nameof získá název řetězce proměnné, typu nebo člena. V některých případech, kdy nameof jsou užitečné chyby protokolování a získání názvu řetězce výčtu:

// Get the string name of an enum:
enum Difficulty {Easy, Medium, Hard};
private void Start()
{
    Debug.Log(nameof(Difficulty.Easy));
    RecordHighScore("John");
    // Output:
    // Easy
    // playerName
}
// Validate parameter:
private void RecordHighScore(string playerName)
{
    Debug.Log(nameof(playerName));
    if (playerName == null) throw new ArgumentNullException(nameof(playerName));
}

Atributy informací o volajícím

Atributy informací o volajícím poskytují informace o volající metodě. Pro každý parametr, který chcete použít s atributem Informace o volajícím, musíte zadat výchozí hodnotu:

private void Start ()
{
    ShowCallerInfo("Something happened.");
}
public void ShowCallerInfo(string message,
        [System.Runtime.CompilerServices.CallerMemberName] string memberName = "",
        [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "",
        [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
{
    Debug.Log($"message: {message}");
    Debug.Log($"member name: {memberName}");
    Debug.Log($"source file path: {sourceFilePath}");
    Debug.Log($"source line number: {sourceLineNumber}");
}
// Output:
// Something happened
// member name: Start
// source file path: D:\Documents\unity-scripting-upgrade\Unity Project\Assets\CallerInfoTest.cs
// source line number: 10

Použití statického

Použití statické umožňuje používat statické funkce bez zadání názvu třídy. Pokud používáte statickou funkci, můžete ušetřit místo a čas, pokud potřebujete použít několik statických funkcí ze stejné třídy:

// .NET 3.5
using UnityEngine;
public class Example : MonoBehaviour
{
    private void Start ()
    {
        Debug.Log(Mathf.RoundToInt(Mathf.PI));
        // Output:
        // 3
    }
}
// .NET 4.x
using UnityEngine;
using static UnityEngine.Mathf;
public class UsingStaticExample: MonoBehaviour
{
    private void Start ()
    {
        Debug.Log(RoundToInt(PI));
        // Output:
        // 3
    }
}

Důležité informace o programu IL2CPP

Při exportu hry na platformy, jako je iOS, Unity použije svůj modul IL2CPP k "transpilování" IL do kódu C++, který se pak zkompiluje pomocí nativního kompilátoru cílové platformy. V tomto scénáři existuje několik funkcí .NET, které nejsou podporované, například části Reflexe a použití klíčového dynamic slova. I když můžete řídit používání těchto funkcí ve vlastním kódu, můžete narazit na problémy s používáním knihoven DLL a sad SDK třetích stran, které nebyly napsané s Unity a IL2CPP. Další informace o tomto článku najdete v dokumentaci k omezení skriptování na webu Unity.

Kromě toho, jak je uvedeno v příkladu Json.NET výše, Unity se pokusí během procesu exportu IL2CPP odstranit nepoužívaný kód. I když tento proces obvykle není problém s knihovnami, které používají Reflexe ion, může omylem odstranit vlastnosti nebo metody, které budou volána za běhu, které nelze určit v době exportu. Pokud chcete tyto problémy vyřešit, přidejte do projektu soubor link.xml , který obsahuje seznam sestavení a oborů názvů, aby se proces odstraňování nespustí. Další informace najdete v dokumentaci Unity k odstranění bajtového kódu.

Ukázkový projekt Unity pro .NET 4.x

Ukázka obsahuje příklady několika funkcí .NET 4.x. Projekt si můžete stáhnout nebo zobrazit zdrojový kód na GitHubu.

Další materiály