Utambuzi # wa Mgongano wa 2D katika Mchezo wa Mono: Kutoka Mistatili ya Msingi hadi Mifumo Kamili ya Pixel-Sifuri
Unapounda michezo ya P2 katika injini zinazoonekana kama vile Unity au Godot, utambuzi wa mgongano mara nyingi huhisi kama mfululizo wa visanduku vya kuteua vya wakaguzi: unapiga BoxCollider2D au CircleCollider2D, ambatisha Rigidbody, na unatumai kuwa hatua ya fizikia ya ndani haitakwama kwenye vifaa vya mkononi.
Katika MonoGame na C#, hata hivyo, uko katika udhibiti kamili. Hakuna sehemu ya juu ya fizikia iliyofichwa, hakuna hali ya mzunguko isiyohitajika, na hakuna miiba ya ajabu ya ukusanyaji wa takataka (GC) inayoiba bajeti yako ya fremu.
Kwenye Michezo ya Arar, tulipojenga Iliyozuiwa: Pixel Panzer na Paint Trek, misururu ya michezo yetu ya ukumbini ilihitaji kuchakata mamia ya risasi za adui za kasi, gridi za matofali zinazolipuka, turrets za tank zinazozunguka, flybys za ndege ya kivita, na ngao ndogo kwenye Windows 60 hadi 120 kwenye vifaa vya Android na Kompyuta. Injini ya fizikia ya madhumuni ya jumla ilikuwa nje ya swali - tulihitaji usanifu wa mgongano uliojengwa kwa kusudi, wa ngazi.
Katika mwongozo huu wa kina, unaoendeshwa na msimbo, tutaanza kutoka kwa misingi kamili ya mgongano wa MonoGame (Mstatili.Intersects na Bullet na Enemy sprites halisi) na tujenge hadi ukaguzi wa hali ya juu wa mduara, kubana kwa mchanganyiko, miale ya kuzuia tunnel, kiwango cha uzalishaji PixelPati, Gridi ya JuuPixel*, Gridi ya Juu Zellication Zellication** Zellication* Zellication* Zellication* Zellication. imeboreshwa kwa matumizi ya GC ya rununu.
1. The Foundation: Rahisi MonoGame Sprite Hierarkia
Kabla ya kugundua migongano, tunahitaji huluki safi za mchezo. Katika MonoGame, huluki huwa na nafasi, muundo na mstatili unaofunga.
Huu hapa ni usanifu wa msingi wa huluki unaotumiwa katika michezo yetu yote:
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);
}
}
Sasa hebu tuunde madarasa madhubuti ya Mchezaji, Adui, na 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;
}
}
Huluki hizi zikiwa mahali, hebu tuchunguze jinsi ya kugundua migongano kati yao, kuanzia njia rahisi zaidi.
2. Kiwango cha 1: Mgongano Rahisi Zaidi - Rectangle.Intersects (AABB)
Hundi ya msingi zaidi ya 2D ya mgongano katika MonoGame ni jaribio la Kisanduku cha Kufunga Kilichopangiwa Mhimili (AABB). Neno "iliyopangwa kwa mhimili" linamaanisha tu kwamba kingo za mstatili zinalingana kabisa na shoka za \(X\) na \(Y\) za skrini (hakuna mzunguko).
MonoGame hutoa mbinu ya haraka, iliyojengewa ndani: Rectangle.Intersects(Thamani ya Mstatili).
Jinsi Mstatili.Inaingiliana Inafanya kazi Chini ya Kofia
Chini ya uso, MonoGame hufanya ulinganisho kamili nne:
public bool Intersects(Rectangle value)
{
return value.Left < this.Right &&
this.Left < value.Right &&
value.Top < this.Bottom &&
this.Top < value.Bottom;
}
Ikiwa masharti yote manne yametimizwa, mistatili inaingiliana. Ikiwa hata hali moja itashindwa, mhimili tupu huwatenganisha, na hakuna mgongano unaowezekana.
Msimbo Halisi wa Uchezaji: Bullet dhidi ya Adui katika Mchezo1. Sasisha
Hivi ndivyo unavyoangalia migongano kati ya orodha ya risasi zinazoendelea na orodha ya maadui wanaofanya kazi ndani ya kitanzi chako kikuu cha MonoGame Sasisha:
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);
}
}
Kidokezo cha Utendaji: Tambua tunarudia kurudi nyuma (
kwa (int i = list.Hesabu - 1; i >= 0; i--))! Ukitumiaforeachna kujaribu kupiga_bullets.Ondoa(bullet), C# itatupaInvalidOperationException: Mkusanyiko ulirekebishwa. Kurudia kurudi nyuma huruhusu uondoaji wa kipengele salama bila matatizo ya kuorodhesha upya kumbukumbu.
Siri ya Arcade: "Fair Hitboxes" kupitia Inflate
Katika michezo ya nyuma kama vile Imezuiwa: Pixel Panzer, maumbo ya sprite mara nyingi hujumuisha ukingo wa uwazi au miisho ya antena. Tangi la mchezaji likilipuka kwa sababu risasi iligusa kona tupu yenye uwazi ya umbile lake, mchezaji atahisi ametapeliwa.
Ili kufanya mgongano kuhisi msikivu na sawa, michezo hutumia Hitbox ndogo ndani ya mkondo kwa kutumia 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. Kiwango cha 2: Mgongano wa Mviringo-kwa-Mzunguko (Kinga ya Mzunguko)
Mistatili hufanya kazi vizuri kwa vizuizi tuli na vigae vya gridi, lakini hushindwa wakati sprites zinapozunguka. Chombo cha anga kisicho na mraba kinapozungushwa katika Paint Trek, kisanduku cha kufunga kilichopangiliwa mhimili lazima kipanue ili kuziba pembe zinazozunguka, na kusababisha "migongano ya phantom" yenye kukatisha tamaa katika hewa tupu.
Kwa asteroidi za duara, orbs za nishati homing, na vyombo vya anga vinavyozunguka, Miduara ya Kufungana ndio suluhisho bora.
The Square Root Trap
Miduara miwili inagongana wakati umbali kati ya vituo vyao ni chini ya au sawa na jumla ya radii zao:
\(\text{Umbali}(C_A, C_B) \le r_A + r_B\)
Katika msimbo, kukokotoa umbali wa Euclidean hutumia Math.Sqrt (au Vector2.Distance). Walakini, kuhesabu mizizi ya mraba kwenye kitanzi na projectile 200 huchoma mamia ya mizunguko ya CPU isiyo ya lazima!
Kwa kulinganisha umbali wa mraba dhidi ya jumla ya kipenyo cha mraba, tunaondoa kabisa mzizi wa mraba:
\(\text{DistanceSquared} \le (r_A + r_B)^2\)
Utekelezaji wa Mchezo Mmoja: Mduara dhidi ya Mduara
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);
}
}
Sasa unganisha hii moja kwa moja kwenye chombo:
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);
}
}
Mizizi ya mraba sifuri, kinga dhidi ya mzunguko wa sprite, na haraka sana.
4. Kiwango cha 3: Maumbo Mchanganyiko – Mduara dhidi ya Sanduku (MathHelper.Clamp)
Nini hutokea wakati chombo cha anga za juu katika Paint Trek kinapopitia msongamano mkali wa vizuizi vya ulinzi vya mstatili, au risasi ya duara inapogonga kizuizi cha mraba katika Iliyozuiwa: Pixel Panzer?
Tunahitaji mgongano wa Mduara dhidi ya Mstatili.
Kanuni ya Kubana
Mkakati ni kutafuta uhakika kwenye mstatili ulio karibu zaidi na kituo cha duara, na kisha kupima kama umbali kutoka sehemu hiyo ya karibu hadi katikati ni chini ya kipenyo cha duara.
MathHelper.Clamp ya MonoGame inafanya jambo hili kuwa dogo:
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);
}
}
Sasa unaweza kujaribu ngao za wachezaji dhidi ya matofali ya mstatili na mgao sufuri na usahihi wa juu!
5. Kiwango cha 4: Utambuzi wa Mgongano Unaoendelea (CCD) na Miale Iliyofagiwa
Je, umewahi kurusha risasi ya risasi yenye kasi kubwa au leza ya reli kwenye mchezo wako, na kutazama tu risasi ikipita moja kwa moja kwenye meli nyembamba ya adui bila kushughulikia uharibifu?
Mdudu huyu anajulikana kama kuelekeza tunnel.
Kwa sababu michezo mahususi husasishwa kwa hatua za wakati (\(\Delta t = 16.6\text{ms}\) kwa ramprogrammen 60), kitu kinachotembea kwa pikseli 1,800 kwa sekunde husafiri pikseli 30 katika fremu moja. Ikiwa sehemu ya adui ina unene wa saizi 15 tu, risasi ilikuwa mbele ya adui kwenye Fremu 1, na nyuma kabisa ya adui kwenye Fremu 2.
Frame 1: [ Bullet ] ---> | Enemy Wall |
Frame 2: | Enemy Wall | ---> [ Bullet ]
(NO HIT DETECTED!)
Suluhisho: Sehemu Iliyofagiliwa dhidi ya Sanduku (Njia ya Slab)
Badala ya kujaribu nukta moja, tunajaribu sehemu nzima ya laini inayounganisha nafasi ya kitone kwenye Fremu 1 (PreviousPosition) hadi Fremu 2 (currentPosition).
Hapa kuna njia ya makutano ya slab ya uzalishaji kutoka kwa kichwa chetu 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;
}
}
Katika Iliyozuiwa: Pixel Panzer, ustadi unaoendelea wa mchezaji wa Laser Beam hutumia mionzi hii haswa kugawanya safu za vizuizi vya kushuka bila kukosa mgongano hata mmoja.
6. Kiwango cha 5: Utambuzi wa Mgongano wa Kiwango cha Uzalishaji wa Pixel-Perfect
Sasa tunafikia kiwango cha mwisho cha usahihi wa 2D: Pixel-Perfect Collision.
Katika mpiga risasiji wa tanki la retro au mapambano ya mbwa wa anga, maumbo yasiyo ya kawaida (mapipa ya tanki, mbawa, vyumba vya marubani) yamezungukwa na saizi za uwazi katika muundo wa sprite. Wakati kombora la adui linapiga nafasi hiyo ya uwazi, wachezaji wanaona mara moja.
Mgongano wa Pixel-kamilifu hukagua njia halisi za alfa (uwazi) za maumbo yanayopishana. Ikiwa pikseli mbili zisizo na uwazi zinapishana katika kuratibu sawa za ulimwengu, mgongano wa kweli umetokea.
Kosa Kuu: GetData ndani ya Sasisha()
Mafunzo mengi huwaagiza wanaoanza kufanya hivi:
// DO NOT DO THIS!
Color[] dataA = new Color[textureA.Width * textureA.Height];
textureA.GetData(dataA); // STALLS GPU, CREATES MASSIVE GC LAG!
Kupiga simu kwa Texture2D.GetData() wakati wa utekelezaji hulazimisha CPU kusubiri bomba la GPU kusogea, hivyo kurudisha kumbukumbu ya maandishi juu ya basi. Kufanya hivi kwa michezo mingi kila fremu kutaboresha mchezo wako kutoka ramprogrammen 60 hadi ramprogrammen 5 na kuanzisha ANR za Android mara moja!
Suluhisho Iliyojaribiwa kwa Vita kutoka kwa Imezuiwa: Pixel Panzer
Katika Imezuiwa: Pixel Panzer, Sprite.cs yetu inatekeleza utaratibu wa IntersectsPixel ulioboreshwa zaidi uliojengwa kwa takriban sheria 5 muhimu:
- Akiba ya Mkusanyiko wa Rangi Iliyotulia:
GetDatainaitwa mara moja haswa wakati unamu unapakia, na kuhifadhiwa katikaKamusi<Texture2D, Rangi[]>. - AAABB Walinzi wa Kuondoka Mapema: Ikiwa
Mipaka.Inavuka(Mipaka.Mingine.Mipaka)si kweli, tunatoa dhamana mara moja. 99% ya hundi huondolewa kabla ya kukagua pikseli moja. - Atlasi ya Muundo & Usaidizi wa
Mstatili wa Chanzo: Hushughulikia sprites zilizopakiwa kwenye atlasi za unamu kwa kutumia viasili vya chanzo cha mstatili. - Dirisha Lililokokotolewa la Kuingiliana: Tunazunguka tu juu ya mstatili kamili wa makutano kati ya sprite zote mbili (
Math.Max(a.Juu, b.Juu), n.k.). - Mzunguko Mfupi wa Alpha: Ikiwa pikseli ya Sprite A ni ya uwazi (
A <= 20), Sprite B itarukwa kabisa. - Kupandisha Mali: Kufikia sifa pepe (
Mipaka) katika vitanzi vilivyowekwa kwenye kiota huunda maelfu ya nakala za muundo. Tunazihifadhi katika vigezo vya stack za ndani kabla ya kuingia kwenye kitanzi.
Hapa kuna nambari kamili, tayari kwa uzalishaji:
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;
}
}
Ratiba hii hutekelezwa kwa chini ya milisekunde 0.1 kwenye maunzi ya kisasa ya Android na mifumo ya kompyuta ya mezani.
7. Kiwango cha 6: Gridi ya anga ya Awamu pana na GC ya Simu ya Mkononi ya Mgao sifuri
Fikiria mchezo wako una vitalu 100, risasi 60 na maadui 20. Ukijaribu kila huluki dhidi ya kila huluki nyingine kwa kutumia vitanzi vya for vilivyowekwa, unatekeleza:
\(\frac{180 \mara 179}{2} = 16,110 \text{ hundi kwa kila fremu!}\)
Kwa ramprogrammen 60, hiyo ni takriban hundi 1,000,000 za mgongano kila sekunde.
Ili kuongeza mchezo wako, lazima utekeleze Ugawaji wa Nafasi wa Awamu pana.
Gridi ya Sare ya Spatial Hash
Tunagawanya skrini katika gridi ya 2D ya seli zinazofanana (katika Zilizozuiwa: Pixel Panzer, kila seli ni \(150 \mara 150\) pikseli). Huluki hujaribu tu migongano dhidi ya huluki zingine ambazo hukaa katika seli sawa za gridi.
Tatizo la Kumbukumbu ya Simu: GC Thrashing
Iwapo SpatialGrid yako itaunda Orodha mpya<Point>() au Orodha mpya<Block>() kila fremu, utatenga megabaiti za taka kila dakika kwenye lundo linalodhibitiwa.
Kwenye wakati wa utekelezaji wa Android wa Mono, hii husababisha Mkusanyiko wa Taka mara kwa mara wa Gen-0, na kusababisha:
wakati wa utekelezaji wa mono: Mzozo wa kufuli asili (mono_class_is_subclass_)
Mchezo unadumaa na Google Play inaalamisha mchezo wako kwa maonyo ya ANR!
Suluhisho: Vibafa vya Kukwaruza Vinavyoweza Kutumika tena
Hii hapa ni Zero-Allocation Spatial Hash Gridi kutoka kwa CollisionManager.cs katika Imezuiwa: 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);
}
}
}
}
}
}
}
}
Kwa kutumia tena _scratchCells na _scratchCheckedEntities, hatua nzima ya mgongano inaendeshwa na baiti 0 za mgao wa lundo kwa kila fremu.
8. Jedwali la Kulinganisha Muhtasari
| Mbinu | Gharama ya Hisabati | Usaidizi wa Mzunguko | Je, unatuna salama? | Kesi ya Matumizi Bora katika MonoGame |
|---|---|---|---|---|
AABB (Rectangle.Intersects) |
Haraka Zaidi (~ ulinganisho kamili 4) | Hapana | Hapana | Vizuizi vya gridi, hatua pana, UI |
Mduara (UmbaliMraba) |
Haraka Sana ( mul 3, sqrt 0) | Ndiyo (Invariant) | Hapana | Meli za pande zote, mipira ya moto, orbs za nishati |
Mduara dhidi ya Sanduku (Bana) |
Haraka (Local MathHelper.Clamp) | Ndiyo | Hapana | Mchezaji wa mduara anaabiri kuta zilizobana |
| Mfagio Ray (CCD) | Wastani (Parametric Raycast) | Ndiyo | Ndiyo | Risasi za sniper za haraka, bunduki za reli, leza |
Pixel-Perfect (IntersectsPixel) |
Chaguo (Njia Ndogo Iliyodhibitiwa) | Ndiyo | Hapana | Mtaro wa sprite usio wa kawaida, visanduku vya kugonga vyema |
| Gridi ya anga (Zero-GC) | Awamu pana (\(O(N^2) \to O(N)\)) | N/A | N/A | Mawimbi mazito, risasi za moto, Android 60 FPS |
Maonyesho ya Uzalishaji Halisi ya Ulimwenguni: Michezo ya Arar
Usanifu huu wa migongano si majaribio ya kinadharia—ndio msingi halisi wa uhandisi unaoendesha mada zetu zinazotolewa kibiashara:
- Imezuiwa: Pixel Panzer: Mchezo wetu wa ukumbini wa maisha ya tanki la retro kwenye Google Play na Microsoft Store. Inaangazia mfumo wetu kamili wa mgongano wa viwango viwili: gridi ya anga ya sifuri ya kuchuja vizuizi vinavyoanguka, na ukaguzi wa mgusano wa pixel-kamilifu wa mizinga, ndege za kivita, makombora ya turret na risasi za msingi za ColorWheel.
- Paint Trek: Kifyatulia risasi chetu cha kasi cha juu kinachoangazia mgongano wa mzunguko wa mzunguko, urushaji ray mfululizo na mifumo ya ulinzi ya makombora yenye sauti ya kufagia.
Hitimisho & Hatua Zinazofuata
MonoGame hukupa uwezo wa kubuni ugunduzi wa mgongano unaolingana kikamilifu na mahitaji ya mchezo wako. Kwa kuweka ukaguzi wa gharama kubwa wa Pixel-Perfect baada ya majaribio ya haraka ya AABB, kwa kutumia Umbali Mraba kwa miduara, na kuondoa mkusanyiko wa taka kwa Vipunguzo vya Kukwarua Vinavyoweza Kutumika, unaweza kutoa utendakazi wa ramprogrammen laini 60/120 kwenye kompyuta za mezani na mifumo ya simu.
Angalia michezo yetu kwenye maduka ya programu ili kuona mifumo hii ya mgongano inavyofanya kazi, na uanze kutekeleza ruwaza hizi katika miradi yako ya MonoGame leo!
Viungo na Rasilimali za Duka
- Imezuiwa: Pixel Panzer kwenye Google Play: Pakua kwa ajili ya Android
- Imezuiwa: Pixel Panzer kwenye Microsoft Store: Pakua kwa Windows
- Mfumo wa MonoGame: monogame.net
SEO Keywords & Hashtag
Maneno Muhimu: Utambuzi wa mgongano wa MonoGame 2D, ukuzaji wa mchezo wa C#, Mstatili.Intersects MonoGame, pikseli mgongano kamili C#, gridi ya anga ya hashi MonoGame, mgongano wa mfululizo wa ray, uboreshaji wa utendaji wa mchezo wa indie, kitanzi cha mchezo wa kutenga sifuri, Uboreshaji wa Michezo ya Treni ya Android, Uboreshaji wa Android MonoGame, Uboreshaji wa ArnoGame Panze.
#MonoGame #CSharp #GameDev #IndieDev #GamePhysics #DotNet #2DGameDev #MobileGameDev #PerformanceOptimization #CleanCode #BlockedPixelPanzer #PaintTreGame#GameProgram
Hiç yorum yok:
Yorum Gönder