27 Eylül 2026 Pazar

ਮੋਨੋਗੇਮ ਵਿੱਚ 2D ਟੱਕਰ ਖੋਜ: ਬੇਸਿਕ ਆਇਤਕਾਰ ਤੋਂ ਜ਼ੀਰੋ-ਅਲੋਕੇਸ਼ਨ ਪਿਕਸਲ-ਪਰਫੈਕਟ ਸਿਸਟਮ ਤੱਕ




ਮੋਨੋਗੇਮ ਵਿੱਚ # 2D ਟੱਕਰ ਖੋਜ: ਬੇਸਿਕ ਆਇਤਕਾਰ ਤੋਂ ਜ਼ੀਰੋ-ਅਲੋਕੇਸ਼ਨ ਪਿਕਸਲ-ਪਰਫੈਕਟ ਸਿਸਟਮ ਤੱਕ

ਯੂਨਿਟੀ ਜਾਂ ਗੋਡੋਟ ਵਰਗੇ ਵਿਜ਼ੂਅਲ ਇੰਜਣਾਂ ਵਿੱਚ 2D ਗੇਮਾਂ ਬਣਾਉਂਦੇ ਸਮੇਂ, ਟੱਕਰ ਖੋਜ ਅਕਸਰ ਨਿਰੀਖਕ ਚੈਕਬਾਕਸਾਂ ਦੀ ਇੱਕ ਲੜੀ ਵਾਂਗ ਮਹਿਸੂਸ ਹੁੰਦੀ ਹੈ: ਤੁਸੀਂ ਇੱਕ BoxCollider2D ਜਾਂ CircleCollider2D 'ਤੇ ਥੱਪੜ ਮਾਰਦੇ ਹੋ, ਇੱਕ Rigidbody ਜੋੜਦੇ ਹੋ, ਅਤੇ ਉਮੀਦ ਕਰਦੇ ਹੋ ਕਿ ਅੰਦਰੂਨੀ ਭੌਤਿਕ ਵਿਗਿਆਨ ਕਦਮ ਮੋਬਾਈਲ ਡਿਵਾਈਸਾਂ 'ਤੇ ਅੜਿੱਕਾ ਨਹੀਂ ਪਾਉਂਦੇ ਹਨ।

ਮੋਨੋਗੇਮ ਅਤੇ ਸੀ# ਵਿੱਚ, ਹਾਲਾਂਕਿ, ਤੁਸੀਂ ਪੂਰੀ ਤਰ੍ਹਾਂ ਕੰਟਰੋਲ ਵਿੱਚ ਹੋ। ਇੱਥੇ ਕੋਈ ਲੁਕਿਆ ਹੋਇਆ ਭੌਤਿਕ ਵਿਗਿਆਨ ਨਹੀਂ ਹੈ, ਕੋਈ ਅਣਚਾਹੇ ਰੋਟੇਸ਼ਨਲ ਜੜਤਾ ਨਹੀਂ ਹੈ, ਅਤੇ ਤੁਹਾਡੇ ਫ੍ਰੇਮ ਬਜਟ ਨੂੰ ਚੋਰੀ ਕਰਨ ਵਾਲਾ ਕੋਈ ਰਹੱਸਮਈ ਕੂੜਾ ਇਕੱਠਾ ਕਰਨ (GC) ਸਪਾਈਕ ਨਹੀਂ ਹੈ।

Arar ਗੇਮਾਂ ਵਿੱਚ, ਜਦੋਂ ਅਸੀਂ Blocked: Pixel Panzer ਅਤੇ Paint Trek ਬਣਾਈਆਂ, ਤਾਂ ਸਾਡੀਆਂ ਆਰਕੇਡ ਗੇਮ ਲੂਪਸ ਨੂੰ ਸੈਂਕੜੇ ਹਾਈ-ਸਪੀਡ ਦੁਸ਼ਮਣ ਦੀਆਂ ਗੋਲੀਆਂ, ਵਿਸਫੋਟ ਕਰਨ ਵਾਲੀਆਂ ਇੱਟਾਂ ਦੇ ਗਰਿੱਡਾਂ, ਘੁੰਮਣ ਵਾਲੇ ਟੈਂਕ ਬੁਰਜ, ਲੜਾਕੂ ਜੈੱਟ ਫਲਾਈਬਾਇਜ਼, ਅਤੇ ਕਣ ਸ਼ੀਲਡਾਂ ਨੂੰ Windows 60 ਤੋਂ Android 60 ਤੋਂ FPS ਦੋਵਾਂ ਡਿਵਾਈਸਾਂ 'ਤੇ ਪ੍ਰੋਸੈਸ ਕਰਨ ਲਈ ਲੋੜੀਂਦਾ ਹੈ। ਇੱਕ ਆਮ-ਉਦੇਸ਼ ਵਾਲਾ ਭੌਤਿਕ ਵਿਗਿਆਨ ਇੰਜਣ ਸਵਾਲ ਤੋਂ ਬਾਹਰ ਸੀ-ਸਾਨੂੰ ਇੱਕ ਮਕਸਦ-ਬਣਾਇਆ, ਟਾਇਰਡ ਟੱਕਰ ਆਰਕੀਟੈਕਚਰ ਦੀ ਲੋੜ ਸੀ।

ਇਸ ਵਿਆਪਕ, ਕੋਡ-ਸੰਚਾਲਿਤ ਗਾਈਡ ਵਿੱਚ, ਅਸੀਂ ਮੋਨੋ-ਗੇਮ ਟੱਕਰ ਦੀਆਂ ਪੂਰਨ ਮੂਲ ਗੱਲਾਂ ਤੋਂ ਸ਼ੁਰੂ ਕਰਾਂਗੇ (ਅਸਲ ਬੁਲੇਟ ਅਤੇ ਏਨੀਮੀ ਸਪ੍ਰਾਈਟਸ ਨਾਲ ਚਤਕਾਰ. ਇੰਟਰਸੈਕਟ) ਅਤੇ ਉੱਨਤ ਸਰਕਲ ਜਾਂਚਾਂ, ਮਿਕਸਡ ਕਲੈਂਪਿੰਗ, ਐਂਟੀ-ਟੰਨਲਿੰਗ ਰੇਕਾਸਟ, ਪ੍ਰੋਡਕਸ਼ਨ-ਗ੍ਰੇਡ P-Li** ਅਤੇ ਪੀ. ਜ਼ੀਰੋ-ਅਲੋਕੇਸ਼ਨ ਸਥਾਨਿਕ ਗਰਿੱਡ ਮੋਬਾਈਲ GC ਸਰਵਾਈਵਲ ਲਈ ਅਨੁਕੂਲਿਤ।


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: ਸਭ ਤੋਂ ਸਰਲ ਟੱਕਰ - ਚਤਕਾਰ. ਇੰਟਰਸੈਕਟਸ (AABB)

ਮੋਨੋਗੇਮ ਵਿੱਚ ਸਭ ਤੋਂ ਬੁਨਿਆਦੀ 2D ਟੱਕਰ ਜਾਂਚ ਐਕਸਿਸ-ਅਲਾਈਨਡ ਬਾਊਂਡਿੰਗ ਬਾਕਸ (AABB) ਟੈਸਟ ਹੈ। ਸ਼ਬਦ "ਧੁਰਾ-ਅਲਾਈਨਡ" ਦਾ ਸਿੱਧਾ ਅਰਥ ਹੈ ਕਿ ਆਇਤਕਾਰ ਦੇ ਕਿਨਾਰੇ ਸਕ੍ਰੀਨ ਦੇ \(X\) ਅਤੇ \(Y\) ਧੁਰੇ (ਕੋਈ ਰੋਟੇਸ਼ਨ ਨਹੀਂ) ਦੇ ਪੂਰੀ ਤਰ੍ਹਾਂ ਸਮਾਨਾਂਤਰ ਹਨ।

ਮੋਨੋਗੇਮ ਇੱਕ ਤੇਜ਼, ਬਿਲਟ-ਇਨ ਵਿਧੀ ਪ੍ਰਦਾਨ ਕਰਦੀ ਹੈ: `ਚਤਕਾਰ।

ਹੁੱਡ ਦੇ ਹੇਠਾਂ ਚਤਕਾਰ. ਇੰਟਰਸੈਕਟ ਕਿਵੇਂ ਕੰਮ ਕਰਦਾ ਹੈ

ਸਤ੍ਹਾ ਦੇ ਹੇਠਾਂ, ਮੋਨੋਗੇਮ ਚਾਰ ਪੂਰਨ ਅੰਕਾਂ ਦੀ ਤੁਲਨਾ ਕਰਦਾ ਹੈ:

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

ਜੇਕਰ ਸਾਰੀਆਂ ਚਾਰ ਸ਼ਰਤਾਂ ਪੂਰੀਆਂ ਹੁੰਦੀਆਂ ਹਨ, ਤਾਂ ਆਇਤਕਾਰ ਓਵਰਲੈਪ ਹੁੰਦੇ ਹਨ। ਜੇਕਰ ਇੱਕ ਸਥਿਤੀ ਵੀ ਅਸਫਲ ਹੋ ਜਾਂਦੀ ਹੈ, ਤਾਂ ਇੱਕ ਖਾਲੀ ਧੁਰਾ ਉਹਨਾਂ ਨੂੰ ਵੱਖ ਕਰਦਾ ਹੈ, ਅਤੇ ਕੋਈ ਟੱਕਰ ਸੰਭਵ ਨਹੀਂ ਹੈ।

ਅਸਲ ਗੇਮਪਲੇ ਕੋਡ: Game1.Update ਵਿੱਚ ਬੁਲੇਟ ਬਨਾਮ ਦੁਸ਼ਮਣ

ਇਹ ਹੈ ਕਿ ਤੁਸੀਂ ਆਪਣੇ ਮੁੱਖ ਮੋਨੋਗੇਮ 'ਅੱਪਡੇਟ' ਲੂਪ ਦੇ ਅੰਦਰ ਸਰਗਰਮ ਗੋਲੀਆਂ ਦੀ ਸੂਚੀ ਅਤੇ ਸਰਗਰਮ ਦੁਸ਼ਮਣਾਂ ਦੀ ਸੂਚੀ ਵਿਚਕਾਰ ਟੱਕਰਾਂ ਦੀ ਜਾਂਚ ਕਿਵੇਂ ਕਰਦੇ ਹੋ:

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

ਪ੍ਰਦਰਸ਼ਨ ਸੁਝਾਅ: ਧਿਆਨ ਦਿਓ ਕਿ ਅਸੀਂ ਪਿੱਛੇ ਵੱਲ ਦੁਹਰਾਉਂਦੇ ਹਾਂ (ਲਈ (int i = list.Count - 1; i >= 0; i--))! ਜੇਕਰ ਤੁਸੀਂ foreach ਦੀ ਵਰਤੋਂ ਕਰਦੇ ਹੋ ਅਤੇ _bullets.Remove(bullet) ਨੂੰ ਕਾਲ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰਦੇ ਹੋ, ਤਾਂ C# ਇੱਕ InvalidOperationException: Collection was modified ਸੁੱਟਦਾ ਹੈ। ਪਿੱਛੇ ਵੱਲ ਦੁਹਰਾਉਣਾ ਮੈਮੋਰੀ ਰੀ-ਇੰਡੈਕਸਿੰਗ ਮੁੱਦਿਆਂ ਤੋਂ ਬਿਨਾਂ ਸੁਰੱਖਿਅਤ ਤੱਤ ਹਟਾਉਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।

ਆਰਕੇਡ ਸੀਕਰੇਟ: 'ਇਨਫਲੇਟ' ਰਾਹੀਂ "ਫੇਅਰ ਹਿੱਟਬਾਕਸ"

ਬਲੌਕਡ: ਪਿਕਸਲ ਪੈਨਜ਼ਰ ਵਰਗੀਆਂ ਰੀਟਰੋ ਗੇਮਾਂ ਵਿੱਚ, ਸਪ੍ਰਾਈਟ ਟੈਕਸਟ ਵਿੱਚ ਅਕਸਰ ਪਾਰਦਰਸ਼ੀ ਮਾਰਜਿਨ ਜਾਂ ਐਂਟੀਨਾ ਸਪਾਈਕਸ ਸ਼ਾਮਲ ਹੁੰਦੇ ਹਨ। ਜੇਕਰ ਖਿਡਾਰੀ ਦਾ ਟੈਂਕ ਫਟਦਾ ਹੈ ਕਿਉਂਕਿ ਇੱਕ ਗੋਲੀ ਇਸਦੇ ਟੈਕਸਟ ਦੇ ਇੱਕ ਖਾਲੀ ਪਾਰਦਰਸ਼ੀ ਕੋਨੇ ਨੂੰ ਛੂਹਦੀ ਹੈ, ਤਾਂ ਖਿਡਾਰੀ ਆਪਣੇ ਆਪ ਨੂੰ ਠੱਗਿਆ ਮਹਿਸੂਸ ਕਰੇਗਾ।

ਟੱਕਰ ਨੂੰ ਜਵਾਬਦੇਹ ਅਤੇ ਨਿਰਪੱਖ ਮਹਿਸੂਸ ਕਰਨ ਲਈ, ਗੇਮਾਂ 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: ਸਰਕਲ-ਟੂ-ਸਰਕਲ ਟੱਕਰ (ਘੁੰਮਣ ਵਾਲੀ ਪ੍ਰਤੀਰੋਧਤਾ)

ਆਇਤਕਾਰ ਸਥਿਰ ਬਲਾਕਾਂ ਅਤੇ ਗਰਿੱਡ ਟਾਈਲਾਂ ਲਈ ਵਧੀਆ ਕੰਮ ਕਰਦੇ ਹਨ, ਪਰ ਜਦੋਂ ਸਪ੍ਰਾਈਟਸ ਘੁੰਮਦੇ ਹਨ ਤਾਂ ਉਹ ਅਸਫਲ ਹੋ ਜਾਂਦੇ ਹਨ। ਜਦੋਂ ਇੱਕ ਗੈਰ-ਵਰਗ ਸਪੇਸਸ਼ਿਪ ਪੇਂਟ ਟ੍ਰੈਕ ਵਿੱਚ ਘੁੰਮਦੀ ਹੈ, ਤਾਂ ਇੱਕ ਧੁਰੀ-ਅਲਾਈਨ ਬਾਊਂਡਿੰਗ ਬਾਕਸ ਨੂੰ ਸਪਿਨਿੰਗ ਕੋਨਿਆਂ ਨੂੰ ਘੇਰਨ ਲਈ ਫੈਲਾਉਣਾ ਚਾਹੀਦਾ ਹੈ, ਨਤੀਜੇ ਵਜੋਂ ਖਾਲੀ ਹਵਾ ਵਿੱਚ ਨਿਰਾਸ਼ਾਜਨਕ "ਫੈਂਟਮ ਟੱਕਰ" ਹੁੰਦੀ ਹੈ।

ਗੋਲਾਕਾਰ ਐਸਟੇਰੋਇਡਜ਼, ਹੋਮਿੰਗ ਐਨਰਜੀ ਆਰਬਸ, ਅਤੇ ਘੁੰਮਦੇ ਪੁਲਾੜ ਯਾਨ ਲਈ, ਬਾਊਂਡਿੰਗ ਸਰਕਲ ਆਦਰਸ਼ ਹੱਲ ਹਨ।

ਵਰਗ ਰੂਟ ਟ੍ਰੈਪ

ਦੋ ਚੱਕਰ ਟਕਰਾਉਂਦੇ ਹਨ ਜਦੋਂ ਉਹਨਾਂ ਦੇ ਕੇਂਦਰਾਂ ਵਿਚਕਾਰ ਦੂਰੀ ਉਹਨਾਂ ਦੇ ਰੇਡੀਆਈ ਦੇ ਜੋੜ ਤੋਂ ਘੱਟ ਜਾਂ ਬਰਾਬਰ ਹੁੰਦੀ ਹੈ:

\(\text{ਦੂਰੀ}(C_A, C_B) \le r_A + r_B\)

ਕੋਡ ਵਿੱਚ, ਯੂਕਲੀਡੀਅਨ ਦੂਰੀ ਦੀ ਗਣਨਾ ਕਰਨ ਲਈ Math.Sqrt (ਜਾਂ Vector2.Distance) ਦੀ ਵਰਤੋਂ ਕੀਤੀ ਜਾਂਦੀ ਹੈ। ਹਾਲਾਂਕਿ, 200 ਪ੍ਰੋਜੈਕਟਾਈਲਾਂ ਦੇ ਨਾਲ ਇੱਕ ਲੂਪ ਵਿੱਚ ਵਰਗ ਜੜ੍ਹਾਂ ਦੀ ਗਣਨਾ ਕਰਨਾ ਸੈਂਕੜੇ ਬੇਲੋੜੇ CPU ਚੱਕਰਾਂ ਨੂੰ ਸਾੜ ਦਿੰਦਾ ਹੈ!

ਵਰਗ ਦੂਰੀ ਦੀ ਵਰਗ ਰੇਡੀਅਸ ਜੋੜ ਨਾਲ ਤੁਲਨਾ ਕਰਕੇ, ਅਸੀਂ ਵਰਗ ਮੂਲ ਨੂੰ ਪੂਰੀ ਤਰ੍ਹਾਂ ਖਤਮ ਕਰ ਦਿੰਦੇ ਹਾਂ:

\(\text{DistanceSquared} \le (r_A + r_B)^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: ਮਿਸ਼ਰਤ ਆਕਾਰ – ਚੱਕਰ ਬਨਾਮ ਬਾਕਸ (MathHelper.Clamp)

ਕੀ ਹੁੰਦਾ ਹੈ ਜਦੋਂ ਪੇਂਟ ਟ੍ਰੈਕ ਵਿੱਚ ਇੱਕ ਗੋਲਾਕਾਰ ਸਪੇਸਸ਼ਿਪ ਆਇਤਾਕਾਰ ਰੱਖਿਆ ਰੁਕਾਵਟਾਂ ਦੇ ਇੱਕ ਤੰਗ ਭੁਲੇਖੇ ਵਿੱਚੋਂ ਲੰਘਦੀ ਹੈ, ਜਾਂ ਜਦੋਂ ਇੱਕ ਗੋਲ ਗੋਲੀ ਬਲਾਕਡ: ਪਿਕਸਲ ਪੈਨਜ਼ਰ ਵਿੱਚ ਇੱਕ ਵਰਗ ਬਲਾਕ ਨਾਲ ਟਕਰਾਉਂਦੀ ਹੈ?

ਸਾਨੂੰ ਚੱਕਰ ਬਨਾਮ ਆਇਤ ਟੱਕਰ ਦੀ ਲੋੜ ਹੈ।

ਕਲੈਂਪਿੰਗ ਐਲਗੋਰਿਦਮ

ਰਣਨੀਤੀ ਇਹ ਹੈ ਕਿ ਆਇਤ 'ਤੇ ਬਿੰਦੂ ਨੂੰ ਲੱਭੋ ਜੋ ਚੱਕਰ ਦੇ ਕੇਂਦਰ ਦੇ ਸਭ ਤੋਂ ਨੇੜੇ ਹੈ, ਅਤੇ ਫਿਰ ਜਾਂਚ ਕਰੋ ਕਿ ਕੀ ਉਸ ਨਜ਼ਦੀਕੀ ਬਿੰਦੂ ਤੋਂ ਕੇਂਦਰ ਤੱਕ ਦੀ ਦੂਰੀ ਚੱਕਰ ਦੇ ਘੇਰੇ ਤੋਂ ਘੱਟ ਹੈ ਜਾਂ ਨਹੀਂ।

ਮੋਨੋਗੇਮ ਦਾ 'MathHelper.Clamp' ਇਸ ਨੂੰ ਮਾਮੂਲੀ ਬਣਾਉਂਦਾ ਹੈ:

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: ਨਿਰੰਤਰ ਟੱਕਰ ਖੋਜ (CCD) ਅਤੇ ਸਵੀਪਟ ਕਿਰਨਾਂ

ਕੀ ਤੁਸੀਂ ਕਦੇ ਆਪਣੀ ਗੇਮ ਵਿੱਚ ਇੱਕ ਹਾਈਪਰ-ਵੇਲੋਸਿਟੀ ਸਨਾਈਪਰ ਰਾਊਂਡ ਜਾਂ ਰੇਲਗਨ ਲੇਜ਼ਰ ਨੂੰ ਗੋਲੀਬਾਰੀ ਕੀਤੀ ਹੈ, ਸਿਰਫ ਗੋਲੀ ਨੂੰ ਜਾਦੂਈ ਢੰਗ ਨਾਲ ਇੱਕ ਪਤਲੇ ਦੁਸ਼ਮਣ ਦੇ ਜਹਾਜ਼ ਨੂੰ ਨੁਕਸਾਨ ਪਹੁੰਚਾਏ ਬਿਨਾਂ ਸਿੱਧੇ ਲੰਘਦਾ ਦੇਖਣ ਲਈ?

ਇਸ ਬੱਗ ਨੂੰ ਟਨਲਿੰਗ ਵਜੋਂ ਜਾਣਿਆ ਜਾਂਦਾ ਹੈ।

ਕਿਉਂਕਿ ਵੱਖ-ਵੱਖ ਗੇਮਾਂ ਸਮੇਂ ਦੇ ਪੜਾਅ (\(\Delta t = 16.6\text{ms}\) 60 FPS 'ਤੇ) ਅੱਪਡੇਟ ਹੁੰਦੀਆਂ ਹਨ, 1,800 ਪਿਕਸਲ ਪ੍ਰਤੀ ਸਕਿੰਟ ਦੀ ਗਤੀ ਨਾਲ ਚੱਲਣ ਵਾਲੀ ਵਸਤੂ **ਇੱਕ ਫਰੇਮ ਵਿੱਚ 30 ਪਿਕਸਲ ਯਾਤਰਾ ਕਰਦੀ ਹੈ। ਜੇਕਰ ਦੁਸ਼ਮਣ ਦੀ ਹਲ ਸਿਰਫ 15 ਪਿਕਸਲ ਮੋਟੀ ਹੈ, ਤਾਂ ਗੋਲੀ ਫਰੇਮ 1 'ਤੇ ਦੁਸ਼ਮਣ ਦੇ ਸਾਹਮਣੇ ਸੀ, ਅਤੇ ਫਰੇਮ 2 'ਤੇ ਪੂਰੀ ਤਰ੍ਹਾਂ ਦੁਸ਼ਮਣ ਦੇ ਪਿੱਛੇ ਸੀ।

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

ਹੱਲ: ਸਵੀਪਟ ਖੰਡ ਬਨਾਮ ਬਾਕਸ (ਸਲੈਬ ਵਿਧੀ)

ਇੱਕ ਸਿੰਗਲ ਬਿੰਦੂ ਦੀ ਜਾਂਚ ਕਰਨ ਦੀ ਬਜਾਏ, ਅਸੀਂ ਫਰੇਮ 1 (ਪਿਛਲੀ ਸਥਿਤੀ) ਤੇ ਬੁਲੇਟ ਦੀ ਸਥਿਤੀ ਨੂੰ ਫਰੇਮ 2 (ਵਰਤਮਾਨ ਸਥਿਤੀ) ਨਾਲ ਜੋੜਨ ਵਾਲੇ ਪੂਰੇ ਲਾਈਨ ਹਿੱਸੇ ਦੀ ਜਾਂਚ ਕਰਦੇ ਹਾਂ।

ਇਹ ਸਾਡੇ ਸਾਥੀ ਸਿਰਲੇਖ 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;
    }
}

Blocked: Pixel Panzer ਵਿੱਚ, ਖਿਡਾਰੀ ਦਾ ਨਿਰੰਤਰ ਲੇਜ਼ਰ ਬੀਮ ਹੁਨਰ ਇੱਕ ਵੀ ਟੱਕਰ ਗੁਆਏ ਬਿਨਾਂ ਘਟਦੇ ਬਲਾਕਾਂ ਦੀਆਂ ਕਤਾਰਾਂ ਵਿੱਚ ਕੱਟਣ ਲਈ ਇਸ ਸਹੀ ਰੇਕਾਸਟ ਦੀ ਵਰਤੋਂ ਕਰਦਾ ਹੈ।


6. ਪੱਧਰ 5: ਉਤਪਾਦਨ-ਗ੍ਰੇਡ ਪਿਕਸਲ-ਸੰਪੂਰਨ ਟੱਕਰ ਖੋਜ

ਹੁਣ ਅਸੀਂ 2D ਸ਼ੁੱਧਤਾ ਦੇ ਅੰਤਮ ਪੱਧਰ 'ਤੇ ਪਹੁੰਚ ਗਏ ਹਾਂ: ਪਿਕਸਲ-ਪਰਫੈਕਟ ਟੱਕਰ।

ਰੈਟਰੋ ਟੈਂਕ ਸ਼ੂਟਰ ਜਾਂ ਸਪੇਸਸ਼ਿਪ ਡੌਗਫਾਈਟ ਵਿੱਚ, ਅਨਿਯਮਿਤ ਆਕਾਰ (ਟੈਂਕ ਬੈਰਲ, ਵਿੰਗ, ਕਾਕਪਿਟ ਕਾਕਪਿਟ) ਸਪ੍ਰਾਈਟ ਟੈਕਸਟ ਵਿੱਚ ਪਾਰਦਰਸ਼ੀ ਪਿਕਸਲ ਨਾਲ ਘਿਰੇ ਹੋਏ ਹਨ। ਜਦੋਂ ਦੁਸ਼ਮਣ ਦੀ ਮਿਜ਼ਾਈਲ ਉਸ ਪਾਰਦਰਸ਼ੀ ਸਪੇਸ ਨੂੰ ਮਾਰਦੀ ਹੈ, ਤਾਂ ਖਿਡਾਰੀ ਤੁਰੰਤ ਧਿਆਨ ਦਿੰਦੇ ਹਨ।

ਪਿਕਸਲ-ਸੰਪੂਰਨ ਟੱਕਰ ਓਵਰਲੈਪਿੰਗ ਟੈਕਸਟ ਦੇ ਅਸਲ ਅਲਫ਼ਾ (ਪਾਰਦਰਸ਼ਤਾ) ਚੈਨਲਾਂ ਦੀ ਜਾਂਚ ਕਰਦੀ ਹੈ। ਜੇਕਰ ਦੋ ਗੈਰ-ਪਾਰਦਰਸ਼ੀ ਪਿਕਸਲ ਇੱਕੋ ਵਿਸ਼ਵ ਕੋਆਰਡੀਨੇਟ 'ਤੇ ਓਵਰਲੈਪ ਹੁੰਦੇ ਹਨ, ਤਾਂ ਇੱਕ ਸੱਚਾ ਭੌਤਿਕ ਹਿੱਟ ਹੋਇਆ ਹੈ।

ਘਾਤਕ ਗਲਤੀ: Update() ਦੇ ਅੰਦਰ GetData

ਬਹੁਤ ਸਾਰੇ ਟਿਊਟੋਰਿਅਲ ਸ਼ੁਰੂਆਤ ਕਰਨ ਵਾਲਿਆਂ ਨੂੰ ਅਜਿਹਾ ਕਰਨ ਲਈ ਕਹਿੰਦੇ ਹਨ:

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

ਰਨਟਾਈਮ ਦੌਰਾਨ Texture2D.GetData() ਨੂੰ ਕਾਲ ਕਰਨਾ CPU ਨੂੰ GPU ਪਾਈਪਲਾਈਨ ਦੇ ਫਲੱਸ਼ ਹੋਣ ਦਾ ਇੰਤਜ਼ਾਰ ਕਰਨ ਲਈ ਮਜ਼ਬੂਰ ਕਰਦਾ ਹੈ, ਟੈਕਸਟ ਮੈਮੋਰੀ ਨੂੰ ਬੱਸ ਉੱਤੇ ਵਾਪਸ ਖਿੱਚਦਾ ਹੈ। ਦਰਜਨਾਂ ਸਪ੍ਰਾਈਟਸ ਲਈ ਅਜਿਹਾ ਕਰਨ ਨਾਲ ਹਰ ਫ੍ਰੇਮ ਤੁਹਾਡੀ ਗੇਮ ਨੂੰ 60 FPS ਤੋਂ 5 FPS ਤੱਕ ਲੈ ਜਾਵੇਗਾ ਅਤੇ ਤੁਰੰਤ Android ANR ਨੂੰ ਚਾਲੂ ਕਰ ਦੇਵੇਗਾ!

Blocked: Pixel Panzer ਤੋਂ ਬੈਟਲ-ਟੈਸਟਡ ਹੱਲ

Blocked: Pixel Panzer ਵਿੱਚ, ਸਾਡਾ Sprite.cs ਇੱਕ ਅਤਿ-ਅਨੁਕੂਲਿਤ IntersectsPixel ਰੁਟੀਨ ਨੂੰ ਲਾਗੂ ਕਰਦਾ ਹੈ ਜੋ ਲਗਭਗ 5 ਮਹੱਤਵਪੂਰਨ ਨਿਯਮਾਂ ਵਿੱਚ ਬਣਾਇਆ ਗਿਆ ਹੈ:

  1. ਸਟੈਟਿਕ ਕਲਰ ਐਰੇ ਕੈਸ਼: 'GetData' ਨੂੰ ਬਿਲਕੁਲ ਇੱਕ ਵਾਰ ਕਿਹਾ ਜਾਂਦਾ ਹੈ ਜਦੋਂ ਟੈਕਸਟ ਲੋਡ ਹੁੰਦਾ ਹੈ, ਅਤੇ ਇੱਕ 'ਡਿਕਸ਼ਨਰੀ<Texture2D, Color[]>` ਵਿੱਚ ਸਟੋਰ ਕੀਤਾ ਜਾਂਦਾ ਹੈ।
  2. AABB ਅਰਲੀ ਐਗਜ਼ਿਟ ਗਾਰਡ: ਜੇਕਰ Bounds.Intersects(other.Bounds) ਗਲਤ ਹੈ, ਤਾਂ ਅਸੀਂ ਤੁਰੰਤ ਜ਼ਮਾਨਤ ਕਰ ਲੈਂਦੇ ਹਾਂ। ਇੱਕ ਸਿੰਗਲ ਪਿਕਸਲ ਦਾ ਨਿਰੀਖਣ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ 99% ਚੈਕਾਂ ਨੂੰ ਖਤਮ ਕਰ ਦਿੱਤਾ ਜਾਂਦਾ ਹੈ।
  3. ਟੈਕਚਰ ਐਟਲਸ ਅਤੇ ਸਰੋਤ ਆਇਤਕਾਰ ਸਮਰਥਨ: ਸਰੋਤ ਆਇਤਕਾਰ ਔਫਸੈੱਟਾਂ ਦੀ ਵਰਤੋਂ ਕਰਦੇ ਹੋਏ ਟੈਕਸਟ ਐਟਲਸ ਵਿੱਚ ਪੈਕ ਕੀਤੇ ਸਪ੍ਰਾਈਟਸ ਨੂੰ ਹੈਂਡਲ ਕਰਦਾ ਹੈ।
  4. ਕੈਲਕੂਲੇਟਿਡ ਓਵਰਲੈਪ ਵਿੰਡੋ: ਅਸੀਂ ਸਿਰਫ਼ ਦੋਨਾਂ ਸਪ੍ਰਾਈਟਸ (Math.Max(a.Top, b.Top), ਆਦਿ) ਦੇ ਵਿਚਕਾਰ ਸਹੀ ਇੰਟਰਸੈਕਸ਼ਨ ਆਇਤ ਉੱਤੇ ਲੂਪ ਕਰਦੇ ਹਾਂ।
  5. ਅਲਫ਼ਾ ਸ਼ਾਰਟ-ਸਰਕਿਟਿੰਗ: ਜੇਕਰ ਸਪ੍ਰਾਈਟ A ਦਾ ਪਿਕਸਲ ਪਾਰਦਰਸ਼ੀ ਹੈ (A <= 20), ਸਪ੍ਰਾਈਟ B ਨੂੰ ਪੂਰੀ ਤਰ੍ਹਾਂ ਛੱਡ ਦਿੱਤਾ ਜਾਂਦਾ ਹੈ।
  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: ਬਰਾਡ-ਫੇਜ਼ ਸਥਾਨਿਕ ਗਰਿੱਡ ਅਤੇ ਜ਼ੀਰੋ-ਅਲੋਕੇਸ਼ਨ ਮੋਬਾਈਲ GC

ਕਲਪਨਾ ਕਰੋ ਕਿ ਤੁਹਾਡੀ ਗੇਮ ਵਿੱਚ 100 ਬਲਾਕ, 60 ਗੋਲੀਆਂ ਅਤੇ 20 ਦੁਸ਼ਮਣ ਹਨ। ਜੇਕਰ ਤੁਸੀਂ ਨੇਸਟਡ 'ਫੌਰ' ਲੂਪਸ ਦੀ ਵਰਤੋਂ ਕਰਕੇ ਹਰ ਇਕਾਈ ਦੇ ਵਿਰੁੱਧ ਹਰੇਕ ਇਕਾਈ ਦੀ ਜਾਂਚ ਕਰਦੇ ਹੋ, ਤਾਂ ਤੁਸੀਂ ਇਹ ਪ੍ਰਦਰਸ਼ਨ ਕਰਦੇ ਹੋ:

\(\frac{180 \times 179}{2} = 16,110 \text{ ਚੈਕ ਪ੍ਰਤੀ ਫਰੇਮ!}\)

60 FPS 'ਤੇ, ਇਹ ਲਗਭਗ 1,000,000 ਟੱਕਰ ਜਾਂਚ ਹਰ ਸਕਿੰਟ ਹੈ।

ਆਪਣੀ ਖੇਡ ਨੂੰ ਸਕੇਲ ਕਰਨ ਲਈ, ਤੁਹਾਨੂੰ ਬ੍ਰੌਡ-ਫੇਜ਼ ਸਥਾਨਿਕ ਵਿਭਾਗੀਕਰਨ ਨੂੰ ਲਾਗੂ ਕਰਨਾ ਚਾਹੀਦਾ ਹੈ।

ਯੂਨੀਫਾਰਮ ਸਪੇਸ਼ੀਅਲ ਹੈਸ਼ ਗਰਿੱਡ

ਅਸੀਂ ਸਕ੍ਰੀਨ ਨੂੰ ਇਕਸਾਰ ਸੈੱਲਾਂ ਦੇ 2D ਗਰਿੱਡ ਵਿੱਚ ਵੰਡਦੇ ਹਾਂ (ਬਲੌਕ ਕੀਤੇ: ਪਿਕਸਲ ਪੈਨਜ਼ਰ ਵਿੱਚ, ਹਰੇਕ ਸੈੱਲ \(150 \ ਵਾਰ 150\) ਪਿਕਸਲ ਹੈ)। ਇੱਕ ਇਕਾਈ ਕੇਵਲ ਉਸੇ ਗਰਿੱਡ ਸੈੱਲਾਂ ਵਿੱਚ ਰਹਿੰਦੀਆਂ ਦੂਜੀਆਂ ਸੰਸਥਾਵਾਂ ਦੇ ਵਿਰੁੱਧ ਟੱਕਰਾਂ ਦੀ ਜਾਂਚ ਕਰਦੀ ਹੈ।

ਮੋਬਾਈਲ ਮੈਮੋਰੀ ਸਮੱਸਿਆ: GC ਥ੍ਰੈਸ਼ਿੰਗ

ਜੇਕਰ ਤੁਹਾਡਾ SpatialGrid ਹਰ ਫਰੇਮ ਨਵੀਂ ਸੂਚੀ<Point>() ਜਾਂ ਨਵੀਂ ਸੂਚੀ<Block>() ਬਣਾਉਂਦਾ ਹੈ, ਤਾਂ ਤੁਸੀਂ ਪ੍ਰਬੰਧਿਤ ਢੇਰ 'ਤੇ ਹਰ ਮਿੰਟ ਮੈਗਾਬਾਈਟ ਕੂੜਾ ਅਲਾਟ ਕਰਦੇ ਹੋ।

ਐਂਡਰਾਇਡ ਦੇ ਮੋਨੋ ਰਨਟਾਈਮ 'ਤੇ, ਇਹ ਵਾਰ-ਵਾਰ Gen-0 ਕੂੜਾ ਇਕੱਠਾ ਕਰਨ ਨੂੰ ਚਾਲੂ ਕਰਦਾ ਹੈ, ਜਿਸ ਕਾਰਨ: ਮੋਨੋ ਰਨਟਾਈਮ: ਨੇਟਿਵ ਲਾਕ ਵਿਵਾਦ (ਮੋਨੋ_ਕਲਾਸ_ਇਸ_ਸਬਕਲਾਸ_)

ਗੇਮ ਸਟਟਰਸ ਅਤੇ Google Play ਤੁਹਾਡੀ ਗੇਮ ਨੂੰ ANR ਚੇਤਾਵਨੀਆਂ ਨਾਲ ਫਲੈਗ ਕਰਦਾ ਹੈ!

ਹੱਲ: ਮੁੜ ਵਰਤੋਂ ਯੋਗ ਸਕ੍ਰੈਚ ਬਫਰ

ਇੱਥੇ Blocked: Pixel Panzer ਵਿੱਚ CollisionManager.cs ਤੋਂ ਜ਼ੀਰੋ-ਅਲੋਕੇਸ਼ਨ ਸਪੇਸ਼ੀਅਲ ਹੈਸ਼ ਗਰਿੱਡ ਹੈ:

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

_scratchCells ਅਤੇ _scratchCheckedEntities ਦੀ ਮੁੜ ਵਰਤੋਂ ਕਰਨ ਨਾਲ, ਪੂਰਾ ਟੱਕਰ ਪੜਾਅ 0 ਬਾਈਟ ਹੀਪ ਅਲੋਕੇਸ਼ਨ ਪ੍ਰਤੀ ਫਰੇਮ ਨਾਲ ਚੱਲਦਾ ਹੈ।


8. ਸੰਖੇਪ ਤੁਲਨਾ ਸਾਰਣੀ

ਤਕਨੀਕ ਗਣਿਤਿਕ ਲਾਗਤ ਰੋਟੇਸ਼ਨਲ ਸਪੋਰਟ ਟਨਲਿੰਗ ਸੁਰੱਖਿਅਤ? ਮੋਨੋਗੇਮ ਵਿੱਚ ਸਭ ਤੋਂ ਵਧੀਆ ਵਰਤੋਂ ਦਾ ਕੇਸ
AABB (ਚਤਕਾਰ। ਇੰਟਰਸੈਕਟਸ) ਅਤਿ-ਤੇਜ਼ (~4 ਪੂਰਨ ਅੰਕ ਤੁਲਨਾਵਾਂ) ਨਹੀਂ ਨਹੀਂ ਗਰਿੱਡ ਬਲਾਕ, ਬੁਲੇਟ ਬਰਾਡ-ਫੇਜ਼, UI
ਚੱਕਰ (ਦੂਰੀ ਵਰਗ) ਬਹੁਤ ਤੇਜ਼ (3 muls, 0 sqrt) ਹਾਂ (ਇਨਵੇਰੀਅਨ) ਨਹੀਂ ਗੋਲ ਜਹਾਜ਼, ਅੱਗ ਦੇ ਗੋਲੇ, ਊਰਜਾ ਚੱਕਰ
ਸਰਕਲ ਬਨਾਮ ਬਾਕਸ (ਕਲੈਂਪ) ਤੇਜ਼ (ਸਥਾਨਕ ਮੈਥਹੈਲਪਰ. ਕਲੈਂਪ) ਹਾਂ ਨਹੀਂ ਤੰਗ ਕੰਧਾਂ 'ਤੇ ਨੈਵੀਗੇਟ ਕਰਨ ਵਾਲਾ ਗੋਲਾਕਾਰ ਖਿਡਾਰੀ
ਸਵੀਪਟ ਰੇ (ਸਲੈਬ CCD) ਮੱਧਮ (ਪੈਰਾਮੀਟ੍ਰਿਕ ਰੇਕਾਸਟ) ਹਾਂ ਹਾਂ ਤੇਜ਼ ਸਨਾਈਪਰ ਗੋਲੀਆਂ, ਰੇਲਗੰਨ, ਲੇਜ਼ਰ
ਪਿਕਸਲ-ਪਰਫੈਕਟ (ਇੰਟਰਸੈਕਟਸਪਿਕਸਲ) ਚੋਣਵੇਂ (ਪ੍ਰਤੀਬੰਧਿਤ ਉਪ-ਰੈਕਟ) ਹਾਂ ਨਹੀਂ ਅਨਿਯਮਿਤ ਸਪ੍ਰਾਈਟ ਕੰਟੋਰਸ, ਨਿਰਪੱਖ ਹਿੱਟਬਾਕਸ
ਸਪੇਸ਼ੀਅਲ ਗਰਿੱਡ (ਜ਼ੀਰੋ-ਜੀਸੀ) ਵਿਆਪਕ-ਪੜਾਅ (\(O(N^2) \to O(N)\)) ਲਾਗੂ ਨਹੀਂ ਲਾਗੂ ਨਹੀਂ ਸੰਘਣੀ ਲਹਿਰਾਂ, ਬੁਲੇਟ ਹੈਲਜ਼, ਐਂਡਰਾਇਡ 60 FPS

ਰੀਅਲ-ਵਰਲਡ ਪ੍ਰੋਡਕਸ਼ਨ ਸ਼ੋਅਕੇਸ: ਅਰਾਰ ਗੇਮਜ਼

ਇਹ ਟੱਕਰ ਆਰਕੀਟੈਕਚਰ ਸਿਧਾਂਤਕ ਪ੍ਰਯੋਗ ਨਹੀਂ ਹਨ - ਇਹ ਸਾਡੇ ਵਪਾਰਕ ਤੌਰ 'ਤੇ ਜਾਰੀ ਕੀਤੇ ਸਿਰਲੇਖਾਂ ਨੂੰ ਸ਼ਕਤੀ ਦੇਣ ਵਾਲੀ ਅਸਲ ਇੰਜੀਨੀਅਰਿੰਗ ਬੁਨਿਆਦ ਹਨ:

  • ਬਲੌਕ ਕੀਤਾ ਗਿਆ: Pixel Panzer: Google Play ਅਤੇ Microsoft ਸਟੋਰ 'ਤੇ ਸਾਡੀ ਰੈਟਰੋ ਟੈਂਕ ਸਰਵਾਈਵਲ ਆਰਕੇਡ ਗੇਮ। ਇਸ ਵਿੱਚ ਸਾਡੀ ਪੂਰੀ ਦੋ-ਪੱਧਰੀ ਟੱਕਰ ਪ੍ਰਣਾਲੀ ਦੀ ਵਿਸ਼ੇਸ਼ਤਾ ਹੈ: ਇੱਕ ਜ਼ੀਰੋ-ਅਲੋਕੇਸ਼ਨ ਸਥਾਨਿਕ ਹੈਸ਼ ਗਰਿੱਡ ਫਿਲਟਰਿੰਗ ਡਿੱਗਣ ਵਾਲੇ ਬਲਾਕ, ਅਤੇ ਟੈਂਕਾਂ, ਲੜਾਕੂ ਜਹਾਜ਼ਾਂ, ਬੁਰਜ ਸ਼ੈੱਲਾਂ, ਅਤੇ ਕਲਰਵੀਲ ਐਲੀਮੈਂਟਲ ਅਸਲਾ ਲਈ ਪਿਕਸਲ-ਸੰਪੂਰਨ ਸੰਪਰਕ ਜਾਂਚ।
  • ਪੇਂਟ ਟ੍ਰੇਕ: ਸਾਡਾ ਤੇਜ਼ ਰਫ਼ਤਾਰ ਸਪੇਸ ਸ਼ੂਟਰ ਜਿਸ ਵਿੱਚ ਰੋਟੇਸ਼ਨਲ ਸਰਕਲ ਟੱਕਰ, ਲਗਾਤਾਰ ਰੇਕਾਸਟਿੰਗ, ਅਤੇ ਸਵੀਪਟ-ਆਵਾਜ਼ ਮਿਜ਼ਾਈਲ ਰੱਖਿਆ ਪ੍ਰਣਾਲੀਆਂ ਸ਼ਾਮਲ ਹਨ।

ਸਿੱਟਾ ਅਤੇ ਅਗਲੇ ਕਦਮ

ਮੋਨੋਗੇਮ ਤੁਹਾਨੂੰ ਟੱਕਰ ਖੋਜ ਨੂੰ ਡਿਜ਼ਾਈਨ ਕਰਨ ਦੀ ਸ਼ਕਤੀ ਦਿੰਦਾ ਹੈ ਜੋ ਤੁਹਾਡੀ ਗੇਮ ਦੀਆਂ ਲੋੜਾਂ ਨਾਲ ਪੂਰੀ ਤਰ੍ਹਾਂ ਮੇਲ ਖਾਂਦਾ ਹੈ। ਤੇਜ਼ AABB ਟੈਸਟਾਂ ਦੇ ਪਿੱਛੇ ਮਹਿੰਗੇ ਪਿਕਸਲ-ਪਰਫੈਕਟ ਜਾਂਚਾਂ ਦੇ ਕੇ, ਚੱਕਰਾਂ ਲਈ ਵਰਗ ਦੂਰੀਆਂ ਦੀ ਵਰਤੋਂ ਕਰਕੇ, ਅਤੇ ਮੁੜ ਵਰਤੋਂ ਯੋਗ ਸਕ੍ਰੈਚ ਬਫਰਜ਼ ਨਾਲ ਕੂੜਾ ਇਕੱਠਾ ਕਰਨ ਨੂੰ ਖਤਮ ਕਰਕੇ, ਤੁਸੀਂ ਡੈਸਕਟਾਪ ਅਤੇ ਮੋਬਾਈਲ ਦੋਵਾਂ ਪਲੇਟਫਾਰਮਾਂ 'ਤੇ ਕੰਸੋਲ-ਸਮੂਥ 60/120 FPS ਪ੍ਰਦਰਸ਼ਨ ਪ੍ਰਦਾਨ ਕਰ ਸਕਦੇ ਹੋ।

ਇਹਨਾਂ ਟੱਕਰ ਪ੍ਰਣਾਲੀਆਂ ਨੂੰ ਐਕਸ਼ਨ ਵਿੱਚ ਦੇਖਣ ਲਈ ਐਪ ਸਟੋਰਾਂ 'ਤੇ ਸਾਡੀਆਂ ਗੇਮਾਂ ਨੂੰ ਦੇਖੋ, ਅਤੇ ਅੱਜ ਹੀ ਆਪਣੇ ਮੋਨੋਗੇਮ ਪ੍ਰੋਜੈਕਟਾਂ ਵਿੱਚ ਇਹਨਾਂ ਪੈਟਰਨਾਂ ਨੂੰ ਲਾਗੂ ਕਰਨਾ ਸ਼ੁਰੂ ਕਰੋ!


ਸਟੋਰ ਲਿੰਕ ਅਤੇ ਸਰੋਤ


SEO ਕੀਵਰਡਸ ਅਤੇ ਹੈਸ਼ਟੈਗ

ਕੀਵਰਡਸ: ਮੋਨੋਗੇਮ 2ਡੀ ਟੱਕਰ ਖੋਜ, ਸੀ# ਗੇਮ ਡਿਵੈਲਪਮੈਂਟ, ਆਇਤਕਾਰ। ਇੰਟਰਸੈਕਟਸ ਮੋਨੋਗੇਮ, ਪਿਕਸਲ ਪਰਫੈਕਟ ਟੱਕਰ C#, ਸਪੇਸ਼ੀਅਲ ਹੈਸ਼ ਗਰਿੱਡ ਮੋਨੋਗੇਮ, ਸਵੀਪਟ ਰੇ ਕੰਟੀਨਿਊਟ ਟੱਕਰ, ਇੰਡੀ ਗੇਮ ਪਰਫਾਰਮੈਂਸ ਓਪਟੀਮਾਈਜੇਸ਼ਨ, ਜ਼ੀਰੋ ਅਲੋਕੇਸ਼ਨ ਗੇਮ, ਐਂਡਰਾਇਡ ਮੋਨੋਗੇਮ ਲੂਪ ਲੂਪ, ਬੀ. ਪੈਨਜ਼ਰ, ਪੇਂਟ ਟ੍ਰੈਕ।

#MonoGame #CSharp #GameDev #IndieDev #Gamephysics #DotNet #2DGameDev #MobileGameDev #PerformanceOptimization #CleanCode #BlockedPixelPanzersars #ਗੇਮਪ੍ਰੋਗਰਾਮਿੰਗ




Hiç yorum yok:

Yorum Gönder