首页 文章详情

只用2小时,开发足球射门游戏

云加社区 | 252 2022-12-18 03:00 0 0 0
UniSMS (合一短信)

导读 | 世界杯即将闭幕,你是否还未过瘾?近期体育竞技类游戏层出叠见,本文特邀架构师以一款简单有趣的点球射门游戏为例,通过代码实现游戏主体界面的绘制实现基础的游戏场景,而后阐述各类常见逻辑的实现方法。想了解游戏场景内各种复杂操作逻辑怎么实现游戏开发中是否所有场景都只能通过加载素材资源实现?读完本文,你能独立开发出下图游戏,并掌握一定游戏开发实操方法。




游戏界面实现思路&代码

1)界面术语定义

界面分为游戏区和计分区,其中游戏区又分为:观众区、球门区、游戏元素、射门区。

2)观众区绘制思路&代码

观众区用swing代码进行实现,绘制的思路如下:绘制背景矩形框;绘制次排观众——首先,绘制一个圆和椭圆合并成一个观众样式。然后,绘制一个小一点的圆和椭圆合并成一个观众样式。最后组合并放置到第2排位置,并铺满整排;绘制首排观众【与上一步一致,注意换观众的颜色】——首先,绘制一个圆和椭圆合并成一个观众样式;然后,绘制一个小一点的圆和椭圆合并成一个观众样式;最后,组合并放置到第1排位置,并铺满整排;绘制两个不同颜色的矩形框,并摆放最前面遮挡第1排观众的“脚”;绘制几根黑线,遮挡广告边缘。


通过swing中的Graphics2D对象绘制第二排观众:

// 观众背景g2d.setColor(personBgColor);g2d.fillRect(0,y,getWidth(),100);//画矩形for (int i = 0; i < getWidth(); i+=46) {// 观众第二排   g2d.setColor(person2);   g2d.fillOval(i,36,16,16);//画圆   g2d.fillArc(i-7,50,30,50,0,180);// 画扇行   g2d.fillOval(i+24,40,13,13);   g2d.fillArc(i+18,51,24,49,0,180);}

通过swing中的Graphics2D对象绘制第一排观众

// 观众第一排y+=90;g2d.fillRect(0,y-30,getWidth(),50);g2d.setColor(person1);for (int i = 0; i < getWidth(); i+=46) {   g2d.fillOval(i+7,y-41,16,16);   g2d.fillArc(i,y-25,30,50,0,180);   g2d.fillOval(i+31,y-34,13,13);   g2d.fillArc(i+25,y-24,24,49,0,180);}

通过swing中的Graphics2D对象绘制广告栏和黑线

// 绘制广告栏和黑线g2d.setColor(personBgColor1);g2d.fillRect(0,y,getWidth(),20);g2d.setColor(personLineColor);g2d.fillRect(0,y,getWidth(),2);y+=20;g2d.setColor(personBgColor2);g2d.fillRect(0,y,getWidth(),18);y+=18;g2d.setColor(personLineColor);g2d.fillRect(0,y,getWidth(),2);for (int i = 1; i < 5; i++) {   g2d.fillRect(getWidth()*i/5,y-38,2,38);}


3)草坪绘制思路&代码

草坪区用swing代码进行实现,绘制的思路如下:
绘制两种不同颜色的矩形;用两种不同颜色的矩形铺满整个屏幕;调整矩形的高度是从上下一次递增,呈现立体视觉。

动画呈现如下:


参考实现代码:

// 足球草地int count =0;int h1=60;for (int i = y; i < getHeight(); i++) {if(count%2==0){       g2d.setColor(bgColor1);  }else{       g2d.setColor(bgColor2);  }   g2d.fillRect(0,y,getWidth(),h1+count*10);   y+=h1+count*10;count++;}

4)禁区三维绘制思路&代码

禁区用swing代码进行实现,绘制的思路如下:绘制一个空心的圆角矩形;使用变换技术让圆角矩形具有三维立体效果;调整禁区框的位置;用一个绿色矩形遮挡多余的禁区部分。


动画呈现如下:


通过swing中的Graphics2D对象绘制两个圆角矩形合并:

g2d.setColor(Color.WHITE);g2d.fillRect(0,175,getWidth(),5);AffineTransform oldTx = g2d.getTransform();Stroke stroke = new BasicStroke(5);g2d.setStroke(stroke);AffineTransform tx = new AffineTransform();tx.setToShear(-0.5, 0);g2d.setTransform(tx);g2d.drawRoundRect(getWidth()*5/20,175,getWidth()*6/10,120,10,10);AffineTransform tx2= new AffineTransform();tx2.setToShear(0.5, 0);g2d.setTransform(tx2);g2d.drawRoundRect(getWidth()*3/20,175,getWidth()*6/10,120,10,10);g2d.setTransform(oldTx);

擦除中间不要的部分

//清理内部多余的线g2d.setColor(bgColor1);g2d.fillRect(0,173,getWidth(),2);g2d.fillRect(getWidth()*3/20+10,180,getWidth()*7/10-20,20);g2d.fillRect(getWidth()*3/20+10,270,getWidth()*6/10-20,23);g2d.setColor(bgColor2);g2d.fillRect(getWidth()*3/20+10,200,getWidth()*6/10-20,70);


5)球门球网三维绘制思路&代码

球门与球网的绘制是最难的,卡点在于如何利用平面技术实现三维效果,绘制的思路如下:绘制一个圆角矩形,作为球门框;绘制两条弯折折线,作为球门内门柱;绘制多条水平的三线段折线,作为球门水平球门线;绘制多条垂直的二线段折线,作为球门垂直球门线。


定义对应的尺寸大小,绘制球网竖线:

g2d.setColor(Color.WHITE);int step = 15;int startX = getWidth()*4/20+20;int centerX = getWidth()/2;int startY = 85;// 球网竖线for (int i = startX; i < (getWidth()*4/5); i=startX) {   int x[] = {startX,startX+(startX<centerX?+8:-8),startX+(startX<centerX?+12:-12)};   int y[] = {startY,startY+10,155};   if((centerX-startX)!=-10) {       g2d.drawPolyline(x, y, x.length);//画折线  }   startX+=step;}

实现球门内柱偏移:

// 球迷柱左里Stroke stroke = new BasicStroke(5);g2d.setStroke(stroke);startX = getWidth()*4/20+20;int tempX[] = {startX-18,startX-2,startX-2};int tempY[] = {startY+3,startY+10,153};g2d.drawPolyline(tempX, tempY, tempY.length);startX = getWidth()*4/5;int rightX[] = {startX-2,startX-20,startX-24};int rightY[] = {startY+3,startY+10,153};g2d.drawPolyline(rightX, rightY, rightY.length);stroke = new BasicStroke(1);g2d.setStroke(stroke);startX = getWidth()*4/20;

通过swing中的Graphics2D对象绘制球网横线:

// 球网横线int endX = getWidth()*4/5;for (int i = startY; i < 156; i=startY) {   int x[] = {startX,startX+(startX<centerX?+18:-18),endX-12,endX};   int y[] = {startY,startY-3,startY-3,startY};   g2d.drawPolyline(x,y,x.length);   startY+=step;}int x[] = {startX,startX+(startX<centerX?+18:-18),endX-12,endX};int y[] = {158,155,155,158};g2d.drawPolyline(x,y,x.length);

通过swing中的Graphics2D对象绘制门框:

// 绘制门框g2d.setColor(doorColor);stroke = new BasicStroke(9);g2d.setStroke(stroke);g2d.drawRoundRect(getWidth()*4/20,85,getWidth()*3/5,95,20,20);// 带有圆角的矩形stroke = new BasicStroke(5);g2d.setStroke(stroke);g2d.setColor(Color.WHITE);g2d.drawRoundRect(getWidth()*4/20,86,getWidth()*3/5,95,20,20);g2d.fillRect(0,175,getWidth(),5);


6射门区绘制思路&代码

射门区用swing代码进行实现,绘制的思路如下:绘制两种不同颜色的扇形;叠加两个扇形;配上文字。


参考实现代码:

// 蓄力区g2d.setColor(Color.WHITE);g2d.fillArc((getWidth()-64)/2-80,460,224,160,0,180);g2d.setColor(Color.ORANGE);g2d.fillArc((getWidth()-64)/2-80,460,224,160,0,arc);// 文字Font font = new Font("阿里巴巴普惠体 Medium",1,24);g2d.setFont(font);g2d.setColor(Color.WHITE);g2d.drawString("Ctrl + 鼠标:移动球",30,400);g2d.drawString("拖动鼠标:调整角度",50,440);g2d.drawString("点击鼠标:射门",50,480);


7计分区绘制思路&代码

计分区用swing代码进行实现,绘制的思路如下:绘制两种不同颜色的矩形;用两种不同颜色的矩形铺满整个屏幕;调整矩形的高度是从上下一次递增,呈现立体视觉。


参考实现代码:

Graphics2D g2d = (Graphics2D)g;g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);g.setColor(bgColor1);g.fillRect(0,0,getWidth(),getHeight());g.setColor(bgColor2);g.fillRect(0,8,getWidth(),getHeight()-8);Font font = new Font("阿里巴巴普惠体 Medium",1,32);g.setFont(font);g2d.setColor(Color.BLACK);g2d.drawString("分数",30,45);g2d.drawString("TIME",250,45);g2d.drawString("次数",540,45);g2d.setColor(textColor);g2d.fillRoundRect(100,20,80,30,25,25);g2d.fillRoundRect(335,20,120,30,25,25);g2d.fillRoundRect(620,20,80,30,25,25);g2d.setColor(Color.WHITE);g2d.drawString(String.format("%02d",score),120,47);g2d.drawString(String.format("%02d:%02d",time/60,time%60),345,47);g2d.drawString(String.format("%02d",count),638,47);

8守门员&球绘制思路&代码

守门员、球绘与石头这里涉及比较多复杂细节,利用图片代替代码绘制,实现的思路如下:加载图片;把图片摆放到对应位置。


编写通过图片导入实现绘制球的代码

// 球public class Ball extends JLabel{int startX = 0 ;int startY = 0;public Ball(){this.setPreferredSize(new Dimension(64,64));this.setIcon(new ImageIcon(ResourcesUtil.getRootPath()+"\\ball\\football.png"));// 加载图片  }}
编写通过图片导入实现绘制守门员的代码
// 守门员public Goalkeeper(BackgroundPanel backgroundPanel){   this.backgroundPanel = backgroundPanel;   this.setBounds(backgroundPanel.getWidth()/2,100,78,128);   this.setPreferredSize(new Dimension(78,128));   this.setIcon(new ImageIcon(ResourcesUtil.getRootPath()+"\\ball\\smy.png"));}

编写通过图片导入实现绘制石头的代码

// 石头public class Shitou extends JLabel implements Obstacle {   BackgroundPanel backgroundPanel;   public Shitou(BackgroundPanel backgroundPanel){       this.backgroundPanel = backgroundPanel;       this.setBounds(backgroundPanel.getWidth()/2+50,100,316,100);//设置图片放置的位置       this.setPreferredSize(new Dimension(316,100));       this.setIcon(new ImageIcon(ResourcesUtil.getRootPath()+"\\ball\\st.png"));  }   @Override   public String name() {       return "石头";   }
@Override public JComponent getComponent() { return this; } public void start(){ }
public void stop(){ }}



如何实现游戏逻辑

整个游戏的实现逻辑,按照对象来分,包括:球对象逻辑拖动鼠标:调整球射出的轨迹逻辑,支持上下左右的拖动调整;Ctrl+拖动鼠标:摆放球逻辑;点击球:球按照轨迹飞出运动的逻辑)、星星对象逻辑(球在飞行轨迹与星星重合则消除星星的逻辑)、守门员对象逻辑(在球门区左右来回移动的逻辑)、石头对象逻辑(禁止遮挡部分球门,球不能从此射进的逻辑)、球门对象逻辑(进球逻辑)、积分区对象逻辑(记时逻辑、进球积分逻辑)。下文我将详细阐述各个游戏逻辑实现思路。



1)调整射门轨迹的逻辑实现

轨迹实现的思路如下:假设有两点,黄色点为足球的中心点,红色点是球门的中心点;在黄点和红点之间就存在一条红色线段;在红色线段上随机取N个点,用白色表示,这样就形成了一个直线的轨迹点。


接着再说一下拖动鼠标,轨迹跟着鼠标移动的实现思路鼠标向上拖动,黄点和红点同步向上平移,这样线段上的轨迹点也同步平移;鼠标向下拖动,黄点和红点同步向下平移,这样线段上的轨迹点也同步平移;鼠标向右拖动,黄点和红点同步向右平移,这样线段上的轨迹点也同步平移;鼠标向左拖动,黄点和红点同步向左平移,这样线段上的轨迹点也同步平移。


注意,黄点和红点之间如果是曲线,效果更贴近自然,所以最后还需要把黄点和红点之间使用二次曲线进行实现。


记录起始点与终点:

// 记录红黄点 public void reDraw(Ball ball,BackgroundPanel backgroundPanel,int stepX,int stepY,boolean isControlDown){     // 开始点——黄点     startY = ball.getY()+ball.getHeight()/2;     startX = ball.getX()+ball.getWidth()/2;     // 结束点——红点     endX = getWidth()/2+stepX/3;     endY = 0; }
根据起始点与终点以及二次曲线绘制轨迹线
// 画出轨迹线public void paintComponent(Graphics g) {    Graphics2D g2d = (Graphics2D) g;    g2d.setColor(Color.RED);    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);    // 二次曲线    QuadCurve2D quadCurve2D = new QuadCurve2D.Double(startX,startY, startX+stepX,endY+stepY+50, endX, endY+stepY);    // 通过二次曲线,随机生成线上的几个点    PathIterator pi = quadCurve2D.getPathIterator(g2d.getTransform(),6);// 从二次曲线中取出特征点    points = new ArrayList<>(25);    while (!pi.isDone()) {        double[] coords = new double[6];        switch (pi.currentSegment(coords)) {            case PathIterator.SEG_MOVETO:            case PathIterator.SEG_LINETO:                points.add(new Point2D.Double(coords[0], coords[1]));                break;        }        pi.next();    }    // 在每个点 画上小白圆圈    g2d.setColor(Color.WHITE);    Point2D.Double point = null;    for (Point2D.Double temp : points) {        point = temp;        g2d.fillOval((int) point.x, (int) point.y,10,10);//在每个特征点上画一个小圆圈    }}
实现鼠标拖动的代码:
ball.addMouseMotionListener(new MouseAdapter() {public void mouseDragged(MouseEvent e){// 记录拖动最后的坐标点,用于记录拖动平移的差量int stepX = e.getX();int stepY = e.getY();        line.reDraw(ball,BackgroundPanel.this,stepX,stepY,e.isControlDown());        repaint();    }});

2)拖动球,摆放球的逻辑实现

拖动球可以摆放球的位置,相关实现思路是:拖动开始时(按下鼠标时)设置一个其实点,黄点;拖动过程中(按下鼠标,并同时移动位置)换点跟随鼠标点;拖动结束时(松开鼠标)球平移到最后的位置。


参考实现代码:
public void reDraw(Ball ball,BackgroundPanel backgroundPanel,int stepX,int stepY,boolean isControlDown){// 移动最后的位置点this.stepX = stepX;this.stepY = stepY;// 按下Ctrl键拖动才是,摆放if(isControlDown) {this.setVisible(false);        ball.setBounds(ball.getX() + stepX, ball.getY(), ball.getWidth(), ball.getHeight());    }}


3)射门,球按照轨迹飞行实现

鼠标点击球,球就按照之前的轨迹点飞行。实现的思路如下:获取到从二次曲线上的随机轨迹点,如下图中的白色小圆;点击球是,开启一个线程
在线程中,把球平移到轨迹点的第一个点,并休眠100毫秒;100毫秒后,又把球平移到轨迹点的下一个点,再次休眠100毫秒。重复上一步动作,知道球平移到最后一个轨迹点。


参考实现代码:

// 监听点击球ball.addMouseListener(new MouseAdapter() {@Overridepublic void mouseClicked(MouseEvent e) {// 用一个线程让球按照瞄准轨迹飞行new Thread(new Runnable() {@Overridepublic void run() {int star =0;// 获取到轨迹线上的轨迹点for (Point2D.Double point : line.getPoints()) {// 把球移动到轨迹点上        ball.setBounds((int) point.x, (int) point.y,ball.getWidth(),ball.getHeight());try {// 休眠100毫秒                        Thread.sleep(100);                    } catch (InterruptedException ex) {                        ex.printStackTrace();                    }                }            }        }).start();    }});

4消除轨迹点重合的星星逻辑实现

消除星星实际就是判断球的坐标点是否与星星重合,如果是则消除。具体实现思路如下:星星当作是一个正方形,有4个点;球也当作是一个正方形,有4个点;球在飞行过程中,每移动到一个轨迹点时检查星星和球的四个点是否有重合的区域;如果有重合的区域则消除星星——从游戏界面中删除星星。


参考实现代码:

ball.addMouseListener(new MouseAdapter() {@Overridepublic void mouseClicked(MouseEvent e) {new Thread(new Runnable() {@Overridepublic void run() {int star =0;for (Point2D.Double point : line.getPoints()) {                    ball.setBounds((int) point.x, (int) point.y,ball.getWidth(),ball.getHeight());try {// 球在飞行过程中,没移动一个点,需要检查一下是否有星星需要消除                        star += obstacleStart();                        Thread.sleep(100);                    } catch (InterruptedException ex) {                        ex.printStackTrace();                    }                }                check(star);            }        }).start();        CLICK_CLIP.play();    }});public int obstacleStart(){    Rectangle ballBounds = ball.getBounds();int tempX[] = {ballBounds.x,ballBounds.x+ballBounds.width,ballBounds.x,ballBounds.x+ballBounds.width};int tempY[] = {ballBounds.y,ballBounds.y,ballBounds.y+ballBounds.height,ballBounds.y+ballBounds.height};int count = 0;// 获取所有的星星,进行循环检查for (Component component : this.getComponents()) {if(component instanceof Star){            Star obstacle = (Star)component;            Rectangle goalkeeperBounds = obstacle.getBounds();int minX = goalkeeperBounds.x;int maxX = goalkeeperBounds.x+goalkeeperBounds.width;int minY = goalkeeperBounds.y;int maxY = goalkeeperBounds.y+goalkeeperBounds.height;            miukoo:for (int i = 0; i < tempY.length; i++) {// 如何球的4个点,在星星的区域内,则命中if(tempX[i]>minX&&tempX[i]<maxX&&tempY[i]>minY&&tempY[i]<maxY){                    System.out.println("================命中星星");                    count++;                    STAR_CLIP.play();this.remove(obstacle);// 消除星星,自己删除即可break miukoo;                }            }        }    }return count;}

5)守门员来回移动的逻辑实现

守门员在球门前左右移动、干扰射球的飞行过程的实现思路:开启一个线程;向右平移守门员位置+30像素,并判断是否超出了最右边球门边缘,如果是则设置向左移动,然后休眠100毫秒;向左平移守门员位置-30像素,并判断是否超出了最左边球门边缘,如果是则设置向右移动,然后休眠100毫秒。

参考实现代码:
public void move() {try {        Rectangle bounds = this.getBounds();double width = bounds.getX();if (isAdd) {// 向右            width += 30;if (width < backgroundPanel.getWidth() * 4 / 5) {                backgroundPanel.repaint();this.setBounds((int) width, bounds.y, bounds.width, bounds.height);                Thread.sleep(100);            } else {                isAdd = false;            }        } else {// 向左            width -= 30;if (width > backgroundPanel.getWidth() * 1 / 5) {                backgroundPanel.repaint();this.setBounds((int) width, bounds.y, bounds.width, bounds.height);                Thread.sleep(100);            } else {                isAdd = true;            }        }    }catch (Exception e){        e.printStackTrace();    }}
@Overridepublic void run() {while (true&&!Thread.interrupted()){ move(); }}

6)石头的逻辑实现

石头仅一张图片,把其摆放到对应位置即可。其遮挡射门的逻辑,主要在球门逻辑中去判断。

参考代码:

public class Shitou extends JLabel implements Obstacle {    BackgroundPanel backgroundPanel;public Shitou(BackgroundPanel backgroundPanel){this.backgroundPanel = backgroundPanel;this.setBounds(backgroundPanel.getWidth()/2+50,100,316,100);//设置图片放置的位置this.setPreferredSize(new Dimension(316,100));this.setIcon(new ImageIcon(ResourcesUtil.getRootPath()+"\\ball\\st.png"));    }
@Overridepublic String name() {return "石头"; }
@Overridepublic JComponent getComponent() {return this; }
public void start(){ }
public void stop(){ }
}

7)进球逻辑实现

进球逻辑实际是一个对象边框重合检查的过程。其实现思路如下:球门、守门员、石头、球都有自己的边界,都是平行四边形;当前射出的球移动到轨迹最后一个点时,开始判断以上元素的边界是否重合,依此来判断是否进球;进球依据:球的四个点都在球门四个点内部;守住依据:球与守门员、石头有任意一个点的重合即为守住;出界依据:球任意一个点不在球门范围内,则为球出界。


监听球的移动事件,判断是否移动完成:
ball.addMouseListener(new MouseAdapter() {@Overridepublic void mouseClicked(MouseEvent e) {        infoPanel.addCount();new Thread(new Runnable() {@Overridepublic void run() {int star =0;for (Point2D.Double point : line.getPoints()) {    ball.setBounds((int) point.x, (int) point.y,ball.getWidth(),ball.getHeight());try {                        star += obstacleStart();                        Thread.sleep(100);                    } catch (InterruptedException ex) {                        ex.printStackTrace();                    }                }// 轨迹点移动完成后,开始检查球是否进球                check(star);            }        }).start();    }});

检查守门员与球的位置关系:

public void check(int star){//停止所有的线程,以便获取守门员最后的位置,与球判重    stop();    Rectangle ballBounds = ball.getBounds();// 检查是否被石头还有守门员    boolean isLan = obstacle();int x = ballBounds.x;int y = ballBounds.y;    boolean isOut = false;if(!isLan) {// 判断是否出界if (x < getWidth() / 5 || x > getWidth() * 4 / 5 || y < 85) {            isOut = true;            FAIL_CLIP.play();            repaint();            JOptionPane.showMessageDialog(null, "球出界了...", "Tipe", JOptionPane.ERROR_MESSAGE);        }    }if(!isLan&&!isOut){// 没有被守住,也没有出界,则进球啦~~~if(result!=null){this.remove(result);        }        infoPanel.addScore(star);        System.out.println("===========赢了,开始显示祝贺彩带和播放音乐");        result = new Result();        result.setPreferredSize(new Dimension(230,187));        result.setBounds((getWidth()-230)/2,(getHeight()-187)/2,230,187);this.add(result);this.repaint();        WIN_CLIP.play();// 进球后,休眠5秒,然后自动复位球try {            Thread.sleep(5000);        } catch (InterruptedException e) {            e.printStackTrace();        }        WIN_CLIP.stop();this.remove(result);    }else{        FAIL_CLIP.stop();    }    line.setVisible(false);    ball.setBounds((getWidth()-64)/2,470,64,64);// 重新开启线程,让守门员再次动起来    start();}

检查球与障碍物的位置关系:

public boolean obstacle(){    Rectangle ballBounds = ball.getBounds();int tempX[] = {ballBounds.x,ballBounds.x+ballBounds.width,ballBounds.x,ballBounds.x+ballBounds.width};int tempY[] = {ballBounds.y,ballBounds.y,ballBounds.y+ballBounds.height,ballBounds.y+ballBounds.height};for (Component component : this.getComponents()) {if(component instanceof Obstacle){// 守门员和石头都抽象成障碍物,判断球是否与障碍物重合            Obstacle obstacle = (Obstacle)component;            Rectangle goalkeeperBounds = obstacle.getComponent().getBounds();int minX = goalkeeperBounds.x;int maxX = goalkeeperBounds.x+goalkeeperBounds.width;int minY = goalkeeperBounds.y;int maxY = goalkeeperBounds.y+goalkeeperBounds.height;boolean isLan = false;for (int i = 0; i < tempY.length; i++) {if(tempX[i]>minX&&tempX[i]<maxX&&tempY[i]>minY&&tempY[i]<maxY){                    isLan = true;                    FAIL_CLIP.play();                    repaint();                    JOptionPane.showMessageDialog(null,"球被"+obstacle.name()+"守住了...","Tipe",JOptionPane.ERROR_MESSAGE);return true;                }            }        }    }return false;}


8)记时逻辑实现

记时实现的思路如下:进入游戏时初始一个数字变量;开启一个线程,把数字变量增加1,然后休眠1秒;循环增加1、循环休眠1秒。


参考代码:

long time = 0;
@Overridepublic void run() {while (true){try { Thread.sleep(1000); time++; } catch (InterruptedException e) { e.printStackTrace(); } repaint();// 刷新线程时间// 显示时间时把time转成对应格式,g2d.drawString(String.format("%02d:%02d",time/60,time%60),345,47); }}

9)进球积分逻辑实现

在本游戏中积分的规则有以下两点:进球得1分;进球的同时,消除一颗星星得1分——举个例子,如下图所示,射门进球同时消除了一颗星星,则得2分。

相关实现逻辑思路如下:在球飞行过程中进来消除星星的数量;在进球时,把星星的数量当作分数累计。


在监听球移动的过程,判断其位置是否与星星重合:

ball.addMouseListener(new MouseAdapter() {@Overridepublic void mouseClicked(MouseEvent e) {        infoPanel.addCount();new Thread(new Runnable() {@Overridepublic void run() {// 记录飞行过程中消除星星的数量int star =0;for (Point2D.Double point : line.getPoints()) {                    ball.setBounds((int) point.x, (int) point.y,ball.getWidth(),ball.getHeight());try {// 计算星星是否被消除,如果是则累计                        star += obstacleStart();                        Thread.sleep(100);                    } catch (InterruptedException ex) {                        ex.printStackTrace();                    }                }// 把消除星星作为分数,传递给计分区                check(star);            }        }).start();        CLICK_CLIP.play();    }});
根据是否进球一步确认星星的得分:
// 检查是否进球public void check(int star){    ........if(!isLan&&!isOut){// 进球了if(result!=null){this.remove(result);        }// 增加计分区的数字,有多少星星则记多少        infoPanel.addScore(star);        ........}

腾讯工程师技术干货直达:
1、算法工程师深度解构ChatGPT技术
2、10分钟!从架构视角读懂K8s
3、探秘微信业务优化:DDD从入门到实践
4、祖传代码重构:从25万行到5万行的血泪史




good-icon 0
favorite-icon 0
收藏
回复数量: 0
    暂无评论~~
    Ctrl+Enter