2D kokkupõrgete tuvastamine MonoGame'is: tavalistest ristkülikutest kuni nulljaotusega pikslite täiusliku süsteemini
Kui ehitate 2D-mänge visuaalsetes mootorites, nagu Unity või Godot, tundub kokkupõrke tuvastamine sageli nagu kontrollijate märkeruutude seeria: koputate "BoxCollider2D" või "CircleCollider2D" külge, kinnitate "Rigidbody" ja loodate, et sisemine füüsika samm mobiilseadmetes ei sega.
Mängides MonoGame ja C# on teil aga täielik kontroll. Peal pole varjatud füüsikat, soovimatut pöörlemisinertsust ega salapäraseid prügikogumise (GC) naelu, mis rööviksid teie kaadri eelarvet.
Arar Games, kui ehitasime Blocked: Pixel Panzer ja Paint Trek, vajasid meie arkaadmängusilmused sadade kiirete vaenlase kuulide, plahvatavate tellisvõrkude, pöörlevate tankitornide, hävituslennukite möödalende ja osakeste kaitset PC-arvutites ja Android-seadmetes kiirusega 60 kuni 60 kaadrit sekundis. Üldotstarbeline füüsikamootor ei tulnud kõne allagi – vajasime eesmärgipäraselt ehitatud mitmetasandilist põrkearhitektuuri.
Selles põhjalikus koodipõhises juhendis alustame MonoGame'i kokkupõrke absoluutsetest põhitõdedest ("Rectangle. Intersects" tõeliste "Bullet" ja "Enemy" spraitidega) ning jätkame täiustatud ringikontrollide, segatud kinnituste, tunnelitevastaste kiirsaadete, tootmistasemel Pixel-PerfectationZero-Perfectation** ja Zollation-collicationiga. optimeeritud mobiilse GC ellujäämiseks.
1. Sihtasutus: lihtne monomängu "Sprite" hierarhia
Enne kokkupõrgete tuvastamist vajame puhtaid mänguolemeid. MonoGame'is on olemil põhimõtteliselt positsioon, tekstuur ja piirav ristkülik.
Siin on meie mängudes kasutatav põhiolemi arhitektuur:
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);
}
}
Nüüd loome konkreetsed klassid "Mängija", "Vaenlane" ja "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;
}
}
Kui need olemid on paigas, uurime, kuidas tuvastada nendevahelisi kokkupõrkeid, alustades kõige lihtsamast meetodist.
2. Tase 1: Lihtsaim kokkupõrge – "Ristkülik. Lõikused" (AABB)
MonoGame'i kõige põhilisem 2D-kokkupõrgete kontroll on Axis-Aligned Bounding Box (AABB) test. Mõiste "teljega joondatud" tähendab lihtsalt seda, et ristküliku servad on täiesti paralleelsed ekraani \(X\) ja \(Y\) telgedega (ei pööra pöörlemist).
MonoGame pakub kiiret sisseehitatud meetodit: "Rectangle.Intersects(Rectangle value)".
Kuidas Rectangle.Intersects kapoti all töötab
Pinna all teostab MonoGame nelja täisarvu võrdlust:
public bool Intersects(Rectangle value)
{
return value.Left < this.Right &&
this.Left < value.Right &&
value.Top < this.Bottom &&
this.Top < value.Bottom;
}
Kui kõik neli tingimust on täidetud, kattuvad ristkülikud. Kui kasvõi üks tingimus ebaõnnestub, eraldab need tühi telg ja kokkupõrge pole võimalik.
Päris mängukood: "Bullet" vs. "Enemy" mängus "Game1.Update"
Siit saate teada, kuidas saate kontrollida kokkupõrkeid aktiivsete täppide loendi ja aktiivsete vaenlaste loendi vahel oma MonoGame'i peamises "Update" tsüklis:
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);
}
}
Toimivuse nõuanne: Pange tähele, et me itereerime tagurpidi (
for (int i = list.Count - 1; i >= 0; i--))! Kui kasutate käsku "foreach" ja proovite kutsuda käsku "_bullets.Remove(bullet)", annab C# teate "InvalidOperationException: Collection was modified". Tagurpidi itereerimine võimaldab elementide ohutut eemaldamist ilma mälu uuesti indekseerimise probleemideta.
Arkaadi saladus: "Fair Hitboxes" funktsiooni "Inflate" kaudu
Retromängudes, nagu Blokeeritud: Pixel Panzer, sisaldavad spraitide tekstuurid sageli läbipaistvaid veerisi või antenni naelu. Kui mängija paak plahvatab, kuna kuul puudutas selle tekstuuri tühja läbipaistvat nurka, tunneb mängija end petetuna.
Et kokkupõrge tunduks tundlik ja õiglane, kasutavad mängud spraiti sees väiksemat Hitboxi, kasutades funktsiooni „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. Tase 2: Ring-ringi kokkupõrge (pöörlemiskindlus)
Ristkülikud sobivad suurepäraselt staatiliste plokkide ja ruudustikuplaatide jaoks, kuid need ebaõnnestuvad spraitide pöörlemisel. Kui mitteruudukujuline kosmoselaev Paint Trekis pöörleb, peab teljega joondatud piirdekast laienema, et ümbritseda pöörlevad nurgad, mille tulemuseks on masendavad "fantoomkokkupõrked" tühjas õhus.
Ringikujuliste asteroidide, suunduvate energiakerade ja pöörlevate kosmoselaevade jaoks on Bounding Circles ideaalne lahendus.
Ruutjuure lõks
Kaks ringi põrkuvad, kui nende keskpunktide vaheline kaugus on väiksem või võrdne nende raadiuste summaga:
\(\text{Distance}(C_A, C_B) \le r_A + r_B\)
Koodis kasutab Eukleidilise kauguse arvutamine parameetrit "Math.Sqrt" (või "Vector2.Distance"). Ruutjuurte arvutamine 200 mürsuga silmuses põletab aga sadu mittevajalikke protsessoritsükleid!
Võrreldes ruutkaugust raadiuse ruudu summaga, välistame ruutjuure täielikult:
\(\text{DistanceSquared} \le (r_A + r_B)^2\)
MonoGame'i rakendamine: "Ring" vs. "Circle".
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);
}
}
Nüüd integreerige see otse olemisse:
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);
}
}
Null ruutjuurt, immuunne spraitide pöörlemisele ja välkkiire.
4. Tase 3: segatud kujundid – ring vs. kast ("MathHelper.Clamp")
Mis juhtub, kui Paint Treki ringikujuline kosmoselaev navigeerib läbi tiheda ristkülikukujuliste kaitsetõkete rägastiku või kui ümmargune kuul tabab ruudukujulist plokki Blokeeritud: Pixel Panzeris?
Vajame Ring vs ristküliku kokkupõrget.
Kinnitusalgoritm
Strateegia on leida ristkülikul punkt, mis on ringi keskpunktile kõige lähemal, ja seejärel katsetada, kas kaugus lähimast punktist keskpunktini on väiksem kui ringi raadius.
MonoGame'i "MathHelper.Clamp" muudab selle triviaalseks:
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);
}
}
Nüüd saate testida mängijate kilpe ristkülikukujuliste telliste vastu nulleraldiste ja suure täpsusega!
5. Tase 4: pidev kokkupõrke tuvastamine (CCD) ja pühkimiskiired
Kas olete kunagi oma mängus tulistanud ülikiire snaipripauku või rööbasrelvade laserit, et jälgida, kuidas kuul maagiliselt läbi õhukese vaenlase laeva ilma kahju tekitamata otse läbi lööb?
Seda viga tuntakse kui tunneldamist.
Kuna diskreetseid mänge värskendatakse ajasammude kaupa (\(\Delta t = 16,6\text{ms}\) kiirusega 60 kaadrit sekundis), liigub objekt, mis liigub kiirusega 1800 pikslit sekundis, 30 pikslit ühes kaadris. Kui vaenlase kere paksus on vaid 15 pikslit, oli kuul 1. kaadris vaenlase ees ja 2. kaadris täiesti vaenlase taga.
Frame 1: [ Bullet ] ---> | Enemy Wall |
Frame 2: | Enemy Wall | ---> [ Bullet ]
(NO HIT DETECTED!)
Lahendus: pühitud segment vs. kast (plaatmeetod)
Ühe punkti testimise asemel testime kogu joonelõiku, mis ühendab kuuli asukoha kaadris 1 ("eelmine positsioon") kaadriga 2 ("praegune positsioon").
Siin on tootmise kiirvalamise plaadi-ristumismeetod meie kaaspealkirjast 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;
}
}
Mängus Blokeeritud: Pixel Panzer kasutab mängija pidev laserkiire oskus seda täpset kiirsaadet, et lõigata läbi laskuvate plokkide rida, ilma et ükski kokkupõrge vahele jääks.
6. Tase 5: tootmisklassi pikslite täiuslik kokkupõrketuvastus
Nüüd saavutame 2D täpsuse ülima taseme: Pixel-Perfect Collision.
Retro-tankitulistaja või kosmoselaeva koeravõitluses ümbritsevad ebakorrapäraseid kujundeid (tanki tünnid, tiivad, kokpiti kabiinid) sprite-tekstuuris läbipaistvad pikslid. Kui vaenlase rakett tabab seda läbipaistvat ruumi, märkavad mängijad seda kohe.
Pikslite täiuslik kokkupõrge kontrollib kattuvate tekstuuride tegelikke alfa- (läbipaistvus) kanaleid. Kui kaks läbipaistmatut pikslit kattuvad samal maailmakoordinaadil, on toimunud tõeline füüsiline tabamus.
Saatuslik viga: 'GetData' 'Update()' sees
Paljud õpetused juhendavad algajaid seda tegema:
// DO NOT DO THIS!
Color[] dataA = new Color[textureA.Width * textureA.Height];
textureA.GetData(dataA); // STALLS GPU, CREATES MASSIVE GC LAG!
Funktsiooni „Texture2D.GetData()” kutsumine käitusajal sunnib CPU-d ootama, kuni GPU konveier tühjeneb, tõmmates tekstuurimälu tagasi üle siini. Kui teete seda kümnete spraitide jaoks iga kaader, suurendab teie mäng 60 kaadrit sekundis kuni 5 kaadrit sekundis ja käivitab kohe Androidi ANR-id!
Lahingus testitud lahendus Blocked: Pixel Panzerilt
Funktsioonis Blokeeritud: Pixel Panzer rakendab meie Sprite.cs ülioptimiseeritud rutiini IntersectsPixel, mis põhineb 5 olulisel reeglil:
- Staatilise värvimassiivi vahemälu: 'GetData' kutsutakse tekstuuri laadimisel täpselt üks kord ja see salvestatakse sõnaraamatusse <Texture2D, Color[]>.
- AABB Early Exit Guard: kui 'Bounds.Intersects(other.Bounds)' on vale, päästame kohe välja. 99% kontrollimistest kõrvaldatakse enne ühe piksli kontrollimist.
- Tektuuriatlase ja 'SourceRectangle'i tugi: käsitleb tekstuuriatlastesse pakitud spraite, kasutades lähteristküliku nihkeid.
- Arvutatud kattumise aken: teeme tsükli ainult mõlema spraidi vahelise täpse ristküliku ristküliku kohal (Math.Max(a.Top, b.Top) jne).
- Alfa lühis: kui Sprite A piksel on läbipaistev (A <= 20), jäetakse Sprite B täielikult vahele.
- Property Hoisting: pesastatud tsüklites virtuaalsetele atribuutidele ("Bounds") juurdepääs loob tuhandeid struktuurikoopiaid. Enne tsüklisse sisenemist salvestame need kohalikesse pinumuutujatesse.
Siin on täielik tootmisvalmis kood:
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;
}
}
See rutiin käivitub tänapäevastes Androidi riistvaras ja töölauasüsteemides vähem kui 0,1 millisekundiga.
7. Tase 6: laiafaasiline ruumivõrk ja nulljaotusega mobiilne GC
Kujutage ette, et teie mängus on 100 plokki, 60 kuuli ja 20 vaenlast. Kui testite iga olemit kõigi teiste olemite suhtes, kasutades pesastatud for-silmuseid, toimite järgmiselt.
\(\frac{180 \times 179}{2} = 16 110 \text{ tšekki kaadri kohta!}\)
60 kaadrit sekundis on see peaaegu 1 000 000 kokkupõrkekontrolli igas sekundis.
Mängu skaleerimiseks peate rakendama laiafaasilise ruumijaotuse.
Ühtne ruumiline räsivõrk
Jagame ekraani ühtlaste lahtrite 2D-ruudustikuks (Blocked: Pixel Panzer puhul on iga lahter 150 \(\ korda 150\) pikslit). Olem testib kokkupõrkeid ainult teiste samades ruudustiku lahtrites asuvate üksustega.
Mobiilse mälu probleem: GC Thrashing
Kui teie "SpatialGrid" loob iga kaadri "uue loendi
Androidi Mono käitusajal käivitab see sagedased Gen-0 prügikogumised, põhjustades:
mono käitusaeg: natiivse lukustuse vaidlus (mono_class_is_subclass_)
Mäng kogeleb ja Google Play märgib teie mängu ANR-i hoiatustega!
Lahendus: korduvkasutatavad kriimustuspuhvrid
Siin on Nulljaotuse ruumiline räsiruudustik saidilt `CollisionManager.cs, jaotises Blokeeritud: 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);
}
}
}
}
}
}
}
}
Kui kasutate ühikuid "_scratchCells" ja "_scratchCheckedEntities", taaskasutatakse kogu kokkupõrkeetapp 0 baiti hunniku jaotusega kaadri kohta.
8. Kokkuvõtlik võrdlustabel
| Tehnika | Matemaatiline kulu | Rotatsioonitugi | Kas tunneldamine on ohutu? | Parim kasutusjuht MonoGame'is |
|---|---|---|---|---|
| AABB ("Ristkülik. Lõikub") | Ülikiire (~4 täisarvu võrdlust) | Ei | Ei | Võreplokid, laiafaasiline täpp, kasutajaliides |
Ring (DistanceSquared) |
Väga kiire (3 mulsi, 0 ruutmeetrit) | Jah (muutmatu) | Ei | Ümmargused laevad, tulekerad, energiakerad |
| Ring vs kast ("klamber") | Kiire (Local MathHelper.Clamp) | Jah | Ei | Ringikujuline mängija, mis liigub kitsastes seintes |
| Swept Ray (Slab CCD) | Mõõdukas (parameetriline kiirsaade) | Jah | Jah | Kiired snaiprikuulid, rööbasrelvad, laserid |
| Pixel-Perfect ("IntersectsPixel") | Valikuline (piiratud alamõigus) | Jah | Ei | Ebakorrapärased sprite kontuurid, õiglased hitboxid |
| Ruumivõrk (Zero-GC) | Laifaas (\(O(N^2) \ kuni O(N)\)) | N/A | N/A | Tihedad lained, kuulipõrgud, Android 60 FPS |
Reaalse maailma toodangu esitlused: Arari mängud
Need kokkupõrkearhitektuurid ei ole teoreetilised eksperimendid – need on tõeline insenerifond, mis toidab meie kaubanduslikult välja antud pealkirju:
- Blokeeritud: Pixel Panzer: Meie retro tanki ellujäämisarkaadmäng Google Plays ja Microsoft Store'is. Sellel on meie täielik kahetasandiline kokkupõrkesüsteem: nulljaotusega ruumiline räsivõre, mis filtreerib langevaid plokke, ja pikslite täiuslik kontaktikontroll tankide, hävitajate, tornikeste ja ColorWheeli elementaarlaskemoona jaoks.
- Paint Trek: Meie kiire tempoga kosmoselaskemäng, mis sisaldab pöörlemisringi kokkupõrget, pidevat kiirteedastust ja suure mahuga raketitõrjesüsteeme.
Järeldus ja järgmised sammud
MonoGame annab teile võimaluse kujundada kokkupõrketuvastus, mis sobib ideaalselt teie mängu vajadustega. Kaasates kallite Pixel-Perfect kontrollide kiirete AABB testide taga, kasutades ringide jaoks ruudu vahemaad ja kõrvaldades prügi kogumise korduvkasutatavate kriimustuspuhvrite abil, saate pakkuda konsooli sujuvat 60/120 kaadrit sekundis nii lauaarvutitel kui ka mobiilplatvormidel.
Vaadake meie mänge rakenduste poodides, et näha neid kokkupõrkesüsteeme töös, ja alustage nende mustrite rakendamist oma MonoGame'i projektides juba täna!
Poe lingid ja ressursid
- Blokeeritud: Pixel Panzer teenuses Google Play: Laadi alla Androidile
- Blokeeritud: Pixel Panzer Microsoft Store'is: Laadi alla Windowsile
- MonoGame Framework: monogame.net
SEO märksõnad ja räsimärgid
Märksõnad: MonoGame 2D kokkupõrgete tuvastamine, C# mänguarendus, ristkülik. Lõikab MonoGame'i, pikslite täiuslik kokkupõrge C#, ruumiline räsivõrk MonoGame, pidev kokkupõrge, indie-mängu jõudluse optimeerimine, nulljaotuse mängusilmus, Android Arlock B, optimeerimine, Panzer B, optimeerimine, mängumäng. Trek.
#MonoGame #CSharp #GameDev #IndieDev#Mängufüüsika#DotNet #2DGameDev #MobileGameDev #PerformanceOptimization #CleanCode #PanintTreixel ``BlockedP #ArarGames #Mänguprogrammeerimine`
Hiç yorum yok:
Yorum Gönder