500行代码实现pong游戏

时间:2022-06-20
本文章向大家介绍500行代码实现pong游戏,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

全局变量是不好的、但是都用matlab了谁还管这些,全篇全局变量,把计算和绘图拆分到不同的函数中,每次先计算下一时刻数据、再统一绘制图像

function [] = pong_game()

close all

clear

clc

%----------------------参数设置----------------------

%游戏参数

MAX_POINTS = 5;

START_DELAY = 1;

%运动参数

FRAME_DELAY = .01; %刷新时间

MIN_BALL_SPEED = .8; %最小(起始)速度

MAX_BALL_SPEED = 3; %最大速度

BALL_ACCELERATION = 0.05; %反弹加速

PADDLE_SPEED = 1.3; %挡板速度

%B、P因子调节横纵向运动比例

B_FACTOR = 1;

P_FACTOR = 2;

%Y因子防止小球卡住

Y_FACTOR = 0.01;

%得分区域

GOAL_BUFFER = 5;

%绘图尺寸

BALL_RADIUS = 1.5; %小球半径

WALL_WIDTH = 3;

FIGURE_WIDTH = 800; %单位:像素

FIGURE_HEIGHT = 480;

PLOT_W = 150; %绘图宽度

PLOT_H = 100; %高度

GOAL_SIZE = 50;

GOAL_TOP = (PLOT_H+GOAL_SIZE)/2;

GOAL_BOT = (PLOT_H-GOAL_SIZE)/2;

PADDLE_H = 18; %挡板高

PADDLE_W = 3; %挡板宽

PADDLE = [0 PADDLE_W PADDLE_W 0 0; PADDLE_H PADDLE_H 0 0 PADDLE_H];

PADDLE_SPACE = 10; %挡板和空门距离

%绘图样式

FIGURE_COLOR = [0, 0, 0]; %背景

AXIS_COLOR = [.15, .15, .15]; %围墙

CENTER_RADIUS = 15; %中央圆圈

BALL_MARKER_SIZE = 10; %小球标志半径

BALL_COLOR = [.1, .7, .1];

BALL_OUTLINE = [.7, 1, .7];

BALL_SHAPE = 'o';

PADDLE_LINE_WIDTH = 2;

WALL_COLOR = [.3, .3, .8]; %墙

PADDLE_COLOR = [1, .5, 0];

CENTERLINE_COLOR = PADDLE_COLOR .* .8; %地线

PAUSE_BACKGROUND_COLOR = FIGURE_COLOR;

PAUSE_TEXT_COLOR = [.9, .9, .9];

PAUSE_EDGE_COLOR = BALL_COLOR;

TITLE_COLOR = 'w';

%提示信息

PAUSE_WIDTH = 36; %信息框宽度

MESSAGE_X = 38; %位置

MESSAGE_Y = 55;

MESSAGE_PAUSED = ['           游戏暂停' 10 10];

MESSAGE_INTRO = [...

  '     先得 ' num2str(MAX_POINTS) ' 分者赢!' 10 10 ...

  '    玩家1:    玩家2:' 10 ...

  '   a、z键     上下键' 10 10 ...

  ];

MESSAGE_CONTROLS = '  暂停:(p)   重置:(r)   结束:(q)';

%----------------------全局参数初始化----------------------

fig = []; %主程序绘图句柄

quitGame = false; %主程序停止标志

paused = []; %暂停标志

score = []; %得分

winner = []; %赢家标志

ballPlot = []; %墙、球句柄

paddle1Plot = []; %挡板句柄

paddle2Plot = [];

ballVector=[]; %小球运动参数

ballSpeed=[];

ballX = []; %小球位置

ballY = [];

paddle1V = []; %挡板运动参数

paddle2V = [];

paddle1 = []; 

paddle2 = [];

%-----------------------工具函数----------------------

%------------绘图------------

%初始墙、球、挡板

  function createFigure

    %整体尺寸: [左边位置, 底部位置, 宽度, 高度]:

    scrsz = get(0,'ScreenSize');

    fig = figure('Position',[(scrsz(3)-FIGURE_WIDTH)/2 ...

      (scrsz(4)-FIGURE_HEIGHT)/2 ...

      FIGURE_WIDTH, FIGURE_HEIGHT]);

    %绑定键盘事件

    set(fig,'KeyPressFcn',@keyDown, 'KeyReleaseFcn', @keyUp);

    %不可拉伸窗口

    set(fig, 'Resize', 'off');

    axis([0 PLOT_W 0 PLOT_H]);

    axis manual;

    %设定边界颜色、隐藏刻度

    set(gca, 'color', AXIS_COLOR, 'YTick', [], 'XTick', []);

    %背景色

    set(fig, 'color', FIGURE_COLOR);

    hold on;

    %墙

    topWallXs = [0,0,PLOT_W,PLOT_W];

    topWallYs = [GOAL_TOP,PLOT_H,PLOT_H,GOAL_TOP];

    bottomWallXs = [0,0,PLOT_W,PLOT_W];

    bottomWallYs = [GOAL_BOT,0,0,GOAL_BOT];

    plot(topWallXs, topWallYs, '-', ...

      'LineWidth', WALL_WIDTH, 'Color', WALL_COLOR);

    plot(bottomWallXs, bottomWallYs, '-', ...

      'LineWidth', WALL_WIDTH, 'Color', WALL_COLOR);

    %中央圆圈

    thetas = linspace(0, (2*pi), 100);

    circleXs = (CENTER_RADIUS .* cos(thetas)) + (PLOT_W / 2);

    circleYs = (CENTER_RADIUS .* sin(thetas))+ (PLOT_H / 2);

    %地线

    centerline = plot([PLOT_W/2, PLOT_W/2],[PLOT_H, 0],'--');

    set(centerline, 'Color', CENTERLINE_COLOR);

    centerCircle = plot(circleXs, circleYs,'--');

    set(centerCircle, 'Color', CENTERLINE_COLOR);

    %小球

    ballPlot = plot(0,0);

    set(ballPlot, 'Marker', BALL_SHAPE);

    set(ballPlot, 'MarkerEdgeColor', BALL_OUTLINE);

    set(ballPlot, 'MarkerFaceColor', BALL_COLOR);

    set(ballPlot, 'MarkerSize', BALL_MARKER_SIZE);

    %挡板

    paddle1Plot = plot(0,0, '-', 'LineWidth', PADDLE_LINE_WIDTH);

    paddle2Plot = plot(0,0, '-', 'LineWidth', PADDLE_LINE_WIDTH);

    set(paddle1Plot, 'Color', PADDLE_COLOR);

    set(paddle2Plot, 'Color', PADDLE_COLOR);

  end

%------------新游戏------------

%恢复初始

  function newGame

    winner = 0;

    score = [0, 0];

    paddle1V = 0; %速度

    paddle2V = 0; 

    paddle1 = [PADDLE(1,:)+PADDLE_SPACE; ...

      PADDLE(2,:)+((PLOT_H - PADDLE_H)/2)];

    paddle2 = [PADDLE(1,:)+ PLOT_W - PADDLE_SPACE - PADDLE_W; ...

      PADDLE(2,:)+((PLOT_H - PADDLE_H)/2)];

    resetGame;

    if ~quitGame 

      pauseGame([MESSAGE_INTRO, MESSAGE_CONTROLS]);

    end

  end

%------------重置------------

%恢复初始值

  function resetGame

    bounce([1-(2*rand), 1-(2*rand)]);

    ballSpeed=MIN_BALL_SPEED;

     ballX = PLOT_W/2;

     ballY = PLOT_H/2;

    %19个空格

    titleStr = sprintf('%d / %d%19d / %d', ...

      score(1), MAX_POINTS, score(2), MAX_POINTS);

    t = title(titleStr, 'Color', TITLE_COLOR);

    set(t, 'FontName', 'Courier','FontSize', 15, 'FontWeight', 'Bold');

    refreshPlot;

    if ~quitGame 

      pause(START_DELAY);

    end

  end

%------------球的运动------------

%各种碰撞检测

  function moveBall

    %挡板边界

    p1T = paddle1(2,1);

    p1B = paddle1(2,3);

    p1L = paddle1(1,1);

    p1R = paddle1(1,2);

    p1Center = ([p1L p1B] + [p1R p1T]) ./ 2;

    p2T = paddle2(2,1);

    p2B = paddle2(2,3);

    p2L = paddle2(1,1);

    p2R = paddle2(1,2);

    p2Center = ([p2L p2B] + [p2R p2T]) ./ 2;

    %下一时刻的位置

    newX = ballX + (ballSpeed * ballVector(1));

    newY = ballY + (ballSpeed * ballVector(2));

    %是否碰撞右墙

    if (newX > (PLOT_W - BALL_RADIUS) ...

        && (ballY<GOAL_BOT+BALL_RADIUS || newY>GOAL_TOP-BALL_RADIUS))

      %右墙

      if (newY > GOAL_BOT && newY < GOAL_TOP - BALL_RADIUS)

        %空门底部

        bounce([newX - PLOT_W, newY - GOAL_BOT]);

      elseif (newY < GOAL_TOP && newY > GOAL_BOT + BALL_RADIUS)

        %空门顶部

        bounce([newX - PLOT_W, newY - GOAL_TOP]);

      else

        %右墙余下部分

        bounce([-1 * abs(ballVector(1)), ballVector(2)]);

      end

      %是否碰撞左墙

    elseif (newX < BALL_RADIUS ...

        && (newY<GOAL_BOT+BALL_RADIUS || newY>GOAL_TOP-BALL_RADIUS))

      %左墙

      if (newY > GOAL_BOT && newY < GOAL_TOP - BALL_RADIUS)

        %空门底部

        bounce([newX, newY - GOAL_BOT]);

      elseif (newY < GOAL_TOP && newY > GOAL_BOT + BALL_RADIUS)

        %空门顶部

        bounce([newX, newY - GOAL_TOP]);

      else

          %左墙余下部分

        bounce([abs(ballVector(1)), ballVector(2)]);

      end

      %顶部

    elseif (newY > (PLOT_H - BALL_RADIUS))

      bounce([ballVector(1), -1 * (Y_FACTOR + abs(ballVector(2)))]);

      %底部

    elseif (newY < BALL_RADIUS)

      bounce([ballVector(1), (Y_FACTOR + abs(ballVector(2)))]);

      %挡板1

    elseif (newX < p1R + BALL_RADIUS ...

        && newX > p1L - BALL_RADIUS ...

        && newY < p1T + BALL_RADIUS ...

        && newY > p1B - BALL_RADIUS)

      bounce([(ballX-p1Center(1)) * P_FACTOR, newY-p1Center(2)]);

      %挡板2

    elseif (newX < p2R + BALL_RADIUS ...

        && newX > p2L - BALL_RADIUS ...

        && newY < p2T + BALL_RADIUS ...

        && newY > p2B - BALL_RADIUS)

      bounce([(ballX-p2Center(1)) * P_FACTOR, newY-p2Center(2)]);

    else

      %没有碰撞

    end

    %移动小球

    ballX = newX;

    ballY = newY;

  end

%------------挡板移动------------

%更新位置

  function movePaddles

    %纵向位置

    paddle1(2,:) = paddle1(2,:) + (PADDLE_SPEED * paddle1V);

    paddle2(2,:) = paddle2(2,:) + (PADDLE_SPEED * paddle2V);

    %边界确认

    if paddle1(2,1) > PLOT_H

      paddle1(2,:) = PADDLE(2,:) + PLOT_H - PADDLE_H;

    elseif paddle1(2,3) < 0

      paddle1(2,:) = PADDLE(2,:);

    end

    if paddle2(2,1) > PLOT_H

      paddle2(2,:) = PADDLE(2,:) + PLOT_H - PADDLE_H;

    elseif paddle2(2,3) < 0

      paddle2(2,:) = PADDLE(2,:);

    end

  end

%------------更新绘图------------

%暂停时间也是刷新时间

  function refreshPlot

    set(ballPlot, 'XData', ballX, 'YData', ballY);

    set(paddle1Plot, 'Xdata', paddle1(1,:), 'YData', paddle1(2,:));

    set(paddle2Plot, 'Xdata', paddle2(1,:), 'YData', paddle2(2,:));

    drawnow;

    pause(FRAME_DELAY);

  end

%------------得分检测------------

%只需检测横向坐标、如果赢得比赛就重置

  function checkGoal

    goal = false;

    if ballX > PLOT_W + BALL_RADIUS + GOAL_BUFFER

      score(1) = score(1) + 1;

      if score(1) == MAX_POINTS

        winner = 1;

      end

      goal = true;

    elseif ballX < 0 - BALL_RADIUS - GOAL_BUFFER

      score(2) = score(2) + 1;

      if score(2) == MAX_POINTS

        winner = 2;

      end

      goal = true;

    end

    if goal %得分

      pause(START_DELAY);

      resetGame;

      if winner > 0 %有人赢了

        pauseGame(['      玩家 ' num2str(winner) ' 赢了!!!' 10])

        newGame;

      else %没人赢

      end

    end

  end

%------------暂停------------

%全局暂停标志设为真

  function pauseGame(input)

    paused = true;

    str = '      按任意键继续...';

    spacer = 1:PAUSE_WIDTH;

    spacer(:) = uint8(' ');

    while paused

      printText = [spacer 10 input 10 str 10];

      h = text(MESSAGE_X,MESSAGE_Y,printText);

      set(h, 'BackgroundColor', PAUSE_BACKGROUND_COLOR)

      set(h, 'Color', PAUSE_TEXT_COLOR)

      set(h,'EdgeColor',PAUSE_EDGE_COLOR);

      set(h, 'FontSize',5,'FontName','Courier','LineStyle','-','LineWidth',1);

      pause(FRAME_DELAY)

      delete(h);

    end

  end

%------------从暂停中恢复------------

%全局暂停标志设为假

  function unpauseGame()

    paused = false;

  end

%------------碰撞------------

%计算碰撞后速度

  function bounce (tempV)

    %确保水平速度大于垂直速度

    tempV(1) = tempV(1) * ((rand/B_FACTOR) + 1);

    %归一化速度

    tempV = tempV ./ (sqrt(tempV(1)^2 + tempV(2)^2));

    ballVector = tempV;

    %碰撞后加速

    if (ballSpeed + BALL_ACCELERATION < MAX_BALL_SPEED)

      ballSpeed = ballSpeed + BALL_ACCELERATION;

    end

  end

%------------键盘按下事件------------

%按键按下后执行

  function keyDown(~,event)

    switch event.Key

      case 'a'

        paddle1V = 1;

      case 'z'

        paddle1V = -1;

      case 'uparrow'

        paddle2V = 1;

      case 'downarrow'

        paddle2V = -1;

      case 'p'

        if ~paused

          pauseGame([MESSAGE_PAUSED MESSAGE_CONTROLS]);

        end

      case 'r'

        newGame;

      case 'q'

        unpauseGame;

        quitGame = true;

    end

    unpauseGame;

  end

%------------键盘弹起事件------------

%按键弹起后执行

  function keyUp(~,event)

    switch event.Key

      case 'a'

        if paddle1V == 1

          paddle1V = 0;

        end

      case 'z'

        if paddle1V == -1

          paddle1V = 0;

        end

      case 'uparrow'

        if paddle2V == 1

          paddle2V = 0;

        end

      case 'downarrow'

        if paddle2V == -1

          paddle2V = 0;

        end

    end

  end

%----------------------主程序----------------------

createFigure;

newGame;

while ~quitGame

  moveBall;

  movePaddles;

  refreshPlot;

  checkGoal;

end

close(fig);

end