技术交流,DH讲解.
今天来实现一个简单的五子棋,直接用GDI来画的一个游戏.
首先我们来想下怎么存放数据,哪些地方是白棋,哪些地方是黑棋,哪些地方没有下棋?
对,我们用一个二维数组,如果数组中某一个位置的值为0代表没有下棋,为1代表是白棋,为2代表是黑棋.
好就这么说定了,为了使用方便,我们打算做成一个控件,因为要画界面所以我们从TGraphicControl继承下来.
看一下类定义的代码:
Type TLastPlayer= (LpP1, LpP2); TGameEvent= Procedure(S: TObject; P: TLastPlayer) Of Object; TFiveGame= Class(TGraphicControl) Private // 如果是空,就是0,白棋就是1,黑棋就是2 FData: Array Of Array Of Byte; // 棋盘的长和宽 FRows, FColumns: Integer; // 格子的宽度/ FCellWidth: Integer; // 是否自动大小 FAutoSize: Boolean; // 棋盘线条的颜色. FLineColor: TColor; // 2个选手的颜色 FPlayerOneColor: TColor; FPlayerTwoColor: TColor; // 边距 FHDistance, FVDistance: Integer; // 上次操作的选手 FLastPlayer: TLastPlayer; // 三个事件 FWinEvent: TGameEvent; FErrorEvent: TGameEvent; FSuccessEvent: TGameEvent; Procedure SetRows(Const Value: Integer); Procedure SetColumns(Const Value: Integer); Procedure SetCellWidth(Const Value: Integer); Procedure SetAutoSize(Const Value: Boolean); Procedure SetLineColor(Const Value: TColor); Procedure SetPlayerOneColor(Const Value: TColor); Procedure SetPlayerTwoColor(Const Value: TColor); Procedure SetHDistance(Const Value: Integer); Procedure SetVDistance(Const Value: Integer); Procedure Paint; Override; Function AddChessMan(X, Y, V: Integer): Boolean; Procedure MyButtonDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); Function Judge(X, Y, V: Integer): Boolean; Protected Public Constructor Create(AOwner: TComponent); Override; Destructor Destroy; Override; // Procedure ReStart; Function AddP1Chessman(X, Y: Integer): Boolean; Function AddP2Chessman(X, Y: Integer): Boolean; Published Property Rows: Integer Read FRows Write SetRows; Property Columns: Integer Read FColumns Write SetColumns; Property CellWidth: Integer Read FCellWidth Write SetCellWidth; Property AutoSize: Boolean Read FAutoSize Write SetAutoSize; Property LineColor: TColor Read FLineColor Write SetLineColor; Property PlayerOneColor : TColor Read FPlayerOneColor Write SetPlayerOneColor; Property PlayerTwoColor : TColor Read FPlayerTwoColor Write SetPlayerTwoColor; Property HDistance: Integer Read FHDistance Write SetHDistance; Property VDistance: Integer Read FVDistance Write SetVDistance; Property WinEvent: TGameEvent Read FWinEvent Write FWinEvent; Property LastPlayer: TLastPlayer Read FLastPlayer Write FLastPlayer; Property ErrorEvent: TGameEvent Read FErrorEvent Write FErrorEvent; Property SuccessEvent: TGameEvent Read FSuccessEvent Write FSuccessEvent; End;
我们这样定义了一个类,接下来看看怎么实现它的:
{ TFiveGame } // 增加一颗棋子 Function TFiveGame.AddChessMan(X, Y, V: Integer): Boolean; Begin Result:= False; If (X< 0)Or (Y< 0)Or (X> Columns- 1)Or (Y> Rows- 1) Then Exit; If FData[X, Y]<> 0 Then Exit; FData[X, Y]:= Byte(V); Result:= True; End; // 一号选手增加棋子 Function TFiveGame.AddP1Chessman(X, Y: Integer): Boolean; Begin Result:= AddChessMan(X, Y, 1); End; // 二号选手增加棋子 Function TFiveGame.AddP2Chessman(X, Y: Integer): Boolean; Begin Result:= AddChessMan(X, Y, 2) End; Constructor TFiveGame.Create(AOwner: TComponent); Begin Inherited; // 初始化 FAutoSize:= False; FRows:= 5; FColumns:= 5; FCellWidth:= 30; FLineColor:= ClRed; FPlayerOneColor:= ClBlack; FPlayerTwoColor:= ClYellow; FHDistance:= 20; FVDistance:= 20; FLastPlayer:= LpP2; SetLength(FData, FRows, FColumns); // 要响应鼠标,必须加上这个 ControlStyle:= ControlStyle+ [CsReplicatable, CsCaptureMouse, CsClickEvents]; Self.OnMouseDown:= MyButtonDown; End; Destructor TFiveGame.Destroy; Begin Inherited; End; // 判断是否胜利 Function TFiveGame.Judge(X, Y, V: Integer): Boolean; Var C1, C2: Integer; X1, Y1: Integer; Procedure Reset; Begin C1:= 0; C2:= 0; X1:= X; Y1:= Y; End; Begin Result:= True; // 这个编译指令是代码折叠,但是D7以及之前版本没有这个编译指令.不要惊恐 // 横向 {$IFDEF D7UP} {$REGION '横向'} {$ENDIF} Begin Reset; // 向左 Dec(X1); While X1>= 0 Do Begin If FData[X1, Y]<> V Then Break; Inc(C1); If C1= 4 Then Exit; Dec(X1); End; // 向右 X1:= X+ 1; While X1< Columns Do Begin If FData[X1, Y]<> V Then Break; Inc(C2); If C1+ C2= 4 Then Exit; Inc(X1); End; End; {$IFDEF D7UP} {$ENDREGION} // 竖向 {$REGION '竖向'} {$ENDIF} Begin Reset; // 向上 Dec(Y1); While Y1>= 0 Do Begin If FData[X, Y1]<> V Then Break; Inc(C1); If C1= 4 Then Exit; Dec(Y1); End; // 向下 Y1:= Y+ 1; While Y1< Rows Do Begin If FData[X, Y1]<> V Then Break; Inc(C2); If C1+ C2= 4 Then Exit; Inc(Y1); End; End; {$IFDEF D7UP} {$ENDREGION} // 斜向1 {$REGION '斜向1'} {$ENDIF} Begin Reset; Dec(X1); Dec(Y1); While (X1>= 0)And (Y1>= 0) Do Begin If FData[X1, Y1]<> V Then Break; Inc(C1); If C1= 4 Then Exit; Dec(X1); Dec(Y1); End; X1:= X+ 1; Y1:= Y+ 1; While (X1< Columns)And (Y1< Rows) Do Begin If FData[X1, Y1]<> V Then Break; Inc(C2); If C1+ C2= 4 Then Exit; Inc(X1); Inc(Y1) End; End; {$IFDEF D7UP} {$ENDREGION} // 斜向2 {$REGION '斜向2'} {$ENDIF} Begin Reset; Dec(X1); Inc(Y1); While (X1>= 0)And (Y1< Rows) Do Begin If FData[X1, Y1]<> V Then Break; Inc(C1); If C1= 4 Then Exit; Dec(X1); Inc(Y1); End; X1:= X+ 1; Y1:= Y- 1; While (X1< Columns)And (Y1>= 0) Do Begin If FData[X1, Y1]<> V Then Break; Inc(C2); If C1+ C2= 4 Then Exit; Inc(X1); Dec(Y1); End; End; {$IFDEF D7UP} {$ENDREGION} {$ENDIF} Result:= False; End; Procedure TFiveGame.MyButtonDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); Var I, J, K: Integer; B: Boolean; Begin // 不是左键 If Button<> MbLeft Then Exit; // 计算x,y的位置 If (X< HDistance)Or (X> HDistance+ Columns* CellWidth) Then Exit; If (Y< VDistance)Or (Y> VDistance+ Rows* CellWidth) Then Exit; I:= (X- HDistance)Div CellWidth; J:= (Y- VDistance)Div CellWidth; // 加棋子 Case FLastPlayer Of LpP1: Begin B:= AddP2Chessman(I, J); If B Then FLastPlayer:= LpP2 End; LpP2: Begin B:= AddP1Chessman(I, J); If B Then FLastPlayer:= LpP1 End; End; // 结果处理 If B Then Begin Invalidate; If Assigned(FSuccessEvent) Then FSuccessEvent(Self, FLastPlayer); If Judge(I, J, Ord(FLastPlayer)+ 1) Then If Assigned(FWinEvent) Then FWinEvent(Self, FLastPlayer); End Else If Assigned(FErrorEvent) Then FErrorEvent(Self, FLastPlayer); End; Procedure TFiveGame.Paint; Procedure DrawBkg; Var I: Integer; OldColor: TColor; Begin With Canvas Do Begin OldColor:= Pen.Color; Pen.Color:= LineColor; Rectangle(HDistance, VDistance, HDistance+ Columns* CellWidth, VDistance+ Rows* CellWidth); For I:= 1 To Columns- 1 Do Begin MoveTo(HDistance+ I* CellWidth, VDistance); LineTo(HDistance+ I* CellWidth, VDistance+ Rows* CellWidth); End; For I:= 1 To Rows- 1 Do Begin MoveTo(HDistance, VDistance+ I* CellWidth); LineTo(HDistance+ Columns* CellWidth, VDistance+ I* CellWidth); End; Pen.Color:= OldColor; End; End; Procedure DrawChessMan(X, Y, V: Integer); Var C1, C2: TColor; Begin With Canvas Do Begin C1:= Brush.Color; C2:= Pen.Color; Case V Of 1: Brush.Color:= PlayerOneColor; 2: Brush.Color:= PlayerTwoColor; End; Pen.Color:= Brush.Color; Ellipse(HDistance+ X* CellWidth+ 1, VDistance+ Y* CellWidth+ 1, HDistance+ (X+ 1)* CellWidth- 1, VDistance+ (Y+ 1)* CellWidth- 1); Pen.Color:= C2; Brush.Color:= C1; End; End; Var J: Integer; K: Integer; Begin //如果自动大小 就调整大小 If AutoSize Then Begin Width:= HDistance* 2+ Columns* CellWidth; Height:= VDistance* 2+ Rows* CellWidth; End; // 画棋盘 DrawBkg; // 画棋子 For J:= 0 To Rows- 1 Do For K:= 0 To Columns- 1 Do If FData[J, K]<> 0 Then DrawChessMan(J, K, FData[J, K]); End; Procedure TFiveGame.ReStart; Var I: Integer; J: Integer; Begin For I:= 0 To Rows- 1 Do For J:= 0 To Columns- 1 Do FData[I, J]:= 0; Invalidate; End; Procedure TFiveGame.SetAutoSize(Const Value: Boolean); Begin If FAutoSize= Value Then Exit; FAutoSize:= Value; Invalidate; End; Procedure TFiveGame.SetCellWidth(Const Value: Integer); Begin If FCellWidth= Value Then Exit; FCellWidth:= Value; Invalidate; End; Procedure TFiveGame.SetColumns(Const Value: Integer); Begin If FColumns= Value Then Exit; FColumns:= Value; SetLength(FData, FRows, FColumns); Invalidate; End; Procedure TFiveGame.SetHDistance(Const Value: Integer); Begin If FHDistance= Value Then Exit; FHDistance:= Value; Invalidate; End; Procedure TFiveGame.SetPlayerOneColor(Const Value: TColor); Begin If FPlayerOneColor= Value Then Exit; FPlayerOneColor:= Value; Invalidate; End; Procedure TFiveGame.SetLineColor(Const Value: TColor); Begin If FLineColor= Value Then Exit; FLineColor:= Value; Invalidate; End; Procedure TFiveGame.SetPlayerTwoColor(Const Value: TColor); Begin If FPlayerTwoColor= Value Then Exit; FPlayerTwoColor:= Value; Invalidate; End; Procedure TFiveGame.SetRows(Const Value: Integer); Begin If FRows= Value Then Exit; FRows:= Value; SetLength(FData, FRows, FColumns); Invalidate; End; Procedure TFiveGame.SetVDistance(Const Value: Integer); Begin If FVDistance= Value Then Exit; FVDistance:= Value; Invalidate; End;
要想把这个类加到控件面板上面去就需要再添加一个Register全局函数,我这里没有添加,所以也不演示那句话了.
类给出来了,我们看看怎么使用这个类.
定义一个该类的对象:
FiveGame:TFiveGame;
窗体创建的时候的时候,实例化这个对象:
procedure TForm1.FormCreate(Sender: TObject); begin FiveGame:=TFiveGame.Create(Self); FiveGame.Parent:=Self; FiveGame.Align:=alClient; FiveGame.WinEvent:=OnWin; FiveGame.ErrorEvent:=Onerror; FiveGame.SuccessEvent:=OnSuccess; end;
窗体结束的时候不要忘记释放这个对象哟.代码我就不贴了,给出一个完整代码下载的链接吧.
下载地址:点这里
代码很简单,也很不完善,打算有空加入人机对战和联网对战,然后界面再改漂亮一些吧,比如用贴图或者用DX来弄.
这里只是一个抛砖引玉吧.
我是DH,今天到这里.