admin 管理员组

文章数量: 887021


2024年3月1日发(作者:linux镜像)

用matlab编写的俄罗斯方块小游戏

function RussiaBlock( varargin )

if nargin == 0

OldHandle = findobj( 'Type', 'figure', 'Tag', 'RussiaBlock' ) ;

if ishandle( OldHandle )

delete( OldHandle ) ;

end

FigureHandle = figure( 'Name', '俄罗斯方块MATLAB版', 'Tag', 'RussiaBlock',

'NumberTitle', 'off',...

'Menubar', 'none', 'DoubleBuffer', 'on', 'Resize', 'off', 'visible', 'on',...

'KeyPressFcn', 'RussiaBlock( ''KeyPress_Callback'', gcbo )',...

'HelpFcn', 'helpdlg(''帮不了你- -!'',''不好意思'')',...

'CloseRequestFcn', 'RussiaBlock( ''CloseFigure_Callback'', gcbo )' ) ;

generate_FigureContent( FigureHandle ) ;

init_FigureContent( FigureHandle ) ;

set( FigureHandle, 'Visible', 'on' ) ;

elseif ischar( varargin{1} )

feval( varargin{:} ) ;

end

% -------------------------------------------------------------------------

function generate_FigureContent( FigureHandle )

TabSpace = 30 ;

BlockWidth = 20 ;

BlockHeight = 20 ;

FigureWidth = BlockWidth * (12 + 1) + TabSpace * 7;

FigureHeight = 500 ;

set( FigureHandle, 'Position', [0 0 FigureWidth FigureHeight] ) ;

movegui( FigureHandle, 'center' ) ;

% 创建菜单

BeginMenu = uimenu( FigureHandle, 'Label', '开始' ) ;

StartMenu = uimenu( BeginMenu, 'Label', '开始新游戏', 'Accelerator', 'N',...

'Callback', 'RussiaBlock( ''StartNewGame_Callback'', gcbo )');

SaveMenu = uimenu( BeginMenu, 'Label', '保存', 'Accelerator', 'S', 'Enable', 'off',...

'Separator', 'on', 'Cal', 'RussiaBlock( ''SaveGame_Callback'', gcbo )' );

LoadMenu = uimenu( BeginMenu, 'Label', '读取', 'Accelerator', 'L', 'Enable', 'off',...

'Cal', 'RussiaBlock( ''LoadGame_Callback'', gcbo )' );

QuitMenu = uimenu( BeginMenu, 'Label', '退出', 'Accelerator', 'Q', 'Separator', 'on', 'Cal',

'close(gcf)');

OperationMenu = uimenu( FigureHandle, 'Label', '功能' );

BoardConfigMenu = uimenu( OperationMenu, 'label', '键盘设置', 'Enable', 'off',...

'Cal', 'RussiaBlock( ''BoardConfig_Callback'', gcbo )' );

FigureConfigMenu = uimenu( OperationMenu, 'label', '界面设置', 'Enable', 'off',...

'Cal', 'RussiaBlock( ''FigureConfig_Callback'', gcbo )' );

HighScoreMenu = uimenu( OperationMenu, 'label', '最高记录', 'Separator', 'on',...

'Cal', 'RussiaBlock( ''HighScore_Callback'', gcbo )', 'Enable', 'off' );

GameLevelMenu = uimenu( OperationMenu, 'Label', '游戏难度',...

'Cal','RussiaBlock( ''GameLevel_Callback'', gcbo )' );

HelpMenu = uimenu( FigureHandle, 'Label', '帮助' );

AboutMenu = uimenu( HelpMenu, 'Label', '关于此软件', 'Cal', 'helpdlg(''俄罗斯方块MATLAB版'',''关于此软件…………'')');

HelpDlgMenu = uimenu( HelpMenu, 'Label', '游戏帮助', 'Separator', 'on', 'Cal', 'helpdlg(''帮不了你- -!'',''不好意思'')' );

% 创建工具条,图标可以用imread从图片读取,但图片不要太大

BeginTool = uipushtool( 'ToolTipString', '开始', 'CData', rand(16,16,3), 'Tag', 'BeginTool',...

'ClickedCallback', 'RussiaBlock( ''StartNewGame_Callback'', gcbo )' ) ;

PauseTool = uitoggletool( 'ToolTipString', '暂停', 'Tag', 'PauseTool', 'Tag', 'PauseTool',...

'CData', reshape( repmat( [1 1 0], 16, 16), [16,16,3] ),...

'ClickedCallback', 'RussiaBlock( ''PauseGame_Callback'', gcbo )' ) ;

% 创建游戏窗口

MainWindowXPos = TabSpace;

MainWindowYPos = TabSpace;

MainWindowWidth = BlockWidth * 12 ;

MainWindowHeight = BlockHeight * 22 ;

MainWindowPosition = [MainWindowXPos MainWindowYPos MainWindowWidth

MainWindowHeight] ;

% 定义游戏窗口的右键菜单

AxesContextMenu = uicontextmenu( 'Tag', 'uicontextmenu' ) ;

uimenu( AxesContextMenu, 'Label', '设置窗口颜色', 'Cal',

'RussiaBlock( ''WindowColor_Callback'', gcbo )' )

uimenu( AxesContextMenu, 'Label', '设置背景图片', 'Cal',

'RussiaBlock( ''WindowPicture_Callback'', gcbo )' )

uimenu( AxesContextMenu, 'Label', '设置方块颜色', 'Cal',

'RussiaBlock( ''BlockColor_Callback'', gcbo )' )

uimenu( AxesContextMenu, 'Label', '恢复默认', 'Cal', 'RussiaBlock( ''Default_Callback'',

gcbo )' )

MainAxes = axes( 'Units', 'pixels', 'Pos', MainWindowPosition, 'XTick', [], 'YTick',[],

'XTickLabel', [],...

'YTickLabel', [], 'Box', 'on', 'Tag', 'MainAxes', 'UicontextMenu', AxesContextMenu,...

'XLim', [0 MainWindowWidth], 'YLim', [0 MainWindowHeight] ) ;

hold on;

% 创建一个窗口用于显示下一个方块的图形

NextBlockWndXPos = MainWindowXPos + MainWindowWidth + TabSpace ;

NextBlockWndHeight = 4 * TabSpace + BlockHeight ;

NextBlockWndYPos = MainWindowYPos + MainWindowHeight - NextBlockWndHeight ;

NextBlockWndWidth = TabSpace * 4 + BlockWidth ;

NextBlockWndPosition = [NextBlockWndXPos NextBlockWndYPos NextBlockWndWidth

NextBlockWndHeight] ;

NextBlockAxes = axes( 'Units', 'pixels', 'Pos', NextBlockWndPosition, 'XTick', [], 'YTick',[],...

'XTickLabel', [], 'YTickLabel', [], 'XLim', [0 NextBlockWndWidth],...

'YLim', [0 NextBlockWndHeight], ...

'Box', 'on', 'Tag', 'NextBlockAxes', 'Color', [0.85 0.85 0.85] ) ;

% 创建一组控件,包括(两个文本框用于显示当前方块数和成绩,两个按钮用于暂停和退出)

ButtonTag = { 'QuitButton', 'PauseButton', 'BlockNumText', 'ScoreText' } ;

ButtonStyle = { 'pushbutton', 'togglebutton', 'text', 'text' } ;

FontColor = { [0 0 0], [1 0 0], [0 0 1], [1 0 1] } ;

ButtonColor = { [0.7 0.8 0.9], [0.3 1 0.3], [0.5 1 1], [0.5 1 1] } ;

ButtonString = { '退出', '暂停', '方块数', '积分' };

ButtonCallback = { 'close(gcf)', 'RussiaBlock( ''ButtonPauseGame_Callback'', gcbo )', '', '' } ;

ButtonNumber = length( ButtonTag ) ;

ButtonWidth = NextBlockWndWidth ;

ButtonHeight = 50 ;

ButtonXPos = NextBlockWndXPos ;

ButtonYPos = MainWindowYPos + TabSpace ;

ButtonPosition = [ButtonXPos ButtonYPos ButtonWidth ButtonHeight] ;

ButtonTabSpace = (NextBlockWndYPos - 2 * TabSpace - ButtonHeight * ButtonNumber) /

ButtonNumber ;

for num = 1: ButtonNumber

TempButtonPosition = ButtonPosition ;

TempButtonPosition(2) = ButtonPosition(2) + (num - 1) * (ButtonTabSpace +

ButtonHeight);

if findstr( ButtonStyle{num}, 'button' )

TempButtonPosition(1) = TempButtonPosition(1) + 10 ;

TempButtonPosition(2) = TempButtonPosition(2) + 5 ;

TempButtonPosition(3) = TempButtonPosition(3) - 10 * 2 ;

TempButtonPosition(4) = TempButtonPosition(4) - 5 * 2 ;

else

TempButtonPosition(1) = TempButtonPosition(1) - 10 ;

TempButtonPosition(2) = TempButtonPosition(2) - 5 ;

TempButtonPosition(3) = TempButtonPosition(3) + 10 * 2;

TempButtonPosition(4) = TempButtonPosition(4) + 5 * 2 ;

end

ButtonHandle = uicontrol( 'Tag', ButtonTag{num}, 'Style', ButtonStyle{num}, 'Pos',

TempButtonPosition,...

'Foregroundcolor', FontColor{num}, 'Backgroundcolor', ButtonColor{num},...

'Fontsize', 16, 'String', ButtonString{num}, 'Cal', ButtonCallback{num} ) ;

if findstr( ButtonStyle{num}, 'text' )

set( ButtonHandle, 'Max', 2 ) ;

end

if findstr( ButtonTag{num}, 'PauseButton' )

set( ButtonHandle, 'Enable', 'inactive', 'ButtonDownFcn', ButtonCallback{num}, 'Cal', '' ) ;

end

end

MainBlockAxes = axes( 'Units', 'pixels', 'Pos', MainWindowPosition, 'XTick', [], 'YTick',[],

'XTickLabel', [],...

'YTickLabel', [], 'Box', 'on', 'Tag', 'MainBlockAxes', 'Hittest', 'off',...

'XLim', [0 MainWindowWidth], 'YLim', [0 MainWindowHeight], 'Color', 'none' ) ;

line( 'Visible', 'on', 'Tag', 'BlockHandle', 'Markersize', 18, 'Parent', MainBlockAxes, 'HitTest',

'off',...

'Marker', 's', 'MarkerEdgeColor', 'k', 'XData', nan, 'YData', nan, 'LineStyle', 'none' ) ;

line( 'Visible', 'off', 'Tag', 'TempBlock', 'Markersize', 18, 'Parent', MainBlockAxes, 'HitTest',

'off',...

'Marker', 's', 'MarkerEdgeColor', 'k', 'XData', 130, 'YData', 30, 'LineStyle', 'none' ) ;

line( 'Visible', 'off', 'Tag', 'NextBlock', 'Markersize', 18, 'Parent', NextBlockAxes, 'HitTest',

'off',...

'Marker', 's', 'MarkerEdgeColor', 'k', 'XData', 30, 'YData', 30, 'LineStyle', 'none' ) ;

setappdata( FigureHandle, 'XLim', [0 MainWindowWidth] )

setappdata( FigureHandle, 'YLim', [0 MainWindowHeight] )

handles = guihandles( FigureHandle ) ;

guidata( FigureHandle, handles ) ;

% -------------------------------------------------------------------------

function init_FigureContent( FigureHandle )

handles = guidata( FigureHandle ) ;

ColorInfo = [] ;

try

ColorInfo = load('') ;

catch

end

if isempty( ColorInfo )

olor = GetDefaultBlockColor ;

esColor = GetDefaultMainAxesColor ;

ata = [] ;

end

set( es, 'Color', esColor ) ;

if ~isempty( ata )

ImageHandle = image( ata, 'Parent',

es ) ;

set( ImageHandle, ty ) ;

setappdata( FigureHandle, 'ImageData', ata ) ;

end

set( andle, 'MarkerFaceColor', olor ) ;

set( ock, 'MarkerFaceColor', olor ) ;

set( ock, 'MarkerFaceColor', olor ) ;

setappdata( FigureHandle, 'BlockColor', olor ) ;

% ------------------------------------------------------------

function StartNewGame_Callback( h, StartType )

handles = guidata( h ) ;

global PauseTime

if nargin == 1

StartType = 'NewStart' ;

setappdata( Block, 'BlockNumber', 0 ) ;

set( umText, 'String', {'方块数','0'} ) ;

setappdata( Block, 'CurrentScore', 0 ) ;

set( ext, 'String', {'积分','0'} ) ;

set( andle, 'XData', nan, 'YData', nan ) ;

set( ock, 'XData', nan, 'YData', nan ) ;

TextHandle = findobj( 'Parent', ockAxes, 'Type', 'text' ) ;

delete( TextHandle ) ;

else

end

set( ock, 'Visible', 'on' ) ;

set( ock, 'Visible', 'on' ) ;

set( ool, 'State', 'off' ) ;

set( utton, 'Value', 0 ) ;

YLim = get( es, 'YLim' ) ;

while( ishandle( h ) )

TotalYData = get( andle, 'YData' ) ;

if any( TotalYData >= YLim(2) )

% Game over

text( 20, 200, 'GameOver', 'Parent', ockAxes,...

'FontSize', 30, 'Color', 'r', 'FontAngle', 'italic' ) ;

break;

end

if length( TotalYData ) >= 4

TotalXData = get( andle, 'XData' ) ;

LastBlockYData = TotalYData( end - 3: end ) ;

LastBlockYData = unique( LastBlockYData ) ;

CompleteLine = [] ;

UsefulIndex = [] ;

for num = 1: length( LastBlockYData )

[YData, Index] = find( TotalYData == LastBlockYData(num) ) ;

if length( YData ) == 12

CompleteLine = [CompleteLine, LastBlockYData(num)] ;

UsefulIndex = [UsefulIndex, Index] ;

end

end

if ~isempty( CompleteLine )

TotalXData( UsefulIndex ) = [] ;

TotalYData( UsefulIndex ) = [] ;

LineNumber = length( CompleteLine ) ;

ScoreArray = [100 300 600 1000] ;

NewScore = ScoreArray(LineNumber) ;

CurrentScore = getappdata( Block, 'CurrentScore' ) ;

TextString = get( ext, 'String' ) ;

TextString{2} = CurrentScore + NewScore ;

set( ext, 'String', TextString ) ;

setappdata( Block, 'CurrentScore', CurrentScore + NewScore ) ;

UpdateGameLevel( Block, CurrentScore + NewScore ) ;

% 处理需要下移的方块

for num = LineNumber : -1 : 1

[YData, Index] = find( TotalYData > LastBlockYData(num) ) ;

TotalYData(Index) = TotalYData(Index) - 20 ;

end

end

set( andle, 'XData', TotalXData, 'YData', TotalYData ) ;

end

BlockNumber = getappdata( Block, 'BlockNumber' ) ;

TextString = get( umText, 'String' ) ;

TextString{2} = BlockNumber + 1 ;

set( umText, 'String', TextString ) ;

setappdata( Block, 'BlockNumber', BlockNumber + 1 ) ;

GameLevel = getappdata( Block, 'GameLevel' ) ;

if isempty( GameLevel )

PauseTime = 0.3 ;

else

PauseTime = ceil( 20 / GameLevel ) / 100 ;

end

ep = 0 ;

ep = 0 ;

setappdata( Block, 'TempBlockPos', TempBlockPos ) ;

Status = 1;

[BlockXArray,BlockYArray] = Com_GetBlock( h ) ;

set( ock, 'XData', BlockXArray, 'YData', BlockYArray ) ;

while( Status & ishandle( h ) )

if(PauseTime) ~= 0

pause( PauseTime )

end

Status = test_MoveBlock( h, 'Down' ) ;

end

end

% -------------------------------------------------------------------------

function KeyPress_Callback( h )

handles = guidata( h ) ;

PauseState = get( ool, 'State' ) ;

if strcmp( PauseState, 'on' )

return

end

BoardConfig = getappdata( Block, 'BoardConfig' ) ;

if isempty( BoardConfig )

Left = 'leftarrow' ;

Right = 'rightarrow' ;

Down = 'downarrow' ;

Change = 'uparrow' ;

Drop = 'space' ;

else

Left = ;

Right = ;

Down = ;

Change = ;

Drop = ;

end

CurrentKey = get( Block, 'CurrentKey' ) ;

switch CurrentKey

case Left

test_MoveBlock( h, 'Left' ) ;

case Right

test_MoveBlock( h, 'Right' ) ;

case Down

test_MoveBlock( h, 'Down' ) ;

case Change

test_MoveBlock( h, 'Change' ) ;

case Drop

test_MoveBlock( h, 'Drop' ) ;

otherwise

return ;

end

% -------------------------------------------------------------------------

function WindowColor_Callback( h )

handles = guidata( h ) ;

CurrentColor = get( es, 'Color' ) ;

NewColor = uisetcolor( CurrentColor, '请选择窗口颜色' ) ;

if length( NewColor ) == 0

return;

else

set( es, 'Color', NewColor ) ;

end

% -------------------------------------------------------------------------

function WindowPicture_Callback( h )

handles = guidata( h ) ;

[PictureFile, Path] = uigetfile( {'*.jpg; *.bmp'},'请选择图片' );

if isnumeric( PictureFile )

return ;

else

% if length( PictureFile ) > 31

% errordlg( '文件名过长,读取失败' ) ;

% end

try

Picture = imread( [Path, PictureFile] ) ;

for num = 1: size( Picture, 3 )

ValidPicture(:, :, num) = flipud( Picture(:,:,num) ) ;

end

AxesXLim = get( es, 'XLim' ) ;

AxesYLim = get( es, 'YLim' ) ;

BlockHandle = findobj( es, 'Style', 'line' ) ;

cla( BlockHandle ) ;

ImageXLimit = size(Picture, 2) ;

ImageYLimit = size(Picture, 1) ;

if diff( AxesXLim ) < size(Picture, 2) | diff( AxesYLim ) < size(Picture, 1)

% 超出坐标轴范围,压缩显示

XScale = diff( AxesXLim ) / size(Picture, 2) ;

YScale = diff( AxesYLim ) / size(Picture, 1) ;

% 取较小比例压缩

Scale = min( XScale, YScale ) ;

ImageXLimit = size(Picture, 2) * Scale ;

ImageYLimit = size(Picture, 1) * Scale ;

end

ImageXData(1) = AxesXLim(1) + (diff( AxesXLim ) - ImageXLimit) / 2 + 1;

ImageXData(2) = ImageXData(1) + ImageXLimit - 1;

ImageYData(1) = AxesYLim(1) + (diff( AxesYLim ) - ImageYLimit) / 2 + 1;

ImageYData(2) = ImageYData(1) + ImageYLimit - 1;

image( ValidPicture, 'Parent', es, 'Hittest', 'off', ...

'XData',ImageXData,'YData',ImageYData, 'Tag', 'MainImage' );

setappdata( Block, 'ImageData', ValidPicture ) ;

catch

ErrorString = sprintf( ['读取图片失败,错误信息为:n',lasterr] ) ;

errordlg( ErrorString ) ;

end

end

% -------------------------------------------------------------------------

function BlockColor_Callback( h )

handles = guidata( h ) ;

CurrentColor = getappdata( Block, 'BlockColor' ) ;

if isempty( CurrentColor )

CurrentColor = GetDefaultBlockColor ;

setappdata( Block, 'BlockColor', CurrentColor ) ;

end

NewColor = uisetcolor( CurrentColor, '请选择方块颜色' ) ;

if length( NewColor ) == 0

return;

else

setappdata( Block, 'BlockColor', NewColor ) ;

set( andle, 'MarkerFaceColor', NewColor ) ;

set( ock, 'MarkerFaceColor', NewColor ) ;

set( ock, 'MarkerFaceColor', NewColor ) ;

end

% ------------------------------------------------------------------------

function Default_Callback( h )

handles = guidata( h ) ;

BlockColor = GetDefaultBlockColor ;

AxesColor = GetDefaultMainAxesColor ;

set( es, 'Color', AxesColor ) ;

set( andle, 'MarkerFaceColor', BlockColor ) ;

set( ock, 'MarkerFaceColor', BlockColor ) ;

set( ock, 'MarkerFaceColor', BlockColor ) ;

% -------------------------------------------------------------------------

function PauseGame_Callback( h )

handles = guidata( h ) ;

ToolStart = get( ool, 'State' ) ;

if strcmp( ToolStart, 'on' )

set( utton, 'Value', 1 ) ;

waitfor( ool, 'State', 'off' ) ;

else

set( utton, 'Value', 0 ) ;

end

% -------------------------------------------------------------------------

function ButtonPauseGame_Callback( h )

handles = guidata( h ) ;

ToggleButtonValue = get( h, 'Value' ) ;

if ToggleButtonValue == 0

set( h, 'Value', 1 ) ;

set( h, 'String', '继续' ) ;

set( ool, 'State', 'on' ) ;

waitfor( ool, 'State', 'off' ) ;

else

set( h, 'Value', 0 ) ;

set( h, 'String', '暂停' ) ;

set( ool, 'State', 'off' ) ;

set( Block, 'CurrentObject', es ) ;

end

% ------------------------------------------------------------------------

function CloseFigure_Callback( h )

handles = guidata( h ) ;

BlockColor = getappdata( Block, 'BlockColor' ) ;

MainAxesColor = get( es, 'Color' ) ;

MainAxesImageHandle = findobj( es, 'Type', 'image' ) ;

if ~isempty( MainAxesImageHandle )

= get( MainAxesImageHandle, 'Tag' );

t = get( MainAxesImageHandle, 'Hittest' );

= get( MainAxesImageHandle, 'XData' );

= get( MainAxesImageHandle, 'YData' );

ata = getappdata( Block, 'ImageData' ) ;

else

ata = [] ;

end

save BlockColor MainAxesColor MainAxesImage

delete( Block ) ;

% -------------------------------------------------------------------------

function Color = GetDefaultBlockColor

Color = [0 0 1] ;

% -------------------------------------------------------------------------

function Color = GetDefaultMainAxesColor

Color = [1 1 1] ;

% ----------------------------------------------------------------

function [BlockXArray, BlockYArray] = Com_GetBlock( varargin )

global BlockIndex ;

BlockXArray = [] ;

BlockYArray = [] ;

handles = guidata( varargin{1} ) ;

if nargin == 1

BlockArray = getappdata( Block, 'BlockArray' ) ;

BlockIndex = ceil( rand(1) * 24 ) ;

else % nargin == 2

BlockIndex = varargin{2} ;

end

switch(BlockIndex)

case {1,2,3,4} % 方块

BlockXArray = [0;0;1;1] * 20 - 10 ;

BlockYArray = [0;1;1;0] * 20 - 10 ;

case {5,6} % 竖长条

BlockXArray = [0;0;0;0] * 20 - 10 ;

BlockYArray = [-1;0;1;2] * 20 - 10 ;

case {7,8} % 横长条

BlockXArray = [-1;0;1;2] * 20 - 10 ;

BlockYArray = [1;1;1;1] * 20 - 10 ;

case {9} % 4类T T1

BlockXArray = [-1;0;1;0] * 20 - 10 ;

BlockYArray = [1;1;1;0] * 20 - 10 ;

case {10} % T2

BlockXArray = [0;0;1;0] * 20 - 10 ;

BlockYArray = [2;1;1;0] * 20 - 10 ;

case {11} % T3

BlockXArray = [0;0;1;-1] * 20 - 10 ;

BlockYArray = [2;1;1;1] * 20 - 10 ;

case {12} % T4

BlockXArray = [0;0;0;-1] * 20 - 10 ;

BlockYArray = [2;1;0;1] * 20 - 10 ;

case {13} % 8类L L1

BlockXArray = [0;0;0;1] * 20 - 10 ;

BlockYArray = [1;0;-1;-1] * 20 - 10 ;

case {14} % L2

BlockXArray = [-1;0;1;1] * 20 - 10 ;

BlockYArray = [0;0;0;1] * 20 - 10 ;

case {15} % L3

BlockXArray = [-1;0;0;0] * 20 - 10 ;

BlockYArray = [1;1;0;-1] * 20 - 10 ;

case {16} % L4

BlockXArray = [-1;-1;0;1] * 20 - 10 ;

BlockYArray = [-1;0;0;0] * 20 - 10 ;

case {17} % L5

BlockXArray = [-1;0;0;0] * 20 - 10 ;

BlockYArray = [-1;-1;0;1] * 20 - 10 ;

case {18} % L6

BlockXArray = [-1;-1;0;1] * 20 - 10 ;

BlockYArray = [1;0;0;0] * 20 - 10 ;

case {19} % L7

BlockXArray = [0;0;0;1] * 20 - 10 ;

BlockYArray = [-1;0;1;1] * 20 - 10 ;

case {20} % L8

BlockXArray = [-1;0;1;1] * 20 - 10 ;

BlockYArray = [0;0;0;-1] * 20 - 10 ;

case {21 22} % 4类Z Z1

BlockXArray = [-1;0;0;1] * 20 - 10 ;

BlockYArray = [1;1;0;0] * 20 - 10 ;

case {23 24} % Z2

BlockXArray = [0;0;1;1] * 20 - 10 ;

BlockYArray = [-1;0;0;1] * 20 - 10 ;

case {25 26} % Z3

BlockXArray = [-1;0;0;1] * 20 - 10 ;

BlockYArray = [0;0;1;1] * 20 - 10 ;

case {27 28} % Z4

BlockXArray = [0;0;1;1] * 20 - 10 ;

BlockYArray = [1;0;0;-1] * 20 - 10 ;

end

if nargin == 1

Array = BlockXArray ;

Array = BlockYArray ;

ndex = BlockIndex ;

NextAxesXLim = get( ockAxes, 'XLim' ) ;

NextAxesYLim = get( ockAxes, 'YLim' ) ;

set( ock, 'XData', [BlockXArray + 0.5 * diff( NextAxesXLim ) -

ceil( sum( BlockXArray ) / 4 ) ],...

'YData', [BlockYArray + 0.5 * diff( NextAxesYLim )] - ceil( sum( BlockYArray ) / 4 ) ) ;

setappdata( Block, 'BlockArray', NewBlockArray ) ;

if isempty( BlockArray )

Com_GetBlock( varargin{1} ) ;

else

BlockXArray = Array ;

BlockYArray = Array ;

BlockIndex = ndex ;

end

end

AxesXLim = getappdata( Block, 'XLim' ) ;

AxesYLim = getappdata( Block, 'YLim' ) ;

BlockXArray = BlockXArray + 0.5 * diff( AxesXLim ) ;

BlockYArray = BlockYArray + diff( AxesYLim ) ;

% -------------------------------------------------------------------------

function Status = test_MoveBlock( h, MoveMode )

Status = 1;

if ~ishandle( h )

return

end

handles = guidata( h ) ;

TempXData = get( ock, 'XData' ) ;

TempYData = get( ock, 'YData' ) ;

TempXData = TempXData';

TempYData = TempYData' ;

TotalXData = get( andle, 'XData' ) ;

TotalYData = get( andle, 'YData' ) ;

TotalXData = TotalXData' ;

TotalYData = TotalYData' ;

TempBlockPos = getappdata( Block, 'TempBlockPos' ) ;

if isempty( TempBlockPos )

return

end

AxesXLim = getappdata( Block, 'XLim' ) ;

AxesYLim = getappdata( Block, 'YLim' ) ;

switch MoveMode

case 'Left'

if any( TempXData - 20 < AxesXLim(1) )

return

end

TestArray = ismember( [TempXData - 20, TempYData], [TotalXData, TotalYData],

'rows' ) ;

if any( TestArray )

return;

else

set( ock, 'XData', TempXData - 20 ) ;

ep = ep + 1 ;

setappdata( Block, 'TempBlockPos', TempBlockPos ) ;

end

case 'Right'

if any( TempXData + 20 > AxesXLim(2) )

return

end

TestArray = ismember( [TempXData + 20, TempYData], [TotalXData, TotalYData],

'rows' ) ;

if any( TestArray )

return;

else

set( ock, 'XData', TempXData + 20 ) ;

ep = ep - 1 ;

setappdata( Block, 'TempBlockPos', TempBlockPos ) ;

end

case 'Down'

if any( TempYData - 20 < AxesYLim(1) )

set( andle, 'XData', [TotalXData; TempXData],...

'YData', [TotalYData; TempYData] ) ;

Status = 0 ;

return

end

TestArray = ismember( [TempXData, TempYData - 20], [TotalXData, TotalYData],

'rows' ) ;

if any( TestArray )

set( andle, 'XData', [TotalXData; TempXData],...

'YData', [TotalYData; TempYData] ) ;

Status = 0 ;

else

set( ock, 'YData', TempYData - 20 ) ;

ep = ep + 1 ;

setappdata( Block, 'TempBlockPos', TempBlockPos ) ;

end

case 'Drop'

global PauseTime

PauseTime = 0 ;

case 'Change'

global BlockIndex

OldBlockIndex = BlockIndex ;

switch BlockIndex

case {1,2,3,4}

return;

case {5,6}

NewIndex = 7 ;

case {7,8}

NewIndex = 5 ;

case {9,10,11,12}

NewIndex = mod( OldBlockIndex, 4 ) + 9;

case {13,14,15,16}

NewIndex = mod( OldBlockIndex, 4 ) + 13;

case {17,18,19,20}

NewIndex = mod( OldBlockIndex, 4 ) + 17;

case {21,22}

NewIndex = 23;

case {23,24}

NewIndex = 21;

case {25,26}

NewIndex = 27 ;

case {27,28}

NewIndex = 25 ;

end

[BlockXArray, BlockYArray] = Com_GetBlock( h, NewIndex ) ;

NewTempXData = BlockXArray - ep * 20 ;

NewTempYData = BlockYArray - ep * 20 ;

if any( NewTempXData < AxesXLim(1) ) | any( NewTempXData > AxesXLim(2) ) |...

any( NewTempYData < AxesYLim(1) )

BlockIndex = OldBlockIndex ;

return;

end

TestArray = ismember( [NewTempXData, NewTempYData], [TotalXData, TotalYData],

'rows' ) ;

if any( TestArray )

BlockIndex = OldBlockIndex ;

else

BlockIndex = NewIndex ;

set( ock, 'XData', NewTempXData, 'YData', NewTempYData ) ;

end

end

% -------------------------------------------------------------------------

function UpdateGameLevel( FigureHandle, Score ) ;

GameLevel = ceil( Score / 10000 ) ;

if GameLevel > 9

GameLevel = 9 ;

end

setappdata( FigureHandle, 'GameLevel', GameLevel ) ;

% -------------------------------------------------------------------------

function GameLevel_Callback( h )

handles = guidata( h ) ;

ScoreFigure = figure( 'name', '游戏难度设置', 'menubar', 'none', 'numbertitle', 'off',...

'pos', [200 200 200 180], 'windowstyle', 'modal', 'resize', 'off' ) ;

uicontrol( 'Parent', ScoreFigure, 'Style', 'listbox', 'Pos', [20 20 80 140],...

'String', [sprintf( '%d 级|', 1:8 ),'9 级'] ) ;

uicontrol( 'Parent', ScoreFigure, 'Style', 'pushbutton', 'Pos', [120 100 60 50], 'String', '确定',...

'Cal', ['GameLevel = get( findobj( ''Style'', ''listbox''), ''Value'');',...

'set( 0, ''Userdata'', GameLevel ); close( gcf );'] ) ;

uicontrol( 'Parent', ScoreFigure, 'Style', 'pushbutton', 'Pos', [120 30 60 50], 'String', '取消',...

'Cal', 'close( gcf )' ) ;

waitfor( ScoreFigure ) ;

ButtonAction = questdlg( '重新设置游戏难度将初始化界面,是否继续?', '设置确认', '是的', '取消', '取消' ) ;

if strcmp( '取消', ButtonAction )

return

end

GameLevel = get( 0, 'Userdata' ) ;

setappdata( Block, 'GameLevel', GameLevel ) ;

摘 要

随着科技的发展,许多用户希望在工作之余有一定的娱乐,而贪吃蛇这款经典游戏正符合大众的心里。因此,为了迎合众多用户的需求,我们开发出一套适合各阶层人士的具有很强的娱乐性和交互性的贪吃蛇小游戏。

贪吃蛇,之所以取这样的名字,去用意还是很明显的.贪吃蛇其实并不贪,它是人不断向前追求的象征.就像现代的人只有不断努力向前才能得到自己想要的。而食物也是随机出现的,就像现在社会存在的机会,而我们只有找好自己的目标才能成功。

虽然现在市面上存在着各种各样的游戏版本,可是贪吃蛇其市场还是相当大的。因为它的特殊在于它能吸引人更深入,爱不释手.随着游戏速度不断加快,其刺激性也更强。可以说该游戏的优势在于它的简单易行,还在于它很快顺利的运行。对于在外忙碌的人,不可能花费大量时间在娱乐上,大型游戏是行不通的。这样的小游戏刚好迎合了他们的需求。

关键词: 贪吃蛇,汇编语言,函数,游戏

目 录

目 录 ............................................................................................................................................ 18

前 言 ............................................................................................................................................ 19

第1章 系统分析 .......................................................................................................................... 20

1.1 需求概述 ........................................................................................................................ 20

1.2 需求环境 ........................................................................................................................ 20

1.3 功能描述 ........................................................................................................................ 20

第2章 系统总体设计................................................................................................................... 21

2.1 程序功能模块................................................................................................................. 21

2.2 程序流程图..................................................................................................................... 22

第3章 详细设计 .......................................................................................................................... 23

3.1详细设计 ......................................................................................................................... 23

3.1.1 数据段................................................................................................................. 23

3.1.2代码段.................................................................................................................. 23

3.1.3 初始化新游戏的各种状态 ................................................................................. 25

3.1.4 蛇身移动............................................................................................................. 25

3.1.5 窗口过程............................................................................................................. 26

3.1.6 入口函数............................................................................................................. 30

4.1可行性分析...................................................................................................................... 32

4.2测试结果分析.................................................................................................................. 32

第5章 软件使用说明书 .............................................................................................................. 33

5.1游戏运行截图.................................................................................................................. 33

参考文献 ........................................................................................................................................ 38

致 谢 .......................................................................................................................................... 39

附 录 .......................................................................................................................................... 40

前 言

人类已经跨入了二十二世纪,正在进入信息时代。计算机技术和通讯技术的发展和应用及,促进了社会的高速发展,同时也改变着人们的工作、学习、生活和娱乐的方式以及思想观念。随着计算机的日益普及,工作的生活节奏的加快,休闲小游戏越来越受到人们的青睐,尤其贪食蛇类的经典游戏在人类的生活娱乐中占有一席之地,随着计算机技术的成熟,各种各样的贪食蛇游戏也流传在网路中,人们可以方便的体验别样风格的贪食蛇。

第1章 系统分析

1.1 需求概述

本设计所开发的是基于汇编语言的一个贪吃蛇游戏软件,程序关键在于表示蛇的图形及蛇的移动。用一个小矩形快表示蛇的一节身体,身体每长一节,增加一个矩形块,蛇头用俩节表示。移动时必须从蛇头开始,所以蛇不能向相反的方向移动,如果不按任意键,蛇自行在当前方向上前移,但按下有效方向键后,蛇头朝着该方向移动,一步移动一节身体,所以按下有效方向键后,先确定蛇头的位置,而后蛇的身体随蛇头移动,图形的实现是从蛇头新位置开始画出蛇,这时,由于未清屏的原因,原来的蛇的位置和新蛇的位置差一个单位,所以看起来蛇多一节身体,所以将蛇的最后一节用背景色覆盖。食物的出现与消失也是画矩形块和覆盖矩形块。蛇撞在墙上则游戏结束。

1.2 需求环境

本课程设计需要的设备为硬件要求和软件配置要求具体要求如下:

①硬件要求:一台计算机。

②软件配置:WINDOWS运行环境

1.3 功能描述

本课程设计是利用图形学的相关知识在Windows环境下利用窗口化界面实现经典贪吃蛇游戏,其基本的功能描述如下:

此游戏程序应实现使一条蛇在密闭的围墙内游动,在围墙内随机出现一个食物,通过按键盘上的四个光标键控制蛇向上、下、左、右四个方向移动,蛇头撞到食物时,则表示食物被蛇吃掉,蛇的身体同时增长一节,并同时计分加10分,接着再出现食物,重复刚才的过程。如果蛇在移动过程中,撞到墙壁或身体交叉即蛇头撞到自己的身体,则游戏结束。

第2章 系统总体设计

贪吃蛇是一个家喻户晓的益智类小游戏。本设计是采用汇编中的宏定义和调用,子程序的调用,BIOS,DOS中断等一些汇编知识编制而成的一个贪吃蛇小游戏,通过这个小游戏的制作,我们可以得到很多的益处:巩固微机原理与汇编语言的基础知识:进一步掌握计算机软,硬件设计方法;增强了软件开发过程中实际操作的能力;能够掌握如何使用汇编语言进行键盘控制,显示控制等功能。

2.1 程序功能模块

由需求分析知,本次课程设计是用方向键实现贪吃蛇游戏程序,故其功能模块分为:开始画面(描绘围墙、蛇体、食物等及其颜色),游戏过程(通过四个方向键控制蛇头带动身体移动),游戏结束(显示总分并判断是否重新游戏)模块三个模块。

开始游戏

开始画面

游戏过程

游戏结束

是否重新游戏

关闭程序

图2.1 功能模块图

2.2 程序流程图

开始

定义结构体数组food和snake存食物和蛇(life存活为0,死亡为1),设置围墙格式

游戏开始画面

蛇初始向右移动,方向键改变移动方向

Life==0

继续游戏

游戏结束

蛇长度和分增加,继续控制

蛇移动 重新开始

游戏结束,显示最终得分

关闭程序

2.2 程序流程图

第3章 详细设计

3.1详细设计

主要功能模块的算法设计思路:

3.1.1 数据段

szCaptionMain db '贪食蛇',0

szCaptionPause db '已暂停',0

szCaptionDie db '游戏结束,请按N键开始新游戏',0

szDown db '下',0

szUp db '上',0

szRight db '右',0

szLeft db '左',0

szScore db '得分:%d',0

3.1.2代码段

包括随机数的生成,初始化所有蛇身节点把前三个置为有效,商是y坐标,玉树是X坐标,数组的首元素即为蛇尾,最后一个不为零的元素为蛇头,查看指定结点是否在(x, y) ,在绘制单个节点中先取标志状态,如果是无效,直接退出下一个食物,总是随机出现。更好的方法应该是把所有结点串成两个链表,蛇身链表和非, 蛇身链表。每次随机地从非蛇身链表里取出结点插入蛇身链表中,这样就不会造成随机生成的位置在蛇身上从而重新生成了。

.code

; 随机数生成

_NextInt proc uses edx ebx max

push edx

invoke GetTickCount

add eax, seed

add seed, eax

xor edx, edx

mov ebx, max

div ebx

xor seed, eax

mov eax, edx

inc eax

pop edx

ret

_NextInt endp

; 初始化所有蛇身结点把前三个置为有效

_InitNodes proc

push edi

push edx

push ebx

push ecx

xor edi, edi

@@: xor edx, edx

mov eax, edi

mov ebx, 17h

div ebx; 商是 y 坐标,余数是 x 坐标

xor ecx, ecx

or ecx, edx

inc ecx

shl ecx,8

or ecx, eax

inc ecx

shl ecx,8

or ecx, BODY

shl ecx,8 ; ecx 为结点, ebx 为有效结点列表

; nodeList 数组首元素即蛇尾,最后一个不为0的元素为蛇头

xor ebx, ebx

.if edi <4

or ecx,1

mov ebx, ecx

.endif

mov [offset nodes + edi *4], ecx

mov [offset nodeList + edi *4], ebx

inc edi .if edi >= 187h

jmp @F

.endif

jmp @B

@@: pop ecx

pop ebx

pop edx

pop edi

ret

_InitNodes endp

3.1.3 初始化新游戏的各种状态

_NewGame proc

invoke _InitNodes

invoke _NextFood

mov bTurnPending, FALSE

mov isPaused,0

mov isAlive,1

mov score,0

mov speed,8

mov timerCounts,0

mov eatCounts,0

mov curX,4

mov curY,1

mov direction, RIGHT

ret

_NewGame endp

3.1.4 蛇身移动

_Move proc

Local @nextX: DWORD

Local @nextY: DWORD

push ebx

push curX

push curY

pop @nextY

pop @nextX

mov bTurnPending, FALSE

.if direction == RIGHT

inc @nextX

.elseif direction == DOWN

inc @nextY

.elseif direction == LEFT

dec @nextX

.elseif direction == UP

dec @nextY

.endif

.if (@nextX ==0) || (@nextX >23) || (@nextY ==0) || (@nextY >17)

mov isAlive,0

jmp @F

.endif

invoke _QueryNodeStat, @nextX, @nextY

.if eax

mov isAlive,0

jmp @F

.endif

mov eax, foodX

mov ebx, foodY

.if (@nextX == eax) && (@nextY == ebx)

invoke _PieFromSky

.endif; 头的处理,下一个位置即将激活

invoke _Activate, @nextX, @nextY,1

push @nextX

push @nextY

pop curY

pop curX; 把新头加入队列头,并指示移除队列尾

invoke _GetNodeAt, curX, curY

invoke _AddBody, eax, TRUE

@@: pop ebx

Ret

_Move endp

3.1.5 窗口过程

_ProcWinMain proc hWnd, uMsg, wParam, lParam

Local @stPs:PAINTSTRUCT

Local @stRect:RECT

Local @hBmp: HBITMAP

Local @hOldBmp: HBITMAP

Local @memDC: HDC

Local @hDC: HDC

mov eax, uMsg

.if eax == WM_PAINT

invoke BeginPaint, hWnd, addr @stPs

mov @hDC, eax

invoke CreateCompatibleBitmap, @hDC, wndWidth, wndHeight

mov @hBmp, eax

invoke CreateCompatibleDC, @hDC

mov @memDC, eax

invoke SelectObject, @memDC, @hBmp

mov @hOldBmp, eax

invoke _DrawBody, @memDC

invoke _DrawNode, @memDC, food

invoke _DrawScore, @memDC

invoke BitBlt, @hDC, , , wndWidth, wndHeight,

@memDC, , , SRCCOPY

invoke SelectObject, @memDC, @hOldBmp

invoke DeleteObject, @hBmp

invoke DeleteDC, @memDC

invoke EndPaint, hWnd, addr @stPs

.elseif eax == WM_TIMER

.if !isAlive

invoke SetWindowText, hWnd, offset szCaptionDie

jmp @F

.endif

.if isPaused

jmp @F

.endif

mov eax, timerCounts

.if speed != eax

inc timerCounts

jmp @F

.endif

mov timerCounts,0

invoke _Move

invoke InvalidateRect, hWnd, addr rClient, FALSE

.elseif eax == WM_CLOSE

invoke KillTimer, hWnd, TIMER

invoke DestroyWindow, hWinMain

invoke PostQuitMessage, NULL

.elseif eax == WM_CREATE

invoke SetTimer, hWnd, TIMER,100, NULL

invoke GetClientRect, hWnd, offset rClient

mov bRanColor, TRUE

mov eax,

sub eax,

mov wndWidth, eax

mov eax,

sub eax,

mov wndHeight, eax

invoke _NewGame

.elseif eax == WM_KEYDOWN

mov eax, wParam; 按P键暂停游戏

.if eax =='P'

.if isPaused

and isPaused,0

invoke SetWindowText, hWnd, offset szCaptionMain

.else

or isPaused,1

invoke SetWindowText, hWnd, offset szCaptionPause

.endif; 按了上键

.elseif eax =='W' || eax == VK_UP

.if isPaused || bTurnPending

jmp @F

.endif

mov eax, direction

.if eax == RIGHT || eax == LEFT

push UP

pop direction

invoke SetWindowText, hWnd, offset szUp

mov bTurnPending, TRUE

.endif

; 按了下键

.elseif eax =='S' || eax == VK_DOWN

.if isPaused || bTurnPending

jmp @F

.endif

mov eax, direction

.if eax == RIGHT || eax == LEFT

push DOWN

pop direction

invoke SetWindowText, hWnd, offset szDown

mov bTurnPending, TRUE

.endif; 按了左键

.elseif eax =='A' || eax == VK_LEFT

.if isPaused || bTurnPending

jmp @F

.endif

mov eax, direction

.if eax == UP || eax == DOWN

push LEFT

pop direction

invoke SetWindowText, hWnd, offset szLeft

mov bTurnPending, TRUE

.endif; 按了右键

.elseif eax =='D' || eax == VK_RIGHT

.if isPaused || bTurnPending

jmp @F

.endif

mov eax, direction

.if eax == UP || eax == DOWN

push RIGHT

pop direction

invoke SetWindowText, hWnd, offset szRight

mov bTurnPending, TRUE

.endif

.elseif eax =='C'

.if bRanColor

and bRanColor,0

.else

or bRanColor,1

.endif; Q或者ESC退出游戏

.elseif eax =='Q' || eax == VK_ESCAPE

invoke PostQuitMessage,0 ; 开始新游戏

.elseif eax =='N'

invoke _NewGame

invoke SetWindowText, hWnd, offset szCaptionMain

.endif

.else

invoke DefWindowProc, hWnd, uMsg, wParam, lParam

ret

.endif

@@: xor eax, eax

Ret

_ProcWinMain endp

3.1.6 入口函数

_Main proc

Local @stWndClass:WNDCLASSEX

Local @stMsg:MSG

mov food, FALSE

invoke RtlZeroMemory, addr @stWndClass, sizeof @stWndClass

invoke GetModuleHandle, NULL

************************,eax invoke LoadCursor, NULL, IDC_ARROW

**********************,eax invoke LoadIcon, NULL, IDI_INFORMATION

********************,eax*********************,sizeofWNDCLASSEX********************,CS_HREDRAWorCS_VREDRAW**************************,offset_ProcWinMain****************************,COLOR_WINDOW****************************,offsetszClassNameinvoke RegisterClassEx, addr @stWndClass; 显示窗口

invoke CreateWindowEx, WS_EX_CLIENTEDGE, offset szClassName,

offset szCaptionMain,

WS_OVERLAPPEDWINDOW and not WS_MAXIMIZEBOX and not WS_THICKFRAME,

200,100,500,404, NULL, NULL, hInstance, NULL

mov hWinMain, eax

invoke ShowWindow, hWinMain, SW_SHOWNORMAL

invoke UpdateWindow, hWinMain

; 消息循环

.while TRUE

invoke GetMessage, addr @stMsg, NULL,0,0

.break .if eax ==0

invoke TranslateMessage, addr @stMsg

invoke DispatchMessage, addr @stMsg

.endw

ret

_Main endp

start:

call _Main

invoke ExitProcess, NULL

end start

第4章 系统测试

4.1可行性分析

首先,8086指令系统中包括了较为齐全的Dos系统功能调用指令,利用这些指令我们可以实现Dos键盘功能调用和Dos显示功能调用,这就为该游戏的显示和操作提供了条件,使人机对话操作成为了可能;其次,该游戏本身的程序设计并非异常的复杂,无需使用高级语言程序也能够实现,这给汇编语言编写提供了可能,也使得程序量不至于过大而难以实现;此外,经过一个学期8086微机系统的学习我们基本掌握了8086的编程操作,具备了一定的编写程序的能力,并能够自行查找资料独立解决Dos系统功能调用问题(未授);最后也是最重要的一点:我们拥有“贪吃蛇”游戏的示例程序,这让编程能力有限的我能够有章可循不至于无从下手,通过对该示例的了解、分析优化、改进逐步实现设计的预设功能得到较为满意的设计结果。

4.2测试结果分析

贪吃蛇的各个功能都已经实现,可以很好的进行游戏。在游戏过程中发现的bug都已在修改中修复。但是还存在问题,就是如果蛇头的前方是墙壁或者蛇身的话,游戏一定失败。无论反应多快。这个是还待修改但为修改的地方。

第5章 软件使用说明书

双击开始游戏,在游戏中使用键盘方向键控制蛇头方向前进,游戏中蛇头为白色方块,食物为绿色方块,当蛇头触到墙或自身时游戏结束,此时按键盘N,再次开始游戏,游戏中按P,可暂停游戏,再次按P继续游戏。按C可变换蛇身颜色,当需要游戏结束时可关闭窗口、按ESC或按Q键均可关闭。

5.1游戏运行截图

1、游戏开始

2、游戏暂停

3、游戏控制—上

4、游戏控制—下

5、游戏控制—左

6、游戏控制—右

7、游戏中变换蛇身颜色

设计总结

紧张而有趣的课程设计结束了,虽然只有短短的一周时间,可我却获益匪浅,而从中体会到的乐趣更是难以用言语表达。

这次我们组做的题目是小游戏贪吃蛇,我负责的那部分是开始新游戏时的初始化,蛇身移动,窗口过程和入口函数。初看题目,真有点摸不找头脑的感觉,可经过我们小组成员的共同讨论,各抒己见,还有指导老师的讲解分析,我们每一个人都大体清楚了个人的模块和总体的框架,找到了入手点。

通过这次课程设计我对汇编语言有了进一步的了解。知道编制图形程序是程序设计中非常有趣和有价值的工作之一,在图形绘制方面,汇编语言具有潜在的优点,因为显示屏幕上的每一个图象都是由成千上万的元素组成,处理这些象素需要大量的指令,就速度而言,汇编语言比高级语言要快的多。程序采用的是调色板来配置颜色的,虽然颜色的选择很方便,但是美中不足的是它供选择的颜色过于单调。

在此次课程设计中我认识到,写一段代码,首先让它可以运行,然后让它可以正确的运行,最后再去提高效率。写好一段代码比调试一段代码的时间要少的多,而许多许多的问题也是在你写代码的时候所不能发现的。

想想看一路过来学过的课程都是如此:算法数据结构教会我们在什么情况下应该选取怎样的方式去处理数据,操作系统教会我们系统如何处理数据,编译原理教会我们编译器如何处理数据。语言也是一种处理数据的工具,没有哪种工具是万能的,只有合适的场合采用合适的工具。同时,以后再学习一种新的“规则”时,也需要抓住这些重点:这个规则适用的场合,适用的数据,处理数据的方式。

最后,感谢我组每一位同学在此次课程设计中作出的努力,和老师的帮助和指导。

参考文献

[1] 王爽编著.《汇编语言—第二版》.清华大学出版社,2008.4

[2] 沈美明,温冬婵.IBM-PC汇编语言程序设计(第二版).清华大学出版社,

2001。

[3] [美]Kip 著,温玉洁,等译.Intel汇编语言程序设计(第五版).电子工业出版社, 2007。

[4]钱晓捷.汇编语言程序设计(第二版). 电子工业出版社, 2003。

[5] 罗云彬.Windows环境下32位汇编语言程序设计(第二版).电子工业出版社, 2006。

[6] Windows API函数参考手册.人民邮电出版社, 2002。

[7]参考网址:

[8]参考网址:

致 谢

通过这段时间的学习,我学会了很多东西。同时也非常感谢刘老师这段时间的教育与指导。使我从对程序一无所知到勉强能够看懂程序,同时也使我了解到了程序的博大精深,体会到编制程序的无穷乐趣。这段时间的学习我看到了老师无私的奉献,也看到了老师不辞辛苦的来回奔波,让我们非常的感动,同时也更加的激发起了我们学习的兴趣。

附 录

;****************************************************************

.386

.model flat, stdcall

option casemap:none

;****************************************************************

;Include

;****************************************************************

include

include

includelib

include

includelib

include

includelib

includelib

;****************************************************************

; 这个结构并未使用,只是大致描述下一个结点的样子,一个节点是一个DWORD *

; 从高位到低位分别是: x坐标 y坐标 类型(食物或蛇身) 有效标志 *

; 每个字段点一个字节 *

;****************************************************************

Node struct

x BYTE ?

y BYTE ?

ntype BYTE ?

bValid BYTE ?

Node ends

TIMER equ 1

FOOD equ 0

BODY equ 1

RIGHT equ 2

LEFT equ 3

UP equ 4

DOWN equ 5

;****************************************************************

; 数据段

;****************************************************************

.data?

hInstance dd ?

hWinMain dd ?

hDc dd ?

isPaused dd ?

isAlive dd ?

bRanColor dd ?

bTurnPending dd ?

direction dd ?

food dd ?

nodes dd 391 dup (?)

nodeList dd 391 dup (?)

curX dd ?

curY dd ?

foodX dd ?

foodY dd ?

score dd ?

rClient RECT <>

wndWidth dd ?

wndHeight dd ?

szScoreBuffer db 32 dup (?)

seed dd ?

speed dd ?

timerCounts dd ?

eatCounts dd ?

.const

szClassName db '1',0

szCaptionMain db '贪食蛇',0

szCaptionPause db '已暂停',0

szCaptionDie db '游戏结束,请按N键开始新游戏',0

szDown db '下',0

szUp db '上',0

szRight db '右',0

szLeft db '左',0

szScore db '得分:%d',0

;****************************************************************

; 代码段

;****************************************************************

.code

;****************************************************************

; 随机数生成

;****************************************************************

_NextInt proc uses edx ebx max

push edx

invoke GetTickCount

add eax, seed

add seed, eax

xor edx, edx

mov ebx, max

div ebx

xor seed, eax

mov eax, edx

inc eax

pop edx

ret

_NextInt endp

;****************************************************************

; 初始化所有蛇身结点把前三个置为有效

;****************************************************************

_InitNodes proc

push edi

push edx

push ebx

push ecx

xor edi, edi

@@: xor edx, edx

mov eax, edi

mov ebx, 17h

div ebx; 商是 y 坐标,余数是 x 坐标

xor ecx, ecx

or ecx, edx

inc ecx

shl ecx,8

or ecx, eax

inc ecx

shl ecx,8

or ecx, BODY

shl ecx,8 ; ecx 为结点, ebx 为有效结点列表

; nodeList 数组首元素即蛇尾,最后一个不为0的元素为蛇头

xor ebx, ebx

.if edi <4

or ecx,1

mov ebx, ecx

.endif

mov [offset nodes + edi *4], ecx

mov [offset nodeList + edi *4], ebx

inc edi

.if edi >= 187h

jmp @F

.endif

jmp @B

@@: pop ecx

pop ebx

pop edx

pop edi

ret

_InitNodes endp

;****************************************************************

; 节点构造函数,参数分别是X坐标,Y坐标,类型(食物还是身体),有效标志

;****************************************************************

_CreateNode proc nx, ny, nntype, nInvalid

xor eax, eax

or eax, nx

shl eax,8

or eax, ny

shl eax,8

or eax, nntype

shl eax,8

or eax, nInvalid

ret

_CreateNode endp

;****************************************************************

; 查看指定结点是否在(x, y)

;****************************************************************

_IsNodeAt proc node, x, y

push ebx

push ecx

mov eax, node

mov ebx, eax

mov ecx, eax

shr ebx,24

and ebx, 0ffh

sub ebx, x

shr ecx,16

and ecx, 0ffh

sub ecx, y

.if ecx ==0 && ebx ==0

xor eax, eax

.endif

pop ecx

pop ebx

ret

_IsNodeAt endp

;****************************************************************

; 绘制单个节点

;****************************************************************

_DrawNode proc uses ebx ecx _hDC, hNode

local @stColor: COLORREF

local @hBrush: HBRUSH

local @hOldBrush: HBRUSH

local @stRect: RECT

push ecx

push ebx

mov eax, hNode; 先取标志状态,如果是无效,直接退出

and eax, 000000ffh

.if !eax

jmp @F

.endif

invoke _IsNodeAt, hNode, curX, curY

mov ecx, eax

mov eax, hNode

and eax, 0000ff00h

shr eax,8

.if (eax == BODY) && (ecx ==0)

mov @stColor, 00ffffffh

.elseif eax == BODY

.if bRanColor

invoke _NextInt,255

mov ecx, eax

shl ecx,8

invoke _NextInt,255

or ecx, eax

shl ecx,8

invoke _NextInt,255

or ecx, eax

mov @stColor, ecx

.else

mov @stColor, 000000ffh

.endif

.else

mov @stColor, 0000ff00h

.endif

invoke CreateSolidBrush, @stColor

mov @hBrush, eax

invoke SelectObject, _hDC, @hBrush

mov @hOldBrush, eax; 算出左右要画的地方

mov ecx, hNode

and ecx, 0ff000000h

shr ecx,24

xor eax, eax

mov al, cl

mov bl,20

mul bl

sub eax,4

***************,eaxadd eax,18

****************,eax;算出上下要画的地方

mov ecx, hNode

and ecx, 00ff0000h

shr ecx,16

xor eax, eax

mov al, cl

mov bl,20

mul bl

sub eax,4

**************,eaxadd eax,18

*****************,eax invoke FillRect, _hDC, addr @stRect, NULL

invoke SelectObject, _hDC, @hOldBrush; 狂他妈漏啊……

invoke DeleteObject, @hBrush

@@:

pop ebx

pop ecx

ret

_DrawNode endp

;****************************************************************; 画蛇身,其实就是遍历所有结点画,绘画函数会自己根据结点的状态来选择绘

; 制方式。

;****************************************************************

_DrawBody proc uses ecx edi _hDC

push edi

push ecx

push eax

xor edi, edi

mov ecx, 187h

@@: mov eax, [offset nodeList + edi *4]

.if !eax

jmp @F

.endif

invoke _DrawNode, _hDC, eax

inc edi

loop @B

@@:

pop eax

pop ecx

pop edi

ret

_DrawBody endp

;****************************************************************

; 绘制分数

;****************************************************************

_DrawScore proc _hDC

Local @oldColor: COLORREF

Local @oldBkMode: DWORD

local @szBuffer[128]: BYTE

local @hFont: HFONT

local @hOldFont: HFONT

local @hPen: HPEN

local @hOldPen: HPEN

local @strLen: DWORD

push ebx

invoke wsprintf, offset szScoreBuffer, offset szScore, score; 求字符串长度

xor eax, eax

@@:

mov bl, BYTE ptr [offset szScoreBuffer + eax]

.if bl !=0

inc eax

jmp @B

.endif

mov @strLen, eax

invoke SetBkMode, _hDC, TRANSPARENT

mov @oldBkMode, eax

invoke SetTextColor, _hDC, 0000ffffh

mov @oldColor, eax

invoke CreateFont,14,7,0,0,8, FALSE, FALSE, FALSE, GB2312_CHARSET,

OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS,5, DEFAULT_PITCH, NULL

mov @hFont, eax

invoke SelectObject, _hDC, eax

mov @hOldFont, eax

invoke CreatePen, PS_SOLID,1, 00ffffffh

mov @hPen, eax

invoke SelectObject, _hDC, eax

mov @hOldPen, eax

invoke TextOut, _hDC,410,15, offset szScoreBuffer, @strLen

invoke MoveToEx, _hDC,10,10,0

invoke LineTo, _hDC,480,10

invoke LineTo, _hDC,480,360

invoke LineTo, _hDC,10,360

invoke LineTo, _hDC,10,10

invoke SetBkMode, _hDC, @oldBkMode

invoke SetTextColor, _hDC, @oldColor

invoke SelectObject, _hDC, @hOldFont

invoke SelectObject, _hDC, @hOldPen

invoke DeleteObject, @hFont

invoke DeleteObject, @hPen

pop ebx

ret

_DrawScore endp

;****************************************************************

; 获得指定位置上的结点

;****************************************************************

_GetNodeAt proc xLocation, yLocation

push ebx

mov eax, yLocation

dec eax

mov ebx, 17h

mul ebx

add eax, xLocation

dec eax

mov eax, [offset nodes + eax *4]

pop ebx

ret

_GetNodeAt endp

;****************************************************************

; 由传入的X Y 位置查询目标结点的状态(是否激活)

;****************************************************************

_QueryNodeStat proc xLocation, yLocation

invoke _GetNodeAt, xLocation, yLocation

and eax, 000000ffh

ret

_QueryNodeStat endp

;****************************************************************

; 激活或压制一个已处于激活状态的结点; 参数分别是 x 坐标, y坐标, 是否激活的布尔变量

;****************************************************************

_Activate proc xLocation, yLocation, bActivate

push ebx

push edi

mov eax, yLocation

dec eax

mov ebx, 17h

mul ebx

add eax, xLocation

dec eax

mov edi, eax

mov eax, [offset nodes + eax *4]

.if bActivate

or eax, 0000001h

.else

and eax, 0ffffff00h

.endif

mov [offset nodes + edi *4], eax

pop edi

pop ebx

ret

_Activate endp

;****************************************************************

; 下一个食物,总是随机出现。并且不会出现在蛇身上

; 这种实现似乎比较2,更好的方法应该是把所有结点串成两个链表,蛇身链表和非

; 蛇身链表。每次随机地从非蛇身链表里取出结点插入蛇身链表中,这样就不会造成

; 随机生成的位置在蛇身上从而重新生成了。

; 重新生巢呗陨在蛇身将要填充满整个区域时性能急剧下降。

;****************************************************************

_NextFood proc uses ebx

invoke _NextInt,17

mov foodY, eax

invoke _NextInt,23

mov foodX, eax

invoke _QueryNodeStat, foodX, foodY

.if eax

invoke _NextFood

.endif

invoke _CreateNode, foodX, foodY, FOOD,1

mov food, eax

ret

_NextFood endp

;****************************************************************

; 往队列头加一个结点,刚吃了一顿或走了一步后都会调用此函数。

; 第一个参数是要加入的结点,第二个是指示是否移除队列尾。正常移动时需要

; 在队列头加结点的同时移除队列尾,以达到好像在移动的假象……

;****************************************************************

_AddBody proc node, bRmvTail

push edi

push ebx

push ecx; 如果是刚吃的,先把类型改成 BODY

mov eax, node

or eax, 00000100h

xor edi, edi

@@:

mov ebx, [offset nodeList + edi *4]

.if ebx !=0

inc edi

jmp @B

.endif

mov [offset nodeList + edi *4], eax

; 如果要移除队列尾,则先把队列尾的激活状态压下去,然后集体左移一个索引位置

; 此时edi里保存着队列应有的长度

.if bRmvTail; 取第一个元素,这是队列头,先反激活它

mov eax, [nodeList]

mov ebx, eax

and ebx, 0ff000000h

shr ebx,24

mov ecx, eax

and ecx, 00ff0000h

shr ecx,16

invoke _Activate, ebx, ecx, FALSE; 然后移动这个队列,把后面的都左移

mov ecx, edi

xor edi, edi

@@: mov eax, [offset nodeList + edi *4 +4]

mov [offset nodeList + edi *4], eax

inc edi

loop @B

mov [offset nodeList + edi *4],0

.endif

pop ecx

pop ebx

pop edi

ret

_AddBody endp

;****************************************************************

; 吃掉食物,吃掉时候分数增加,并且生成下一个食物

;****************************************************************

_PieFromSky proc

mov eax, score

add eax,10

mov score, eax

mov eax, eatCounts

.if eax !=3

inc eatCounts

.else

mov eax, speed

.if eax >1

dec speed

.endif

mov eatCounts,0

.endif

invoke _GetNodeAt, foodX, foodY

invoke _AddBody, eax, FALSE

invoke _NextFood

ret

_PieFromSky endp

;****************************************************************

; 新游戏,初始化各种状态

;****************************************************************

_NewGame proc

invoke _InitNodes

invoke _NextFood

mov bTurnPending, FALSE

mov isPaused,0

mov isAlive,1

mov score,0

mov speed,8

mov timerCounts,0

mov eatCounts,0

mov curX,4

mov curY,1

mov direction, RIGHT

ret

_NewGame endp

;****************************************************************

; 蛇身移动

;****************************************************************

_Move proc

Local @nextX: DWORD

Local @nextY: DWORD

push ebx

push curX

push curY

pop @nextY

pop @nextX

mov bTurnPending, FALSE

.if direction == RIGHT

inc @nextX

.elseif direction == DOWN

inc @nextY

.elseif direction == LEFT

dec @nextX

.elseif direction == UP

dec @nextY

.endif

.if (@nextX ==0) || (@nextX >23) || (@nextY ==0) || (@nextY >17)

mov isAlive,0

jmp @F

.endif

invoke _QueryNodeStat, @nextX, @nextY

.if eax

mov isAlive,0

jmp @F

.endif

mov eax, foodX

mov ebx, foodY

.if (@nextX == eax) && (@nextY == ebx)

invoke _PieFromSky

.endif; 头的处理,下一个位置即将激活

invoke _Activate, @nextX, @nextY,1

push @nextX

push @nextY

pop curY

pop curX; 把新头加入队列头,并指示移除队列尾

invoke _GetNodeAt, curX, curY


本文标签: 游戏 结点 蛇头 程序 移动