27 Eylül 2026 Pazar

2Д детекција колизије у МоноГаме-у: од основних правоугаоника до савршених система пиксела са нултом алокацијом




2Д детекција колизије у МоноГаме-у: од основних правоугаоника до савршених система пиксела са нултом алокацијом

Када правите 2Д игре у визуелним машинама као што су Унити или Годот, откривање колизије често изгледа као низ контролних поља за потврду: ударите БокЦоллидер2Д или ЦирцлеЦоллидер2Д, причврстите Ригидбоди и надате се да интерни корак физике неће застој на мобилним уређајима.

У МоноГаме и Ц#, међутим, имате потпуну контролу. Нема скривених физичких трошкова, нема нежељене ротационе инерције и нема мистериозних скокова за сакупљање смећа (ГЦ) који краду ваш буџет за оквир.

У Арар Гамес, када смо направили Блоцкед: Пикел Панзер и Паинт Трек, наше петље аркадних игара биле су потребне за обраду стотина брзих непријатељских метака, експлодирајуће мреже од цигли, ротирајућих тенкова, прелетања борбених авиона и штитова од честица на Виндовс 1 и 60 ФПС-а на ПЦ уређајима. Физички мотор опште намене није долазио у обзир - била нам је потребна наменска, вишеслојна архитектура судара.

У овом свеобухватном водичу вођеном кодом, почећемо од апсолутних основа колизије МоноГаме-а („Правоугаоник. Укршта се“ са правим Метак и Непријатељ спријтовима) и надоградити до напредних провера кругова, мешовитог стезања, анти-тунелирања раицаст-а, продукционог нивоа **П-ерфецтион-а, Алло Пик Просторне мреже оптимизоване за преживљавање мобилног ГЦ-а.


1. Тхе Фоундатион: Једноставна МоноГаме Сприте хијерархија

Пре откривања судара, потребни су нам чисти ентитети игре. У МоноГаме-у, ентитет у основи поседује позицију, текстуру и гранични правоугаоник.

Ево основне архитектуре ентитета која се користи у нашим играма:

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

Сада направимо конкретне класе Плаиер, Енеми и Буллет:

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

Са овим ентитетима на месту, хајде да истражимо како да откријемо колизије између њих, почевши од најједноставније методе.


2. Ниво 1: Најједноставнији судар – Правоугаоник. Пресецања (ААББ)

Најосновнија 2Д провера колизије у МоноГаме-у је тест Акис-Алигнед Боундинг Бок (ААББ). Термин „поравнано по оси“ једноставно значи да су ивице правоугаоника потпуно паралелне са осама \(Кс\) и \(И\) екрана (без ротације).

МоноГаме пружа брзу, уграђену методу: Рецтангле.Интерсецтс(Рецтангле валуе).

Како Рецтангле.Интерсетс ради испод хаубе

Испод површине, МоноГаме извршава четири целобројна поређења:

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

Ако су испуњена сва четири услова, правоугаоници се преклапају. Ако чак и један услов не успе, празна оса их раздваја и колизија није могућа.

Прави код игре: Метак против Непријатеља у Гаме1.Упдате

Ево како проверавате колизије између листе активних метака и листе активних непријатеља унутар ваше главне МоноГаме Упдате петље:

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

Савет за перформансе: Приметите да понављамо уназад (фор (инт и = лист.Цоунт - 1; и >= 0; и--))! Ако користите фореацх и покушате да позовете _буллетс.Ремове(буллет), Ц# избацује ИнвалидОператионЕкцептион: колекција је измењена. Итерација уназад омогућава безбедно уклањање елемената без проблема са поновним индексирањем меморије.

Тхе Арцаде Сецрет: "Фаир Хитбокес" преко Инфлате

У ретро играма као што је Блоцкед: Пикел Панзер, текстуре сприте-а често укључују прозирне маргине или антенске шиљке. Ако резервоар играча експлодира јер је метак додирнуо празан провидни угао његове текстуре, играч ће се осећати превареним.

Да би се судар осећао осетљиво и поштено, игре користе мањи Хитбок унутар сприте-а користећи Рецтангле.Инфлате:

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: судар од круга до круга (отпорност на ротацију)

Правоугаоници одлично функционишу за статичне блокове и плочице мреже, али не успевају када се спријтови ротирају. Када се свемирски брод који није квадрата ротира у Паинт Трек, гранична кутија поравната по оси мора да се прошири да би обухватила окретне углове, што доводи до фрустрирајућих „фантомских судара“ у празном ваздуху.

Боундинг Цирцлес су идеално решење за кружне астероиде, кугле за навођење енергије и ротирајуће летелице.

Замка квадратног корена

Два круга се сударају када је растојање између њихових центара мање или једнако збиру њихових полупречника:

\(\тект{Удаљеност}(Ц_А, Ц_Б) \ле р_А + р_Б\)

У коду, израчунавање еуклидске удаљености користи Матх.Скрт (или Вецтор2.Дистанце). Међутим, израчунавање квадратних корена у петљи са 200 пројектила сагорева стотине непотребних ЦПУ циклуса!

Упоређивањем квадрата растојања са збиром квадрата полупречника, потпуно елиминишемо квадратни корен:

\(\тект{ДистанцеСкуаред} \ле (р_А + р_Б)^2\)

Имплементација МоноГаме-а: Круг против Круг

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

Сада интегришите ово директно у ентитет:

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

Нулти квадратни корен, имун на ротацију сприте-а и муњевито брз.


4. Ниво 3: Мешани облици – круг против кутије (МатхХелпер.Цламп)

Шта се дешава када се кружни свемирски брод у Паинт Трек креће кроз уски лавиринт правоугаоних одбрамбених баријера или када округли метак погоди квадратни блок у Блоцкед: Пикел Панзер?

Потребан нам је судар Круг против правоугаоника.

Алгоритам стезања

Стратегија је пронаћи тачку на правоугаонику која је најближа центру круга, а затим тестирати да ли је растојање од те најближе тачке до центра мање од полупречника круга.

МоноГаме-ов МатхХелпер.Цламп чини ово тривијалним:

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

Сада можете тестирати штитове играча против правоугаоних цигли са нултом алокацијом и високом прецизношћу!


5. Ниво 4: Континуирана детекција судара (ЦЦД) и зраци

Да ли сте икада у својој игри испалили снајперски метак хипер-брзине или ласер на шинама, само да бисте гледали како метак магично пролази право кроз танки непријатељски брод без наношења штете?

Ова грешка је позната као тунелирање.

Пошто се дискретне игре ажурирају у временским корацима (\(\Делта т = 16,6\тект{мс}\) при 60 ФПС), објекат који се креће брзином од 1800 пиксела у секунди путује 30 пиксела у једном кадру. Ако је непријатељски труп дебео само 15 пиксела, метак је био испред непријатеља у оквиру 1, а потпуно иза непријатеља на оквиру 2.

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

Решење: Свепт сегмент вс. Бок (метод плоча)

Уместо да тестирамо једну тачку, тестирамо цео сегмент линије који повезује позицију метка на оквиру 1 (претходна позиција) са оквиром 2 (тренутна позиција).

Ево производне методе пресека плоча за ливење зрака из нашег пратећег наслова СпиралВар:

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

У Блоцкед: Пикел Панзер, играчева континуирана вештина ласерског снопа користи овај тачан раицаст за пресецање низова силазних блокова без пропуштања ниједног судара.


6. Ниво 5: Детекција савршене колизије пиксела у производњи

Сада достижемо врхунски ниво 2Д тачности: Пикел-Перфецт Цоллисион.

У ретро тенковској пуцачи или борби свемирских бродова, неправилни облици (цеви тенкова, крила, кабине кокпита) су окружени провидним пикселима у текстури сприте-а. Када непријатељски пројектил погоди тај провидни простор, играчи то одмах примећују.

Пикел-перфецт цоллисион проверава стварне алфа (транспарентност) канале текстура које се преклапају. Ако се два нетранспарентна пиксела преклапају на истој светској координати, догодио се прави физички погодак.

Фатална грешка: ГетДата унутар Упдате()

Многи туторијали упућују почетнике да то ураде:

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

Позивање Тектуре2Д.ГетДата() током времена извршавања приморава ЦПУ да сачека да се ГПУ цевовод испразни, повлачећи меморију текстуре назад преко магистрале. Ако ово урадите за десетине спријтова у сваком кадру, ваша игра ће се убрзати са 60 ФПС на 5 ФПС и одмах покренути Андроид АНР-ове!

Решење тестирано у борби од Блоцкед: Пикел Панзер

У Блоцкед: Пикел Панзер, наш Сприте.цс имплементира ултраоптимизовану ИнтерсецтсПикел рутину изграђену око 5 кључних правила:

  1. Кеш статичког низа боја: ГетДата се позива тачно једном када се текстура учита и чува у Речник<Тектуре2Д, Цолор[]>.
  2. Заштита раног изласка ААББ-а: Ако је Боундс.Интерсецтс(отхер.Боундс) лажна, одмах се ослобађамо. 99% провера се елиминише пре прегледа једног пиксела.
  3. Подршка за Атлас текстуре и Изворни правоугаоник: Рукује спрајтовима упакованим у атласе текстура користећи помаке изворног правоугаоника.
  4. Прозор израчунатог преклапања: Прелазимо само преко тачног правоугаоника пресека између оба спритеа (Матх.Мак(а.Топ, б.Топ), итд.).
  5. Алфа кратко спајање: Ако је пиксел Сприте А провидан (А <= 20), Сприте Б је потпуно прескочен.
  6. Подизање својстава: Приступ виртуелним својствима (Границе) у угнежђеним петљама ствара хиљаде копија структуре. Чувамо их у локалним променљивим стека пре уласка у петљу.

Ево комплетног кода који је спреман за производњу:

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

Ова рутина се извршава за мање од 0,1 милисекунде на модерним Андроид хардверским и десктоп системима.


7. Ниво 6: просторна мрежа широке фазе и мобилни ГЦ са нултом алокацијом

Замислите да ваша игра има 100 блокова, 60 метака и 20 непријатеља. Ако тестирате сваки ентитет у односу на сваки други ентитет користећи угнежђене фор петље, изводите:

\(\фрац{180 \тимес 179}{2} = 16.110 \тект{ провера по кадру!}\)

При 60 ФПС, то је скоро 1.000.000 провера судара сваке секунде.

Да бисте скалирали своју игру, морате да примените просторно партиционисање широких фаза.

Униформна просторна хеш мрежа

Екран делимо на 2Д мрежу униформних ћелија (у Блокирано: Пикел Панзер, свака ћелија је 150 \(\ пута 150\) пиксела). Ентитет само тестира колизије против других ентитета који се налазе у истим ћелијама мреже.

Проблем са мобилном меморијом: ГЦ Тхрасхинг

Ако ваш СпатиалГрид креира нову листу<тачка>() или нову листу<блок>() сваки оквир, ви додељујете мегабајте смећа сваког минута на управљаној хрпи.

У Андроид-овом Моно времену извођења, ово покреће честа прикупљања смећа Ген-0, узрокујући: моно рунтиме: нативно закључавање сукоба (моно_цласс_ис_субцласс_)

Игра запиње, а Гоогле Плаи означава вашу игру АНР упозорењима!

Решење: вишекратни бафери за огреботине

Ево просторне хеш мреже нулте алокације из ЦоллисионМанагер.цс у Блокирано: Пикел Панзер:

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

Поновним коришћењем _сцратцхЦеллс и _сцратцхЦхецкедЕнтитиес, цео корак колизије се покреће са 0 бајтова алокације гомиле по оквиру.


8. Табела поређења резимеа

Техника Математички трошак Ротациона подршка Туннелинг Сафе? Најбољи случај употребе у МоноГаме-у
ААББ (Правоугаоник. Пресече) Ултра-брзо (~4 поређења целих бројева) Не Не Мрежни блокови, буллет широка фаза, УИ
Круг (ДистанцеСкуаред) Вери Фаст (3 мулс, 0 скрт) Да (Инваријантно) Не Округли бродови, ватрене кугле, енергетске кугле
Круг против кутије (Кламп) Фаст (Лоцал МатхХелпер.Цламп) Да Не Кружни играч који се креће кроз тесне зидове
Свепт Раи (Слаб ЦЦД) Умерено (Параметриц Раицаст) Да Да Брзи снајперски меци, шинске пушке, ласери
Пикел-Перфецт (ИнтерсецтсПикел) Селективно (ограничени под-рек) Да Не Неправилне контуре сприте-а, поштени хитбоксови
Просторна мрежа (Зеро-ГЦ) Широка фаза (\(О(Н^2) \до О(Н)\)) Н/А Н/А Густи таласи, пакао од метака, Андроид 60 ФПС

Реал-Ворлд Продуцтион Сховцасес: Арар Гамес

Ове архитектуре судара нису теоретски експерименти – оне су права инжењерска основа која покреће наше комерцијално објављене наслове:

  • Блокирано: Пикел Панзер: Наша ретро аркадна игра за преживљавање тенка на Гоогле Плаи-у и Мицрософт Сторе-у. Поседује наш комплетан двослојни систем судара: просторну хеш мрежу са нултом алокацијом која филтрира падајуће блокове и проверу савршеног контакта за тенкове, борбене авионе, гранате куполе и елементарну муницију ЦолорВхеел.
  • Паинт Трек: Наша брза свемирска пуцачина која садржи судар ротационих кругова, континуирано емитовање зрака и противракетне одбрамбене системе велике запремине.

Закључак и следећи кораци

МоноГаме вам даје моћ да дизајнирате детекцију судара која савршено одговара потребама ваше игре. Увођењем скупих Пикел-Перфецт провера иза брзих ААББ тестова, коришћењем Квадратних растојања за кругове и елиминисањем сакупљања смећа помоћу Бафера за гребање за вишекратну употребу, можете да испоручите перформансе од 60/120 ФПС у конзоли и на десктоп и мобилним платформама.

Погледајте наше игре у продавницама апликација да бисте видели ове системе колизије у акцији и почните да примењујете ове обрасце у својим сопственим МоноГаме пројектима већ данас!


Везе и ресурси продавнице


СЕО кључне речи и хештегови

Кључне речи: МоноГаме 2Д детекција колизије, Ц# развој игара, Рецтангле.Интерсетс МоноГаме, пикел перфецт цоллисион Ц#, просторна хеш мрежа МоноГаме, континуална колизија свепт раи, оптимизација перформанси индие игре, нулта алокација игре петља, оптимизација игре са нултом алокацијом, оптимизација игре за Андроид, Блоцк Ар МоноГ Паинт Трек.

#МоноГаме #ЦСхарп #ГамеДев #ИндиеДев #ГамеПхисицс #ДотНет #2ДГамеДев #МобилеГамеДев #ПерформанцеОптимизатион #ЦлеанБлоцкПани#ЦлеанБлоцкПанк #ПаинтТрек #АрарГамес #ГамеПрограмминг




Hiç yorum yok:

Yorum Gönder