XNA / MonoGame — как проверить коллизию для каждого объекта в списке, используя прямоугольник в качестве ограничивающей рамки

#c# #xna #monogame

#c# #xna #monogame

Вопрос:

Я использую фреймворк MonoGame для создания платформерной игры. В игре есть некоторые базовые механики, такие как: гравитация и обнаружение столкновений, которые работают только с последним объектом в списке.

Вот код для игровой области игры (основной скрипт): `

     using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Graphics;
    using Microsoft.Xna.Framework.Input;
    using Survive.Engine;
    using System.Collections.Generic;

    namespace Game
    {
        public class MainGame : Game
        {
            GraphicsDeviceManager graphics;
            SpriteBatch spriteBatch;

            Player player = new Player();

            List<Box> boxes = new List<Box>();

            public MainGame()
            {
                graphics = new GraphicsDeviceManager(this);
                Content.RootDirectory = "Content";
            }

            protected override void Initialize()
            {
                Box box = new Box();
                box.Init(new Vector2(100, 400), Content.Load<Texture2D>("Player"));

                Box box2 = new Box();
                box2.Init(new Vector2(132, 400), Content.Load<Texture2D>("Player"));

                player.Init(new Vector2(100, 100), Content.Load<Texture2D>("Player"));

                boxes.Add(box);
                boxes.Add(box2);

                base.Initialize();
            }

            protected override void LoadContent()
            {
                spriteBatch = new SpriteBatch(GraphicsDevice);
            }

            protected override void UnloadContent()
            {

            }

            protected override void Update(GameTime gameTime)
            {
                if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
                    Exit();

                for (int i = 0; i < boxes.Count; i  )
                {
                    if (player.Colliding(boxes[i]))
                    {
                        player.falling = false;
                    }

                    if (!player.Colliding(boxes[i]))
                    {
                        player.falling = true;
                    }
                }

                player.Update();
                boxes.ForEach(k => k.Update());

                base.Update(gameTime);
            }

            protected override void Draw(GameTime gameTime)
            {
                GraphicsDevice.Clear(Color.CornflowerBlue);

                spriteBatch.Begin();

                player.Draw(spriteBatch);

                for (int i = 0; i < boxes.Count; i  )
                {
                    boxes[i].Draw(spriteBatch);
                }

                spriteBatch.End();

                base.Draw(gameTime);
            }
        }
    }`
  

Вот класс объекта (из которого построен player и box):

`

 using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Game.Engine
{
    public class Object2D
    {
        public int Health = 100;
        public Vector2 Position;
        public Texture2D Texture;
        public Rectangle BoundingBox;

        public bool Colliding(Object2D obj)
        {
            bool col = false;

            if (BoundingBox.Intersects(obj.BoundingBox))
            {
                col = true;
            }

            return col;
        }

        public virtual void Init(Vector2 pos, Texture2D text)
        {
            Position = pos;
            Texture = text;
        }

        public virtual void Update()
        {
            BoundingBox = new Rectangle((int)Position.X, (int)Position.Y, Texture.Width, Texture.Height);
        }

        public virtual void Draw(SpriteBatch spriteBatch)
        {
            spriteBatch.Draw(Texture, Position, Color.White);
        }
    }
}
  

`

Сценарий игрока:

`

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Survive.Engine;
using Microsoft.Xna.Framework.Input;

namespace Survive
{
    public class Player : Object2D
    {
        public float vsp = 0;
        public float grav = 0.1f;
        public bool falling = true;
        public bool jmping = false;

        public override void Init(Vector2 pos, Texture2D text)
        {
            base.Init(pos, text);
        }

        public override void Update()
        {
            if (Keyboard.GetState().IsKeyDown(Keys.A))
            {
                Position.X -= 2;
            }

            if (Keyboard.GetState().IsKeyDown(Keys.D))
            {
                Position.X  = 2;
            }

            if (falling)
            {
                vsp  = grav;
                Position.Y  = vsp;
            }

            base.Update();
        }

        public override void Draw(SpriteBatch spriteBatch)
        {
            base.Draw(spriteBatch);
        }
    }
}`
  

Поле просто наследуется Object2D.cs , код не был изменен.

Но в обновлении пусто MainGame.cs , где мы проверяем наличие столкновения:

`

         if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
            Exit();

        for (int i = 0; i < boxes.Count; i  )
        {
            if (player.Colliding(boxes[i]))
            {
                player.falling = false;
            }

            if (!player.Colliding(boxes[i]))
            {
                player.falling = true;
            }
        }

        player.Update();
        boxes.ForEach(k => k.Update());`
  

Код работает, но только для второго объекта в нашем списке, когда я перехожу через первое добавленное поле, я просто падаю, поскольку применяется сила тяжести.

Я не уверен, почему он проверяет только последнее поле в списке полей.

Ответ №1:

Вы не останавливаете поиск, как только обнаружите коллизию. Измените этот код:

     for (int i = 0; i < boxes.Count; i  )
    {
        if (player.Colliding(boxes[i]))
        {
            player.falling = false;
        }

        if (!player.Colliding(boxes[i]))
        {
            player.falling = true;
        }
    }
  

…к чему-то вроде:

 var isFalling=true;  // assume the worst

for (int i = 0; i < boxes.Count; i  )
    {
        if (player.Colliding(boxes[i]))
        {
            isFalling = false;
            break;
        }
    }

player.falling = isFalling;
  

Комментарии:

1. Спасибо! Я попробую это.

2. Да, он просто не обнаруживает коллизию для первого добавленного поля, я только что провел тест, если удалить второе поле, оно обнаруживает коллизию для первого, но когда добавляется второе, оно обнаруживает его только для первого

3. К сожалению, он обнаруживает коллизию только для второго поля, когда добавляется второе