• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

delphi控制台贪吃蛇

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

 

游戏的界面

主要的功能实现

1 键盘消息

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  windows,
  uConsoleClass in 'uConsoleClass.pas',
  uSnake in 'uSnake.pas';

// 参考
/// http://blog.csdn.net/haiou327/article/details/5695237
var
  MyMsg   : TMsg;
begin
  while windows.GetMessage(MyMsg, 0, 0, 0) do
  begin
    DispatchMessage(MyMsg);
  end;
end.

 

 

2 定时器 

这里用的是API 

procedure TimerProc(window : Hwnd ; message,idEvent :UInt; dwTime: dword);stdcall;
begin
  if Snake.StartSnake then
    Snake.MoveSnake();
end;


FTimer := SetTimer(0, 0, TIMERINTERVAL, @TimerProc);

 

 3 蛇控制单元

unit uSnake;

interface

uses
  Windows, classes, uConsoleClass, ExtCtrls;

const
  GAMEROW = 16;
  GAMECOL = 54;

  TIMERINTERVAL = 300;
type

  TMoveDir   = (MD_Right, MD_Left, MD_Up, MD_Down);
  TPointType = (PT_Head, PT_Body, PT_Tail, PT_Food);


  TGamePoint = record
    Row        : byte;
    Col        : byte;
    PointType  : TPointType;
  end;
  PGamePoint = ^TGamePoint;


  TReadKeyThread = Class(TThread)
  private
    FMoveDir        : TMoveDir;
    FStartRead      : boolean;
    FPause          : boolean;

    procedure SetStartRead(const Value: boolean);
  public

    property Pause            : boolean read FPause write FPause;
    property StartRead        : boolean read FStartRead write SetStartRead;
    property MoveDir          : TMoveDir read FMoveDir write FMoveDir;
  protected
    procedure Execute; override;
  end;

  TSnake = class
  private
    //FGameMap        : array[0..GAMEROW - 1, 0..GAMECOL - 1] of byte;
    FFoodPoint      : PGamePoint;

    FSnakePointList : TList;
    FLastPoint      : PGamePoint;
    FMyConsole      : TConsoleControl;

    FStartSnake     : boolean;
    FReadKeyThread  : TReadKeyThread;

    FEatFoodCount   : integer;
//    FScores         : integer;

    procedure InitGameMap();
    procedure FreeSnakeList();
    function CheckInSnake(Row, Col: integer): boolean;

    procedure PrintSnake();



    function GetSnakeBodyType(bodyType: TPointType): PGamePoint;
    procedure GetFood();

    procedure ShowScores(add: boolean = false);

    procedure Start();
    function CheckGameOver(): boolean;
    procedure GameOver();
    function EatFood(): boolean;

    function GetMoveDir(): TMoveDir;

    property Dir: TMoveDir read GetMoveDir;
    property StartSnake: boolean read FStartSnake write FStartSnake;

  public
    constructor Create();
    destructor Destroy;override;

    procedure StartGame();
    procedure MoveSnake();
    function ThreadPause(): boolean;
  end;


implementation

uses SysUtils;

var
  Snake   : TSnake;
  FTimer  : Integer;

procedure TimerProc(window : Hwnd ; message,idEvent :UInt; dwTime: dword);stdcall;
begin
  if Snake.StartSnake then
    Snake.MoveSnake();
end;

{ TSnake }

function TSnake.CheckGameOver: boolean;
var
  Head: PGamePoint;
  I: integer;
  P: PGamePoint;
begin
  Result  := false;
  Head    := GetSnakeBodyType(PT_Head);
//  FMyConsole.SetCursorTo(0, 16);
//  FMyConsole.WriteText('Row: ' + inttostr(Head^.Row) + ' Col: ' + inttostr(Head^.Col));

  if Dir = MD_Up then
  begin
    if Head^.Row = 1 then
      Result := true;
  end;

  // 判断撞到上下的墙
  if (Head^.Row < 1) or (Head^.Row > GAMEROW - 3) then
    Result := true;

  // 判断撞到左右的墙
  if (Head^.Col < 3) or (Head^.Col > GAMECOL - 6) then
    Result := true;

  // 判断是否撞到自己

  for I := 2 to FSnakePointList.Count - 1 do
  begin
    P := FSnakePointList.Items[I];
    case Dir of
    MD_Right:
      begin
        if (Head^.Col + 1 = P^.Col) and (Head^.Row = P^.Row) then
          Result := true;
      end;
    MD_Left:
      begin
        if (Head^.Col - 1 = P^.Col) and (Head^.Row = P^.Row) then
          Result := true;
      end;
    MD_Up:
      begin
        if (Head^.Row - 1 = P^.Row) and (Head^.Col = P^.Col) then
          Result := true;
      end;
    MD_Down:
      begin
        if (Head^.Row + 1 = P^.Row) and (Head^.Col = P^.Col) then
          Result := true;
      end;
    end;
  end;
end;

function TSnake.CheckInSnake(Row, Col: integer): boolean;
var
  P: PGamePoint;
  I: integer;
begin
  Result := false;
  for I := 0 to FSnakePointList.Count - 1 do
  begin
    P := FSnakePointList.Items[I];
    if (P^.Row = Row) and (P^.Col= Col) then
    begin
      Result := true;
      break;
    end;
  end;
end;

constructor TSnake.Create();
begin
  FReadKeyThread  := TReadKeyThread.Create(true);
  FSnakePointList := TList.Create();
  New(FFoodPoint);
  New(FLastPoint);
  FMyConsole:= TConsoleControl.Create;
  FMyConsole.SetWindowTitle('【贪吃蛇】 V1.0');
  InitGameMap();
end;

destructor TSnake.Destroy;
begin
  Dispose(FFoodPoint);
  Dispose(FLastPoint);
  FreeAndNil(FSnakePointList);
  FMyConsole.Free;
  FReadKeyThread.Free();
  inherited;
end;

function TSnake.EatFood: boolean;
var
  Head : PGamePoint;
begin
  Result := false;
  Head := GetSnakeBodyType(PT_Head);
  if (Head^.Row = FFoodPoint^.Row) and (Head^.Col = FFoodPoint^.Col) then
  begin
    ShowScores(true);
    Result := true;
  end;
  ShowScores();
end;

procedure TSnake.FreeSnakeList;
var
  P: PGamePoint;
  Index: integer;
begin
  if FSnakePointList.Count > 0 then
  begin
    repeat
      Index := FSnakePointList.Count - 1;
      P     := FSnakePointList.Items[Index];
      FSnakePointList.Delete(Index);
      Dispose(P);
    until FSnakePointList.Count = 0;
  end;
end;

procedure TSnake.GameOver;
var
  S: string;
begin
  StartSnake               := false;
  FReadKeyThread.StartRead := false;
//
  FMyConsole.SetCursorTo(0, 16);
  FMyConsole.WriteText('                                                      ');
  FMyConsole.SetCursorTo(0, 16);
  FMyConsole.WriteText('游戏结束重新开始吗? (y/n):');
  Readln(S);
  if LowerCase(S) = 'y' then
  begin
    //FMyConsole.SetCursorTo(0, 16);
    //FMyConsole.WriteText('游戏开始                      ');
    InitGameMap();
    Start();
  end;
end;

procedure TSnake.GetFood;
begin
  Randomize;
  repeat
    FFoodPoint^.Row := Random(GAMEROW - 7) + 5;
    FFoodPoint^.Col := Random(GAMECOL - 10) + 5;
  until not CheckInSnake(FFoodPoint^.Row, FFoodPoint^.Col);
  FMyConsole.SetForegroundColor(true, false, true, false);
  FMyConsole.SetCursorTo(FFoodPoint^.Col, FFoodPoint^.Row);
  FMyConsole.WriteText('O');
end;

function TSnake.GetMoveDir: TMoveDir;
begin
  Result := FReadKeyThread.MoveDir;
end;

function TSnake.GetSnakeBodyType(bodyType: TPointType): PGamePoint;
var
  I: integer;
begin
  Result := nil;
  for I := 0 to FSnakePointList.Count - 1 do
  begin
    Result := FSnakePointList.Items[I];
    if Result.PointType = bodyType then break;
  end;
end;

procedure TSnake.InitGameMap;
var
//  I, J: integer;
  P: PGamePoint;
begin
  FMyConsole.ClearScreen;
//  for I := 0 to GAMEROW - 1 do
//  begin
//    for J := 0 to GAMECOL - 1 do
//    begin
//      if (I = 0) or (I = GAMEROW - 1) then
//        FGameMap[I][J] := 1
//      else
//        FGameMap[I][J] := 0;
//
//      if (J = 0) or (J = 1) or (J = GAMECOL - 1 ) or (J = GAMECOL - 2 ) then
//        FGameMap[I][J] := 1
//      else
//        FGameMap[I][J] := 0;
//    end;
//  end;

  FreeSnakeList();

  // 头 先添加
  New(P);
  P^.Row := 2;
  P^.Col := 7;
  P^.PointType := PT_Head;
  FSnakePointList.Add(P);

  // 身体
  New(P);
  P^.Row := 2;
  P^.Col := 6;
  P^.PointType := PT_Body;
  FSnakePointList.Add(P);
  New(P);
  P^.Row := 2;
  P^.Col := 5;
  P^.PointType := PT_Body;
  FSnakePointList.Add(P);
  New(P);
  P^.Row := 2;
  P^.Col := 4;
  P^.PointType := PT_Body;
  FSnakePointList.Add(P);
  New(P);
  P^.Row := 2;
  P^.Col := 3;
  P^.PointType := PT_Tail;
  FSnakePointList.Add(P);

//  // 蛇的初始位置
//  for J := 1 to 5 do
//    FGameMap[1][J] := 1;

  // 食物初始位置
//  FFoodPoint^.Row := 10;
//  FFoodPoint^.Col := 30;
//  FFoodPoint^.PointType := PT_Food;

//  FGameMap[10][30] := 1; 
  FMyConsole.SetCursorTo(0, 0);
  FMyConsole.SetForegroundColor(true, false, false, false);
  FMyConsole.WriteTextLine('┏━━━━━━━━━━━━━━━━━━━━━━━━┓');
  FMyConsole.WriteTextLine('┃                                                ┃');
  FMyConsole.WriteTextLine('┃                                                ┃');
  FMyConsole.WriteTextLine('┃                                                ┃');
  FMyConsole.WriteTextLine('┃                                                ┃');
  FMyConsole.WriteTextLine('┃                                                ┃');
  FMyConsole.WriteTextLine('┃                                                ┃');
  FMyConsole.WriteTextLine('┃                                                ┃');
  FMyConsole.WriteTextLine('┃                                                ┃');
  FMyConsole.WriteTextLine('┃                                                ┃');
  FMyConsole.WriteTextLine('┃                                                ┃');
  FMyConsole.WriteTextLine('┃                                                ┃');
  FMyConsole.WriteTextLine('┃                                                ┃');
  FMyConsole.WriteTextLine('┃                                                ┃');
  FMyConsole.WriteTextLine('┃                                                ┃');
  FMyConsole.WriteTextLine('┗━━━━━━━━━━━━━━━━━━━━━━━━┛');

  GetFood();
end;

procedure TSnake.MoveSnake;
var
  Head  : PGamePoint;
  Tail  : PGamePoint;
  P1, P2: PGamePoint;
  I     : integer;
  NewBody: PGamePoint;
  eat: boolean;
begin

  if ThreadPause then
  begin
    FMyConsole.SetCursorTo(0, 16);
    FMyConsole.WriteText('游戏已暂停请按空格键继续...                    ');
  end
  else
  begin
    if CheckGameOver() then
    begin
      GameOver();
    end
    else
    begin
      eat := EatFood();

      //保存最后一个要擦除的点
      Tail := GetSnakeBodyType(PT_Tail);
      FLastPoint^.Row := Tail^.Row;
      FLastPoint^.Col := Tail^.Col;

      if eat then
      begin
        New(NewBody);
        NewBody^.Row := Tail^.Row;
        NewBody^.Col := Tail^.Col;
        NewBody^.PointType := PT_Tail;
        FSnakePointList.add(NewBody);

        Tail^.PointType := PT_Body;

        GetFood();
      end;

      // 移动蛇的位置
      for I := FSnakePointList.Count - 1 downto 1 do
      begin
        P1 := FSnakePointList.Items[I];
        P2 := FSnakePointList.Items[I - 1];

        P1^.Row := P2^.Row;
        P1^.Col := P2^.Col;
      end;

      Head := GetSnakeBodyType(PT_Head);
      case Dir of
        MD_Right: Inc(Head^.Col);
        MD_Left : Dec(Head^.Col);
        MD_Up   : Dec(Head^.Row);
        MD_Down : Inc(Head^.Row);
      end;

      PrintSnake();

        // 清空蛇尾
      if FStartSnake and not eat then
      begin
        FMyConsole.SetCursorTo(FLastPoint^.Col, FLastPoint^.Row);
        FMyConsole.WriteText(' ');
      end;
    end;
  end;

end;

procedure TSnake.PrintSnake;
var
  P: PGamePoint;
  I: integer;
begin
  FMyConsole.SetForegroundColor(false, true, false, false);
  for I := 0 to FSnakePointList.Count - 1 do
  begin
    P := FSnakePointList.Items[I];
    FMyConsole.SetCursorTo(P^.Col, P^.Row);
    case P^.PointType of
      PT_Head: FMyConsole.WriteText('#');
      PT_Body: FMyConsole.WriteText('*');
      PT_Tail: FMyConsole.WriteText('*');
    end;
  end;

//  FMyConsole.WriteTextLine('┏━━━━━━━━━━━━━━━━━━━━━━━━┓');
//  FMyConsole.WriteTextLine('┃****#                                           ┃');
//  FMyConsole.WriteTextLine('┃                                                ┃');
//  FMyConsole.WriteTextLine('┃                                                ┃');
//  FMyConsole.WriteTextLine('┃                                                ┃');
//  FMyConsole.WriteTextLine('┃                                                ┃');
//  FMyConsole.WriteTextLine('┃                                                ┃');
//  FMyConsole.WriteTextLine('┃                                                ┃');
//  FMyConsole.WriteTextLine('┃                                                ┃');
//  FMyConsole.WriteTextLine('┃                                                ┃');
//  FMyConsole.WriteTextLine('┃                                         O      ┃');
//  FMyConsole.WriteTextLine('┃                                                ┃');
//  FMyConsole.WriteTextLine('┃                                                ┃');
//  FMyConsole.WriteTextLine('┃                                                ┃');
//  FMyConsole.WriteTextLine('┃                                                ┃');
//  FMyConsole.WriteTextLine('┗━━━━━━━━━━━━━━━━━━━━━━━━┛');
  // 1448end;

procedure TSnake.ShowScores(add: boolean = false);
var
  S: string;
begin
//    FEatFoodCount   : integer;
//    FScores         : integer;
  if add then
  begin
    Inc(FEatFoodCount);
  end;
  S := Format('完成食物个数: %d     得分数: %d    ', [FEatFoodCount, 10 * FEatFoodCount]);
  FMyConsole.SetCursorTo(0, 16);
  FMyConsole.WriteText(S);
end;

procedure TSnake.Start;
begin
  FEatFoodCount   := 0;
  //FScores         := 0;
  StartSnake := true;
  FReadKeyThread.StartRead := true;
end;

procedure TSnake.StartGame;
var
  S: string;
begin
  PrintSnake();

  FMyConsole.SetCursorTo(0, 16);
  FMyConsole.WriteText('现在开始游戏吗? (y/n):');
  Readln(S);
  if LowerCase(S) = 'y' then
  begin
//    FMyConsole.SetCursorTo(0, 16);
//    FMyConsole.WriteText('开始游戏                          ');
    Start();
  end;
end;

function TSnake.ThreadPause: boolean;
begin
  Result := FReadKeyThread.Pause;
end;

{ TReadKeyThread }

procedure TReadKeyThread.Execute;
var
  arrInputRecs   : array[0..9] of TInputRecord;
  dwCur, dwCount : DWORD;
  hInput         : THandle;
begin
  hInput   := GetStdHandle(STD_INPUT_HANDLE);
  while TRUE do
  begin
    ReadConsoleInput(hInput, arrInputRecs[0], 10, dwCount);

    for dwCur := 0 to 10 - 1 do
    begin
      if self.Terminated then break;
      case arrInputRecs[dwCur].EventType of
        KEY_EVENT:
          begin
            with arrInputRecs[dwCur].Event.KeyEvent do
            begin
              if bKeyDown = true then
              begin
                case wVirtualKeyCode of
                  VK_Space:
                    begin
                      Pause := not Pause;
                    end;
                  VK_Left:
                    begin
                      if (MoveDir <> MD_Left) and (MoveDir <> MD_Right) then
                      begin
                        if not FPause then
                          MoveDir := MD_Left;
                      end;
                    end;
                  VK_Right:
                    begin
                      if (MoveDir <> MD_Right) and (MoveDir <> MD_Left) then
                      begin
                        if not  FPause then
                          MoveDir := MD_Right;
                      end;
                    end;
                  VK_Up:
                    begin
                      if (MoveDir <> MD_Up) and (MoveDir <> MD_Down) then
                      begin
                        if not  FPause then
                          MoveDir := MD_Up;
                      end;
                    end;
                  VK_Down:
                    begin
                      if (MoveDir <> MD_Up) and (MoveDir <> MD_Down) then
                      begin
                        if not  FPause then
                          MoveDir := MD_Down;
                      end;
                    end;
                end;
              end;
            end;
          end;
      end;
    end;
  end;
end;

procedure TReadKeyThread.SetStartRead(const Value: boolean);
begin
  FStartRead := Value;
  if FStartRead then
  begin
    MoveDir := MD_Right;
    FPause  := false;
    Resume;
  end
  else
    Suspend;
end;

initialization
  Snake := TSnake.Create;
  Snake.StartGame();
  FTimer := SetTimer(0, 0, TIMERINTERVAL, @TimerProc);
  
finalization
  KillTimer(0, FTimer);
  Snake.Free();

end.

4 控制台单元  这个单元是网上的

 

unit uConsoleClass;

interface

uses Windows;

type
  TConsoleControl = Class
  private
    FhStdIn            : THandle;  // Handle to the standard input
    FhStdOut           : THandle;  // Handle to the standard output
    FhStdErr           : THandle;  // Handle to the standard error (Output)
    FbConsoleAllocated : Boolean;  // Creation Flag
    FBgAttrib          : Cardinal; // Currently set BackGround Attribs.
    FFgAttrib          : Cardinal; // Currently set ForeGround Attribs.

  public
    (* Creates a new consolewindow, or connects the current window *)
    constructor Create;
    destructor Destroy;override;

    (* Cleanup of the class structures  
                       
                    
                    

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
理解 Delphi 的类(十) - 深入方法[14] - 在TForm1 类内声明的方法发布时间:2022-07-18
下一篇:
DelphiRichEdit读取剪切板发布时间:2022-07-18
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap