<4D F736F F D20C5D7C6AEB8AEBDBA20B0D4C0D320B8B8B5E9B1E22E646F63>

Similar documents
歯MDI.PDF

PowerPoint 프레젠테이션

Microsoft PowerPoint - java1-lab5-ImageProcessorTestOOP.pptx

gnu-lee-oop-kor-lec06-3-chap7

제11장 프로세스와 쓰레드

제8장 자바 GUI 프로그래밍 II

歯처리.PDF

JUNIT 실습및발표

PowerPoint Presentation

Cluster management software

untitled

PowerPoint Presentation

q 이장에서다룰내용 1 객체지향프로그래밍의이해 2 객체지향언어 : 자바 2

C# Programming Guide - Types

PowerPoint Presentation

JAVA PROGRAMMING 실습 08.다형성

Design Issues

JAVA 프로그래밍실습 실습 1) 실습목표 - 메소드개념이해하기 - 매개변수이해하기 - 새메소드만들기 - Math 클래스의기존메소드이용하기 ( ) 문제 - 직사각형모양의땅이있다. 이땅의둘레, 면적과대각

<30342DBCF6C3B3B8AEBDC3BCB33228C3D6C1BE292E687770>

UI TASK & KEY EVENT

PowerPoint 프레젠테이션

PowerPoint Template

2 단계 : 추상화 class 오리 { class 청둥오리 extends 오리 { class 물오리 extends 오리 { 청둥오리 mallardduck = new 청둥오리 (); 물오리 redheadduck = new 물오리 (); mallardduck.swim();

쉽게 풀어쓴 C 프로그래밍

(Microsoft PowerPoint - 07\300\345.ppt [\310\243\310\257 \270\360\265\345])

제목

. 스레드 (Thread) 란? 스레드를설명하기전에이글에서언급되는용어들에대하여알아보도록하겠습니다. - 응용프로그램 ( Application ) 사용자에게특정서비스를제공할목적으로구현된응용프로그램을말합니다. - 컴포넌트 ( component ) 어플리케이션을구성하는기능별요

MVVM 패턴의 이해

No Slide Title

PowerPoint 프레젠테이션

RVC Robot Vaccum Cleaner

(8) getpi() 함수는정적함수이므로 main() 에서호출할수있다. (9) class Circle private double radius; static final double PI= ; // PI 이름으로 로초기화된정적상수 public

Visual Basic 반복문

Ext JS À¥¾ÖÇø®ÄÉÀ̼ǰ³¹ß-³¹Àå.PDF

Microsoft PowerPoint - 6-PythonGUI-sprite

<4D F736F F F696E74202D2036C0CFC2B05FB0B4C3BCC1F6C7E2C7C1B7CEB1D7B7A1B9D62E707074>

API - Notification 메크로를통하여어느특정상황이되었을때 SolidWorks 및보낸경로를통하여알림메시지를보낼수있습니다. 이번기술자료에서는메크로에서이벤트처리기를통하여진행할예정이며, 메크로에서작업을수행하는데유용할것입니다. 알림이벤트핸들러는응용프로그램구현하는데있어

 메소드 오버로딩

var answer = confirm(" 확인이나취소를누르세요."); // 확인창은사용자의의사를묻는데사용합니다. if(answer == true){ document.write(" 확인을눌렀습니다."); else { document.write(" 취소를눌렀습니다.");

쉽게 풀어쓴 C 프로그래밍

쉽게 풀어쓴 C 프로그래밍

Microsoft PowerPoint - 04-UDP Programming.ppt

<4D F736F F F696E74202D20C1A63038C0E520C5ACB7A1BDBABFCD20B0B4C3BC4928B0ADC0C729205BC8A3C8AF20B8F0B5E55D>

쉽게 풀어쓴 C 프로그래밍

Modern Javascript

Microsoft PowerPoint PythonGUI-sprite

<4D F736F F F696E74202D C61645FB3EDB8AEC7D5BCBA20B9D720C5F8BBE7BFEBB9FD2E BC8A3C8AF20B8F0B5E55D>

UI TASK & KEY EVENT

Microsoft PowerPoint - 2강

chap 5: Trees

제이쿼리 (JQuery) 정의 자바스크립트함수를쉽게사용하기위해만든자바스크립트라이브러리. 웹페이지를즉석에서변경하는기능에특화된자바스크립트라이브러리. 사용법 $( 제이쿼리객체 ) 혹은 $( 엘리먼트 ) 참고 ) $() 이기호를제이쿼리래퍼라고한다. 즉, 제이쿼리를호출하는기호

Microsoft PowerPoint - 09-Object Oriented Programming-3.pptx

학습목표 함수프로시저, 서브프로시저의의미를안다. 매개변수전달방식을학습한다. 함수를이용한프로그래밍한다. 2

소프트웨어공학 Tutorial #2: StarUML Eun Man Choi

Week5

<4D F736F F F696E74202D20C1A63034B0AD202D20C7C1B7B9C0D3B8AEBDBAB3CABFCD20B9ABB9F6C6DBC0D4B7C2>

PowerPoint Presentation

쉽게 풀어쓴 C 프로그래밍

[ 그림 7-1] 프로젝트 res 폴더 이미지뷰 [ 예제 7-1] 이미지뷰 1 <LinearLayout 2 ~~~~ 중간생략 ~~~~ 3 android:orientation="vertical" > 4 <ImageView

Microsoft PowerPoint - ÀÚ¹Ù08Àå-1.ppt

PowerPoint 프레젠테이션

Microsoft PowerPoint - PL_03-04.pptx

PowerPoint 프레젠테이션

EMBARCADERO TECHNOLOGIES (Humphery Kim) RAD Studio : h=p://tech.devgear.co.kr/ : h=p://blog.hjf.pe.kr/ Facebook : h=p://d.com/hjfactory :

PowerPoint Presentation

歯엑셀모델링

트레이 아이콘 어플리케이션의 제작

Microsoft Word - ntasFrameBuilderInstallGuide2.5.doc

Network Programming

Microsoft PowerPoint - chap02-C프로그램시작하기.pptx

델파이 4 프로그래밍의 이해 (Understandings of Delphi 4 Programming)

Contents. 1. PMD ㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍ 2. Metrics ㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍ 3. FindBugs ㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍ 4. ㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍㆍ

05-class.key

목차 BUG offline replicator 에서유효하지않은로그를읽을경우비정상종료할수있다... 3 BUG 각 partition 이서로다른 tablespace 를가지고, column type 이 CLOB 이며, 해당 table 을 truncate

gnu-lee-oop-kor-lec10-1-chap10

Microsoft Word - Armjtag_문서1.doc

USER GUIDE

< B0B3C0CEC1A4BAB8BAD0C0EFC1B6C1A4BBE7B7CAC1FD2E687770>

[ 그림 8-1] XML 을이용한옵션메뉴설정방법 <menu> <item 항목ID" android:title=" 항목제목 "/> </menu> public boolean oncreateoptionsmenu(menu menu) { getme

예제 2) Test.java class A intvar= 10; void method() class B extends A intvar= 20; 1"); void method() 2"); void method1() public class Test 3"); args) A

PowerPoint Presentation

Chapter 4. LISTS

오버라이딩 (Overriding)

02 C h a p t e r Java

어댑터뷰

API 매뉴얼

- JPA를사용하는경우의스프링설정파일에다음을기술한다. <bean id="entitymanagerfactory" class="org.springframework.orm.jpa.localentitymanagerfactorybean" p:persistenceunitname=

Microsoft PowerPoint - Lect04.pptx

쉽게

삼성955_965_09

Microsoft PowerPoint - java2 [호환 모드]

Semantic Consistency in Information Exchange

Microsoft PowerPoint - chap11

Javascript.pages

U.Tu System Application DW Service AGENDA 1. 개요 4. 솔루션 모음 1.1. 제안의 배경 및 목적 4.1. 고객정의 DW구축에 필요한 메타정보 생성 1.2. 제품 개요 4.2. 사전 변경 관리 1.3. 제품 특장점 4.3. 부품화형

Microsoft Word - java19-1-midterm-answer.doc

기본적인 컴포넌트의 제작

ThisJava ..

제4장 기본 의미구조 (Basic Semantics)

목차 BUG DEQUEUE 의 WAIT TIME 이 1 초미만인경우, 설정한시간만큼대기하지않는문제가있습니다... 3 BUG [qp-select-pvo] group by 표현식에있는컬럼을참조하는집합연산이존재하지않으면결괏값오류가발생할수있습니다... 4

Transcription:

Chapter 0 테트리스게임만들기 본강좌의목적은테트리스게임자체보다는사용자인터페이스와논리구조를분리하여분석 / 설계하고코딩하는과정을설명하기위한것이다. 특히 BDS 2006에서새로추가된 Together for Delphi를이용하여보다쉽게객체지향적설계를실제업무에도입하는과정을설명하고자한다.

1 단계 UI 와 Logic 의분리 이번강좌에서는필자가원래사용하던문서작성법이아닌클래스다이어그램만으로기능설계와구조설계를병행하도록하겠다. User-Interface 의기능분석 [ 그림 1] UI 와 Logic 의분리 [ 그림 1] 에서우선우측의 UI_Interface.TUI 클래스를주목하자. 해당클래스의멤버들에서사용된작명규칙을보면, Public 메쏘드앞에는아무런접두어가붙지않지만, 이벤트에해당하는메소드앞에는 on_ 을붙여놨다. 이것은필자만의습관으로, 이름만으로해당메쏘드의역할을쉽게구별하기위해서이다. 추후내부처리용으로사용되는 Private 메쏘드는 do_ 를앞에붙이게된다. [ 그림 1] 의 UI_Interface.TUI 클래스는아래와같은기능을가져야한다. 기능 - GameEnd : 게임종료처리 이벤트 - on_start : 사용자가게임을시작하도록하였음 - on_keydown : 사용자가키보드를클릭하였음

Logic-Interface의기능분석이어서 [ 그림 1] 의좌측 Logic_Interface.TLI 클래스는테트리스게임의논리계층의최상의클래스이며, 해당클래스는다음과같은기능을가져야한다. 기능 - StartGame - Process_KeyDown - Draw 이벤트 - on_tick - on_gameend

동적분석 UI Logoic on_start StartGame while GameStarted = True on_tick on_keydown Draw MoveDown GameStarted? Yes Process_KeyDown GameEnd? Yes GameEnd on_gameend [ 그림 2] Job Flow [ 그림 2] 에서는 UI 와 Logic 이서로호출하는순서와이벤트의흐름을설명하고있다. on_start이벤트가발생하면 Logic의 GameStarted 속성을 True로변경한다. GameStarted 속성이 True로변경되면내부타이머가작동하면서주기적으로 on_tick 이벤트를발생한다. On_Tick 이벤트는블록을한칸씩밑으로이동하는메소드를호출한다. 이동을담당하고있는 Process_KeyDown 메소드는더이상블록을내려놓을수없을때, GameStarted 속성을 False로변경시키면서 on_gameend 이벤트를발생시킨다. 사용자가키를누르게되면 on_keydown 이벤트가발생하게되고, 게임중일경우에만 Logic에게이메시지를전달하게된다. 사실기능분석이분석의가장기본이되기는하지만, 업무분석에는동적분석이더욱효율적일때가많다. 어느분석을먼저하더라도분석은단일프로세스로끝나는경우는거의없다. 즉, 기능분석을마치고동적분석을하는동안필요한기능이분석되지않은것을발견했을경우다시기능분석을변경하고, 동적분석후에도기능분석을통해서동적분석의오류를발견할수도있다.

기능분석과구조분석그리고동적분석은진행하는동안서로보완될경우가많다. 그리고, 반드시 어느것을먼저해야한다 라는규칙은없다.

[ 소스 1] UI_Interface Unit 의소스 1 : unit UI_Interface; 2 : 3 : interface 4 : 5 : uses 6 : Classes, SysUtils; 7 : 8 : type 9 : TUI = class 10 : protected 11 : procedure on_keydown(key:word); 12 : procedure on_start; 13 : public 14 : class function GetObject:TUI; 15 : procedure GameEnd; 16 : end; 17 : 18 : implementation 19 : 20 : uses 21 : Logic_Interface; 22 : 23 : var 24 : MyObject : TUI = Nil; 25 : 26 : { TUI } 27 : 28 : class function TUI.GetObject: TUI; 29 : begin 30 : if MyObject = Nil then MyObject:= TUI.Create; 31 : Result:= MyObject; 32 : end; 33 :

34 : procedure TUI.on_Start; 35 : begin 36 : TLI.GetObject.StartGame; 37 : end; 38 : 39 : procedure TUI.on_KeyDown(Key:Word); 40 : begin 41 : if TLI.GetObject.GameStarted = True then TLI.GetObject.Process_KeyDown(Key); 42 : end; 43 : 44 : procedure TUI.GameEnd; 45 : begin 46 : {ToDo : } 47 : end; 48 : 49 : end.

[ 소스 2] Logic_Interface Unit 의소스 1 : unit Logic_Interface; 2 : 3 : interface 4 : 5 : uses 6 : BlockShape, BlockCell, GameTimer, Windows, Classes, SysUtils; 7 : 8 : type 9 : TLI = class 10 : private 11 : FGameStarted: Boolean; 12 : procedure SetGameStarted(const Value: Boolean); 13 : protected 14 : procedure on_gameend; 15 : procedure on_tick; 16 : public 17 : GameTimer : TGameTimer; 18 : BlockCell : TBlockCell; 19 : BlockShape : TBlockShape; 20 : class function GetObject:TLI; 21 : procedure StartGame; 22 : procedure Draw; 23 : procedure Process_KeyDown(Key:Word); 24 : published 25 : property GameStarted : Boolean read FGameStarted write SetGameStarted; 26 : end; 27 : 28 : implementation 29 : 30 : uses 31 : UI_Interface; 32 : 33 : var

34 : MyObject : TLI = Nil; 35 : 36 : { TLI } 37 : 38 : class function TLI.GetObject: TLI; 39 : begin 40 : if MyObject = Nil then MyObject:= TLI.Create; 41 : Result:= MyObject; 42 : end; 43 : 44 : procedure TLI.SetGameStarted(const Value: Boolean); 45 : begin 46 : FGameStarted := Value; 47 : 48 : if Value = True then Self.StartGame 49 : else Self.on_GameEnd; 50 : end; 51 : 52 : procedure TLI.StartGame; 53 : begin 54 : {ToDo : } 55 : end; 56 : 57 : procedure TLI.on_GameEnd; 58 : begin 59 : TUI.GetObject.GameEnd; 60 : end; 61 : 62 : procedure TLI.on_Tick; 63 : begin 64 : Self.Process_KeyDown(VK_Down); 65 : end; 66 : 67 : procedure TLI.Draw; 68 : begin 69 : {ToDo : }

70 : end; 71 : 72 : procedure TLI.Process_KeyDown(Key:Word); 73 : begin 74 : case Key of 75 : VK_Left : {ToDo : }; 76 : VK_Right : {ToDo : }; 77 : VK_Up : {ToDo : }; 78 : VK_Down : {ToDo : if 게임종료 then GameStarted:= False}; 79 : VK_Space : {ToDo : 바닥까지 VK_Down 처리 }; 80 : end; 81 : 82 : Self.Draw; 83 : end; 84 : 85 : end.

2 단계 Logic 설계 기능분석 [ 그림 3] Logic Interface Class Diagram 우선 Logic을구성하는각클래스의역할분담에대해서설명하도록하겠다. TGameTimer 는주기적으로이벤트를발생하여테트리스의블록들이바닥으로떨어지게하는역할을담당한다. TBlockCell은테트리스의블록이쌓여있는정보를간직하기위해서작성되었다. ( 복수형태인 TBlockCells라고이름을지어야할것을나중에발견했다. 일단넘어가자.)

TBlockShape 는현재떨어지고있는블록들의묶음에대한정보를관리한다. [ 화면 1] PDA 용으로작성된프로그램의실행화면 [ 화면 1] 은이강좌를진행하기위해만들어진데모프로그램의실행화면이다. [ 화면 1] 에서보듯이현재떨어지고있는블록모양 (TBlcokShape) 은총 4개의단위블록으로구성되어있다. 이것을표현하기위해서 TShapeBlock이라는클래스를생성하였다. 또한바닥에이미쌓여있는블록은 TBlockCell 클래스가그정보를관리하게된다. TGameTimer의기능목록 기능 - 없음 이벤트 - on_timerfired TBlockShape의기능목록 기능 - CreateNext - Draw - Process_KeyDown

이벤트 - 없음 TBlockCell의기능목록 기능 - Clear - Draw - BlockLanding - Checkcollision 이벤트 - 없음

동적분석 TLI TGameTimer TBlockShape TBlockCell StartGame Clear Enabled:= True TBlockCell.CheckCollision while Enabled = True False? on_tick VK_Down on_timerfired Yes do_move TBlockShape.IsEmpty? No No Yes True? Yes BlockLanding TBlockShape.CreateNext TBlockCell.CheckCollision 게임종료? No, VK_Down VK_Down Yes VK_Down? Process_KeyDown Process_KeyDown on_gameend Yes Draw Draw Draw [ 그림 4] Job Flow [ 그림 4] 에서는동적분석을통해서 Logic 부분의전반적인흐름을표현하고있다. 여기서는기능분석에서발견하지못했던 do_move 메소드가필요하다는것을발견하였다. 이제기능분석을통해작성한클래스다이어그램을수정하여야한다. 이것은소스상에서만반영하도록하겠다. 현재필자가사용하고있는 BDS 2006에서는소스를변경하면클래스다이어그램이자동으로변경되기때문에변경작업에의한문서작성에대해서는스트레스받을필요가없다. 또한, do_move 는메쏘드이름에서알수있듯이 Private 메쏘드이다. 따라서, 기능분석동안에찾아내기는쉽지않다.

[ 소스 3] Logic_Interface Unit 의수정 1 : unit Logic_Interface; 2 : 3 : interface 4 : 5 : uses 6 : BlockShape, BlockCell, GameTimer, Windows, Classes, SysUtils; 7 : 8 : type 9 : TLI = class 10 : private 11 : function GetGameStarted: Boolean; 12 : protected 13 : procedure on_gameend; 14 : procedure on_tick; 15 : public 16 : GameTimer : TGameTimer; 17 : BlockCell : TBlockCell; 18 : BlockShape : TBlockShape; 19 : constructor Create; 20 : destructor Destroy; override; 21 : class function GetObject:TLI; 22 : procedure StartGame; 23 : procedure Draw; 24 : procedure Process_KeyDown(Key:Word); 25 : published 26 : property GameStarted : Boolean read GetGameStarted; 27 : end; 28 : 29 : implementation 30 : 31 : uses 32 : UI_Interface; 33 :

34 : var 35 : MyObject : TLI = Nil; 36 : 37 : { TLI } 38 : 39 : function TLI.GetGameStarted: Boolean; 40 : begin 41 : Result:= GameTimer.Enabled; 42 : end; 43 : 44 : class function TLI.GetObject: TLI; 45 : begin 46 : if MyObject = Nil then MyObject:= TLI.Create; 47 : Result:= MyObject; 48 : end; 49 : 50 : procedure TLI.StartGame; 51 : begin 52 : GameTimer.Enabled:= True; 53 : end; 54 : 55 : procedure TLI.on_GameEnd; 56 : begin 57 : GameTimer.Enabled:= False; 58 : TUI.GetObject.GameEnd; 59 : end; 60 : 61 : procedure TLI.on_Tick; 62 : begin 63 : if BlockShape.IsEmpty = True then BlockShape.CreateNext; 64 : Self.Process_KeyDown(VK_Down); 65 : end; 66 : 67 : constructor TLI.Create; 68 : begin 69 : inherited;

70 : 71 : GameTimer:= TGameTimer.Create; 72 : BlockCell:= TBlockCell.Create; 73 : BlockShape:= TBlockShape.Create; 74 : end; 75 : 76 : destructor TLI.Destroy; 77 : begin 78 : GameTimer.Free; 79 : BlockCell.Free; 80 : BlockShape.Free; 81 : 82 : inherited; 83 : end; 84 : 85 : procedure TLI.Draw; 86 : begin 87 : TUI.GetObject._fmMain.InitBoardCanvas; 88 : BlockCell.Draw; 89 : BlockShape.Draw; 90 : end; 91 : 92 : procedure TLI.Process_KeyDown(Key:Word); 93 : begin 94 : BlockShape.Process_KeyDown(Key); 95 : Self.Draw; 96 : end; 97 : 98 : end.

[ 소스 4] GameTimer Unit 의소스 1 : unit GameTimer; 2 : 3 : interface 4 : 5 : uses 6 : Classes, SysUtils, ExtCtrls; 7 : 8 : type 9 : TGameTimer = class 10 : private 11 : FTimer : TTimer; 12 : function GetEnabled: Boolean; 13 : procedure SetEnabled(const Value: Boolean); 14 : protected 15 : procedure on_timerfired(sender:tobject); 16 : public 17 : constructor Create; 18 : Destructor Destroy; override; 19 : published 20 : property Enabled : Boolean read GetEnabled write SetEnabled; 21 : end; 22 : 23 : implementation 24 : 25 : uses 26 : Logic_Interface; 27 : 28 : type 29 : TFLI = class(tli) 30 : end; 31 : 32 : { TGameTimer } 33 :

34 : constructor TGameTimer.Create; 35 : begin 36 : inherited; 37 : 38 : FTimer:= TTimer.Create(Nil); 39 : FTimer.Interval:= 50; 40 : FTimer.OnTimer:= Self.on_TimerFired; 41 : FTimer.Enabled:= False; 42 : end; 43 : 44 : destructor TGameTimer.Destroy; 45 : begin 46 : FTimer.Free; 47 : 48 : inherited; 49 : end; 50 : 51 : procedure TGameTimer.on_TimerFired(Sender: TObject); 52 : begin 53 : TFLI(TFLI.GetObject).on_Tick; 54 : end; 55 : 56 : function TGameTimer.GetEnabled: Boolean; 57 : begin 58 : Result:= FTimer.Enabled; 59 : end; 60 : 61 : procedure TGameTimer.SetEnabled(const Value: Boolean); 62 : begin 63 : FTimer.Enabled:= Value; 64 : end; 65 : 66 : end.

[ 소스 5] BlockShape Unit 의소스 1 : unit BlockShape; 2 : 3 : interface 4 : 5 : uses 6 : Classes, SysUtils, Windows; 7 : 8 : type 9 : TBlockShape = class 10 : public 11 : procedure Draw; 12 : procedure Process_KeyDown(Key:Word); 13 : procedure CreateNext; 14 : private 15 : function GetIsEmpty: Boolean; 16 : function do_movedown:boolean; 17 : procedure do_drop; 18 : procedure do_move(key:word); 19 : published 20 : property IsEmpty : Boolean read GetIsEmpty; 21 : end; 22 : 23 : implementation 24 : 25 : uses 26 : Logic_Interface; 27 : 28 : procedure TBlockShape.Draw; 29 : begin 30 : {Todo : } 31 : end; 32 : 33 : function TBlockShape.GetIsEmpty: Boolean;

34 : begin 35 : {Todo : } 36 : end; 37 : 38 : procedure TBlockShape.Process_KeyDown(Key:Word); 39 : begin 40 : if Key = VK_Down then do_movedown 41 : else do_move(key); 42 : end; 43 : 44 : function TBlockShape.do_MoveDown:Boolean; 45 : begin 46 : Result:= not TLI.GetObject.BlockCell.CheckCollision(VK_Down); 47 : 48 : if Result = True then 49 : {Todo : } 50 : else 51 : TLI.GetObject.BlockCell.BlockLanding; 52 : end; 53 : 54 : procedure TBlockShape.do_Drop; 55 : var 56 : bmoved : Boolean; 57 : begin 58 : Repeat 59 : bmoved:= do_movedown; 60 : until bmoved = False; 61 : end; 62 : 63 : procedure TBlockShape.do_Move(Key:Word); 64 : begin 65 : if TLI.GetObject.BlockCell.CheckCollision(Key) = True then Exit; 66 : 67 : case Key of 68 : VK_Left : {ToDo : }; 69 : VK_Right : {ToDo : };

70 : VK_Up : {ToDo : }; 71 : VK_Space : do_drop; 72 : end; 73 : end; 74 : 75 : procedure TBlockShape.CreateNext; 76 : begin 77 : {Todo : } 78 : end; 79 : 80 : end. [ 그림 4] 의 Job Flow와는달리좀더로직을쉽게접근할수있도록 Private 메쏘드몇개가추가되었다. Flow 자체는동일하다.

[ 소스 6] TBlockCell Unit 의소스 1 : unit BlockCell; 2 : 3 : interface 4 : 5 : type 6 : TBlockCell = class 7 : public 8 : procedure Draw; 9 : procedure Clear; 10 : procedure BlockLanding; 11 : function CheckCollision(Key:Word):Boolean; 12 : end; 13 : 14 : implementation 15 : 16 : uses 17 : Logic_Interface; 18 : 19 : procedure TBlockCell.Draw; 20 : begin 21 : {Todo : } 22 : end; 23 : 24 : procedure TBlockCell.Clear; 25 : begin 26 : {Todo : } 27 : end; 28 : 29 : procedure TBlockCell.BlockLanding; 30 : begin 31 : {Todo : } 32 : 33 : {Todo : 종료조건처리 }

34 : end; 35 : 36 : function TBlockCell.CheckCollision(Key:Word):Boolean; 37 : begin 38 : {Todo : } 39 : end; 40 : 41 : end.

3 단계 User Interface 설계 기능분석 [ 그림 5] User Interface 클래스다이어그램 TUI 클래스는변경된사항이없으니, _fmmain 클래스의기능목록만살펴보자. 기능 - GameEnd - InitBoardCanvas : 실제로는구현과정중에추가된메쏘드이다. 이벤트 - on_keydown - on_start _fmmain은메인폼에대한부모클래스이다. TUI는직접메인폼에대하여의존하지않도록중간에 _fmmain 클래스를생성하고, 메인폼은이것을상속받아서사용하도록한다. 상속받아서처리하는이유는 TUI 자체의논리계층과구현계층을분리하여변경사항이발생하더라도변경사항에대한쇼크를줄이기위해서이다.

예를들어 TLI에서게임이종료되었음을알리면 TUI.GameEnd가호출된다. 이때 TUI는 _fmmain.gameend라는 abstract 메쏘드를실행하게된다. 추후메인폼이 _fmmain에서상속을받아서해당메쏘드를 override 하여게임종료처리를완료하게되는것이다. 메인폼이게임종료처리를어떠한방식으로진행하던지이제 TUI는알필요가없는것이다. 그것은 TUI가메인폼이아닌 _fmmain에만의존관계를가지고있기때문이다.

동적분석 TUI.GetObject _fmmain on_start on_start on_keydown on_keydown GameEnd GameEnd [ 그림 6] Job Flow 현재의설계는 _fmmain에서 on_start 등의이벤트가발생하면 TUI의같은 on_start가호출되어중복된메쏘드가나오게된다. 이것은 Demeter 법칙을준수하기위한것이다. TLI 가 TUI이너무깊숙한곳까지알게되면소스코드의결합도가높아지기때문이다. 하지만, 필자는 TUI가너무많은메쏘드를처리하게되는시점에서모든메쏘드는 TUI 하위클래스에게위임한다. 하나의클래스에너무많은메쏘드를포함시키면가독성이떨어지기때문이다. 일반적으로는다른클래스의내장을후벼파는것은소스의유연성을떨어트리게된다. 따라서, 우선적으로 Demeter 법칙을준수하다가어느일정수준이넘어서가독성이문제가된다고판단이될때, 필자는메쏘드를하위클래스에서만정의하는방식을채택했다. ( 객체지향적 ) 설계에서는때에따라서그적용방법을달리해야할때가발생한다. 반드시 라는법칙은없다. ( 어쩌면그것이다행일지도모르겠다. 만약 반드시 라는법칙이통한다면사람이아닌기계가대신설계를도맡아할지도모르겠다.)

[ 소스 7] UI_Interface Unit 의수정 1 : unit UI_Interface; 2 : 3 : interface 4 : 5 : uses 6 : Classes, SysUtils, _frmmain; 7 : 8 : type 9 : TUI = class 10 : protected 11 : procedure on_keydown(key:word); 12 : procedure on_start; 13 : public 14 : _fmmain : T_fmMain; 15 : class function GetObject:TUI; 16 : procedure GameEnd; 17 : end; 18 : 19 : implementation 20 : 21 : uses 22 : Logic_Interface; 23 : 24 : var 25 : MyObject : TUI = Nil; 26 : 27 : { TUI } 28 : 29 : class function TUI.GetObject: TUI; 30 : begin 31 : if MyObject = Nil then MyObject:= TUI.Create; 32 : Result:= MyObject; 33 : end;

34 : 35 : procedure TUI.on_Start; 36 : begin 37 : TLI.GetObject.StartGame; 38 : end; 39 : 40 : procedure TUI.on_KeyDown(Key:Word); 41 : begin 42 : if TLI.GetObject.GameStarted = True then TLI.GetObject.Process_KeyDown(Key); 43 : end; 44 : 45 : procedure TUI.GameEnd; 46 : begin 47 : Self._fmMain.GameEnd; 48 : end; 49 : 50 : end.

[ 소스 8] _fmmain Unit 의소스 1 : unit _frmmain; 2 : 3 : interface 4 : uses 5 : Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms; 6 : 7 : type 8 : T_fmMain = class(tform) 9 : private 10 : protected 11 : function GetBoardCanvas: TCanvas; virtual; abstract; 12 : procedure on_keydown(key:word); 13 : procedure on_start; 14 : public 15 : constructor Create(AOwner:TComponent); override; 16 : procedure InitBoardCanvas; 17 : procedure GameEnd; virtual; abstract; 18 : published 19 : property BoardCanvas : TCanvas read GetBoardCanvas; 20 : end; 21 : 22 : implementation 23 : 24 : uses 25 : UI_Interface; 26 : 27 : type 28 : TFUI = class(tui) 29 : end; 30 : 31 : { T_fmMain } 32 : 33 : constructor T_fmMain.Create(AOwner: TComponent);

34 : begin 35 : inherited; 36 : 37 : TUI.GetObject._fmMain:= Self; 38 : end; 39 : 40 : procedure T_fmMain.on_KeyDown(Key: Word); 41 : begin 42 : TFUI(TUI.GetObject).on_KeyDown(Key); 43 : end; 44 : 45 : procedure T_fmMain.on_Start; 46 : begin 47 : TFUI(TUI.GetObject).on_Start; 48 : end; 49 : 50 : procedure T_fmMain.InitBoardCanvas; 51 : var 52 : Loop: Integer; 53 : begin 54 : BoardCanvas.Brush.Color:= $004F4F4F; 55 : BoardCanvas.FillRect(Rect(0, 0, 120, 240)); 56 : 57 : // 배경에수직선그리기 58 : for Loop := 1 to 10 do begin 59 : BoardCanvas.Pen.Color:= clwhite; 60 : BoardCanvas.MoveTo((Loop-1)*12, 0); 61 : BoardCanvas.LineTo((Loop-1)*12, 240); 62 : end; 63 : 64 : end; 65 : 66 : end.

중간점검필자는하나의프로젝트 ( 델파이에서는 *.dpr) 파일에관련된범위를프로세스모듈이라고부른다. 그리고, 여기까지가프로세스모듈에대한논리계층의설계과정이다. 이제부터는구현계층에대해서설명하고자한다. 만약프로세스모듈에대한논리계층이변경되어야한다면, 그것은업그레이드가아닌버전업의시점이라고필자는생각한다. 구현계층이이루어져있는상황에서논리계층을변경하는것은건물을거의지어놓은상태에서철골구조를새로하려는것과같다. ( 실제프로젝트에서는그것보다더위험하다.)

4 단계 Logic 에대한구현계층설계 필자는구현계층에대한분석과설계과정은다소복잡한부분을제외하고는대부분생략하는편이다. User Interface 준비 사용자인터페이스는개발과정에서최대한뒤로미루는것이좋다. 모든것이그러하듯이우선골격을먼저구성하고살을붙여가는것이실수와비용을줄이는지름길이다. 소프트웨어개발에서는 Logic이바로그골격에해당하므로, 이에대한설계와테스트가완료되고난뒤에사용자인터페이스를완성해나가는것이유리하다. 하지만, 테스트를위해서최소한의사용자인터페이스는미리분석과설계를거쳐서작성하는편이좋다. 필자는지금이바로그시점이라고생각한다. [ 화면 2] 메인폼디자인 테스트를위해서 [ 화면 2] 처럼콤포넌트를내려놓고프로퍼티를아래와같이수정한다. TPanel의경우에는 Canvas 속성이 Protected로되어있어접근이불가능하여약간의트릭을이용하였다. 이점은소스를참고하기바란다.

1 : object fmmain: TfmMain 2 : Left = 0 3 : Top = 0 4 : Caption = 테트리스 5 : ClientHeight = 296 6 : ClientWidth = 240 7 : OnKeyDown = FormKeyDown 8 : object plgameboard: TPanel 9 : Left = 4 10 : Top = 4 11 : Width = 120 12 : Height = 240 13 : BevelOuter = bvnone 14 : Color = 5197647 15 : end 16 : object MainMenu: TMainMenu 17 : object mistart: TMenuItem 18 : Caption = 'Start' 19 : OnClick = mistartclick 20 : end 21 : end 22 : end

[ 소스 9] frmmain Unit 의소스 1 : unit frmmain; 2 : 3 : interface 4 : 5 : uses 6 : Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 7 : Dialogs, Menus, _frmmain, ExtCtrls, CanvasPanel; 8 : 9 : type 10 : TfmMain = class(t_fmmain) 11 : MainMenu: TMainMenu; 12 : mistart: TMenuItem; 13 : plgameboard: TPanel; 14 : procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); 15 : procedure mistartclick(sender: TObject); 16 : private 17 : { Private declarations } 18 : function GetBoardCanvas: TCanvas; override; 19 : public 20 : { Public declarations } 21 : procedure GameEnd; override; 22 : end; 23 : 24 : var 25 : fmmain: TfmMain; 26 : 27 : implementation 28 : 29 : {$R *.dfm} 30 : 31 : procedure TfmMain.FormKeyDown(Sender: TObject; var Key: Word; 32 : Shift: TShiftState); 33 : begin

34 : Self.on_KeyDown(Key); 35 : end; 36 : 37 : procedure TfmMain.GameEnd; 38 : begin 39 : ShowMessage('Game Over!!!'); 40 : end; 41 : 42 : function TfmMain.GetBoardCanvas: TCanvas; 43 : begin 44 : Result:= plgameboard.canvas; 45 : end; 46 : 47 : procedure TfmMain.miStartClick(Sender: TObject); 48 : begin 49 : Self.on_Start; 50 : end; 51 : 52 : end.

테스트시나리오작성테스트를위해서사용자가접근할수있는모든가능성에대한시나리오를찾아내고이를하나씩수행하여본다. 본강좌에서는사용자가게임을시작하기위해서 Start 메뉴를실행하는초기단계부터코딩과테스트를병행하면서설명을하도록하겠다. 시나리오 Step 1 사용자가 Start 메뉴를실행하였음사용자가 Start 메뉴를실행하면우선 [ 소스 9] 의 49: 라인이실행된다. 이것은 TLI.GetObject.GameTimer.Enabled:= True 를실행하게되고, 결과적으로 TLI.on_Tick 이벤트가주기적으로실행된다. 그리고해당이벤트가실행될때마다, TLI.Draw 메쏘드가호출된다. 아래는해당메쏘드의변경내용이다. 1 : procedure TLI.Draw; 2 : begin 3 : TUI.GetObject._fmMain.InitBoardCanvas; 4 : BlockCell.Draw; 5 : BlockShape.Draw; 6 : end; 3: 라인에서게임보드를초기화하는소스는이미작성되어있으니, 4: 라인의 BLockCel.Draw에대해서코딩을하겠다.

TBlock 클래스추가 [ 화면 1] 에서보았듯이테트리스에는최소단위의블록을관리하는무엇인가가필요하다. 필자는 TBlock 클래스를추가하여이를해결하도록할것이다. [ 그림 7] TBlockCell 의클래스다이어그램 [ 그림 7] 에서보듯이 TBlockCell은 TBlock 클래스를배열로관리하고있다. 배열의사이즈는 10X20으로현재작성하려고하는테트리스의셀크기이다. TBlock의기능목록은아래와같다. 기능 - Draw : 자기자신을그린다. - DropDown : 바닥으로한칸씩떨어진다. 이벤트 - 없음 TBlock 은단위사이즈를나타내기위해 Read-Only 속성 Width 와 Height 를가지고있다.

[ 소스 10] Block Unit 의소스 1 : unit Block; 2 : 3 : interface 4 : 5 : uses 6 : Classes, SysUtils, Graphics; 7 : 8 : type 9 : TBlock = class 10 : private 11 : function GetHeight: Integer; 12 : function GetWidth: Integer; 13 : public 14 : X, Y : Integer; 15 : procedure Draw; 16 : procedure DropDown; 17 : procedure MoveLeft; 18 : procedure MoveRight; 19 : published 20 : property Width : Integer read GetWidth; 21 : property Height : Integer read GetHeight; 22 : end; 23 : 24 : implementation 25 : 26 : uses 27 : UI_Interface; 28 : 29 : { TBlock } 30 : 31 : procedure TBlock.Draw; 32 : begin 33 : TUI.GetObject._fmMain.BoardCanvas.Brush.Color:= cllime;

34 : TUI.GetObject._fmMain.BoardCanvas.FillRect(Rect(X*Width, Y*Height, (X+1)*Width, (Y+1) 35 : *Height)); 36 : TUI.GetObject._fmMain.BoardCanvas.Pen.Color:= clred; 37 : TUI.GetObject._fmMain.BoardCanvas.Rectangle(Rect(X*Width, Y*Height, (X+1)*Width, (Y+1) 38 : *Height)); 39 : end; 40 : 41 : procedure TBlock.DropDown; 42 : begin 43 : Y:= Y + 1; 44 : end; 45 : 46 : function TBlock.GetHeight: Integer; 47 : begin 48 : Result:= 12; 49 : end; 50 : 51 : function TBlock.GetWidth: Integer; 52 : begin 53 : Result:= 12; 54 : end; 55 : 56 : procedure TBlock.MoveLeft; 57 : begin 58 : X:= X -1; 59 : end; 60 : 61 : procedure TBlock.MoveRight; 62 : begin 63 : X:= X + 1; 64 : end; 65 : end. 위의소스는메인폼에버턴을올려두고아래와같은소스로제대로블록이그려지는지테스트를진행해보았다. 강좌의특성으로인해자잘한과정을생략하고넘어갈수밖에없

음이아쉽다. 1 : implementation 2 : 3 : uses 4 : Block; 5 : 6 : {$R *.dfm} 7 : 8 : procedure TfmMain.Button1Click(Sender: TObject); 9 : var 10 : Block : TBlock; 11 : begin 12 : Block:= TBlock.Create; 13 : Block.Draw; 14 : end;

[ 소스 11] BlockCell Unit 의수정 1 : unit BlockCell; 2 : 3 : interface 4 : 5 : uses 6 : Classes, SysUtils, Graphics, Block; 7 : 8 : type 9 : TBlockCell = class 10 : private 11 : FCells : Array [0..9, 0..19] of TBlock; 12 : public 13 : constructor Create; 14 : procedure Draw; 15 : procedure Clear; 16 : procedure BlockLanding; 17 : function CheckCollision(Key:Word):Boolean; 18 : end; 19 : 20 : implementation 21 : 22 : uses 23 : Logic_Interface; 24 : 25 : procedure TBlockCell.Draw; 26 : var 27 : LoopX: Integer; 28 : LoopY: Integer; 29 : begin 30 : for LoopX := 0 to 10-1 do 31 : for LoopY := 0 to 20-1 do 32 : if FCells[LoopX, LoopY] <> Nil then FCells[LoopX, LoopY].Draw; 33 : end;

34 : 35 : procedure TBlockCell.Clear; 36 : var 37 : LoopX: Integer; 38 : LoopY: Integer; 39 : begin 40 : for LoopX := 0 to 10-1 do 41 : for LoopY := 0 to 20-1 do 42 : if FCells[LoopX, LoopY] <> Nil then begin 43 : FCells[LoopX, LoopY].Free; 44 : FCells[LoopX, LoopY]:= Nil; 45 : end; 46 : end; 47 : 48 : constructor TBlockCell.Create; 49 : var 50 : LoopX: Integer; 51 : LoopY: Integer; 52 : begin 53 : inherited; 54 : 55 : for LoopX := 0 to 10-1 do 56 : for LoopY := 0 to 20-1 do 57 : FCells[LoopX, LoopY]:= Nil; 58 : end; 59 : 60 : procedure TBlockCell.BlockLanding; 61 : begin 62 : {Todo : } 63 : 64 : {Todo : 종료조건처리 } 65 : if false then TLI.GetObject.GameStarted:= False; 66 : end; 67 : 68 : function TBlockCell.CheckCollision(Key:Word):Boolean; 69 : begin

70 : {Todo : } 71 : end; 72 : 73 : end.

[ 소스 12] BlockShape Unit 의수정 1 : unit BlockShape; 2 : 3 : interface 4 : 5 : uses 6 : Classes, SysUtils, Windows, Block; 7 : 8 : type 9 : TBlockShape = class 10 : private 11 : FBlockList : TList; 12 : function GetIsEmpty: Boolean; 13 : procedure do_drop; 14 : procedure do_move(key:word); 15 : function do_movedown:boolean; 16 : procedure do_moveleft; 17 : procedure do_moveright; 18 : procedure do_createblockshape1; 19 : procedure do_createblockshape2; 20 : procedure do_createblockshape3; 21 : procedure do_createblockshape4; 22 : procedure do_createblockshape5; 23 : procedure do_createblockshape6; 24 : procedure do_createblockshape7; 25 : public 26 : constructor Create; 27 : destructor Destroy; override; 28 : procedure Draw; 29 : procedure Process_KeyDown(Key:Word); 30 : procedure CreateNext; 31 : published 32 : property IsEmpty : Boolean read GetIsEmpty; 33 : end;

34 : 35 : implementation 36 : 37 : uses 38 : Logic_Interface; 39 : 40 : procedure TBlockShape.Draw; 41 : var 42 : Loop: Integer; 43 : begin 44 : for Loop := 0 to FBlockList.Count - 1 do 45 : TBlock(FBlockList.Items[Loop]).Draw; 46 : end; 47 : 48 : function TBlockShape.GetIsEmpty: Boolean; 49 : begin 50 : Result:= FBlockList.Count = 0; 51 : end; 52 : 53 : procedure TBlockShape.Process_KeyDown(Key:Word); 54 : begin 55 : if Key = VK_Down then do_movedown 56 : else do_move(key); 57 : end; 58 : 59 : function TBlockShape.do_MoveDown:Boolean; 60 : var 61 : Loop: Integer; 62 : begin 63 : Result:= not TLI.GetObject.BlockCell.CheckCollision(VK_Down); 64 : 65 : if Result = True then begin 66 : for Loop := 0 to FBlockList.Count - 1 do 67 : TBlock(FBlockList.Items[Loop]).DropDown; 68 : end else 69 : TLI.GetObject.BlockCell.BlockLanding;

70 : end; 71 : 72 : procedure TBlockShape.do_MoveLeft; 73 : var 74 : Loop: Integer; 75 : begin 76 : for Loop := 0 to FBlockList.Count - 1 do 77 : TBlock(FBlockList.Items[Loop]).MoveLeft; 78 : end; 79 : 80 : procedure TBlockShape.do_MoveRight; 81 : var 82 : Loop: Integer; 83 : begin 84 : for Loop := 0 to FBlockList.Count - 1 do 85 : TBlock(FBlockList.Items[Loop]).MoveRight; 86 : end; 87 : 88 : destructor TBlockShape.Destroy; 89 : begin 90 : FBlockList.Free; 91 : 92 : inherited; 93 : end; 94 : 95 : procedure TBlockShape.do_CreateBlockShape1; 96 : var 97 : Block : TBlock; 98 : begin 99 : Block:= TBlock.Create; 100 : Block.X:= 4; 101 : Block.Y:= -2; 102 : FBlockList.Add(Block); 103 : 104 : Block:= TBlock.Create; 105 : Block.X:= 5;

106 : Block.Y:= -2; 107 : FBlockList.Add(Block); 108 : 109 : Block:= TBlock.Create; 110 : Block.X:= 4; 111 : Block.Y:= -1; 112 : FBlockList.Add(Block); 113 : 114 : Block:= TBlock.Create; 115 : Block.X:= 5; 116 : Block.Y:= -1; 117 : FBlockList.Add(Block); 118 : end; 119 : 120 : procedure TBlockShape.do_CreateBlockShape2; 121 : var 122 : Block : TBlock; 123 : begin 124 : Block:= TBlock.Create; 125 : Block.X:= 4; 126 : Block.Y:= -3; 127 : FBlockList.Add(Block); 128 : 129 : Block:= TBlock.Create; 130 : Block.X:= 4; 131 : Block.Y:= -2; 132 : FBlockList.Add(Block); 133 : 134 : Block:= TBlock.Create; 135 : Block.X:= 5; 136 : Block.Y:= -2; 137 : FBlockList.Add(Block); 138 : 139 : Block:= TBlock.Create; 140 : Block.X:= 5; 141 : Block.Y:= -1;

142 : FBlockList.Add(Block); 143 : end; 144 : 145 : procedure TBlockShape.do_CreateBlockShape3; 146 : var 147 : Block : TBlock; 148 : begin 149 : Block:= TBlock.Create; 150 : Block.X:= 5; 151 : Block.Y:= -3; 152 : FBlockList.Add(Block); 153 : 154 : Block:= TBlock.Create; 155 : Block.X:= 4; 156 : Block.Y:= -2; 157 : FBlockList.Add(Block); 158 : 159 : Block:= TBlock.Create; 160 : Block.X:= 5; 161 : Block.Y:= -2; 162 : FBlockList.Add(Block); 163 : 164 : Block:= TBlock.Create; 165 : Block.X:= 4; 166 : Block.Y:= -1; 167 : FBlockList.Add(Block); 168 : end; 169 : 170 : procedure TBlockShape.do_CreateBlockShape4; 171 : var 172 : Block : TBlock; 173 : begin 174 : Block:= TBlock.Create; 175 : Block.X:= 4; 176 : Block.Y:= -3; 177 : FBlockList.Add(Block);

178 : 179 : Block:= TBlock.Create; 180 : Block.X:= 4; 181 : Block.Y:= -2; 182 : FBlockList.Add(Block); 183 : 184 : Block:= TBlock.Create; 185 : Block.X:= 5; 186 : Block.Y:= -2; 187 : FBlockList.Add(Block); 188 : 189 : Block:= TBlock.Create; 190 : Block.X:= 4; 191 : Block.Y:= -1; 192 : FBlockList.Add(Block); 193 : end; 194 : 195 : procedure TBlockShape.do_CreateBlockShape5; 196 : var 197 : Block : TBlock; 198 : begin 199 : Block:= TBlock.Create; 200 : Block.X:= 4; 201 : Block.Y:= -4; 202 : FBlockList.Add(Block); 203 : 204 : Block:= TBlock.Create; 205 : Block.X:= 4; 206 : Block.Y:= -3; 207 : FBlockList.Add(Block); 208 : 209 : Block:= TBlock.Create; 210 : Block.X:= 4; 211 : Block.Y:= -2; 212 : FBlockList.Add(Block); 213 :

214 : Block:= TBlock.Create; 215 : Block.X:= 4; 216 : Block.Y:= -1; 217 : FBlockList.Add(Block); 218 : end; 219 : 220 : procedure TBlockShape.do_CreateBlockShape6; 221 : var 222 : Block : TBlock; 223 : begin 224 : Block:= TBlock.Create; 225 : Block.X:= 4; 226 : Block.Y:= -3; 227 : FBlockList.Add(Block); 228 : 229 : Block:= TBlock.Create; 230 : Block.X:= 4; 231 : Block.Y:= -2; 232 : FBlockList.Add(Block); 233 : 234 : Block:= TBlock.Create; 235 : Block.X:= 4; 236 : Block.Y:= -1; 237 : FBlockList.Add(Block); 238 : 239 : Block:= TBlock.Create; 240 : Block.X:= 5; 241 : Block.Y:= -1; 242 : FBlockList.Add(Block); 243 : end; 244 : 245 : procedure TBlockShape.do_CreateBlockShape7; 246 : var 247 : Block : TBlock; 248 : begin 249 : Block:= TBlock.Create;

250 : Block.X:= 5; 251 : Block.Y:= -3; 252 : FBlockList.Add(Block); 253 : 254 : Block:= TBlock.Create; 255 : Block.X:= 5; 256 : Block.Y:= -2; 257 : FBlockList.Add(Block); 258 : 259 : Block:= TBlock.Create; 260 : Block.X:= 5; 261 : Block.Y:= -1; 262 : FBlockList.Add(Block); 263 : 264 : Block:= TBlock.Create; 265 : Block.X:= 4; 266 : Block.Y:= -1; 267 : FBlockList.Add(Block); 268 : end; 269 : 270 : procedure TBlockShape.do_Drop; 271 : var 272 : bmoved : Boolean; 273 : begin 274 : Repeat 275 : bmoved:= do_movedown; 276 : until bmoved = False; 277 : end; 278 : 279 : procedure TBlockShape.do_Move(Key:Word); 280 : begin 281 : if TLI.GetObject.BlockCell.CheckCollision(Key) = True then Exit; 282 : 283 : case Key of 284 : VK_Left : do_moveleft; 285 : VK_Right : do_moveright;

286 : VK_Up : {ToDo : }; 287 : VK_Space : do_drop; 288 : end; 289 : end; 290 : 291 : constructor TBlockShape.Create; 292 : begin 293 : inherited; 294 : 295 : Randomize; 296 : 297 : FBlockList:= TList.Create; 298 : end; 299 : 300 : procedure TBlockShape.CreateNext; 301 : begin 302 : FBlockList.Clear; 303 : 304 : case Round(Random(7)) of 305 : 0 : do_createblockshape1; 306 : 1 : do_createblockshape2; 307 : 2 : do_createblockshape3; 308 : 3 : do_createblockshape4; 309 : 4 : do_createblockshape5; 310 : 5 : do_createblockshape6; 311 : 6 : do_createblockshape7; 312 : end; 313 : end; 314 : 315 : end.

블록쌓기이번에는블록이바닥에떨어지면쌓이도록소스를변경하도록하겠다. 바닥이나이미쌓여있는블록에닿으면멈추기위해서는우선충돌테스트를위한메쏘드를완성해야한다. 해당메쏘드는블록을현재진행방향으로한칸움직여봐서바닥이나벽또는다른블록에부디치는지를검사하여결과값을알려주게된다. 지금은밑으로떨어지는방향이기때문에양쪽벽에부딪치는것은일단무시하도록하겠다. 우선현재의 BlockShape를진행방향으로한칸움직이고이것이충돌하는지를검사하게되면화면에검사하는과정이표시될뿐아니라, 현재의위치정보를모두저장하고다시복원하는등의문제가복잡해질수있다. 따라서, 필자는현재의 BlockShape에대한클론을만들고클론을통해서충돌테스트를대신하도록진행하기로한다. 클론을만들기위해서 TBlockShape 클래스에 private 메소드를하나더추가하도록하겠다. 그리고, 클론의경우에는충돌테스트없이이동하여야하기때문에자신이클론인지의여부를확인하는플래그 FIsClone을추가하도록하겠다. 상속을통해서삭제할수도있으나, 우선은플래그변수를이용하도록하겠다. 1 : type 2 : TBlockShape = class 3 : private 4 : FIsClone : Boolean; 1 : constructor TBlockShape.Create; 2 : begin 3 : inherited; 4 : 5 : FIsClone:= False; 1 : function TBlockShape.Clone: TBlockShape; 2 : var 3 : Loop: Integer; 4 : Block : TBlock; 5 : begin 6 : Result:= TBlockShape.Create;

7 : Result.FIsClone:= True; 8 : for Loop := 0 to FBlockList.Count - 1 do begin 9 : Block:= TBlock.Create; 10 : Block.X:= TBlock(FBlockList.Items[Loop]).X; 11 : Block.Y:= TBlock(FBlockList.Items[Loop]).Y; 12 : Result.FBlockList.Add(Block); 13 : end; 14 : end; 아래의소스에서는 BlockShape를밑으로떨어트리는메소드를변경한내용이다. BlockShape의클론을전달하기위해서 CheckCollision 메쏘드에파라메터를추가하였다. 또한, do_checkcollision 메쏘드를추가하여클론을통한충돌테스트루틴을이용하는것을간략화하였다. 1 : function TBlockShape.do_CheckCollision(Key:Word):Boolean; 2 : var 3 : CloneShape : TBlockShape; 4 : begin 5 : if FIsClone = False then begin 6 : CloneShape:= Self.Clone; 7 : try 8 : CloneShape.Process_KeyDown(Key); 9 : Result:= TLI.GetObject.BlockCell.CheckCollision(CloneShape.FBlockList, Key); 10 : finally 11 : CloneShape.Free; 12 : end; 13 : end else 14 : Result:= False; 15 : end; 16 : 17 : function TBlockShape.do_MoveDown:Boolean; 18 : var 19 : Loop: Integer; 20 : begin 21 : Result:= not Self.do_CheckCollision(VK_Down);

22 : 23 : if Result = True then begin 24 : for Loop := 0 to FBlockList.Count - 1 do 25 : TBlock(FBlockList.Items[Loop]).DropDown; 26 : end else 27 : TLI.GetObject.BlockCell.BlockLanding; 28 : end; 또한, 내부에 TBlock으로생성된객체가미아가되면서메모리누수가발생할수가있기때문에, TBlockShape의소멸자에서 TBlock의객체들을메모리에서삭제하도록소스를추가하였다. 1 : destructor TBlockShape.Destroy; 2 : var 3 : Loop : Integer; 4 : begin 5 : for Loop := 0 to FBlockList.Count - 1 do 6 : TBlock(FBlockList.Items[Loop]).Free; 7 : 8 : FBlockList.Free; 9 : 10 : inherited; 11 : end; 1 : procedure TBlockShape.do_Move(Key:Word); 2 : begin 3 : if Self.do_CheckCollision(Key) = True then Exit; 다음으로는충돌여부를점검하는 TBlockCell.CheckCollision 메쏘드의소스를아래와같이작성한다. 이미놓여진블록과의충돌을검사하는 do_checkblocksincells 메쏘드가추가되었다. 1 : function TBlockCell.do_CheckBlocksInCells(List: TList): Boolean; 2 : var 3 : Loop: Integer; 4 : Block : TBlock;

5 : begin 6 : Result:= False; 7 : for Loop := 0 to List.Count - 1 do begin 8 : Block:= TBlock(List.Items[Loop]); 9 : if (Block.X in [0..9]) and (Block.Y in [0..19]) then 10 : if FCells[Block.X, Block.Y] <> Nil then begin 11 : Result:= True; 12 : Break; 13 : end; 14 : end; 15 : end; 16 : 17 : function TBlockCell.CheckCollision(List:TList; Key:Word):Boolean; 18 : var 19 : Loop: Integer; 20 : Block : TBlock; 21 : bcondition1, bcondition2, bcondition3 : Boolean; 22 : begin 23 : Result:= False; 24 : 25 : for Loop := 0 to List.Count - 1 do begin 26 : Block:= Pointer(List.Items[Loop]); 27 : 28 : bcondition1:= (Block.X < 0) or (Block.X > 9); 29 : bcondition2:= (Block.Y > 19); 30 : bcondition3:= do_checkblocksincells(list); 31 : if bcondition1 or bcondition2 or bcondition3 then begin 32 : Result:= True; 33 : Break; 34 : end; 35 : end; 36 : end; 이제는바닥에충돌된이후블록을쌓이도록하는 TBlockCell.BlockLanding 메쏘드를작성하도록하겠다.

1 : implementation 2 : 3 : uses 4 : Logic_Interface; 5 : 6 : type 7 : TFLI = class(tli) 8 : end; 45 : procedure TBlockCell.BlockLanding(List:TList); 46 : var 47 : Loop: Integer; 48 : Block : TBlock; 49 : bgameended : Boolean; 50 : begin 51 : bgameended:= False; 52 : for Loop := 0 to List.Count - 1 do begin 53 : Block:= Pointer(List.Items[Loop]); 54 : if (Block.X in [0..9]) and (Block.Y in [0..19]) then 55 : FCells[Block.X, Block.Y]:= Block 56 : else 57 : bgameended:= True; 58 : end; 59 : List.Clear; 60 : 61 : if bgameended = True then TFLI(TLI.GetObject).on_GameEnd; 62 : end;

BlockShape 의회전 0 1 2 3 0 1 2 3 4 0 1 2 3 0 1 2 3 4 [ 그림 8] 블록들을회전하고난뒤의좌표변환 위의그림을참고로해서각블록의좌표 (x,y) 를알고있을때, 회전후의좌표 (x,y ) 를구하는식은다음과같다. ( 아래식은필자가전철안에서노트를통해서구한것이므로최선의방법이라고할수없을지도모른다. 여러분들의보다기발한아이디어를기대하겠다.) x' = StartX + (EndY - Y) y = EndY + (X EndX) 여기서 (StartX, StartY) 는도형이차지하고있는최소한의사각형의좌측상단모서리의좌표이며, (EndX, EndY) 는해당사각형의우측하단모서리의좌표이다.

TRotateBlocks 클래스추가 [ 그림 9] 블록회전을위해서 TRotateBlocks 클래스를추가한다어이그램 아래소스는 BlockShape 유닛에서회전을위해변경된부분이다. 1 : procedure TBlockShape.do_Move(Key:Word); 2 : begin 3 : case Key of 4 : VK_Left : do_moveleft; 5 : VK_Right : do_moveright; 6 : VK_Down : do_movedown;

7 : VK_Up : TRotateBlocks.Rotate(FBlockList); 8 : VK_Space : do_drop; 9 : end; 10 : end;

[ 소스 13] RotateBlocks Unit 의소스 1 : unit RotateBlocks; 2 : 3 : interface 4 : 5 : uses 6 : Classes, SysUtils, Block, Dialogs; 7 : 8 : type 9 : TRotateBlocks = class 10 : private 11 : FBlockList : TList; 12 : function do_getstartx:integer; 13 : function do_getendx:integer; 14 : function do_getendy:integer; 15 : public 16 : constructor Create; 17 : destructor Destroy; override; 18 : class procedure Rotate(List:TList); 19 : end; 20 : 21 : implementation 22 : 23 : { TRotateBlocks } 24 : 25 : constructor TRotateBlocks.Create; 26 : begin 27 : inherited; 28 : 29 : FBlockList:= TList.Create; 30 : end; 31 : 32 : destructor TRotateBlocks.Destroy; 33 : begin

34 : FBlockList.Free; 35 : 36 : inherited; 37 : end; 38 : 39 : function TRotateBlocks.do_GetEndX: Integer; 40 : var 41 : Loop, X : Integer; 42 : begin 43 : Result:= -100; 44 : 45 : for Loop := 0 to FBlockList.Count - 1 do begin 46 : X:= TBlock(FBlockList.Items[Loop]).X; 47 : if X > Result then Result:= X; 48 : end; 49 : 50 : if Result < 0 then Result:= 0; 51 : end; 52 : 53 : function TRotateBlocks.do_GetEndY: Integer; 54 : var 55 : Loop, Y : Integer; 56 : begin 57 : Result:= -100; 58 : 59 : for Loop := 0 to FBlockList.Count - 1 do begin 60 : Y:= TBlock(FBlockList.Items[Loop]).Y; 61 : if Y > Result then Result:= Y; 62 : end; 63 : 64 : if Result < 0 then Result:= 0; 65 : end; 66 : 67 : function TRotateBlocks.do_GetStartX: Integer; 68 : var 69 : Loop, X : Integer;

70 : begin 71 : Result:= 100; 72 : 73 : for Loop := 0 to FBlockList.Count - 1 do begin 74 : X:= TBlock(FBlockList.Items[Loop]).X; 75 : if X < Result then Result:= X; 76 : end; 77 : 78 : if Result > 9 then Result:= 9; 79 : end; 80 : 81 : class procedure TRotateBlocks.Rotate(List: TList); 82 : var 83 : Loop, X, Y, StartX, EndX, EndY : Integer; 84 : Block : TBlock; 85 : RotateBlocks : TRotateBlocks; 86 : begin 87 : RotateBlocks:= TRotateBlocks.Create; 88 : try 89 : RotateBlocks.FBlockList.Assign(List); 90 : 91 : StartX:= RotateBlocks.do_GetStartX; 92 : EndX:= RotateBlocks.do_GetEndX; 93 : EndY:= RotateBlocks.do_GetEndY; 94 : 95 : for Loop := 0 to List.Count - 1 do begin 96 : Block:= Pointer(List.Items[Loop]); 97 : X:= Block.X; 98 : Y:= Block.Y; 99 : Block.X:= StartX + (EndY - Y); 100 : Block.Y:= EndY + (X - EndX); 101 : end; 102 : finally 103 : RotateBlocks.Free; 104 : end; 105 : end;

106 : 107 : end. 끝으로 블록이짜맞춰졌을때해당라인을삭제하는것은숙제로남겨두겠다. ( 참고자료 /2410)