2D sadursmju noteikšana MonoGame: no pamata taisnstūriem līdz nulles sadales pikseļu perfektām sistēmām
Veidojot 2D spēles vizuālos dzinējos, piemēram, Unity vai Godot, sadursmes noteikšana bieži vien šķiet kā inspektora izvēles rūtiņu virkne: jūs uzsitiet uz "BoxCollider2D" vai "CircleCollider2D", pievienojat "Rigidbody" un cerat, ka iekšējās fizikas darbība mobilajās ierīcēs nestostās.
Tomēr spēlēs MonoGame un C# jūs pilnībā kontrolējat. Nav slēptas fizikas virs galvas, nav nevēlamas rotācijas inerces un nav noslēpumainu atkritumu savākšanas (GC) tapa, kas zog jūsu kadru budžetu.
Arar Games, kad veidojām Blocked: Pixel Panzer un Paint Trek, mūsu arkādes spēļu cilpas bija vajadzīgas, lai apstrādātu simtiem ātrdarbīgu ienaidnieka ložu, sprāgstošu ķieģeļu režģus, rotējošus tanku torņus, iznīcinātāju aplidojumus un daļiņu vairogus ar Windows 20 FPS ierīcēm ar ātrumu 60 FPS. Par vispārējas nozīmes fizikas dzinēju nevarētu būt runa — mums bija vajadzīga mērķtiecīga, daudzpakāpju sadursmes arhitektūra.
Šajā visaptverošajā, uz kodu vadītajā rokasgrāmatā mēs sāksim no MonoGame sadursmes ("Rectangle.Intersects") absolūtajiem pamatiem ar īstiem "Bullet" un "Enemy" spraitiem un attīstīsimies līdz uzlabotām apļa pārbaudēm, jauktām iespīlēšanas, prettunelēšanas staru pārraidēm, ražošanas līmeņa Pixel-PerfectationZero-Perfectation-collokation. optimizēts mobilajām GC izdzīvošanai.
1. Pamats: vienkārša MonoGame Sprite hierarhija
Pirms sadursmju noteikšanas mums ir vajadzīgas tīras spēles vienības. MonoGame vienībai pamatā ir pozīcija, faktūra un ierobežojošs taisnstūris.
Šeit ir mūsu spēlēs izmantotā pamata entītiju arhitektūra:
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);
}
}
Tagad izveidosim konkrētas klases "Spēlētājs", "Ienaidnieks" un "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;
}
}
Kad šīs entītijas ir izveidotas, izpētīsim, kā noteikt sadursmes starp tām, sākot ar vienkāršāko metodi.
2. 1. līmenis: visvienkāršākā sadursme — taisnstūris. krustojas (AABB)
Visbūtiskākā 2D sadursmes pārbaude MonoGame ir Axis-Aligned Bounding Box (AABB) tests. Termins "ass izlīdzināts" vienkārši nozīmē, ka taisnstūra malas ir pilnībā paralēlas ekrāna \(X\) un \(Y\) asīm (bez rotācijas).
MonoGame nodrošina ātru, iebūvētu metodi: "Rectangle.Intersects(Rectangle value)".
Kā Taisnstūris.Intersects darbojas zem pārsega
Zem virsmas MonoGame veic četrus veselu skaitļu salīdzinājumus:
public bool Intersects(Rectangle value)
{
return value.Left < this.Right &&
this.Left < value.Right &&
value.Top < this.Bottom &&
this.Top < value.Bottom;
}
Ja ir izpildīti visi četri nosacījumi, taisnstūri pārklājas. Ja kaut viens nosacījums neizdodas, tukša ass tos atdala, un sadursme nav iespējama.
Īsts spēles kods: "Bullet" pret "Enemy" spēlē "Game1.Update"
Lūk, kā jūs varat pārbaudīt sadursmes starp aktīvo ložu sarakstu un aktīvo ienaidnieku sarakstu jūsu galvenajā MonoGame Update cilpā:
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);
}
}
Veiktspējas padoms. Ņemiet vērā, ka mēs atkārtojam atpakaļgaitas (
for (int i = list.Count - 1; i >= 0; i--))! Ja izmantojat "foreach" un mēģināt izsaukt "_bullets.Remove(bullet)", C# izdod "InvalidOperationException: Collection was modified". Atkārtošana atpakaļ ļauj droši noņemt elementus bez atmiņas atkārtotas indeksēšanas problēmām.
Arkādes noslēpums: "Fair Hitboxes", izmantojot 'Inflate'
Retro spēlēs, piemēram, Blocked: Pixel Panzer, sprite tekstūras bieži ietver caurspīdīgas malas vai antenas tapas. Ja spēlētāja tvertne eksplodē, jo lode pieskārās tukšam caurspīdīgam tā faktūras stūrim, spēlētājs jutīsies apkrāpts.
Lai sadursme būtu atsaucīga un godīga, spēlēs sprite iekšpusē tiek izmantots mazāks Hitbox, izmantojot opciju "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. 2. līmenis: sadursme no apļa uz apli (rotācijas imunitāte)
Taisnstūri lieliski darbojas statiskiem blokiem un režģa flīzēm, taču tie neizdodas, kad spraiti griežas. Kad Paint Trek griežas nekvadrātveida kosmosa kuģis, uz asi izlīdzinātam ierobežojošajam lodziņam ir jāpaplašina, lai aptvertu griežamos stūrus, izraisot kaitinošas "fantoma sadursmes" tukšā gaisā.
Apļveida asteroīdiem, enerģijas lodēm un rotējošiem kosmosa kuģiem Bounding Circles ir ideāls risinājums.
Kvadrātsakņu slazds
Divi apļi saduras, ja attālums starp to centriem ir mazāks vai vienāds ar to rādiusu summu:
\(\text{Distance}(C_A, C_B) \le r_A + r_B\)
Kodā, aprēķinot Eiklīda attālumu, tiek izmantots "Math.Sqrt" (vai "Vector2.Distance"). Tomēr kvadrātsakņu aprēķināšana cilpā ar 200 šāviņiem sadedzina simtiem nevajadzīgu CPU ciklu!
Salīdzinot attālumu kvadrātā ar kvadrāta rādiusa summu, mēs pilnībā izslēdzam kvadrātsakni:
\(\text{DistanceSquared} \le (r_A + r_B)^2\)
MonoGame ieviešana: "Aplis" pret "Aplis".
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);
}
}
Tagad integrējiet to tieši entītijā:
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);
}
}
Nulle kvadrātsakņu, imūna pret spraitu rotāciju un zibensātri.
4. 3. līmenis: jauktas formas — aplis pret kastīti (MathHelper.Clamp)
Kas notiek, kad apļveida kosmosa kuģis programmā Paint Trek pārvietojas šaurā taisnstūrveida aizsardzības barjeru labirintā vai kad apaļa lode trāpa kvadrātveida blokā Bloķēts: Pixel Panzer?
Mums ir vajadzīga sadursme Aplis pret taisnstūri.
Saspiešanas algoritms
Stratēģija ir atrast taisnstūra punktu, kas ir vistuvāk apļa centram, un pēc tam pārbaudīt, vai attālums no šī tuvākā punkta līdz centram ir mazāks par apļa rādiusu.
MonoGame "MathHelper.Clamp" padara šo triviālu:
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);
}
}
Tagad varat pārbaudīt spēlētāju vairogus pret taisnstūrveida ķieģeļiem ar nulles sadalījumu un augstu precizitāti!
5. 4. līmenis: nepārtraukta sadursmju noteikšana (CCD) un kustīgi stari
Vai esat kādreiz savā spēlē izšāvis ar ātrgaitas snaipera kārtu vai sliežu pistoles lāzeru, lai tikai noskatītos, kā lode maģiski izskrien cauri plānam ienaidnieka kuģim, nenodarot bojājumus?
Šī kļūda ir pazīstama kā tunelēšana.
Tā kā diskrētās spēles tiek atjauninātas laika soļos (\(\Delta t = 16,6\text{ms}\) pie 60 kadri/s), objekts, kas pārvietojas ar ātrumu 1800 pikseļi sekundē, pārvietojas 30 pikseļus vienā kadrā. Ja ienaidnieka korpuss ir tikai 15 pikseļu biezs, lode atradās ienaidnieka priekšā 1. kadrā un pilnībā aiz ienaidnieka 2. kadrā.
Frame 1: [ Bullet ] ---> | Enemy Wall |
Frame 2: | Enemy Wall | ---> [ Bullet ]
(NO HIT DETECTED!)
Risinājums: iztīrītais segments salīdzinājumā ar kārbu (plātņu metode)
Tā vietā, lai pārbaudītu vienu punktu, mēs pārbaudām visu līnijas segmentu, kas savieno aizzīmes pozīciju 1. kadrā ("iepriekšējā pozīcija") ar 2. kadru ("pašreizējā pozīcija").
Šeit ir ražošanas raycast plātņu krustošanās metode no mūsu pavadošā nosaukuma 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;
}
}
Spēlē Blocked: Pixel Panzer spēlētāja nepārtrauktā Lāzerstara prasme izmanto šo precīzo staru apraidi, lai pāršķirtu lejupejošu bloku rindas, nepalaižot garām nevienu sadursmi.
6. 5. līmenis: ražošanas pakāpes pikseļu perfekta sadursmes noteikšana
Tagad mēs sasniedzam augstāko 2D precizitātes līmeni: Pixel-Perfect Collision.
Retro tanku šāvēja vai kosmosa kuģa kaujā neregulāras formas (tanku mucas, spārni, kabīnes kabīnes) ieskauj caurspīdīgi pikseļi sprite tekstūrā. Kad ienaidnieka raķete sasniedz šo caurspīdīgo vietu, spēlētāji to nekavējoties pamana.
Pikseļu perfekta sadursme pārbauda faktiskos alfa (caurspīdīguma) kanālus, kas pārklājas. Ja divi necaurspīdīgi pikseļi pārklājas vienā pasaules koordinātā, ir noticis īsts fizisks trāpījums.
Liktenīgā kļūda: 'GetData' iekšā 'Update()'
Daudzās pamācībās iesācējiem tas ir norādīts:
// DO NOT DO THIS!
Color[] dataA = new Color[textureA.Width * textureA.Height];
textureA.GetData(dataA); // STALLS GPU, CREATES MASSIVE GC LAG!
Izsaukšana "Texture2D.GetData()" izpildlaikā liek centrālajam procesoram gaidīt, līdz GPU konveijers tiks izskalots, pārvelkot tekstūras atmiņu atpakaļ pa kopni. To darot desmitiem spraitu katrā kadrā, jūsu spēle tiks palielināta no 60 kadri/s līdz 5 kadriem/s un nekavējoties tiks aktivizēti Android ANR!
Kaujās pārbaudīts risinājums no Blocked: Pixel Panzer
Programmā Bloķēts: Pixel Panzer mūsu Sprite.cs ievieš īpaši optimizētu IntersectsPixel rutīnu, kuras pamatā ir 5 svarīgi noteikumi.
1. Statiskā krāsu masīva kešatmiņa: 'GetData' tiek izsaukta tieši vienreiz, kad tekstūra tiek ielādēta, un tiek saglabāta vārdnīcā<Texture2D, Color[]>. 2. AABB Early Exit Guard: Ja vērtība "Bounds.Intersects(other.Bounds)" ir nepatiesa, mēs nekavējoties glābjam. 99% pārbaužu tiek novērstas pirms viena pikseļa pārbaudes. 3. Tekstūras atlanta un avota taisnstūra atbalsts: apstrādā tekstūras atlantos iepakotas sprites, izmantojot avota taisnstūra nobīdes. 4. Aprēķinātais pārklāšanās logs: mēs veicam cilpu tikai pa precīzu krustojuma taisnstūri starp abiem spraitiem (Math.Max(a.Top, b.Top) utt.). 5. Alfa īsslēgums: ja Sprite A pikseļi ir caurspīdīgi (A <= 20), Sprite B tiek pilnībā izlaists. 6. Property Hoisting: Piekļūstot virtuālajiem rekvizītiem (“Robežām”) ligzdotās cilpās, tiek izveidoti tūkstošiem struktūru kopiju. Pirms ievadīšanas cilpā mēs tos uzglabājam lokālos steka mainīgajos.
Šeit ir pilns, ražošanai gatavs kods:
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;
}
}
Šī rutīna tiek izpildīta mazāk nekā 0,1 milisekundē modernās Android aparatūras un galddatoru sistēmās.
7. 6. līmenis: plašas fāzes telpiskais režģis un nulles piešķīruma mobilais GC
Iedomājieties, ka jūsu spēlē ir 100 bloki, 60 lodes un 20 ienaidnieki. Ja pārbaudāt katru entītiju pret visām citām entītijām, izmantojot ligzdotās “for” cilpas, jūs veicat:
\(\frac{180 \times 179}{2} = 16 110 \text{ čeki vienā kadrā!}\)
Pie 60 FPS, tas ir gandrīz 1 000 000 sadursmju pārbaudes katru sekundi.
Lai mērogotu savu spēli, jums ir jāievieš Plašas fāzes telpiskā sadalīšana.
Vienotais telpiskais jaucējrežģis
Mēs sadalām ekrānu vienādu šūnu 2D režģī (Blocked: Pixel Panzer katra šūna ir 150 \( reizes 150 \) pikseļi). Entītija pārbauda tikai sadursmes ar citām entītijām, kas atrodas tajās pašās režģa šūnās.
Mobilās atmiņas problēma: GC Thrashing
Ja jūsu "SpatialGrid" katrā kadrā izveido "jaunu sarakstu
Android Mono izpildlaikā tas aktivizē biežas Gen-0 atkritumu savākšanas, izraisot: "mono izpildlaiks: vietējās bloķēšanas strīds (mono_class_is_subclass_)".
Spēle stostās, un Google Play atzīmē jūsu spēli ar ANR brīdinājumiem!
Risinājums: atkārtoti lietojami skrāpējumu buferi
Šeit ir Zero-Allocation Spatial Hash Grid no `CollisionManager.cs' sadaļā Bloķēts: 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);
}
}
}
}
}
}
}
}
Atkārtoti izmantojot “_scratchCells” un “_scratchCheckedEntities”, viss sadursmes posms tiek izpildīts ar 0 baitu kaudzes piešķiršanu katrā kadrā.
8. Kopsavilkuma salīdzināšanas tabula
| Tehnika | Matemātiskās izmaksas | Rotācijas atbalsts | Vai tunelēšana ir droša? | Labākais lietošanas gadījums MonoGame |
|---|---|---|---|---|
AABB (Taisnstūris.Krustojas) |
Īpaši ātrs (~4 veselu skaitļu salīdzinājumi) | Nē | Nē | Režģa bloki, ložu platfāze, UI |
Aplis (DistanceSquared) |
Ļoti ātrs (3 muls, 0 kvadrātmetri) | Jā (nemainīgs) | Nē | Apaļi kuģi, ugunsbumbas, enerģijas lodes |
| Aplis pret kastīti ("Skava") | Ātri (Local MathHelper.Clamp) | Jā | Nē | Apļveida atskaņotājs pārvietojas šaurās sienās |
| Swept Ray (Slab CCD) | Mērens (Parametriskā Raycast) | Jā | Jā | Ātrās snaipera lodes, sliežu pistoles, lāzeri |
| Pixel-Perfect ("IntersectsPixel") | Selektīvs (ierobežots apakšrekts) | Jā | Nē | Neregulāras sprite kontūras, godīgi hitboxes |
| Spatial Grid (Zero-GC) | Platfāze (\(O(N^2) \līdz O(N)\)) | N/A | N/A | Blīvi viļņi, ložu elles, Android 60 FPS |
Reālās pasaules ražošanas vitrīnas: Arar spēles
Šīs sadursmes arhitektūras nav teorētiski eksperimenti — tās ir reāls inženiertehniskais pamats, kas nodrošina mūsu komerciāli izdoto nosaukumu darbību:
- Bloķēts: Pixel Panzer: Mūsu retro tanku izdzīvošanas arkādes spēle pakalpojumā Google Play un Microsoft Store. Tajā ir mūsu pilnā divu līmeņu sadursmes sistēma: nulles sadalījuma telpiskais jaucējrežģis, kas filtrē krītošos blokus, un pikseļu perfekta kontaktu pārbaude tankiem, iznīcinātājiem, torņu čaulām un ColorWheel elementārajai munīcijai.
- Paint Trek: Mūsu ātrdarbīgā kosmosa šāvēja ar rotācijas apļa sadursmi, nepārtrauktu staru apraidi un liela apjoma pretraķešu aizsardzības sistēmām.
Secinājums un nākamie soļi
MonoGame sniedz jums iespēju izstrādāt sadursmju noteikšanu, kas lieliski atbilst jūsu spēles vajadzībām. Veicot dārgas Pixel-Perfect pārbaudes, izmantojot ātrus AABB testus, izmantojot Kvadrātveida attālumus apļiem un novēršot atkritumu savākšanu ar Atkārtoti lietojamiem Scratch Buffers, varat nodrošināt konsolei vienmērīgu 60/120 FPS veiktspēju gan galddatoros, gan mobilajās platformās.
Apskatiet mūsu spēles lietotņu veikalos, lai redzētu šīs sadursmju sistēmas darbībā, un sāciet ieviest šos modeļus savos MonoGame projektos jau šodien!
Veikalu saites un resursi
- Bloķēts: Pixel Panzer pakalpojumā Google Play: Lejupielādēt Android ierīcēm
- Bloķēts: Pixel Panzer veikalā Microsoft Store: Lejupielādēt operētājsistēmai Windows
- MonoGame Framework: monogame.net
SEO atslēgvārdi un atsauces
Atslēgvārdi: MonoGame 2D sadursmju noteikšana, C# spēļu izstrāde, taisnstūris. Krustojās MonoGame, perfekta pikseļu sadursme C#, telpiskā jaukšanas režģis MonoGame, nepārtraukta sadursme, neatkarīga spēles veiktspējas optimizācija, nulles piešķiršanas spēles cilpa, Android Arlock B spēles optimizācija, Panzer Pix Game. Trek.
#MonoGame #CSharp #Spēļu izstrāde #ArarGames #SpēļuProgrammēšana`
Hiç yorum yok:
Yorum Gönder