2009-05-24 5 views
3

Visual C# 2005에서 테트리스 게임을 프로그래밍하고 있습니다. 아직 설계된 프로그램 중 가장 광범위합니다.C# - 테트리스 클론 - 화살표 키 조합에 대해 적절하게 응답 할 블록을 얻을 수 없음

다른 테트리스 조각의 위치, 이동 및 표시를 제어하기 위해 모양 클래스와 블록 클래스를 만듭니다. 각각의 도형 (및 대응하는 canMoveDown(), canMoveLeft(), canMoveRight() 부울 함수)에 대해 moveDown(), moveLeft() 및 moveRight() 함수를 가지고 있습니다. 이것은 모두 아름답게 작동합니다.

아래쪽, 오른쪽 및 왼쪽 화살표 키를 사용하여 사용자가 블록을 이동할 수있게하고 타이머를 사용하여 셰이프가 너무 많은 밀리 초마다 한 행씩 자동으로 떨어지도록하고 싶습니다.

KeyDown 이벤트 처리기를 사용하여 사용자가 아래쪽, 왼쪽 및 오른쪽 화살표 키를 눌렀을 때를 확인하고 있습니다. 이것은 그렇게 어렵지 않습니다. 문제는 대각선 동작을 허용하고 싶기 때문에 원활하게 작동하도록합니다. 나는 다양한 수준의 성공으로이 문제에 접근하는 여러 가지 방법을 시도해 보았습니다. 그러나 나는 그것을 올바르게 할 수 없다 ...

내 가장 성공적인 접근법은 아래, 왼쪽 및 오른쪽 화살표 키를 누르고있는 시간을 추적하기 위해 3 개의 부울 변수를 사용하는 것이 었습니다. KeyDown 이벤트에서는 부울을 true로 설정하고 KeyUp 이벤트에서는 부울을 false로 설정합니다. KeyDown 이벤트에서 부울 변수를 사용하여 현재 눌려지고있는 조합을 확인하기 위해 이동 방법을 블록에 알려줄 것입니다. 그것은 한 가지를 제외하고는 정말 잘 작동했습니다.

화살표 키 중 하나를 누른 상태에서 두 번째 화살표 키를 누른 다음 두 번째 키를 놓으면 첫 번째 화살표 키 방향으로 계속 이동하는 대신 블록이 완전히 이동하지 않습니다 아직 풀어 봤어. 이것은 두 번째 키가 KeyDown 이벤트를 트리거했기 때문이며, 첫 번째 키가 시작되었다하더라도 KeyUp 이벤트가 시작되고 KeyDp 이벤트가 완전히 시작되는 것을 중지했기 때문입니다.

나는이 문제에 대한 만족스러운 해결책을 찾을 수 없다.

답변

8

대부분의 게임은 이벤트를 기다리지 않습니다. 그들은 필요할 때 입력 장치를 폴링하여 우연히 행동합니다. 사실, XNA를 살펴 본다면, 업데이트 루틴에서 호출 할 Keyboard.GetState() 메서드 (또는 Gamepad.GetState())를 볼 수 있습니다. 결과. Windows.Forms로 작업 할 때이 작업을 수행하는 동안 아무 것도 준비가되어 있지 않지만 Get/BokeState() 함수를 P/Invoke하여이 기능을 활용할 수 있습니다. 이것에 대한 좋은 점은 한 번에 여러 개의 키를 폴링 할 수 있으므로 한 번에 여러 키 누르기에 반응 할 수 있다는 것입니다. 설명하기 위해

http://sanity-free.org/17/obtaining_key_state_info_in_dotnet_csharp_getkeystate_implementation.html

, 나는 기본적으로 주변 키보드 입력에 따라 공을 이동하는 간단한 윈도우 응용 프로그램을 작성 : 다음은 간단한 클래스가 나는이에 도움이 온라인을 발견합니다. 내가 연결 한 클래스를 사용하여 키보드의 상태를 폴링합니다. 한 번에 두 개의 키를 누르고 있으면 대각선으로 이동합니다.

첫째, Ball.cs : 모든 공상

public class Ball 
    { 
     private Brush brush; 

     public float X { get; set; } 
     public float Y { get; set; } 
     public float DX { get; set; } 
     public float DY { get; set; } 
     public Color Color { get; set; } 
     public float Size { get; set; } 

     public void Draw(Graphics g) 
     { 
      if (this.brush == null) 
      { 
       this.brush = new SolidBrush(this.Color); 
      } 
      g.FillEllipse(this.brush, X, Y, Size, Size); 
     } 

     public void MoveRight() 
     { 
      this.X += DX; 
     } 

     public void MoveLeft() 
     { 
      this.X -= this.DX; 
     } 

     public void MoveUp() 
     { 
      this.Y -= this.DY; 
     } 

     public void MoveDown() 
     { 
      this.Y += this.DY; 
     } 
    } 

정말 아무것도 ....

가 여기에 Form1 코드입니다 :

public partial class Form1 : Form 
    { 
     private Ball ball; 
     private Timer timer; 
     public Form1() 
     { 
      InitializeComponent(); 
      this.ball = new Ball 
      { 
       X = 10f, 
       Y = 10f, 
       DX = 2f, 
       DY = 2f, 
       Color = Color.Red, 
       Size = 10f 
      }; 
      this.timer = new Timer(); 
      timer.Interval = 20; 
      timer.Tick += new EventHandler(timer_Tick); 
      timer.Start(); 
     } 

     void timer_Tick(object sender, EventArgs e) 
     { 
      var left = KeyboardInfo.GetKeyState(Keys.Left); 
      var right = KeyboardInfo.GetKeyState(Keys.Right); 
      var up = KeyboardInfo.GetKeyState(Keys.Up); 
      var down = KeyboardInfo.GetKeyState(Keys.Down); 

      if (left.IsPressed) 
      { 
       ball.MoveLeft(); 
       this.Invalidate(); 
      } 

      if (right.IsPressed) 
      { 
       ball.MoveRight(); 
       this.Invalidate(); 
      } 

      if (up.IsPressed) 
      { 
       ball.MoveUp(); 
       this.Invalidate(); 
      } 

      if (down.IsPressed) 
      { 
       ball.MoveDown(); 
       this.Invalidate(); 
      } 


     } 


     protected override void OnPaint(PaintEventArgs e) 
     { 
      base.OnPaint(e); 
      if (this.ball != null) 
      { 
       this.ball.Draw(e.Graphics); 
      } 
     } 
    } 

간단한 작은 응용 프로그램. 그냥 볼과 타이머를 만듭니다. 매 20 밀리 초마다 키보드 상태를 확인하고 키를 누르면 이동하여 다시 칠할 수 있도록 무효화합니다.

+0

안녕하세요, 감사합니다. 코드와 클래스를 링크에서 시험해 보았습니다. 코드가 잘 작동했습니다. 컴파일하기 전에 수정해야했지만 2008 년이 아니기 때문에 Visual Studio 2005를 사용하고 있었기 때문에 생각했습니다. 웹에서 영원히 검색 한 결과 이처럼 멋지게 작동하는 것을 찾을 수 없었습니다. 다른 사람들에게도 도움이되기를 바랍니다. –

+1

죄송합니다. 내 잘못입니다. 당신은 VS2005에서 일하고 있다고 언급했지만, 나는 그것을 알아 차리지 못했습니다. 네, 저는 자동 속성과 객체 이니셜 라이저 및 "var"키워드를 사용하는 데 익숙합니다. 죄송합니다! 다행이 당신을 위해 일 했어. 게임에 대해서는 XNA를 살펴 봐야합니다. 상자에서 벗어난 멋진 것들이 많이 있습니다. – BFree

0

죄송합니다

어떤 도움을 크게 감상 할 수 = 없음), 특정 도움말 ... 그것은 그러나 ... 결국 토요일 밤 :

각 키는 상태를 필요로한다. keydown 이벤트를 받으면 어떤 키가 눌려 졌는지 알 수 있고 유지 관리중인 keystate를 수정할 수 있습니다. 열쇠가 다시 생기면 다시 이벤트를 보면서 어느 것이 었는지 알 수 있습니다. 이벤트가 시작된 키의 상태 만 수정하십시오. 즉, keyup 이벤트로 모든 상태를 재설정하지 마십시오. 그렇지 않으면보고있는 것처럼 메모리에 보유한 키의 상태가 손상됩니다.

1

키 반복을 사용하여 블록 이동을 반복하는 키 다운 이벤트를 보내려는 경우이 방법을 사용하지 않는 것이 좋습니다. 블록은 키 반복과 독립적으로 일관되게 움직여야합니다. 따라서 키 이벤트 중에 블록을 이동해서는 안됩니다. keydown 및 keyup 이벤트 중에 만 키 상태를 추적하고 다른 곳에서 움직임을 처리해야합니다. 실제 움직임은 일종의 타이머 이벤트 (타이머 컨트롤은 아무런 일이 일어나지 않아도 일정한 간격으로 이벤트를 발생시킵니다) 또는 적절한 경우 모든 상태와 움직이는 객체를 지속적으로 점검하는 메인 루프를 가져야합니다. 두 번째 옵션을 사용하면 함수를 끝내지 않고 계속 실행되는 코드가 있으면 프로그램이 keyup 및 keydown 이벤트와 같은 다른 이벤트를 처리하지 않으므로 "DoEvents"를 조사해야합니다. 따라서 각 루프 내에서 DoEvents를 호출하여 키 이벤트를 처리해야합니다 (창 이동과 같은 다른 작업 중에서). System.Threading.Thread를 호출 할 수도 있습니다.잠시 잘 처리 할 필요가 없다면 잠자 요. 타이머 컨트롤을 사용하는 경우 그 중 어떤 것에 대해서도 걱정할 필요가 없습니다.