www.gusucode.com > 用matlab开发的飞鸟游戏源码程序 > 用matlab开发的飞鸟游戏源码程序/flappybird.m
function flappybird %% System Variables: GameVer = '1.0'; % The first full playable game %% Constant Definitions: GAME.MAX_FRAME_SKIP = []; GAME.RESOLUTION = []; % Game Resolution, default at [256 144] GAME.WINDOW_SCALE = 2; % The actual size of the window divided by resolution GAME.FLOOR_TOP_Y = []; % The y position of upper crust of the floor. GAME.N_UPDATES_PER_SEC = []; GAME.FRAME_DURATION = []; GAME.GRAVITY = 0.1356; %0.15; %0.2; %1356; TUBE.MIN_HEIGHT = []; % The minimum height of a tube TUBE.RANGE_HEIGHT = []; % The range of the height of a tube TUBE.SUM_HEIGHT = []; % The summed height of the upper and low tube TUBE.H_SPACE = []; % Horizontal spacing between two tubs TUBE.V_SPACE = []; % Vertical spacing between two tubs TUBE.WIDTH = []; % The 'actual' width of the detection box GAMEPLAY.RIGHT_X_FIRST_TUBE = []; % Xcoord of the right edge of the 1st tube ShowFPS = true; SHOWFPS_FRAMES = 5; %% Handles MainFigureHdl = []; MainAxesHdl = []; MainCanvasHdl = []; BirdSpriteHdl = []; TubeSpriteHdl = []; BeginInfoHdl = []; FloorSpriteHdl = []; ScoreInfoHdl = []; GameOverHdl = []; FloorAxesHdl = []; %% Game Parameters MainFigureInitPos = []; MainFigureSize = []; MainAxesInitPos = []; % The initial position of the axes IN the figure MainAxesSize = []; InGameParams.CurrentBkg = 1; InGameParams.CurrentBird = 1; Flags.IsGameStarted = true; % Flags.IsFirstTubeAdded = false; % Has the first tube been added to TubeLayer Flags.ResetFloorTexture = true; % Result the pointer for the floor texture Flags.PreGame = true; Flags.NextTubeReady = true; CloseReq = false; FlyKeyNames = {'space', 'return', 'uparrow', 'w'}; FlyKeyStatus = false; %(size(FlyKeyNames)); FlyKeyValid = true(size(FlyKeyNames)); % %% Canvases: MainCanvas = []; % The scroll layer for the tubes TubeLayer.Alpha = []; TubeLayer.CData = []; %% RESOURCES: Sprites = []; %% Positions: Bird.COLLIDE_MASK = []; Bird.INIT_SCREEN_POS = [45 100]; % In [x y] order; Bird.WorldX = []; Bird.ScreenPos = [45 100]; %[45 100]; % Center = The 9th element horizontally (1based) % And the 6th element vertically Bird.SpeedXY = [ 0]; Bird.Angle = 0; Bird.XGRID = []; Bird.YGRID = []; Bird.CurFrame = 1; Bird.SpeedY = 0; Bird.LastHeight = 0; SinYRange = 44; SinYPos = []; SinY = []; Score = 0; Tubes.FrontP = 1; % 1-3 Tubes.ScreenX = [300 380 460]-2; % The middle of each tube Tubes.VOffset = ceil(rand(1,3)*105); Best = 0; %% -- Game Logic -- initVariables(); initWindow(); if ShowFPS fps_text_handle = text(10,10, 'FPS:60.0', 'Visible', 'off'); var_text_handle = text(10,20, '', 'Visible', 'off'); % Display a variable total_frame_update = 0; end % Show flash screen CurrentFrameNo = double(0); fade_time = cumsum([1 3 1]); pause(0.5); logo_stl = text(72, 100, 'Stellari Studio', 'FontSize', 20, 'Color',[1 1 1], 'HorizontalAlignment', 'center'); logo_and = text(72, 130, 'and', 'FontSize', 10, 'Color',[1 1 1], 'HorizontalAlignment', 'center'); logo_ilovematlabcn = image([22 122], [150 180], Sprites.MatlabLogo, 'AlphaData',0); stageStartTime = tic; while 1 loops = 0; curTime = toc(stageStartTime); while (curTime >= ((CurrentFrameNo) * GAME.FRAME_DURATION) && loops < GAME.MAX_FRAME_SKIP) if curTime < fade_time(1) set(logo_stl, 'Color',1 - [1 1 1].*max(min(curTime/fade_time(1), 1),0)); set(logo_ilovematlabcn, 'AlphaData', max(min(curTime/fade_time(1), 1),0)); set(logo_and, 'Color',1 - [1 1 1].*max(min(curTime/fade_time(1), 1),0)); elseif curTime < fade_time(2) set(logo_stl, 'Color',[0 0 0]); set(logo_ilovematlabcn, 'AlphaData', 1); set(logo_and, 'Color', [0 0 0]); else set(logo_stl, 'Color',[1 1 1].*max(min((curTime-fade_time(2))/(fade_time(3) - fade_time(2)), 1),0)); set(logo_ilovematlabcn, 'AlphaData',1-max(min((curTime-fade_time(2))/(fade_time(3) - fade_time(2)), 1),0)); set(logo_and, 'Color', [1 1 1].*max(min((curTime-fade_time(2))/(fade_time(3) - fade_time(2)), 1),0)); end CurrentFrameNo = CurrentFrameNo + 1; loops = loops + 1; frame_updated = true; end if frame_updated drawnow; end if curTime > fade_time break; end end delete(logo_stl); delete(logo_ilovematlabcn); delete(logo_and); pause(1); % Main Game while 1 initGame(); CurrentFrameNo = double(0); collide = false; fall_to_bottom = false; gameover = false; stageStartTime = tic; c = stageStartTime; FPS_lastTime = toc(stageStartTime); while 1 loops = 0; curTime = toc(stageStartTime); while (curTime >= ((CurrentFrameNo) * GAME.FRAME_DURATION) && loops < GAME.MAX_FRAME_SKIP) if FlyKeyStatus % If left key is pressed if ~gameover Bird.SpeedY = -2.5; % -2.5; FlyKeyStatus = false; Bird.LastHeight = Bird.ScreenPos(2); if Flags.PreGame Flags.PreGame = false; set(BeginInfoHdl, 'Visible','off'); set(ScoreInfoBackHdl, 'Visible','on'); set(ScoreInfoForeHdl, 'Visible','on'); Bird.ScrollX = 0; end else if Bird.SpeedY < 0 Bird.SpeedY = 0; end end end if Flags.PreGame processCPUBird; else processBird; Bird.ScrollX = Bird.ScrollX + 1; if ~gameover scrollTubes(1); end end addScore; Bird.CurFrame = 3 - floor(double(mod(CurrentFrameNo, 9))/3); %% Cycling the Palette % Update the cycle variables collide = isCollide(); if collide gameover = true; end CurrentFrameNo = CurrentFrameNo + 1; loops = loops + 1; frame_updated = true; % If the bird has fallen to the ground if Bird.ScreenPos(2) >= 200-5; Bird.ScreenPos(2) = 200-5; gameover = true; if abs(Bird.Angle - pi/2) < 1e-3 fall_to_bottom = true; FlyKeyStatus = false; end end end %% Redraw the frame if the world has been processed if frame_updated % drawToMainCanvas(); set(MainCanvasHdl, 'CData', MainCanvas(1:200,:,:)); % Bird.Angle = double(mod(CurrentFrameNo,360))*pi/180; if fall_to_bottom Bird.CurFrame = 2; end refreshBird(); refreshTubes(); if (~gameover) refreshFloor(CurrentFrameNo); end curScoreString = sprintf('%d',(Score)); set(ScoreInfoForeHdl, 'String', curScoreString); set(ScoreInfoBackHdl, 'String', curScoreString); drawnow; frame_updated = false; c = toc(stageStartTime); if ShowFPS total_frame_update = total_frame_update + 1; varname = 'collide';%'Mario.curFrame'; if mod(total_frame_update,SHOWFPS_FRAMES) == 0 % If time to update fps set(fps_text_handle, 'String',sprintf('FPS: %.2f',SHOWFPS_FRAMES./(c-FPS_lastTime))); FPS_lastTime = toc(stageStartTime); end set(var_text_handle, 'String', sprintf('%s = %.2f', varname, eval(varname))); end end if fall_to_bottom if Score > Best Best = Score; for i_save = 1:4 % Try saving four times if error occurs try save sprites2.mat Best -append break; catch continue; end end % If the error still persist even after four saves, then if i_save == 4 disp('FLAPPY_BIRD: Can''t save high score'); end end score_report = {sprintf('Score: %d', Score), sprintf('Best: %d', Best)}; set(ScoreInfoHdl, 'Visible','on', 'String', score_report); set(GameOverHdl, 'Visible','on'); save sprites2.mat Best -append if FlyKeyStatus FlyKeyStatus = false; break; end end if CloseReq delete(MainFigureHdl); clear all; return; end end end function initVariables() Sprites = load('sprites2.mat'); GAME.MAX_FRAME_SKIP = 5; GAME.RESOLUTION = [256 144]; GAME.WINDOW_RES = [256 144]; GAME.FLOOR_HEIGHT = 56; GAME.FLOOR_TOP_Y = GAME.RESOLUTION(1) - GAME.FLOOR_HEIGHT + 1; GAME.N_UPDATE_PERSEC = 60; GAME.FRAME_DURATION = 1/GAME.N_UPDATE_PERSEC; TUBE.H_SPACE = 80; % Horizontal spacing between two tubs TUBE.V_SPACE = 48; % Vertical spacing between two tubs TUBE.WIDTH = 24; % The 'actual' width of the detection box TUBE.MIN_HEIGHT = 36; TUBE.SUM_HEIGHT = GAME.RESOLUTION(1)-TUBE.V_SPACE-... GAME.FLOOR_HEIGHT; TUBE.RANGE_HEIGHT = TUBE.SUM_HEIGHT -TUBE.MIN_HEIGHT*2; TUBE.PASS_POINT = [1 44]; %TUBE.RANGE_HEIGHT_DOWN; % Sorry you just don't have a choice GAMEPLAY.RIGHT_X_FIRST_TUBE = 300; % Xcoord of the right edge of the 1st tube %% Handles MainFigureHdl = []; MainAxesHdl = []; %% Game Parameters MainFigureInitPos = [500 100]; MainFigureSize = GAME.WINDOW_RES([2 1]).*2; MainAxesInitPos = [0 0]; %[0.1 0.1]; % The initial position of the axes IN the figure MainAxesSize = [144 200]; % GAME.WINDOW_RES([2 1]); FloorAxesSize = [144 56]; %% Canvases: MainCanvas = uint8(zeros([GAME.RESOLUTION 3])); bird_size = Sprites.Bird.Size; [Bird.XGRID, Bird.YGRID] = meshgrid([-ceil(bird_size(2)/2):floor(bird_size(2)/2)], ... [ceil(bird_size(1)/2):-1:-floor(bird_size(1)/2)]); Bird.COLLIDE_MASK = false(12,12); [tempx tempy] = meshgrid(linspace(-1,1,12)); Bird.COLLIDE_MASK = (tempx.^2 + tempy.^2) <= 1; Bird.OSCIL_RANGE = [128 4]; % [YPos, Amplitude] SinY = Bird.OSCIL_RANGE(1) + sin(linspace(0, 2*pi, SinYRange))* Bird.OSCIL_RANGE(2); SinYPos = 1; Best = Sprites.Best; % Best Score end %% --- Graphics Section --- function initWindow() % initWindow - initialize the main window, axes and image objects MainFigureHdl = figure('Name', ['Flappy Bird ' GameVer], ... 'NumberTitle' ,'off', ... 'Units', 'pixels', ... 'Position', [MainFigureInitPos, MainFigureSize], ... 'MenuBar', 'figure', ... 'Renderer', 'OpenGL',... 'Color',[0 0 0], ... 'KeyPressFcn', @stl_KeyPressFcn, ... 'WindowKeyPressFcn', @stl_KeyDown,... 'WindowKeyReleaseFcn', @stl_KeyUp,... 'CloseRequestFcn', @stl_CloseReqFcn); FloorAxesHdl = axes('Parent', MainFigureHdl, ... 'Units', 'normalized',... 'Position', [MainAxesInitPos, (1-MainAxesInitPos.*2) .* [1 56/256]], ... 'color', [1 1 1], ... 'XLim', [0 MainAxesSize(1)]-0.5, ... 'YLim', [0 56]-0.5, ... 'YDir', 'reverse', ... 'NextPlot', 'add', ... 'Visible', 'on',... 'XTick',[], 'YTick', []); MainAxesHdl = axes('Parent', MainFigureHdl, ... 'Units', 'normalized',... 'Position', [MainAxesInitPos + [0 (1-MainAxesInitPos(2).*2)*56/256], (1-MainAxesInitPos.*2).*[1 200/256]], ... 'color', [1 1 1], ... 'XLim', [0 MainAxesSize(1)]-0.5, ... 'YLim', [0 MainAxesSize(2)]-0.5, ... 'YDir', 'reverse', ... 'NextPlot', 'add', ... 'Visible', 'on', ... 'XTick',[], ... 'YTick',[]); MainCanvasHdl = image([0 MainAxesSize(1)-1], [0 MainAxesSize(2)-1], [],... 'Parent', MainAxesHdl,... 'Visible', 'on'); TubeSpriteHdl = zeros(1,3); for i = 1:3 TubeSpriteHdl(i) = image([0 26-1], [0 304-1], [],... 'Parent', MainAxesHdl,... 'Visible', 'on'); end BirdSpriteHdl = surface(Bird.XGRID-100,Bird.YGRID-100, ... zeros(size(Bird.XGRID)), Sprites.Bird.CDataNan(:,:,:,1), ... 'CDataMapping', 'direct',... 'EdgeColor','none', ... 'Visible','on', ... 'Parent', MainAxesHdl); FloorSpriteHdl = image([0], [0],[],... 'Parent', FloorAxesHdl, ... 'Visible', 'on '); BeginInfoHdl = text(72, 100, 'Tap SPACE to begin', ... 'FontName', 'Helvetica', 'FontSize', 20, 'HorizontalAlignment', 'center','Color',[.25 .25 .25], 'Visible','off'); ScoreInfoBackHdl = text(72, 50, '0', ... 'FontName', 'Helvetica', 'FontSize', 30, 'HorizontalAlignment', 'center','Color',[0,0,0], 'Visible','off'); ScoreInfoForeHdl = text(70.5, 48.5, '0', ... 'FontName', 'Helvetica', 'FontSize', 30, 'HorizontalAlignment', 'center', 'Color',[1 1 1], 'Visible','off'); GameOverHdl = text(72, 70, 'GAME OVER', ... 'FontName', 'Arial', 'FontSize', 20, 'HorizontalAlignment', 'center','Color',[1 0 0], 'Visible','off'); ScoreInfoHdl = text(72, 110, 'Best', ... 'FontName', 'Helvetica', 'FontSize', 20, 'FontWeight', 'Bold', 'HorizontalAlignment', 'center','Color',[1 1 1], 'Visible', 'off'); end function initGame() % The scroll layer for the tubes TubeLayer.Alpha = false([GAME.RESOLUTION.*[1 2] 3]); TubeLayer.CData = uint8(zeros([GAME.RESOLUTION.*[1 2] 3])); Bird.Angle = 0; Score = 0; %TubeLayer.Alpha(GAME.FLOOR_TOP_Y:GAME.RESOLUTION(1), :, :) = true; Flags.ResetFloorTexture = true; SinYPos = 1; Flags.PreGame = true; % scrollTubeLayer(GAME.RESOLUTION(2)); % Do it twice to fill the % disp('mhaha'); % scrollTubeLayer(GAME.RESOLUTION(2)); % Entire tube layer drawToMainCanvas(); set(MainCanvasHdl, 'CData', MainCanvas); set(BeginInfoHdl, 'Visible','on'); set(ScoreInfoHdl, 'Visible','off'); set(ScoreInfoBackHdl, 'Visible','off'); set(ScoreInfoForeHdl, 'Visible','off'); set(GameOverHdl, 'Visible','off'); set(FloorSpriteHdl, 'CData',Sprites.Floor.CData); Tubes.FrontP = 1; % 1-3 Tubes.ScreenX = [300 380 460]-2; % The middle of each tube Tubes.VOffset = ceil(rand(1,3)*105); refreshTubes; for i = 1:3 set(TubeSpriteHdl(i),'CData',Sprites.TubGap.CData,... 'AlphaData',Sprites.TubGap.Alpha); redrawTube(i); end if ShowFPS set(fps_text_handle, 'Visible', 'on'); set(var_text_handle, 'Visible', 'on'); % Display a variable end end %% Game Logic function processBird() Bird.ScreenPos(2) = Bird.ScreenPos(2) + Bird.SpeedY; Bird.SpeedY = Bird.SpeedY + GAME.GRAVITY; if Bird.SpeedY < 0 Bird.Angle = max(Bird.Angle - pi/10, -pi/10); else if Bird.ScreenPos(2) < Bird.LastHeight Bird.Angle = -pi/10; %min(Bird.Angle + pi/100, pi/2); else Bird.Angle = min(Bird.Angle + pi/30, pi/2); end end end function processCPUBird() % Process the bird when the game is not started Bird.ScreenPos(2) = SinY(SinYPos); SinYPos = mod(SinYPos, SinYRange)+1; end function drawToMainCanvas() % Draw the scrolls and sprites to the main canvas % Redraw the background MainCanvas = Sprites.Bkg.CData(:,:,:,InGameParams.CurrentBkg); TubeFirstCData = TubeLayer.CData(:, 1:GAME.RESOLUTION(2), :); TubeFirstAlpha = TubeLayer.Alpha(:, 1:GAME.RESOLUTION(2), :); % Plot the first half of TubeLayer MainCanvas(TubeFirstAlpha) = ... TubeFirstCData (TubeFirstAlpha); end function scrollTubes(offset) Tubes.ScreenX = Tubes.ScreenX - offset; if Tubes.ScreenX(Tubes.FrontP) <=-26 Tubes.ScreenX(Tubes.FrontP) = Tubes.ScreenX(Tubes.FrontP) + 240; Tubes.VOffset(Tubes.FrontP) = ceil(rand*105); redrawTube(Tubes.FrontP); Tubes.FrontP = mod((Tubes.FrontP),3)+1; Flags.NextTubeReady = true; end end function refreshTubes() % Refreshing Scheme 1: draw the entire tubes but only shows a part % of each for i = 1:3 set(TubeSpriteHdl(i), 'XData', Tubes.ScreenX(i) + [0 26-1]); end end function refreshFloor(frameNo) offset = mod(frameNo, 24); set(FloorSpriteHdl, 'XData', -offset); end function redrawTube(i) set(TubeSpriteHdl(i), 'YData', -(Tubes.VOffset(i)-1)); end %% --- Math Functions for handling Collision / Rotation etc. --- function collide_flag = isCollide() collide_flag = 0; if Bird.ScreenPos(1) >= Tubes.ScreenX(Tubes.FrontP)-5 && ... Bird.ScreenPos(1) <= Tubes.ScreenX(Tubes.FrontP)+6+25 else return; end GapY = [128 177] - (Tubes.VOffset(Tubes.FrontP)-1); % The upper and lower bound of the GAP, 0-based if Bird.ScreenPos(2) < GapY(1)+4 || Bird.ScreenPos(2) > GapY(2)-4 collide_flag = 1; end return; end function addScore() if Tubes.ScreenX(Tubes.FrontP) < 40 && Flags.NextTubeReady Flags.NextTubeReady = false; Score = Score + 1; end end function refreshBird() % move bird to pos [X Y], % and rotate the bird surface by X degrees, anticlockwise = + cosa = cos(Bird.Angle); sina = sin(Bird.Angle); xrotgrid = cosa .* Bird.XGRID + sina .* Bird.YGRID; yrotgrid = sina .* Bird.XGRID - cosa .* Bird.YGRID; xtransgrid = xrotgrid + Bird.ScreenPos(1)-0.5; ytransgrid = yrotgrid + Bird.ScreenPos(2)-0.5; set(BirdSpriteHdl, 'XData', xtransgrid, ... 'YData', ytransgrid, ... 'CData', Sprites.Bird.CDataNan(:,:,:, Bird.CurFrame)); end %% -- Display Infos -- %% -- Callbacks -- function stl_KeyUp(hObject, eventdata, handles) key = get(hObject,'CurrentKey'); % Remark the released keys as valid FlyKeyValid = FlyKeyValid | strcmp(key, FlyKeyNames); end function stl_KeyDown(hObject, eventdata, handles) key = get(hObject,'CurrentKey'); % Has to be both 'pressed' and 'valid'; % Two key presses at the same time will be counted as 1 key press down_keys = strcmp(key, FlyKeyNames); FlyKeyStatus = any(FlyKeyValid & down_keys); FlyKeyValid = FlyKeyValid & (~down_keys); % FlyKeyValid % KeyStatus = (strcmp(key, KeyNames) | LastKeyStatus); end function stl_KeyPressFcn(hObject, eventdata, handles) curKey = get(hObject, 'CurrentKey'); switch true case strcmp(curKey, 'escape') CloseReq = true; % case strcmp(curKey, '+') end end function stl_CloseReqFcn(hObject, eventdata, handles) CloseReq = true; end end