27 Eylül 2026 Pazar

2D detekcia kolízie v hre MonoGame: od základných obdĺžnikov po systémy s nulovým prideľovaním pixelov




2D detekcia kolízie v MonoGame: Od základných obdĺžnikov po systémy s nulovým prideľovaním pixelov

Pri vytváraní 2D hier vo vizuálnych nástrojoch, ako sú Unity alebo Godot, sa detekcia kolízií často javí ako séria kontrolných začiarkavacích políčok: nahodíte „BoxCollider2D“ alebo „CircleCollider2D“, pripojíte „Rigidbody“ a dúfate, že interný fyzikálny krok sa na mobilných zariadeniach nezasekne.

V MonoGame a C# však máte úplnú kontrolu. Neexistuje žiadna skrytá fyzikálna réžia, žiadna nechcená rotačná zotrvačnosť a žiadne záhadné špičky pri zbere odpadu (GC), ktoré kradnú váš rozpočet rámu.

V Arar Games, keď sme postavili Blocked: Pixel Panzer a Paint Trek, naše arkádové herné slučky potrebovali na spracovanie stoviek vysokorýchlostných nepriateľských striel, explodujúcich tehlových mreží, rotujúcich veží tankov, preletov stíhačiek a štítov proti časticiam pri 60 až 120 FPS na počítačoch so systémom Windows aj na zariadeniach so systémom Android. Univerzálny fyzikálny engine neprichádzal do úvahy – potrebovali sme účelovú, stupňovitú kolíznu architektúru.

V tomto komplexnom, kódom riadenom sprievodcovi začneme od úplných základov kolízie MonoGame (Rectangle.Intersects so skutočnými Gullet a Enemy spritami) a vybudujeme si pokročilé kruhové kontroly, zmiešané upínanie, anti-tunelovacie raycasty, produkčnú triedu Pixel-CPerfect Gridtial optimalizované pre mobilné kolízie**, prežitie.


1. Základ: Jednoduchá hierarchia „Sprite“ MonoGame

Pred detekciou kolízií potrebujeme čisté herné entity. V MonoGame má entita v podstate pozíciu, textúru a ohraničujúci obdĺžnik.

Tu je základná architektúra 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);
    }
}

Teraz vytvorme konkrétne triedy Hráč, Nepriateľ a Guľka:

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 mieste poďme preskúmať, ako zistiť kolízie medzi nimi, počnúc najjednoduchšou metódou.


2. Úroveň 1: Najjednoduchšia kolízia – „Obdĺžnik.Priesečník“ (AABB)

Najzákladnejšou 2D kontrolou kolízie v MonoGame je test Axis-Aligned Bounding Box (AABB). Pojem "zarovnaný s osou" jednoducho znamená, že okraje obdĺžnika sú úplne rovnobežné s osami \(X\) a \(Y\) obrazovky (bez otáčania).

MonoGame poskytuje rýchlu, vstavanú metódu: Rectangle.Intersects (hodnota obdĺžnika).

Ako funguje „Obdĺžnik. Pretína“ pod kapotou

Pod povrchom MonoGame vykonáva štyri celočíselné porovnania:

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

Ak sú splnené všetky štyri podmienky, obdĺžniky sa prekrývajú. Ak zlyhá čo i len jedna podmienka, oddeľuje ich prázdna os a nie je možná kolízia.

Kód skutočnej hry: Gullet vs. Enemy v Game1.Update

Takto skontrolujete kolízie medzi zoznamom aktívnych guliek a zoznamom aktívnych nepriateľov vo vašej hlavnej aktualizačnej slučke 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 na zvýšenie výkonu: Všimnite si, že opakujeme dozadu (for (int i = zoznam. Počet - 1; i >= 0; i--))! Ak použijete foreach a pokúsite sa zavolať _bullets.Remove(bullet), C# vyvolá InvalidOperationException: Zbierka bola upravená. Iterácia dozadu umožňuje bezpečné odstránenie prvku bez problémov s opätovným indexovaním pamäte.

Tajomstvo Arkády: „Fair Hitboxes“ cez „Nafúknuť“.

V retro hrách, ako je Blocked: Pixel Panzer, textúry sprite často obsahujú priehľadné okraje alebo hroty antény. Ak hráčova nádrž exploduje, pretože sa guľka dotkla prázdneho priehľadného rohu jeho textúry, hráč sa bude cítiť podvedený.

Aby boli kolízie citlivé a spravodlivé, hry používajú menší Hitbox vo vnútri sprite pomocou 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: Kolízia medzi kruhmi (rotačná imunita)

Obdĺžniky fungujú skvele pre statické bloky a mriežkové dlaždice, ale zlyhávajú, keď sa škriatkovia otáčajú. Keď sa neštvorcová vesmírna loď otáča v programe Paint Trek, osovo zarovnaný ohraničujúci rámček sa musí roztiahnuť, aby uzavrel rotujúce rohy, čo má za následok frustrujúce „fantómové kolízie“ na prázdnom vzduchu.

Pre kruhové asteroidy, navádzacie energetické gule a rotujúce kozmické lode sú Bounding Circles ideálnym riešením.

Pasca na odmocniny

Dva kruhy sa zrazia, keď je vzdialenosť medzi ich stredmi menšia alebo rovná súčtu ich polomerov:

\(\text{Vzdialenosť}(C_A, C_B) \le r_A + r_B\)

V kóde sa na výpočet euklidovskej vzdialenosti používa Math.Sqrt (alebo Vector2.Distance). Výpočet odmocniny v slučke s 200 projektilmi však spáli stovky zbytočných cyklov CPU!

Porovnaním druhej vzdialenosti so súčtom druhej mocniny úplne eliminujeme druhú odmocninu:

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

Implementácia 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);
    }
}

Teraz to integrujte priamo 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, odolné voči rotácii sprite a bleskurýchle.


4. Úroveň 3: Zmiešané tvary – Kruh vs. Box (MathHelper.Clamp)

Čo sa stane, keď sa kruhová vesmírna loď v hre Paint Trek plaví cez tesné bludisko pravouhlých obranných bariér, alebo keď guľatá guľka zasiahne štvorcový blok v hre Blocked: Pixel Panzer?

Potrebujeme kolíziu kruh vs. obdĺžnik.

Algoritmus upínania

Stratégiou je nájsť bod na obdĺžniku, ktorý je najbližšie k stredu kruhu, a potom otestovať, či je vzdialenosť od tohto najbližšieho bodu k stredu menšia ako polomer kruhu.

MathHelper.Clamp od MonoGame robí toto triviálne:

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

Teraz môžete testovať štíty hráčov proti obdĺžnikovým tehlám s nulovým umiestnením a vysokou presnosťou!


5. Úroveň 4: Continuous Collision Detection (CCD) a Swept Rays

Vystrelili ste už niekedy vo svojej hre z hyperrýchlostného ostreľovača alebo železničného lasera, len aby ste sledovali, ako guľka magicky prejde cez tenkú nepriateľskú loď bez spôsobenia poškodenia?

Táto chyba je známa ako tunelovanie.

Keďže diskrétne hry sa aktualizujú v časových krokoch (\(\Delta t = 16,6\text{ms}\) pri 60 FPS), objekt pohybujúci sa rýchlosťou 1 800 pixelov za sekundu prejde 30 pixelov v jednej snímke. Ak je trup nepriateľa hrubý iba 15 pixelov, guľka bola na snímke 1 pred nepriateľom a na snímke 2 úplne za nepriateľom.

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

Riešenie: Zametaný segment vs. škatuľa (metóda dosky)

Namiesto testovania jedného bodu testujeme celý segment čiary spájajúcej polohu guľky na snímke 1 („predchádzajúca pozícia“) a snímku 2 („aktuálna pozícia“).

Tu je produkčná metóda raycasting slab-intersection z nášho sprievodné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;
    }
}

V hre Blocked: Pixel Panzer používa nepretržitá zručnosť laserového lúča hráča na prerezanie radov klesajúcich blokov bez toho, aby unikla jediná kolízia.


6. Úroveň 5: Pixel-Perfect Collision Detection produkčnej kvality

Teraz dosahujeme najvyššiu úroveň presnosti 2D: Perfect Collision.

V retro strieľačke na tanky alebo v súboji vesmírnej lode sú nepravidelné tvary (hlavne tanku, krídla, kokpity kokpitu) obklopené priehľadnými pixelmi v textúre sprite. Keď nepriateľská strela zasiahne tento priehľadný priestor, hráči si to okamžite všimnú.

Dokonalá kolízia pixelov kontroluje skutočné alfa kanály (priehľadnosť) prekrývajúcich sa textúr. Ak sa dva nepriehľadné pixely prekrývajú na rovnakej svetovej súradnici, došlo k skutočnému fyzickému zásahu.

Závažná chyba: GetData v Update()

Mnoho tutoriálov inštruuje začiatočníkov, aby to urobili:

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

Volanie Texture2D.GetData() počas behu núti CPU čakať, kým sa kanál GPU vyprázdni, čím sa textúrová pamäť stiahne späť cez zbernicu. Ak to urobíte pri desiatkach spritov, každý snímok natankuje vašu hru zo 60 FPS na 5 FPS a okamžite spustí Android ANR!

Bojovo testované riešenie od Blocked: Pixel Panzer

V Blocked: Pixel Panzer náš Sprite.cs implementuje ultra optimalizovanú rutinu IntersectsPixel postavenú na 5 kľúčových pravidlách:

  1. Static Color Array Cache: GetData sa volá presne raz pri načítaní textúry a ukladá sa do Dictionary<Texture2D, Color[]>.
  2. AABB Stráž pri predčasnom odchode: Ak je Bounds.Intersects(other.Bounds) nepravdivé, okamžite sa zachránime. 99 % kontrol je odstránených pred kontrolou jedného pixelu.
  3. Texture Atlas & SourceRectangle Support: Zvláda sprity zabalené do textúrových atlasov pomocou odsadenia zdrojového obdĺžnika.
  4. Vypočítané okno prekrytia: Slučujeme iba cez presný priesečník medzi oboma spritami (Math.Max(a.Top, b.Top), atď.).
  5. Alpha Short-Circuiting: Ak je pixel Sprite A priehľadný (A <= 20), Sprite B sa úplne preskočí.
  6. Zdvíhanie vlastností: Prístup k virtuálnym vlastnostiam („Hranice“) vo vnorených slučkách vytvára tisíce kópií štruktúr. Pred vstupom do cyklu ich uložíme do lokálnych premenných zásobníka.

Tu je úplný kód pripravený na výrobu:

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

Táto rutina sa vykoná za menej ako 0,1 milisekúnd na modernom hardvéri a stolných systémoch Android.


7. Úroveň 6: Široká fázová priestorová sieť a mobilné GC s nulovým prideľovaním

Predstavte si, že vaša hra má 100 blokov, 60 guliek a 20 nepriateľov. Ak otestujete každú entitu proti každej inej entite pomocou vnorených cyklov „for“, vykonáte:

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

Pri 60 FPS je to takmer 1 000 000 kontrol kolízie každú sekundu.

Ak chcete škálovať svoju hru, musíte implementovať Broad-Phase Spatial Partitioning.

Jednotná priestorová hashová mriežka

Obrazovku rozdelíme na 2D mriežku jednotných buniek (v Blocked: Pixel Panzer je každá bunka 150 \(\krát 150\) pixelov). Entita testuje kolízie iba proti iným entitám, ktoré sa nachádzajú v rovnakých bunkách mriežky.

Problém mobilnej pamäte: GC Thrashing

Ak vaša SpatialGrid vytvorí nový zoznam<Point>() alebo nový zoznam<Block>() každý snímok, každú minútu alokujete megabajty odpadu na spravovanú haldu.

V Mono runtime Androidu to spúšťa časté zbery odpadu Gen-0, čo spôsobuje: mono runtime: natívny súper o uzamknutie (mono_class_is_subclass_)

Hra sa zadrháva a Google Play označí vašu hru upozorneniami ANR!

Riešenie: Opätovne použiteľné vyrovnávacie pamäte proti poškriabaniu

Tu 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 sa celý krok kolízie spustí s 0 bajtami pridelenia haldy na snímku.


8. Súhrnná porovnávacia tabuľka

Technika Matematické náklady Rotačná podpora Bezpečné tunelovanie? Najlepšie využitie v MonoGame
AABB (Rectangle.Intersects) Ultra-rýchle (~4 celočíselné porovnania) Nie Nie Mriežkové bloky, širokofázové guľky, UI
Kruh ("DistanceSquared") Veľmi rýchly (3 muly, 0 štvorcových) Áno (Invariant) Nie Okrúhle lode, ohnivé gule, energetické gule
Kruh vs škatuľka ("Svorka") Rýchle (Local MathHelper.Clamp) áno Nie Kruhový hráč prechádzajúci tesnými stenami
Swept Ray (Slab CCD) Stredné (parametrické vysielanie) áno áno Rýchle ostreľovacie guľky, railguny, lasery
Pixel-Perfect (IntersectsPixel) Selektívny (obmedzený pododdiel) áno Nie Nepravidelné kontúry sprite, spravodlivé hitboxy
Priestorová mriežka (Zero-GC) Široká fáza (\(O(N^2) \to O(N)\)) neuvádza sa neuvádza sa Husté vlny, guľkové peklo, Android 60 FPS

Ukážky produkcie zo skutočného sveta: Arar Games

Tieto kolízne architektúry nie sú teoretické experimenty – sú skutočným inžinierskym základom, ktorý poháňa naše komerčne vydané tituly:

  • Blokované: Pixel Panzer: Naša retro arkádová hra na prežitie tankov v Google Play a Microsoft Store. Obsahuje náš kompletný dvojvrstvový kolízny systém: priestorová hash mriežka s nulovou alokáciou filtrujúca padajúce bloky a dokonalá kontrola kontaktov pre tanky, bojové lietadlá, náboje do veží a elementárnu muníciu ColorWheel.
  • Paint Trek: Naša rýchla vesmírna strieľačka obsahujúca kolíziu s rotačným kruhom, nepretržité vysielanie lúčov a systémy protiraketovej obrany s veľkým objemom.

Záver a ďalšie kroky

MonoGame vám dáva možnosť navrhnúť detekciu kolízií, ktorá dokonale zodpovedá potrebám vašej hry. Zaradením drahých Pixel-Perfect kontrol za rýchlymi AABB testami, využívaním Squared Distances pre kruhy a elimináciou zbierania odpadu pomocou Opätovne použiteľných vyrovnávacích pamätí Scratch, môžete poskytnúť plynulý výkon 60/120 FPS na stolných aj mobilných platformách.

Pozrite si naše hry v obchodoch s aplikáciami, aby ste videli tieto kolízne systémy v akcii a začnite implementovať tieto vzory vo svojich vlastných projektoch MonoGame ešte dnes!


Odkazy a zdroje obchodu


SEO kľúčové slová a hashtagy

Kľúčové slová: MonoGame 2D detekcia kolízií, C# vývoj hier, Rectangle.Intersects MonoGame, pixel Perfect kolízia C#, priestorová hash mriežka MonoGame, swept ray kontinuálna kolízia, optimalizácia výkonu indie hry, nulová alokácia hernej slučky, Android MonoGame optimalizácia, Arar Games, Blocked Pixel Panzer, Paint Trek.

#MonoGame #CSharp #GameDev #IndieDev #GamePhysics #DotNet #2DGameDev #MobileGameDev #PerformanceOptimization #CleanCodeTixP#BleanCodeTix#ArarGames #Programovanie hier`




Hiç yorum yok:

Yorum Gönder