2011-01-12 6 views
4

2D Pong Cloneを作成するときに、私のUIの上部と下部の 'Walls'からボールを​​跳ね返させようとしています。 これは、それは動作しますが、私は手動で入力ノーマルを使用していると私がしたい私はXNA - ポンクローン - ボールが壁に当たったときに反射しますか?

public void Move(bool IsCollidingWithWall) 
    { 
     if (IsCollidingWithWall) 
     { 
      Vector2 normal = new Vector2(0, 1); 
      Direction = Vector2.Reflect(Direction,normal); 
      this.Position += Direction; 
      Console.WriteLine("WALL COLLISION"); 
     } 
     else 
      this.Position += Direction; 
    } 

私Ball.csでこれを使用しています現時点では私のGame.cs

public void CheckBallPosition() 
{ 
    if (ball.Position.Y == 0 || ball.Position.Y >= graphics.PreferredBackBufferHeight) 
     ball.Move(true); 
    else 
     ball.Move(false); 

    if (ball.Position.X < 0 || ball.Position.X >= graphics.PreferredBackBufferWidth) 
     ball.Reset(); 
} 

です画面の上部と下部の法線を計算する方法を知りたいですか?

enum CollideType 
{ 
    None, 
    Vertical, 
    Horizontal 
} 

を、通常作成するときに、このタイプをチェック:

答えて

1

あなたは、のようないくつか列挙してブールIsCollidingWithWallを変更することができます。

+0

は、しかし、それを行うための、よりエレガントな方法はありますか?私はいつもこのゲームを作るときにハッキングしているような気がする! – JuniorDeveloper1208

+1

クリーナーかどうか分かりませんが、スイッチが入っている列挙型の拡張メソッド(例: 'GetNormal(this CollideType collideType)')を作成し、それぞれに標準を返します。 – Cameron

+0

ありがとう!コードは単純だが、数学は難しい。とにかく4.0のためではなく、Web上に多くのドキュメンテーションがありません。 – JuniorDeveloper1208

0

ボールの位置から壁の位置を差し引いて、そのベクトルを正規化して、ハードコーディングすることなく必要なものを得るのではないでしょうか?

残りのコードは同じにする必要があります。

+0

壁の位置を取得するにはどうすればよいですか? – JuniorDeveloper1208

+0

壁の位置はあなたが知っているものでなければなりません。それは決して動かないので定数か、スプライトオブジェクトであり、位置を持っています。 –

4

まあ、これは私がしかし、この「離散」衝突検出を使用して、あなたはボールが画面の上部/下部に通過した後まで待つこと、それ

public void CheckBallPositionAndMove() 
{ 
    if (ball.Position.Y <= 0 || ball.Position.Y >= graphics.PreferredBackBufferHeight) 
     ball.HandleWallCollision(); 

    ball.Move(); 

    if (ball.Position.X < 0 || ball.Position.X >= graphics.PreferredBackBufferWidth) 
     ball.Reset(); 
} 

//In Ball.cs: 
private void HandleWallCollision(Vector2 normal) 
{ 
    Direction.Y *= -1; //Reflection about either normal is the same as multiplying y-vector by -1 
} 

private void Move() 
{ 
    this.Position += Direction; 
} 

注意を処理する方法をあります衝突を検出する。 フレーム間に発生する衝突は、特にボールが速く動いている場合に顕著には消えることがあります。これは特にです。この衝突検出方法を使用してパドルとの衝突を検出すると、ボールが十分に速く動いている場合、ボールがパドルを右に移動する可能性があります。

この問題の解決策は、Continuous Collision Detectionという名前のものを使用することです。 CCDは、離散衝突検出よりもはるかに複雑です。幸いなことに、ピンポンはCCDがやや複雑になるほど単純です。しかし、方程式を解くにはまだ高校代数の強固な把握が必要です。

まだ興味があるのであれば、this lectureにはCCDの説明があり、this GameDev articleはもう少し詳しく説明しています。それに関連するmanyquestionsもあります。

+0

助けてくれてありがとう:) – JuniorDeveloper1208

1

あなたの世界の各境界は線です。ラインの片側は実線で、もう片方は実線ではありません。計算しようとしている法線は、その線の方程式の一部です。これは、ラインの非固体側を指します。線方程式の他の部分は、線から原点までの距離です。その線の方程式は、その線上の2点から求めることができます。ゲーム空間内で壁が必要な座標に基づいて、これら2つのポイントを定義することができます。

法線は、2つの点で定義された線分を90度回転させ、次に正規化することによって計算されます。

public static Vector2 ComputeNormal(Vector2 point1, Vector2 point2) 
{ 
    Vector2 normal = new Vector2(); 
    normal.X = point2.Y - point1.Y; 
    normal.Y = point1.X - point2.X; 

    normal.Normalize(); 

    return normal; 
} 

あなたは、法線を計算するために使用されるポイントを定義するためにこれらを使用するように、あなたの世界空間を形成するために戻って好まバッファの幅と高さを使用しています。

float left = 0.0f; 
float right = graphics.PreferredBackBufferWidth; 
float top = 0.0f; 
float bottom = graphics.PreferredBackBufferHeight; 

Vector2 topNormal = ComputeNormal(new Vector2(left, top), new Vector2(right, top)); 
Vector2 bottomNormal = ComputeNormal(new Vector2(right, bottom), new Vector2(left, bottom)); 

ポイントは、時計回りの順序で指定する必要があります。これにより、法線が正しい方向を指すようになります。

次XNA 4.0のプログラムが使用中のこれらの概念を示しています。良い解決策だ

using System; 
using Microsoft.Xna.Framework; 
using Microsoft.Xna.Framework.Graphics; 
using Microsoft.Xna.Framework.Input; 

namespace WindowsGame 
{ 
    public class Ball 
    { 
     const int DIAMETER = 40; 
     const float RADIUS = DIAMETER * 0.5f; 
     const float MASS = 0.25f; 
     const int PIXELS = DIAMETER * DIAMETER; 

     static readonly uint WHITE = Color.White.PackedValue; 
     static readonly uint BLACK = new Color(0, 0, 0, 0).PackedValue; 

     Texture2D m_texture; 
     Vector2 m_position; 
     Vector2 m_velocity; 

     public Ball(GraphicsDevice graphicsDevice) 
     { 
      m_texture = new Texture2D(graphicsDevice, DIAMETER, DIAMETER); 

      uint[] data = new uint[PIXELS]; 

      for (int i = 0; i < DIAMETER; i++) 
      { 
       float iPosition = i - RADIUS; 

       for (int j = 0; j < DIAMETER; j++) 
       { 
        data[i * DIAMETER + j] = new Vector2(iPosition, j - RADIUS).Length() <= RADIUS ? WHITE : BLACK; 
       } 
      } 

      m_texture.SetData<uint>(data); 
     } 

     public float Radius 
     { 
      get 
      { 
       return RADIUS; 
      } 
     } 

     public Vector2 Position 
     { 
      get 
      { 
       return m_position; 
      } 
     } 

     public Vector2 Velocity 
     { 
      get 
      { 
       return m_velocity; 
      } 

      set 
      { 
       m_velocity = value; 
      } 
     } 

     public void ApplyImpulse(Vector2 impulse) 
     { 
      Vector2 acceleration = impulse/MASS; 
      m_velocity += acceleration; 
     } 

     public void Update(float dt) 
     { 
      m_position += m_velocity; // Euler integration - innaccurate and unstable but it will do for this simulation 
     } 

     public void Draw(SpriteBatch spriteBatch) 
     { 
      spriteBatch.Draw(m_texture, DrawRectangle, Color.White); 
     } 

     private Rectangle DrawRectangle 
     { 
      get 
      { 
       int x = (int)Math.Round(m_position.X - RADIUS); 
       int y = (int)Math.Round(m_position.Y - RADIUS); 

       return new Rectangle(x, y, DIAMETER, DIAMETER); 
      } 
     } 
    } 

    public class Boundary 
    { 
     private Vector2 m_point1; 
     private Vector2 m_point2; 
     private Vector2 m_normal; 
     private float m_distance; 

     public Boundary(Vector2 point1, Vector2 point2) 
     { 
      m_point1 = point1; 
      m_point2 = point2; 

      m_normal = new Vector2(); 
      m_normal.X = point2.Y - point1.Y; 
      m_normal.Y = point1.X - point2.X; 

      m_distance = point2.X * point1.Y - point1.X * point2.Y; 

      float invLength = 1.0f/m_normal.Length(); 

      m_normal *= invLength; 
      m_distance *= invLength; 
     } 

     public Vector2 Normal 
     { 
      get 
      { 
       return m_normal; 
      } 
     } 

     public void PerformCollision(Ball ball) 
     { 
      float distanceToBallCenter = DistanceToPoint(ball.Position); 

      if (distanceToBallCenter <= ball.Radius) 
      { 
       ResolveCollision(ball); 
      } 
     } 

     public void ResolveCollision(Ball ball) 
     { 
      ball.Velocity = Vector2.Reflect(ball.Velocity, m_normal); 
     } 

     private float DistanceToPoint(Vector2 point) 
     { 
      return 
       m_normal.X * point.X + 
       m_normal.Y * point.Y + 
       m_distance; 
     } 
    } 

    public class World 
    { 
     Boundary m_left; 
     Boundary m_right; 
     Boundary m_top; 
     Boundary m_bottom; 

     public World(float left, float right, float top, float bottom) 
     { 
      m_top = new Boundary(new Vector2(right, top), new Vector2(left, top)); 
      m_right = new Boundary(new Vector2(right, bottom), new Vector2(right, top)); 
      m_bottom = new Boundary(new Vector2(left, bottom), new Vector2(right, bottom)); 
      m_left = new Boundary(new Vector2(left, top), new Vector2(left, bottom)); 
     } 

     public void PerformCollision(Ball ball) 
     { 
      m_top.PerformCollision(ball); 
      m_right.PerformCollision(ball); 
      m_bottom.PerformCollision(ball); 
      m_left.PerformCollision(ball); 
     } 
    } 

    public class Game1 : Microsoft.Xna.Framework.Game 
    { 
     GraphicsDeviceManager graphics; 
     SpriteBatch spriteBatch; 
     Matrix viewMatrix; 
     Matrix inverseViewMatrix; 
     Ball ball; 
     World world; 

     public Game1() 
     { 
      graphics = new GraphicsDeviceManager(this); 
      Content.RootDirectory = "Content"; 
      IsMouseVisible = true; 
     } 

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

      ball = new Ball(GraphicsDevice); 

      float right = Window.ClientBounds.Width * 0.5f; 
      float left = -right; 
      float bottom = Window.ClientBounds.Height * 0.5f; 
      float top = -bottom; 

      world = new World(left, right, top, bottom); 

      viewMatrix = Matrix.CreateTranslation(Window.ClientBounds.Width * 0.5f, Window.ClientBounds.Height * 0.5f, 0.0f); 
      inverseViewMatrix = Matrix.Invert(viewMatrix); 

      base.Initialize(); 
     } 

     private void ProcessUserInput() 
     { 
      MouseState mouseState = Mouse.GetState(); 

      Vector2 mousePositionClient = new Vector2((float)mouseState.X, (float)mouseState.Y); 
      Vector2 mousePositionWorld = Vector2.Transform(mousePositionClient, inverseViewMatrix); 

      if (mousePositionWorld != ball.Position) 
      { 
       Vector2 impulse = mousePositionWorld - ball.Position; 
       impulse *= 1.0f/impulse.LengthSquared(); 
       ball.ApplyImpulse(-impulse); 
      } 
     } 

     protected override void Update(GameTime gameTime) 
     { 
      if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) 
       this.Exit(); 

      float dt = (float)gameTime.ElapsedGameTime.TotalSeconds; 

      ProcessUserInput(); 

      ball.Update(dt); 
      world.PerformCollision(ball); 

      base.Update(gameTime); 
     } 

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

      spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, viewMatrix); 

      ball.Draw(spriteBatch); 

      spriteBatch.End(); 

      base.Draw(gameTime); 
     } 
    } 
} 
関連する問題