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žijeteforeacha 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:
- Static Color Array Cache:
GetDatase volá přesně jednou, když se textura načte, a uloží se doDictionary<Texture2D, Color[]>. - 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. - Atlas textur a podpora
SourceRectangle: Zvládá sprajty zabalené do texturových atlasů pomocí odsazení zdrojových obdélníků. - 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.). - Alpha Short-Circuiting: Pokud je pixel Sprite A průhledný (
A <= 20), Sprite B je zcela přeskočen. - 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
- Blokováno: Pixel Panzer na Google Play: Stáhnout pro Android
- Blokováno: Pixel Panzer na Microsoft Store: Stáhnout pro Windows
- MonoGame Framework: monogame.net
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