처음은 간단하게 공이 움직이고 공을 추가할 수 있는 것이었지만 이것저것 생각하다 만든 요건은 다음과 같다.
- Start 버튼을 클릭하면 공이 움직인다.
- 화면 끝까지 가면 반대쪽으로 튕긴다.
- 공의 버튼을 클릭하면 공이 추가된다.
- 공끼리 부딪히면 튕긴다.
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 는 임계영역으로 설정해야 하는 부분이다.
임계영역이 시작되는 부분에 System.Threading.Monitor.Enter(this) 종료되는 부분에 System.Threading.Monitor.Exit(this) 를 넣어주는 것으로 임계영역 정의는 된다. 그래서 공을 그리는 부분과 공을 추가하는 부분에 임계영역을 설정했다. 처음에 List 에서 공을 꺼내 move 메세지를 보내는 부분 전체를 설정했더니 공을 추가하는 부분에서 가끔 Deadlock 이 걸리는 상황이 발생했고 balls List 를 다른 배열에 복사하는 부분만 임계영역으로 설정하고 공에 move 메세지는 복사본 balls 에서 보내도록 수정했다.
4. 공과 공의 충돌
- 충돌 확인
두 공 사이의 거리와 두 공의 크기를 이용해서 충돌을 확인할 수 있다.
- 출돌 후 각도 변경
공과 공의 충돌시 충돌면의 각을 계산 후 충돌 후의 각도를 보정해야 한다. 대략적인 구현은 생각해 뒀지만 아직 미구현 부분이다.
Microsoft Visual Studio 2008 로 구현한 소스는 구글코드 에서 확인할 수있다.















덧글