2016-03-31 3 views
0

Обнаружение столкновений в последнее время стало для меня огромной проблемой, и я не мог решить эту проблему.Обнаружение столкновений в C# XNA

После того как я получил разумное обнаружение столкновения для моего Super Mario World ремейка в C# (XNA) У меня есть следующая проблема: я получаю то застряли в блоках, когда я прыгаю на них ...

Пример: https://gyazo.com/0f1ac6f4894f41aa4bcbdc73e572e36d

Это мой текущий код, чем обрабатывает столкновения: http://pastebin.com/iWsnffWQ

Если кто-нибудь знает что-нибудь, что могло бы помочь моей проблеме, я искал высоко и низко для решения, но я без толку ...

EDIT:

Эта проблема была исправлена, хотя новая возникла при столкновении объектов с печально известным марионеточным «Mystery Blocks». Всякий раз, когда я стою на них (не двигаясь), марио или мир начинают вибрировать вверх и вниз примерно на один пиксель.

using System; 
using System.Collections.Generic; 
using Microsoft.Xna.Framework; 
using Microsoft.Xna.Framework.Content; 
using Microsoft.Xna.Framework.Graphics; 

namespace PlatFormer 
{ 
    public abstract class Entity 
    { 
     protected ContentManager _Content; 

     protected Texture2D _Image; 
     protected Texture2D _Outline; 

     protected SpriteSheetAnimation _MoveAnimation; 

     protected FileManager _FileManager; 

     protected List<List<string>> _Attributes; 
     protected List<List<string>> _Contents; 

     protected Vector2 _Velocity; 
     protected Vector2 _PrevPosition; 
     protected Vector2 _Frames; 

     protected Rectangle _Collbox; 
     private Rectangle _TileBounds; 

     protected int _Health; 

     protected float _MoveSpeed; 
     protected float _Gravity; 
     protected float _PreviousBottom; 

     protected bool _ActivateGravity; 
     protected bool _TilePositionSync; 
     protected bool _FacingRight; 
     protected const float _Friction = 0.9f; 
     protected const float _Grav = 10f; 
     protected const float _TerminalVelocity = 10f; 
     protected Vector2 _Acceleration; 

     public bool _OnGround; 
     protected bool _IsJumping; 
     protected int collisiondeny; 

     public Vector2 Position; 

     public SpriteSheetAnimation Animation 
     { 
      get { return _MoveAnimation; } 
     } 

     public virtual void LoadContent(ContentManager content) 
     { 
      _Content = new ContentManager(content.ServiceProvider, "Content"); 
      _Attributes = new List<List<string>>(); 
      _Contents = new List<List<string>>(); 
     } 
     public virtual void LoadContent(ContentManager content, InputManager input) 
     { 
      _Content = new ContentManager(content.ServiceProvider, "Content"); 
      _Attributes = new List<List<string>>(); 
      _Contents = new List<List<string>>(); 
     } 

     public virtual void UnloadContent() 
     { 
      _Content.Unload(); 
     } 

     public virtual void Update(GameTime gameTime, List<List<WorldTile>> TileMap, List<Enemy> EntList) 
     { 
      _PrevPosition = Position; 
      Position.X = _FacingRight ? Position.X + _MoveSpeed : Position.X - _MoveSpeed; 
      _Velocity.Y += _Gravity; 
      if (!_OnGround) { } 
      else 
       _Velocity.Y = 0; 
      UpdatePhysics(gameTime, TileMap, EntList); 
      _MoveAnimation.Position = Position; 
      _MoveAnimation.Update(gameTime); 
     } 

     public virtual void Update(GameTime gameTime, InputManager input, List<List<WorldTile>> TileMap) 
     { 

     } 

     public virtual void Draw(SpriteBatch spriteBatch) 
     { 
      _MoveAnimation.Draw(spriteBatch); 
     } 

     protected virtual void UpdatePhysics(GameTime gameTime, List<List<WorldTile>> TileMap, List<Enemy> EntList) 
     { 
      _Acceleration *= _Friction; 
      _Velocity *= _Friction; 
      _Velocity += _Acceleration; 
      Position.X = _FacingRight ? Position.X + _Velocity.X : Position.X - _Velocity.X; 
      Position.Y += _Velocity.Y; 

      if (Math.Abs(_Acceleration.X) < 0.001f) 
      { 
       _MoveAnimation.IsActive = false; 
      } 

      UpdateCollBox(); 

      CollisionHandle(TileMap); 
      EntCollisionHandle(EntList); 

      if (Position.X == _PrevPosition.X) 
       _Velocity.X = 0; 

      if (Position.Y == _PrevPosition.Y) 
       _Velocity.Y = 0; 
     } 

     protected virtual void UpdatePhysics(GameTime gameTime, InputManager input, List<List<WorldTile>> TileMap) 
     { 
      float totalSecElapsed = gameTime.ElapsedGameTime.Milliseconds/1000f; 
      _Acceleration.X *= _Friction; 
      _Velocity.X *= _Friction; 

      _Acceleration.Y = _Grav; 

      _Velocity.Y += _Acceleration.Y * totalSecElapsed; 
      _Velocity.X += _Acceleration.X; 

      if (_Velocity.Y >= _TerminalVelocity) 
      { 
       _Velocity.Y = _TerminalVelocity; 
      } 

      Position += _Velocity; 

      if (Math.Abs(_Acceleration.X) < 0.001f) 
      { 
       _MoveAnimation.IsActive = false; 
      } 

      UpdateCollBox(); 

      CollisionHandle(TileMap); //replace with horizontal collision first then vertical collision 
      //TestCollisionHandle(TileMap); 

      if (Position.X == _PrevPosition.X) 
       _Velocity.X = 0; 

      if (Position.Y == _PrevPosition.Y) 
       _Velocity.Y = 0; 
     } 

     public void ObjectCollision(List<LevelObject> obj) 
     { 
      //OnThisNiceMysteryBox = false; 
      for (int i = 0; i < obj.Count; i++) 
      { 
       if (_Collbox.Intersects(obj[i].Bounds)) 
       { 
        if (obj[i].Collision != TileCollision.Empty) 
        { 
         Vector2 depth = IntersectDepth(_Collbox, obj[i].Bounds); 
         if (depth != Vector2.Zero) 
         { 
          float absDepthX = Math.Abs(depth.X); 
          float absDepthY = Math.Abs(depth.Y); 
          if (absDepthY < absDepthX) 
          { 
           if (_Collbox.Top <= obj[i].Bounds.Bottom && _Collbox.Top >= obj[i].Bounds.Top) 
           { 
            Vector2 tempPos = obj[i].Position; 
            if (obj[i] is MysteryBox) 
            { 
             obj.Remove(obj[i]); 
             obj.Insert(i, new MysteryBox(tempPos)); 
            } 
           } 

           if (_PreviousBottom <= obj[i].Bounds.Top) 
           { 
            _OnGround = true; 
           } 

           if (obj[i].Collision == TileCollision.Solid || _OnGround) 
           { 
            Position = new Vector2((float)Math.Round(Position.X), (float)Math.Round(Position.Y + depth.Y)); 
            _Velocity.Y = 0; 
            UpdateCollBox(); 
           } 
          } 
          else if (obj[i].Collision == TileCollision.Solid) 
          { 
           Position = new Vector2((float)Math.Round(Position.X + depth.X), (float)Math.Round(Position.Y)); 
           UpdateCollBox(); 
          } 
         } 
        } 
       } 
       _PreviousBottom = _Collbox.Bottom; 
      } 
     } 

     protected void EntCollisionHandle(List<Enemy> EntList) 
     { 
      for (int i = 0; i < EntList.Count; i++) 
      { 
       if (!(EntList[i] == this)) 
       { 
        Vector2 intersection = IntersectDepth(this._Collbox, EntList[i]._Collbox); 
        if (intersection != Vector2.Zero) 
        { 
         if (collisiondeny == 0) 
         { 
          _FacingRight = !_FacingRight; 
          Position.X = _FacingRight ? Position.X - intersection.X : Position.X + intersection.X; 
          collisiondeny = 1; 
         } 
         else 
         { 
          collisiondeny--; 
         } 
         //if intersection has occured call both collision handles in colliding classes 
        } 
       } 
      } 
     } 

     protected void CollisionHandle(List<List<WorldTile>> TileMap) 
     { 
      int leftTile = (int)Math.Floor((float)_Collbox.Left/WorldTile.Width); 
      int rightTile = (int)Math.Ceiling(((float)_Collbox.Right/WorldTile.Width)) - 1; 
      int topTile = (int)Math.Floor((float)_Collbox.Top/WorldTile.Height); 
      int bottomTile = (int)Math.Ceiling(((float)_Collbox.Bottom/WorldTile.Height)) - 1; 

      _OnGround = false; 

      for (int y = topTile; y <= bottomTile; y++) 
      { 
       for (int x = leftTile; x <= rightTile; x++) 
       { 
        TileCollision collision = TileCollision.Empty; 
        if (y >= 0) 
        { 
         if (x >= 0) 
         { 
          if (y < TileMap.Count && x < TileMap[y].Count) 
           collision = TileMap[y][x].Collision; 
         } 
         else 
         { 
          collision = TileCollision.Solid; 
         } 
        } 

        if (collision != TileCollision.Empty) 
        { 
         _TileBounds = new Rectangle(x * WorldTile.Width, y * WorldTile.Height, WorldTile.Width, WorldTile.Height); 
         Vector2 depth = IntersectDepth(_Collbox, _TileBounds); 
         if (depth != Vector2.Zero) 
         { 

          float absDepthX = Math.Abs(depth.X); 
          float absDepthY = Math.Abs(depth.Y); 

          if (absDepthY <= absDepthX || collision == TileCollision.OneWay) 
          { 
           if (_PreviousBottom <= _TileBounds.Top) 
            _OnGround = true; 

           if ((collision == TileCollision.Solid) || _OnGround) 
           { 
            Position = new Vector2((int)Math.Round(Position.X), (int)Math.Round(Position.Y + depth.Y)); 
            UpdateCollBox(); 
           } 
          } 
          else if (collision == TileCollision.Solid) 
          { 
           Position = new Vector2((int)Math.Round(Position.X + depth.X), (int)Math.Round(Position.Y)); 
           _FacingRight = !_FacingRight; 
           //_Velocity.Y = 0; 
           UpdateCollBox(); 
          } 
         } 
        } 
       } 
      } 
      _PreviousBottom = _Collbox.Bottom; 
     } 

     protected void TestCollisionHandle(List<List<WorldTile>> TileMap) 
     { 
      int leftTile = (int)Math.Floor((float)_Collbox.Left/WorldTile.Width); 
      int rightTile = (int)Math.Ceiling(((float)_Collbox.Right/WorldTile.Width)) - 1; 
      int topTile = (int)Math.Floor((float)_Collbox.Top/WorldTile.Height); 
      int bottomTile = (int)Math.Ceiling(((float)_Collbox.Bottom/WorldTile.Height)) - 1; 

      _OnGround = false; 

      for (int y = topTile; y <= bottomTile; y++) 
      { 
       for (int x = leftTile; x <= rightTile; x++) 
       { 
        TileCollision collision = TileCollision.Empty; 
        if (y >= 0) 
        { 
         if (x >= 0) 
         { 
          if (y < TileMap.Count && x < TileMap[y].Count) 
           collision = TileMap[y][x].Collision; 
         } 
        } 
        if(collision != TileCollision.Empty) 
        { 
         //if collision can occor, get tilecollisionbox for horizontal 
         _TileBounds = new Rectangle(x * WorldTile.Width, y * WorldTile.Height, WorldTile.Width, WorldTile.Height); 
         //get the horizontal collision depth, will return zero if none is found 
         GetHorizontalIntersectionDepth(_Collbox, _TileBounds); 
        } 
       } 
      } 

      _PreviousBottom = _Collbox.Bottom; 
     } 

     private void UpdateCollBox() 
     { 
      _Collbox = new Rectangle((int)Math.Round(Position.X), (int)Math.Round(Position.Y), Animation.FrameWidth, Animation.FrameHeight); 
     } 

     private Vector2 IntersectDepth(Rectangle rectangleA, Rectangle rectangleB) 
     { 
      float halfWidthA = rectangleA.Width/2.0f; 
      float halfHeightA = rectangleA.Height/2.0f; 
      float halfWidthB = rectangleB.Width/2.0f; 
      float halfHeightB = rectangleB.Height/2.0f; 

      Vector2 centerA = new Vector2(rectangleA.Left + halfWidthA, rectangleA.Top + halfHeightA); 
      Vector2 centerB = new Vector2(rectangleB.Left + halfWidthB, rectangleB.Top + halfHeightB); 

      float distanceX = centerA.X - centerB.X; 
      float distanceY = centerA.Y - centerB.Y; 
      float minDistanceX = halfWidthA + halfWidthB; 
      float minDistanceY = halfHeightA + halfHeightB; 

      // If no intersection is happening, return Vector2.Zero 
      if (Math.Abs(distanceX) >= minDistanceX || Math.Abs(distanceY) >= minDistanceY) 
       return Vector2.Zero; 

      // Calculate instersection depth 
      float depthX = distanceX > 0 ? minDistanceX - distanceX : -minDistanceX - distanceX; 
      float depthY = distanceY > 0 ? minDistanceY - distanceY : -minDistanceY - distanceY; 
      return new Vector2(depthX, depthY); 
     } 

     private float GetHorizontalIntersectionDepth(Rectangle rectA, Rectangle rectB) 
     { 
      // Calculate half sizes. 
      float halfWidthA = rectA.Width/2.0f; 
      float halfWidthB = rectB.Width/2.0f; 

      // Calculate centers. 
      float centerA = rectA.Left + halfWidthA; 
      float centerB = rectB.Left + halfWidthB; 

      // Calculate current and minimum-non-intersecting distances between centers. 
      float distanceX = centerA - centerB; 
      float minDistanceX = halfWidthA + halfWidthB; 

      // If we are not intersecting at all, return (0, 0). 
      if (Math.Abs(distanceX) >= minDistanceX) 
       return 0f; 

      // Calculate and return intersection depths. 
      return distanceX > 0 ? minDistanceX - distanceX : -minDistanceX - distanceX; 
     } 

     private float GetVerticalIntersectionDepth(Rectangle rectA, Rectangle rectB) 
     { 
      // Calculate half sizes. 
      float halfHeightA = rectA.Height/2.0f; 
      float halfHeightB = rectB.Height/2.0f; 

      // Calculate centers. 
      float centerA = rectA.Top + halfHeightA; 
      float centerB = rectB.Top + halfHeightB; 

      // Calculate current and minimum-non-intersecting distances between centers. 
      float distanceY = centerA - centerB; 
      float minDistanceY = halfHeightA + halfHeightB; 

      // If we are not intersecting at all, return (0, 0). 
      if (Math.Abs(distanceY) >= minDistanceY) 
       return 0f; 

      // Calculate and return intersection depths. 
      return distanceY > 0 ? minDistanceY - distanceY : -minDistanceY - distanceY; 
     } 
     public enum Direction 
     { 
      Horizontal, 
      Vertical 
     } 

     private bool TileIntersectsPlayer(Rectangle player, Rectangle block, Direction direction, out Vector2 depth) 
     { 
      depth = direction == Direction.Vertical ? new Vector2(0, GetVerticalIntersectionDepth(player, block)) : new Vector2(GetHorizontalIntersectionDepth(player, block), 0); 
      return depth.Y != 0 || depth.X != 0; 
     } 
    } 
} 
+1

Вам нужно будет опубликовать свой код здесь, чтобы другие исследовали его решение. – ManoDestra

+0

Или посмотрите [BEPUPhysics] (https://bepuphysics.codeplex.com/). Или посмотрите здесь: http://stackoverflow.com/questions/1388293/xna-3d-physics-engine. Или: http://gamedev.stackexchange.com/questions/318/what-are-some-known-2d-3d-physics-engines-for-xna – ManoDestra

+0

. Верно, я добавлю несколько подробностей о моей проблеме, код находится в ссылке pastebin, которая обрабатывает столкновение –

ответ

0

Рассмотрите возможность использования или просто сравнить свой код библиотеки, которые существуют уже: см GeoLib, который очень прост в использовании и/или clipper lib. 'Не изобретать ...'

+0

_ «Не изобретайте» _ - почтительно, большинство разработчиков XNA, если SO есть какое-либо указание, чувствую, что они хотят использовать XNA по самой причине «прокрутки» своих собственных. В противном случае тег 'xna' будет бездействовать со всеми, скажем,' unity3d' или 'unreal'. В любом случае, давайте просто надеемся, что авторы GeoLib и Clipper lib получили мотивацию от других, когда объявили, что они будут «изобретать» обнаружение столкновения. Всегда есть место для улучшения – MickyD

Смежные вопросы