2016-03-31 27 views
0

最近、衝突の検出が大きな問題になっています。C#XNAでの衝突検出

私はC#の(XNA)で私のスーパーマリオワールドのリメイクのための合理的な衝突検出を得た後、私は次のような問題を持っている:私は、私は彼らに対してジャンプするときのブロックのスタックの並べ替えを得続ける...

例: https://gyazo.com/0f1ac6f4894f41aa4bcbdc73e572e36d

これは、衝突を扱うよりも、私の現在のコードです:

誰もが私の問題を助けることができる何かを知っている場合http://pastebin.com/iWsnffWQ、私は解決策のためのハイとローの検索されていますが、私

...無駄に

編集:

悪意のあるマリオの "Mystery Blocks"にオブジェクト衝突が発生したが、この問題は修正されました。私がそれらの上に立っているとき(動かない)、マリオまたは世界は、約1ピクセルだけ上下に振動し始める。

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

使用を検討してか、単に既に存在しているライブラリにあなたのコードを比較:および/またはclipper libを使用することは非常に簡単ですGeoLibを参照してください。 '再作成しないでください...'

+0

_それを再考しないでください_彼らは自分の_rollingの理由のためにXNAを使いたいと思っています。さもなければ 'xna'タグは誰もが' unity3d'または 'unreal'と言ってアイドルになります。とにかく、GeoLibとClipperの作者が衝突の検出を再考すると発表したときに、他の人たちから動機を得てくれることを願っています。常に改善の余地があります – MickyD