C# - Мой код столкновения не приведет к остановке игрока на его треках

Я работал над римейком C# & SFML для одного из моих движков LWJGL. Пока что все идет хорошо, но когда я попытался скопировать / преобразовать код столкновения, все пошло не так хорошо.

Вот мой код для класса игрока / сущности, ограничительной рамки и кода игры.

Сущность:

    using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SFML.Graphics;
using SFML.System;
using SFML.Window;

namespace FastSrc_CS.Core
{
    public class FSEntity
    {
        public Vector2f Position, Size;
        public Color EColor;

        public RectangleShape sprite;

        public BoundingBox Bounds;

        public bool canMove;
        public float VelX, VelY;

        public FSEntity()
        {

        }

        public virtual void Init(Vector2f pos, Vector2f size, Color color)
        {
            Position = pos;
            Size = size;
            EColor = color;

            sprite = new RectangleShape();
            sprite.FillColor = EColor;
            sprite.Position = Position;
            sprite.Size = Size;
            //sprite.Origin = new Vector2f(size.X / 2, size.Y / 2);

            Bounds = new BoundingBox(Position, Size);
        }

        public void SetVelX(float x)
        {
            VelX = x;
        }

        public void SetVelY(float x)
        {
            VelY = x;
        }

        public Vector2f GetOrigin()
        {
            return new Vector2f(Size.X / 2, Size.Y / 2);
        }

        public virtual void Update()
        {

        }

        public void UpdatePos()
        {
            Position.X += VelX;
            Position.Y += VelY;
            sprite.Position = Position;
            Bounds.UpdateBounds(Position, Size);
        }

        public virtual void Render(RenderWindow w)
        {
            w.Draw(sprite);
        }
    }
}

Игрок:

    using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SFML.Graphics;
using SFML.System;
using SFML.Window;

namespace FastSrc_CS
{
    class Player : Core.FSEntity
    {
        public float speed = 6f;

        public override void Init(Vector2f pos, Vector2f size, Color color)
        {
            canMove = true;
            base.Init(pos, size, color);
        }

        public override void Update()
        {
            base.Update();

            Movement();

            Position.X += VelX;
            Position.Y += VelY;
            sprite.Position = Position;
            Bounds.UpdateBounds(Position, Size);
        }

        public void Movement()
        {
            if (Keyboard.IsKeyPressed(Keyboard.Key.A) && canMove == true)
            {
                SetVelX(-speed);
                SetVelY(0);
            }
            else if (Keyboard.IsKeyPressed(Keyboard.Key.D) && canMove == true)
            {
                SetVelX(speed);
                SetVelY(0);
            }
            else if (Keyboard.IsKeyPressed(Keyboard.Key.W) && canMove == true)
            {
                SetVelY(-speed);
                SetVelX(0);
            }
            else if (Keyboard.IsKeyPressed(Keyboard.Key.S) && canMove == true)
            {
                SetVelY(speed);
                SetVelX(0);
            }
            else
            {
                SetVelX(0);
                SetVelY(0);
            }
        }

        public override void Render(RenderWindow w)
        {
            base.Render(w);
        }
    }
}

Ограничительная рамка:

    using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SFML.Graphics;
using SFML.System;
using SFML.Window;

namespace FastSrc_CS.Core
{
    public class BoundingBox
    {
        public FloatRect Rectangle;

        public BoundingBox(Vector2f pos, Vector2f size)
        {
            Rectangle = new FloatRect(pos, size);
        }

        public bool Collide(BoundingBox b)
        {
            bool col = false;

            if (this.Rectangle.Intersects(b.Rectangle))
            {
                col = true;
            }
            else
            {
                col = false;
            }

            return col;
        }

        public void UpdateBounds(Vector2f pos, Vector2f size)
        {
            Rectangle.Width = size.X;
            Rectangle.Height = size.Y;
            Rectangle.Left = pos.X;
            Rectangle.Top = pos.Y;
        }
    }
}

Игра:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FastSrc_CS.Core;
using SFML.Graphics;
using SFML.System;
using SFML.Window;

namespace FastSrc_CS
{
    public class Game : FSGame
    {
        Player p = new Player();
        Wall w = new Wall();

        public override void Init()
        {
            p.Init(new Vector2f(400, 300), new Vector2f(32, 32), Color.Red);
            w.Init(new Vector2f(100, 100), new Vector2f(32, 32), Color.Blue);

            Entities.Add(p);
            Entities.Add(w);
        }

        public override void Update()
        {
            Console.WriteLine(p.Position.X);

            //COLLISION CODE FOR WALLS
            if (p.Bounds.Collide(w.Bounds))
            {
                //Right Collision
                if (p.VelX > 0)
                {
                    Console.WriteLine("Right Collision");
                    p.canMove = false;
                    p.Position.X = w.Position.X - 32;
                    p.SetVelX(0);
                }
                else if (p.VelX < 0)
                {
                    Console.WriteLine("Left Collision");
                    p.canMove = false;
                    p.Position.X = w.Position.X + 32;
                    p.SetVelX(0);
                }

                if (p.VelX == 0)
                {
                    p.canMove = true;
                }
            }

            Entities.ForEach(k => k.Update());
        }

        public override void Render(RenderWindow w)
        {
            Entities.ForEach(k => k.Render(w));
        }
    }
}

Проблема заключается в классе "игры" в этом регионе:

        //COLLISION CODE FOR WALLS
        if (p.Bounds.Collide(w.Bounds))
        {
            //Right Collision
            if (p.VelX > 0)
            {
                Console.WriteLine("Right Collision");
                p.canMove = false;
                p.Position.X = w.Position.X - 32;
                p.SetVelX(0);
            }
            else if (p.VelX < 0)
            {
                Console.WriteLine("Left Collision");
                p.canMove = false;
                p.Position.X = w.Position.X + 32;
                p.SetVelX(0);
            }

            if (p.VelX == 0)
            {
                p.canMove = true;
            }
        }

Когда я запускаю это, игрок перестает двигаться, но он будет слегка имплантирован в стену. Я знаю, о чем ты думаешь, "переместите утверждение if, которое делает p.canMove = true вне оператора if столкновения."Я пробовал это, и это приводит к тому, что игрок отскакивает от куба туда-сюда. Я пробовал разные методы до такой степени, что думал об исключении этого кода. Я решил вернуться вернуться к переполнению стека, чтобы узнать, может ли кто-нибудь мне помочь. Заранее спасибо.

1 ответ

Решение

Я не думаю, что проблема с вашим кодом вообще связана с вашим движком - учитывая результат, и после сканирования кода все выглядит нормально. Поскольку ваш игрок врезается в стену, жестко запрограммированное значение 32 для вашего игрока и вашей стены, скорее всего, неверно. Попробуй это:

        if (p.VelX > 0)
        {
            Console.WriteLine("Right Collision");
            p.canMove = false;
            //move the right of our player to the left of the wall by setting
            //his x to the wall's x minus his width.
            p.Position.X = w.Position.X - p.Size.Width;
            p.SetVelX(0);
        }
        else if (p.VelX < 0)
        {
            Console.WriteLine("Left Collision");
            p.canMove = false;
            //Move the left of our player to the right of the wall, or
            //the wall's x plus the wall's width
            p.Position.X = w.Position.X + w.Size.Width;
            p.SetVelX(0);
         }

Я надеюсь, что это помогло.

РЕДАКТИРОВАТЬ

На самом деле, я думаю, что нашел другую проблему. Когда вы изменяете положение игрока после перемещения его X после обнаружения столкновения, вы забываете вызвать UpdatePos() метод. Таким образом, вы перемещаете его положение, но не фактический прямоугольник, что может вызвать ошибку. Скорее после того, как вы переместите его х, немедленно позвоните UpdatePos()

Другие вопросы по тегам