在网上发现一篇60行javascript超经典俄罗斯方块代码,值得学习,转为Delphi如下,有详细注释,不再另讲解:

unit Block_Unit;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Grids, Vcl.ExtCtrls;

type
  TBlockForm = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormPaint(Sender: TObject);
  private
    procedure WMMyKey(var Msg: TWMKeyDown); message WM_KEYDOWN;
  public
  end;
type
  TIntArr=array of TArray<Integer>;
type TBlock=record
  x,y,s:Integer;
  fk:array [0..3] of Integer;//fk记录方块,4X4二进制矩阵
end;
var
  BlockForm: TBlockForm;
  Map:array  of Integer;
  Tetris: TIntArr= [[$6600],//方块,二进制数据显示方块
                    [$2222, $0f00],//I型
                    [$c600, $2640],//Z型
                    [$6c00, $4620],//反Z型
                    [$4460, $2e0, $6220, $740],//L型
                    [$2260, $0e20, $6440, $4700], //反L型
                    [$2620, $720, $2320, $2700]]; //T型;
  pos,bak:TBlock;//pos当前方块数据,bak备份当前方块数据
  dia:TArray<Integer>;
  rs:TResourceStream;
procedure Rotate(r:Integer);
procedure GameStart;
procedure UpdateMap(b:Boolean);
function HaveBlock:Boolean;
procedure BlockMove(r:Integer);
implementation
  uses Vcl.Imaging.jpeg;
var
  Green,Red:TJPEGImage;
{$R *.dfm}
{此游戏的核心思路是用二进制来记录整个界面的变化,
每个方块设定为4X4的二进制矩阵,整个游戏界面设定为10列、22行。}
{初始化方块}
procedure GameStart;
begin
  dia:=Tetris[Random(7)];//随机方块类别
  pos.x:=4;//初始位置
  pos.y:=0;
  pos.s:=Random(Length(dia));//随机方块形态
  bak:=pos;
  Rotate(0);
end;
{方块旋转}
procedure Rotate(r:Integer);
var
  f,i:Integer;
begin
  //根据方块的形态多少来依次旋转方块
  pos.s:=(pos.s+r) mod Length(dia);
  f:=dia[pos.s];
  for i:= 0 to 3 do
  //旋转变换
      pos.fk[i]:=(f shr (12-i*4) and $F) shl (8-pos.x);
  updateMap(HaveBlock);
end;
{更新界面}
procedure UpdateMap(b:Boolean);
begin
  if b=False then
  begin
    bak:=pos;
    BlockForm.Repaint;
  end;
end;
{判断障碍物}
function HaveBlock:Boolean;
var
  i:Integer;
begin
  Result:=False;
  for I := 0 to 3 do
    if pos.fk[i] and Map[pos.y+i]<>0 then
    begin
      pos:=bak;
      Result:=True;
      Break;
    end;
end;
{游戏结束}
procedure GameOver;
begin
  BlockForm.Timer1.Enabled:=False;
  ShowMessage('GameOver!');
end;
{游戏运行,关键代码}
procedure BlockDown;
begin
  Inc(pos.y);
  if HaveBlock then
  begin
     for var  i:Integer:= 0 to 3 do
     begin
       if  pos.y+i>=22 then Break;
       Map[pos.y+i]:=Map[pos.y+i] or pos.fk[i];//“或”操作来改变数据
       if Map[pos.y+i]=$FFF then//二进制表示满行
       begin
         Delete(Map,pos.y+i,1);//消除满行
         Insert([$801],Map,0);//插入空行
       end;
     end;
    if Map[1]<>$801 then//第一行不为空游戏结束
       GameOver;
     GameStart;
  end;
  UpdateMap(False);
end;
{方块左右移动,-1为左,1为右}
procedure BlockMove(r:Integer);
var
 i:Integer;
begin
  Inc(pos.x,r);
  for I := 0 to 3 do
  begin
    if r=1 then
       pos.fk[i]:=pos.fk[i] shr 1
    else
       pos.fk[i]:=pos.fk[i] shl 1;
  end;
  UpdateMap(HaveBlock);
end;
procedure TBlockForm.Button1Click(Sender: TObject);
begin
  //$801的二进制为100000000001
  for var I:Integer := 0 to 21 do Map[i]:=$801;
  Map[22]:=$FFF;
  //以上代码利用二进制初始化游戏界面,即为
  //100000000001
  //100000000001
  //............
  //111111111111
  GameStart;
  Button1.Enabled:=False;
end;

procedure TBlockForm.FormCreate(Sender: TObject);
var
 st:TMemoryStream;
begin
  DoubleBuffered:=True;
  SetLength(Map,23);//设定共23行,其中最后一行为判断是否触底用
  Randomize;
  rs := TResourceStream.Create(HInstance, 'Green', RT_RCDATA);
  Green:=TJPEGImage.Create;
  Red:=TJPEGImage.Create;
  Green.LoadFromStream(rs);
  rs.Free;
  rs:=TResourceStream.Create(HInstance, 'Red', RT_RCDATA);
  Red.LoadFromStream(rs);
  rs.Free;
end;
procedure TBlockForm.WMMyKey(var Msg: TWMKeyDown);
begin
  //输入法要切换成英文,只能发送给有焦点的Form来响应
  if (Msg.CharCode=VK_UP)or(Msg.CharCode=ord('W')) then Rotate(1);
  if (Msg.CharCode=VK_DOWN)or(Msg.CharCode =ord('S') ) then BlockDown;
  if (Msg.CharCode=VK_LEFT)or(Msg.CharCode = 65) then BlockMove(-1);
  if (Msg.CharCode=VK_RIGHT)or(Msg.CharCode =ord('D')) then BlockMove(1);
  if Msg.CharCode=VK_ESCAPE then //使Button起作用
  begin
    Button1.Enabled:=true;
    Button1.SetFocus;
  end;

end;

procedure TBlockForm.FormPaint(Sender: TObject);
var
  i,j,k:Integer;
begin
  Canvas.Brush.Style:=bsSolid;
  Canvas.Brush.Color:=RGB(255,255,255);
  Canvas.FillRect(Rect(0,0,150,330));
  for I := 0 to 21 do
  begin
    for j := 0 to 9 do
    begin
      k:=Map[i] shr (10-j) and 1;//判断是否触底,触底则变绿
      if k=1 then
        //Canvas.FillRect(Rect(j*15+1,i*15+1,j*15+14,i*15+14));
        Canvas.Draw(j*15+1,i*15+1,Green);
    end;
  end;
  Canvas.Brush.Color:=RGB(255,0,0);
  for I :=0 to 3 do
  begin
    for j := 0 to 9 do
    begin
      k:=bak.fk[i] shr (10-j) and 1;
      if k=1 then//不触底则以红色来画方块
        Canvas.Draw(j*15+1,(i+bak.y)*15+1,Red);
        //Canvas.FillRect(Rect(j*15+1,(i+bak.y)*15+1,j*15+14,(i+bak.y)*15+14));
    end;
  end;
end;

procedure TBlockForm.Timer1Timer(Sender: TObject);
begin
  BlockDown;
end;

end.
//其中Red、Green两个图片大小为15X15像素的资源,如不用资源文件,可以去掉Draw,改用FillRect。

  

内容来源于网络如有侵权请私信删除

文章来源: 博客园

原文链接: https://www.cnblogs.com/zhangxiny/p/15206708.html

你还没有登录,请先登录注册
  • 还没有人评论,欢迎说说您的想法!