2016-03-31 49 views
0

Son zamanlarda benim için çok büyük bir sorun olan çarpışma tespiti, bu özel sorun üzerinde aklımı kırmak oldu.C# XNA'da Çarpışma Algılama

C# (XNA) Aşağıdaki sorun var benim Süper Mario Dünya remake için makul bir çarpışma algılama var sonra: Ben onlara karşı atladığında bloklar halinde sıkışmış çeşit almaya devam ...

Örnek: https://gyazo.com/0f1ac6f4894f41aa4bcbdc73e572e36d

Bu çarpışmayı kolları daha benim geçerli kod:

kimse sorunumu yardımcı olabilecek bir şey biliyorsa http://pastebin.com/iWsnffWQ, ben çözüm için yüksek ve düşük aradık ama ben

... boşuna

EDIT:

Bu sorun, "Mystery Blocks" adındaki kötü niyetli nesne üzerinde bir nesne çarpışmasıyla ortaya çıkmış olsa da giderilmiştir. Ne zaman yanımda durduğumda (hareket etmiyorsa) ya mario ya da dünya bir piksel kadar yukarı ve aşağı titreşmeye başlar.

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

Kodunuzu başkaları için bir çözüm için incelemeye göndermelisiniz. – ManoDestra

+0

Veya [BEPUPhysics] (https://bepuphysics.codeplex.com/) 'a bakın. Veya buraya bakın: http://stackoverflow.com/questions/1388293/xna-3d-physics-engine. Veya: http://gamedev.stackexchange.com/questions/318/what-are-some-known-2d-3d-physics-engines-for-xna – ManoDestra

+0

ALright, Sorunum hakkında daha fazla ayrıntı ekleyeceğim kod, çakışma bağlantısı içinde –

cevap

0

kullanmayı düşünün ya da sadece önceden mevcut kütüphaneler için kodunuzu karşılaştırmak: ve/veya clipper lib kullanımı çok basittir GeoLib bakın. 'Yeniden icat etmeyin ...'

+0

_ "Yeniden icat etmeyin" _ - eğer sayılırsa XNA geliştiricilerinin çoğunluğu herhangi bir endikasyon, XNA'yı kendi dosyalarını _roll etmek için kullanmak istediklerini hissediyorum. Aksi takdirde, 'xna' etiketi herkesle "unity3d" veya "unreal" demiyor. Her neyse, sadece GeoLib ve Clipper lib yazarlarının, çarpışma tespitini "yeniden başlatacaklarını" söyledikleri zaman başkalarından motivasyon almalarını umalım. İyileştirme için her zaman yer var – MickyD