재미로 하는 C# - 공 튕기기

처음은 간단하게 공이 움직이고 공을 추가할 수 있는 것이었지만 이것저것 생각하다 만든 요건은 다음과 같다.

  1. Start 버튼을 클릭하면 공이 움직인다.
  2. 화면 끝까지 가면 반대쪽으로 튕긴다.
  3. 공의 버튼을 클릭하면 공이 추가된다.
  4. 공끼리 부딪히면 튕긴다.

1. 공이 움직인다.

공은 단순하게 System.Windows.Forms.PictureBox 로 구현했으며 움직임은 해당 control 의 Left, Top 속성을 변경시키는 것으로 했다.


2. 화면 끝에서 공이 튕긴다.

튕기는 각도를 계산하기 위해 몇가지 경우의 수를 가지고 산수를 해보니

  • 위쪽과 아래쪽 벽에 부딪히는 경우 : PI - 공의 각도
  • 양 옆쪽에 부딪히는 경우 : 2*PI - 공의 각도

로 정리가 되었다. 공의 각도는 공의 진행 방향의 바닥(흔히 생각하는 X축)에 대한 라디안 값이다.

따라서 각 공은 각자 각도와 속도를 가지게 되었고 폼은 각 공에 move 메세지를 주면 공은 충돌여부를 확인해서 각도를 바꾸게 되었다.


3. 공의 추가

  • 여러종류의 공 추가

여러종류의 공을 추가하기 위해 Ball 클래스를 상속받은 몇가지 클래스를 만들었다. 새로운 클래스들은 Ball 클래스가 가지고 있는System.Windows.Forms.PictureBox 에 이미지를 넣는 메소드만 각자 별도로 가지고 있다.

  • 공이 움직이는 동안 또다른 공 추가

공이 움직이는 동안에도 공이 추가되게 하기 위해 Thread 가 필요하다.

방법: 스레드 만들기 및 종료  

간단히 설명하자면 쓰레드로 실행할 메소드명을 사용해 new Thread([메소드명]) 로 쓰레드를 만들고 Thread.start() 하면 간단히 쓰레드가 실행되며 종료시에는 Thread.abort() 하면 된다. 하지만 abort 하게되면 Thread 로 실행하던 메소드가 실행 도중 중단되어 보통은 메소드 내에 종료조건을 만족하게 만들어 쓰레드를 종료시킨다.

일단은 쓰레드는 간단히 만들었지만 쓰레드 내에서 컨트롤들을 제어하려고 했더니 InvalidOperationException 이 발생했다.

방법: 스레드로부터 안전한 방식으로 Windows Forms 컨트롤 호출

delegate 를 만들어서 폼의 invoke 를 호출하라고 하는데 일단 delegate 가 뭔지 모르기에 예제소스를 참조해서 코드가 동작하도록 하는데 만족했다.

훨씬 자연스럽게 공이 움직였고 공의 추가도 잘 되었지만 오류의 여지가 많았다. 공들은 List<Ball> 로 선언한 balls 에 들어있었고 공을 움직이도록 balls 의 각 ball 에 move 메세지를 보냈고 공을 추가하는 부분에서는 balls.add(ball) 로 공을 추가했다. 따라서 balls 는 임계영역으로 설정해야 하는 부분이다.

C# 쓰레드 이야기: 9. 임계 영역

임계영역이 시작되는 부분에 System.Threading.Monitor.Enter(this) 종료되는 부분에 System.Threading.Monitor.Exit(this) 를 넣어주는 것으로 임계영역 정의는 된다. 그래서 공을 그리는 부분과 공을 추가하는 부분에 임계영역을 설정했다. 처음에 List 에서 공을 꺼내 move 메세지를 보내는 부분 전체를 설정했더니 공을 추가하는 부분에서 가끔 Deadlock 이 걸리는 상황이 발생했고 balls List 를 다른 배열에 복사하는 부분만 임계영역으로 설정하고 공에 move 메세지는 복사본 balls 에서 보내도록 수정했다.


4. 공과 공의 충돌

  • 충돌 확인

두 공 사이의 거리와 두 공의 크기를 이용해서 충돌을 확인할 수 있다.

  • 출돌 후 각도 변경

공과 공의 충돌시 충돌면의 각을 계산 후 충돌 후의 각도를 보정해야 한다. 대략적인 구현은 생각해 뒀지만 아직 미구현 부분이다.



Microsoft Visual Studio 2008 로 구현한 소스는 구글코드 에서 확인할 수있다.


덧글

댓글 입력 영역