27 Eylül 2026 Pazar

2D detekce kolize v MonoGame: Od základních obdélníků k systémům s nulovou alokací pixelů




2D detekce kolize v MonoGame: Od základních obdélníků po systémy s nulovou alokací pixelů

Při sestavování 2D her ve vizuálních enginech, jako je Unity nebo Godot, se detekce kolizí často podobá sérii zaškrtávacích políček inspektora: plácnete na BoxCollider2D nebo CircleCollider2D, připojíte Rigidbody a doufáte, že interní fyzikální krok na mobilních zařízeních nezasekne.

V MonoGame a C# však máte plnou kontrolu. Neexistuje žádná skrytá fyzikální režie, žádná nechtěná rotační setrvačnost a žádné záhadné špičky při sběru odpadu (GC), které okrádají váš rozpočet na rám.

V Arar Games, když jsme postavili Blocked: Pixel Panzer a Paint Trek, naše arkádové herní smyčky potřebovaly ke zpracování stovek vysokorychlostních nepřátelských kulek, explodujících cihlových mříží, rotujících věží tanků, průletů stíhaček a částicových štítů při 60 až 120 FPS na počítačích se systémem Windows i na zařízeních Android. Fyzikální engine pro všeobecné použití nepřicházel v úvahu – potřebovali jsme účelovou, stupňovitou kolizní architekturu.

V tomto komplexním, kódem řízeném průvodci začneme od úplných základů kolizí MonoGame (Rectangle.Intersects se skutečnými spritemi Bullet a Enemy) a posouváme se k pokročilým kruhovým kontrolám, smíšenému upínání, anti-tunelování raycastů, produkčnímu Pixel-CPerfect Gridtial optimalizované kolize pro mobilní telefony**, přežití.


1. The Foundation: Jednoduchá MonoGame Sprite Hierarchie

Před detekcí kolizí potřebujeme čisté herní entity. V MonoGame má entita v zásadě pozici, texturu a ohraničující obdélník.

Zde je základní architektura entity používaná v našich hrách:

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

public class Sprite
{
    public Vector2 Position;
    public Texture2D Texture;
    public Color Tint = Color.White;
    public bool IsActive = true;

    // The raw Axis-Aligned Bounding Box (AABB)
    public virtual Rectangle Bounds => new Rectangle(
        (int)Position.X,
        (int)Position.Y,
        Texture != null ? Texture.Width : 0,
        Texture != null ? Texture.Height : 0
    );

    public virtual void Draw(SpriteBatch spriteBatch)
    {
        if (!IsActive || Texture == null) return;
        spriteBatch.Draw(Texture, Position, Tint);
    }
}

Nyní vytvoříme konkrétní třídy Player, Enemy a Bullet:

public class Bullet : Sprite
{
    public Vector2 Velocity;
    public int Damage = 25;

    public void Update(GameTime gameTime)
    {
        Position += Velocity * (float)gameTime.ElapsedGameTime.TotalSeconds;
    }
}

public class Enemy : Sprite
{
    public int Health = 100;

    public void TakeDamage(int damage)
    {
        Health -= damage;
        if (Health <= 0)
        {
            IsActive = false;
        }
    }
}

public class Player : Sprite
{
    public int Health = 100;
    public bool IsInvulnerable = false;

    public void TakeDamage(int damage)
    {
        if (IsInvulnerable) return;
        Health -= damage;
    }
}

S těmito entitami na místě pojďme prozkoumat, jak detekovat kolize mezi nimi, počínaje nejjednodušší metodou.


2. Úroveň 1: Nejjednodušší kolize – Rectangle.Intersects (AABB)

Nejzákladnější 2D kontrolou kolize v MonoGame je test Axis-Aligned Bounding Box (AABB). Výraz "zarovnán podle osy" jednoduše znamená, že okraje obdélníku jsou zcela rovnoběžné s osami \(X\) a \(Y\) obrazovky (bez rotace).

MonoGame poskytuje rychlou, vestavěnou metodu: Rectangle.Intersects(Rectangle value).

Jak funguje Rectangle.Intersects pod pokličkou

Pod povrchem MonoGame provádí čtyři celočíselná srovnání:

public bool Intersects(Rectangle value)
{
    return value.Left < this.Right &&
           this.Left < value.Right &&
           value.Top < this.Bottom &&
           this.Top < value.Bottom;
}

Pokud jsou splněny všechny čtyři podmínky, obdélníky se překrývají. Pokud selže byť jen jedna podmínka, oddělí je prázdná osa a kolize není možná.

Kód skutečné hry: Bullet vs. Enemy v Game1.Update

Zde je návod, jak zkontrolovat kolize mezi seznamem aktivních kuliček a seznamem aktivních nepřátel ve vaší hlavní aktualizační smyčce MonoGame:

public class Game1 : Game
{
    private List<Bullet> _bullets = new List<Bullet>();
    private List<Enemy> _enemies = new List<Enemy>();

    protected override void Update(GameTime gameTime)
    {
        // 1. Update bullet and enemy positions
        foreach (var bullet in _bullets) bullet.Update(gameTime);

        // 2. Collision Check: Bullets vs Enemies
        for (int b = _bullets.Count - 1; b >= 0; b--)
        {
            var bullet = _bullets[b];
            if (!bullet.IsActive) continue;

            for (int e = _enemies.Count - 1; e >= 0; e--)
            {
                var enemy = _enemies[e];
                if (!enemy.IsActive) continue;

                // The AABB check!
                if (bullet.Bounds.Intersects(enemy.Bounds))
                {
                    // Collision occurred!
                    enemy.TakeDamage(bullet.Damage);
                    bullet.IsActive = false;

                    // Remove inactive bullet immediately
                    _bullets.RemoveAt(b);
                    
                    if (!enemy.IsActive)
                    {
                        _enemies.RemoveAt(e);
                    }

                    // A bullet can only hit one enemy; break the inner loop
                    break;
                }
            }
        }

        base.Update(gameTime);
    }
}

Tip pro výkon: Všimněte si, že iterujeme zpět (for (int i = seznam. Počet - 1; i >= 0; i--))! Pokud použijete foreach a pokusíte se zavolat _bullets.Remove(bullet), C# vyvolá InvalidOperationException: Kolekce byla upravena. Iterace zpět umožňuje bezpečné odstranění prvku bez problémů s reindexací paměti.

Tajemství pasáže: „Spravedlivé hitboxy“ prostřednictvím „Nafouknout“.

V retro hrách, jako je Blocked: Pixel Panzer, textury sprite často obsahují průhledné okraje nebo anténní hroty. Pokud hráčův tank exploduje, protože se kulka dotkla prázdného průhledného rohu jeho textury, bude se hráč cítit podveden.

Aby byly kolize citlivé a spravedlivé, hry používají menší Hitbox uvnitř spritu pomocí Rectangle.Inflate:

public class EnemyTank : Enemy
{
    // Shrink the bounding box by 6 pixels on all sides for fair collision
    public override Rectangle Bounds
    {
        get
        {
            Rectangle raw = base.Bounds;
            raw.Inflate(-6, -6); // Reduces width and height by 12px
            return raw;
        }
    }
}

3. Úroveň 2: Kolize mezi kruhy (rotační imunita)

Obdélníky fungují skvěle pro statické bloky a dlaždice mřížky, ale selhávají, když se skřítci otáčejí. Když se nečtvercová vesmírná loď otáčí v Paint Trek, osově zarovnaný ohraničující rámeček se musí roztáhnout, aby obklopil rotující rohy, což má za následek frustrující „přízračné srážky“ naprázdno.

Pro kruhové asteroidy, naváděcí energetické koule a rotující kosmické lodě jsou Bounding Circles ideálním řešením.

Past na odmocninu

Dva kruhy se srazí, když je vzdálenost mezi jejich středy menší nebo rovna součtu jejich poloměrů:

\(\text{Vzdálenost}(C_A, C_B) \le r_A + r_B\)

V kódu se při výpočtu euklidovské vzdálenosti používá Math.Sqrt (nebo Vector2.Distance). Počítání odmocnin ve smyčce s 200 projektily však spálí stovky zbytečných cyklů CPU!

Porovnáním druhé vzdálenosti s součtem druhých mocnin úplně eliminujeme druhou odmocninu:

\(\text{DistanceSquared} \le (r_A + r_B)^2\)

Implementace MonoGame: Kruh vs. Kruh

public struct Circle
{
    public Vector2 Center;
    public float Radius;

    public Circle(Vector2 center, float radius)
    {
        Center = center;
        Radius = radius;
    }

    public bool Intersects(Circle other)
    {
        float radiusSum = this.Radius + other.Radius;
        
        // MonoGame built-in Vector2.DistanceSquared
        return Vector2.DistanceSquared(this.Center, other.Center) <= (radiusSum * radiusSum);
    }
}

Nyní to integrujte přímo do entity:

public class PaintTrekFighter : Sprite
{
    public float CollisionRadius = 18f;
    public Vector2 Center => Position + new Vector2(Texture.Width * 0.5f, Texture.Height * 0.5f);

    public Circle BoundingCircle => new Circle(Center, CollisionRadius);

    public bool CollidesWith(PaintTrekFighter other)
    {
        return this.BoundingCircle.Intersects(other.BoundingCircle);
    }
}

Nulové odmocniny, imunní vůči rotaci skřítků a bleskově rychlé.


4. Úroveň 3: Smíšené tvary – Kruh vs. Box (MathHelper.Clamp)

Co se stane, když kruhová vesmírná loď ve hře Paint Trek proplouvá úzkým bludištěm obdélníkových obranných bariér, nebo když kulatá kulka zasáhne čtvercový blok ve hře Blocked: Pixel Panzer?

Potřebujeme kolize Kruh vs. Obdélník.

Algoritmus upínání

Strategií je najít bod na obdélníku, který je nejblíže středu kruhu, a poté otestovat, zda je vzdálenost od tohoto nejbližšího bodu ke středu menší než poloměr kruhu.

MathHelper.Clamp od MonoGame dělá toto triviální:

public static class Collision2D
{
    public static bool CircleIntersectsRectangle(Circle circle, Rectangle rect)
    {
        // Find the closest point on the rectangle to the circle center
        float closestX = MathHelper.Clamp(circle.Center.X, rect.Left, rect.Right);
        float closestY = MathHelper.Clamp(circle.Center.Y, rect.Top, rect.Bottom);

        // Vector from closest point to circle center
        float distanceX = circle.Center.X - closestX;
        float distanceY = circle.Center.Y - closestY;

        // Check squared distance against squared radius
        float distanceSquared = (distanceX * distanceX) + (distanceY * distanceY);
        return distanceSquared <= (circle.Radius * circle.Radius);
    }
}

Nyní můžete testovat štíty hráčů proti obdélníkovým kostkám s nulovým přidělením a vysokou přesností!


5. Úroveň 4: Continuous Collision Detection (CCD) a rozmítané paprsky

Už jste někdy ve své hře vypálili odstřelovač s vysokou rychlostí nebo railgun laser, jen abyste sledovali, jak kulka magicky prolétne přímou tenkou nepřátelskou lodí, aniž by způsobila poškození?

Tato chyba je známá jako tunelování.

Protože se jednotlivé hry aktualizují v časových krocích (\(\Delta t = 16,6\text{ms}\) při 60 FPS), objekt pohybující se rychlostí 1800 pixelů za sekundu urazí 30 pixelů v jednom snímku. Pokud je nepřátelský trup tlustý pouze 15 pixelů, byla kulka na snímku 1 před nepřítelem a na snímku 2 zcela za nepřítelem.

Frame 1:  [ Bullet ]  --->       | Enemy Wall |
Frame 2:                          | Enemy Wall |       --->  [ Bullet ]
                               (NO HIT DETECTED!)

Řešení: Swept Segment vs. Box (Slab Method)

Namísto testování jednoho bodu testujeme celý segment čáry spojující pozici odrážky na snímku 1 (předchozí pozice) a snímku 2 (aktuální pozice).

Zde je produkční metoda raycasting slab-intersection z našeho doprovodného titulu SpiralWar:

public static class ContinuousCollision
{
    public static bool IntersectsSweptRay(Vector2 rayStart, Vector2 rayEnd, Rectangle box, out Vector2 hitPoint)
    {
        hitPoint = Vector2.Zero;

        Vector2 direction = rayEnd - rayStart;
        float tMin = 0f;
        float tMax = 1f;

        // Clip against X slabs
        if (MathF.Abs(direction.X) > 1e-6f)
        {
            float invX = 1f / direction.X;
            float t1 = (box.Left - rayStart.X) * invX;
            float t2 = (box.Right - rayStart.X) * invX;
            if (t1 > t2) (t1, t2) = (t2, t1);

            tMin = MathF.Max(tMin, t1);
            tMax = MathF.Min(tMax, t2);
            if (tMin > tMax) return false;
        }
        else if (rayStart.X < box.Left || rayStart.X > box.Right)
        {
            return false;
        }

        // Clip against Y slabs
        if (MathF.Abs(direction.Y) > 1e-6f)
        {
            float invY = 1f / direction.Y;
            float t1 = (box.Top - rayStart.Y) * invY;
            float t2 = (box.Bottom - rayStart.Y) * invY;
            if (t1 > t2) (t1, t2) = (t2, t1);

            tMin = MathF.Max(tMin, t1);
            tMax = MathF.Min(tMax, t2);
            if (tMin > tMax) return false;
        }
        else if (rayStart.Y < box.Top || rayStart.Y > box.Bottom)
        {
            return false;
        }

        hitPoint = rayStart + direction * tMin;
        return true;
    }
}

Ve hře Blocked: Pixel Panzer využívá nepřetržitá schopnost hráče Laser Beam přesně tento raycast k prořezávání řad sestupných bloků, aniž by vynechala jedinou kolizi.


6. Úroveň 5: Dokonalá detekce kolize v produkční kvalitě

Nyní dosahujeme nejvyšší úrovně 2D přesnosti: Pixel-Perfect Collision.

V retro střílečce tanků nebo souboji vesmírné lodi jsou nepravidelné tvary (hlavně tanku, křídla, kokpity kokpitu) obklopeny průhlednými pixely v textuře skřítka. Když nepřátelská střela zasáhne tento průhledný prostor, hráči si toho okamžitě všimnou.

Dokonalá kolize pixelů kontroluje skutečné alfa kanály (průhlednost) překrývajících se textur. Pokud se dva neprůhledné pixely překrývají na stejné světové souřadnici, došlo ke skutečnému fyzickému zásahu.

Závažná chyba: GetData uvnitř Update()

Mnoho tutoriálů instruuje začátečníky, aby to udělali:

// DO NOT DO THIS!
Color[] dataA = new Color[textureA.Width * textureA.Height];
textureA.GetData(dataA); // STALLS GPU, CREATES MASSIVE GC LAG!

Volání Texture2D.GetData() během běhu nutí CPU čekat, až se potrubí GPU vyprázdní, čímž se texturová paměť stáhne zpět přes sběrnici. Když to uděláte pro desítky skřítků, každý snímek natankuje vaši hru z 60 FPS na 5 FPS a okamžitě spustí Android ANR!

Řešení testované bitvou od Blokováno: Pixel Panzer

V Blocked: Pixel Panzer náš Sprite.cs implementuje ultra optimalizovanou rutinu IntersectsPixel postavenou na 5 zásadních pravidlech:

  1. Static Color Array Cache: GetData se volá přesně jednou, když se textura načte, a uloží se do Dictionary<Texture2D, Color[]>.
  2. AABB hlídka předčasných východů: Pokud je Bounds.Intersects(other.Bounds) nepravdivé, okamžitě se zachraňujeme. Před kontrolou jednoho pixelu je eliminováno 99 % kontrol.
  3. Atlas textur a podpora SourceRectangle: Zvládá sprajty zabalené do texturových atlasů pomocí odsazení zdrojových obdélníků.
  4. Okno vypočteného překrytí: Smyčkujeme pouze přes přesný obdélník průsečíku mezi oběma sprity (Math.Max(a.Top, b.Top) atd.).
  5. Alpha Short-Circuiting: Pokud je pixel Sprite A průhledný (A <= 20), Sprite B je zcela přeskočen.
  6. Property Hoisting: Přístup k virtuálním vlastnostem (Bounds) ve vnořených cyklech vytváří tisíce kopií struktur. Před vstupem do smyčky je ukládáme do lokálních proměnných zásobníku.

Zde je úplný kód připravený k výrobě:

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

public class AdvancedSprite : Sprite
{
    private static readonly Dictionary<Texture2D, Color[]> _textureDataCache = new();

    public Rectangle? SourceRectangle { get; set; }

    public static Color[] GetCachedTextureData(Texture2D texture)
    {
        if (!_textureDataCache.TryGetValue(texture, out var data))
        {
            data = new Color[texture.Width * texture.Height];
            texture.GetData(data);
            _textureDataCache[texture] = data;
        }
        return data;
    }

    public bool IntersectsPixel(AdvancedSprite other)
    {
        // 1. Hoist bounds into local stack variables
        Rectangle a = this.Bounds;
        Rectangle b = other.Bounds;

        // 2. Fast AABB Broad-Phase check
        if (!a.Intersects(b)) return false;

        // Fallback to rectangle check if textures are missing
        if (this.Texture == null || other.Texture == null) return true;

        // 3. Retrieve pre-cached color arrays (Zero GPU latency)
        Color[] dataA = GetCachedTextureData(this.Texture);
        Color[] dataB = GetCachedTextureData(other.Texture);

        int aTexW = this.Texture.Width;
        int bTexW = other.Texture.Width;

        // Atlas / SourceRectangle offset mapping
        bool aHasSrc = this.SourceRectangle.HasValue;
        Rectangle aSrc = aHasSrc ? this.SourceRectangle.Value : Rectangle.Empty;
        int aOriginX = aHasSrc ? aSrc.X : 0;
        int aOriginY = aHasSrc ? aSrc.Y : 0;
        int aSpanX   = aHasSrc ? aSrc.Width  : this.Texture.Width;
        int aSpanY   = aHasSrc ? aSrc.Height : this.Texture.Height;

        bool bHasSrc = other.SourceRectangle.HasValue;
        Rectangle bSrc = bHasSrc ? other.SourceRectangle.Value : Rectangle.Empty;
        int bOriginX = bHasSrc ? bSrc.X : 0;
        int bOriginY = bHasSrc ? bSrc.Y : 0;
        int bSpanX   = bHasSrc ? bSrc.Width  : other.Texture.Width;
        int bSpanY   = bHasSrc ? bSrc.Height : other.Texture.Height;

        // 4. Calculate the overlapping sub-rectangle
        int top    = Math.Max(a.Top, b.Top);
        int bottom = Math.Min(a.Bottom, b.Bottom);
        int left   = Math.Max(a.Left, b.Left);
        int right  = Math.Min(a.Right, b.Right);

        // 5. Scan only overlapping pixels
        for (int y = top; y < bottom; y++)
        {
            // Row offsets hoisted outside the inner X loop!
            int colorA_Y = aOriginY + (int)((y - a.Y) / (float)a.Height * aSpanY);
            int colorB_Y = bOriginY + (int)((y - b.Y) / (float)b.Height * bSpanY);
            int rowA = colorA_Y * aTexW;
            int rowB = colorB_Y * bTexW;

            for (int x = left; x < right; x++)
            {
                int colorA_X = aOriginX + (int)((x - a.X) / (float)a.Width * aSpanX);

                // Early Bailout: If Pixel A is transparent, skip B entirely!
                if (dataA[colorA_X + rowA].A <= 20) continue;

                int colorB_X = bOriginX + (int)((x - b.X) / (float)b.Width * bSpanX);

                // If Pixel B is also opaque, we have confirmed contact!
                if (dataB[colorB_X + rowB].A > 20)
                {
                    return true;
                }
            }
        }

        return false;
    }
}

Tato rutina se na moderním hardwaru a stolních systémech Android spustí za méně než 0,1 milisekundy.


7. Úroveň 6: Široká fáze prostorové sítě a mobilní GC s nulovou alokací

Představte si, že vaše hra má 100 bloků, 60 kulek a 20 nepřátel. Pokud otestujete každou entitu proti každé jiné entitě pomocí vnořených smyček „for“, provedete:

\(\frac{180 \times 179}{2} = 16 110 \text{ kontrol na snímek!}\)

Při 60 FPS to je téměř 1 000 000 kontrol kolize každou sekundu.

Chcete-li škálovat svou hru, musíte implementovat Rozdělení prostorových partií na široké fáze.

Uniform Spatial Hash Grid

Obrazovku rozdělíme na 2D mřížku jednotných buněk (v Blocked: Pixel Panzer je každá buňka \(150 \krát 150\) pixelů). Entita pouze testuje kolize proti jiným entitám, které se nacházejí ve stejných buňkách mřížky.

Problém s mobilní pamětí: GC Thrashing

Pokud váš SpatialGrid vytváří nový seznam<bod>() nebo nový seznam<blok>() každý snímek, přidělujete každou minutu na spravované hromadě megabajty odpadu.

V běhovém prostředí Android Mono to spouští časté shromažďování odpadu Gen-0, což způsobuje: mono runtime: Nativní boj o zámek (mono_class_is_subclass_)

Hra se zadrhává a Google Play označí vaši hru varováním ANR!

Řešení: Opakovaně použitelné vyrovnávací paměti proti poškrábání

Zde je Zero-Allocation Spatial Hash Grid z CollisionManager.cs v Blocked: Pixel Panzer:

public class SpatialGridManager
{
    private const int CELL_SIZE = 150;
    private readonly Dictionary<Point, List<Sprite>> _grid = new();

    // Zero-GC: Reusable scratch buffers allocated ONCE at startup
    private readonly List<Point> _scratchCells = new();
    private readonly HashSet<Sprite> _scratchCheckedEntities = new();

    public void BuildGrid(List<Sprite> entities)
    {
        // Clear lists without re-allocating new List objects
        foreach (var list in _grid.Values)
        {
            list.Clear();
        }

        foreach (var entity in entities)
        {
            if (!entity.IsActive) continue;

            GetOccupiedCells(entity.Bounds, _scratchCells);
            foreach (var cell in _scratchCells)
            {
                if (!_grid.TryGetValue(cell, out var list))
                {
                    list = new List<Sprite>();
                    _grid[cell] = list;
                }
                list.Add(entity);
            }
        }
    }

    private void GetOccupiedCells(Rectangle bounds, List<Point> outCells)
    {
        outCells.Clear();

        int startX = bounds.Left / CELL_SIZE;
        int startY = bounds.Top / CELL_SIZE;
        int endX = bounds.Right / CELL_SIZE;
        int endY = bounds.Bottom / CELL_SIZE;

        for (int x = startX; x <= endX; x++)
        {
            for (int y = startY; y <= endY; y++)
            {
                outCells.Add(new Point(x, y));
            }
        }
    }

    public void CheckCollisions(Player player)
    {
        if (player.IsInvulnerable) return;

        GetOccupiedCells(player.Bounds, _scratchCells);
        _scratchCheckedEntities.Clear();

        foreach (var cell in _scratchCells)
        {
            if (_grid.TryGetValue(cell, out var entitiesInCell))
            {
                foreach (var other in entitiesInCell)
                {
                    if (other == player || _scratchCheckedEntities.Contains(other)) continue;
                    _scratchCheckedEntities.Add(other);

                    // 1. Broad-phase AABB test
                    if (player.Bounds.Intersects(other.Bounds))
                    {
                        // 2. High-precision Pixel-Perfect test
                        if (player is AdvancedSprite advPlayer && other is AdvancedSprite advOther)
                        {
                            if (advPlayer.IntersectsPixel(advOther))
                            {
                                player.TakeDamage(10);
                            }
                        }
                    }
                }
            }
        }
    }
}

Opětovným použitím _scratchCells a _scratchCheckedEntities se celý krok kolize spustí s 0 bajty alokace haldy na snímek.


8. Souhrnná srovnávací tabulka

Technika Matematické náklady Rotační podpora Bezpečné tunelování? Nejlepší případ použití v MonoGame
AABB (Rectangle.Intersects) Ultra-rychlý (~4 celočíselné srovnání) Ne Ne Bloky mřížky, širokofázové střely, uživatelské rozhraní
Kruh ("Vzdálenost na druhou") Velmi rychle (3 muls, 0 čtverečních) Ano (neměnný) Ne Kulaté lodě, ohnivé koule, energetické koule
Kruh vs Box ("Svorka") Rychlý (Local MathHelper.Clamp) Ano Ne Kruhový hráč procházející těsnými stěnami
Swept Ray (Slab CCD) Střední (Parametric Raycast) Ano Ano Rychlé odstřelovací kulky, railguny, lasery
Pixel-Perfect (IntersectsPixel) Selektivní (omezený pododdíl) Ano Ne Nepravidelné obrysy skřítků, spravedlivé hitboxy
Spatial Grid (Zero-GC) Široká fáze (\(O(N^2) \to O(N)\)) N/A N/A Husté vlny, kulová pekla, Android 60 FPS

Ukázky skutečné světové produkce: Arar Games

Tyto kolizní architektury nejsou teoretické experimenty – jsou skutečným inženýrským základem, který pohání naše komerčně vydané tituly:

  • Blokováno: Pixel Panzer: Naše retro arkádová hra pro přežití tanků na Google Play a Microsoft Store. Obsahuje náš kompletní dvouvrstvý kolizní systém: prostorovou hashovací mřížku s nulovou alokací filtrující padající bloky a dokonalou kontrolu kontaktu pro tanky, stíhačky, střely do věží a elementární munici ColorWheel.
  • Paint Trek: Naše rychlá vesmírná střílečka s rotačními kruhovými kolizemi, nepřetržitým raycastingem a systémy protiraketové obrany s velkým objemem.

Závěr a další kroky

MonoGame vám dává možnost navrhnout detekci kolizí, která dokonale odpovídá potřebám vaší hry. Zařazením drahých Pixel-Perfect kontrol do rychlých AABB testů, využitím Squared Distances pro kruhy a odstraněním shromažďování odpadků pomocí Reusable Scratch Buffers, můžete poskytovat plynulý výkon 60/120 FPS na stolních i mobilních platformách.

Podívejte se na naše hry v obchodech s aplikacemi, abyste viděli tyto kolizní systémy v akci, a začněte implementovat tyto vzory ve svých vlastních projektech MonoGame ještě dnes!


Odkazy na obchod a zdroje


SEO klíčová slova a hashtagy

Klíčová slova: Detekce kolizí MonoGame 2D, vývoj her v C#, Rectangle.Intersects MonoGame, pixel perfektní kolize C#, prostorová hashovací mřížka MonoGame, nepřetržitá kolize swept ray, optimalizace výkonu indie hry, herní smyčka s nulovou alokací, optimalizace pro Android MonoGame, hry Arar, Blocked Pixel Panzer, Paint Trek.

#MonoGame #CSharp #GameDev #IndieDev #GamePhysics #DotNet #2DGameDev #MobileGameDev #PerformanceOptimization #BleanCodeTixP#Čistý kód Tix #ArarGames #Programování her




Hiç yorum yok:

Yorum Gönder