2048小游戏

Pygame简易版2048小游戏:超详细解说,看完还不会可以剁手了(附完整源码) 导语 哈喽!大家好,我是栗子,感谢大家的支持! 新的一天,新气象,程序猿们的日常开始敲敲敲,改改改——今天给大家来一款简单的小游戏! 2048小游戏——准确的来说确实是一个简单版本的! 呐呐呐 ,主要是担心很多小伙伴儿直接上界面版本的看不懂,所以做了这款简单的2048,先看这 篇简单版本的吧! 正文 为了搞懂这个游戏的规则,小编去直接下载了一款2048的小游戏,上手玩了一波! 然后.........完全停不下来!23333~ 玩法:用手指或键盘上下左右滑动,将两个相同的数字.. 阅读详情

                                                                       2048游戏


 

大家好,这里我给大家介绍一款2048小游戏的思路和具体做法。


 

在这一篇博客里所实现的是2048的做法,具体代码和基本思路。

 

我来具体讲讲这个游戏。

这款游戏里,一共有两个界面。

第一个是外部的主界面:

 

这个主界面中有四个按钮,功能明确。

下面是帮助按钮和关于按钮的效果图。

 

 

 

 

 

 

接下来的面板是游戏内部面板,也就是点击主面板的开始游戏按钮之后出现的面板。

 

下面这个是项目的主要结构。

 

 

 

 

我们先从结构入手,可以很快的了解项目。

 

在项目New2048的bao2048里,有7个源文件。

此外,还有两个.jpg图片文件和两个.wav格式的音乐文件。有一个暂不用文件是音乐的备用文件。这两个图片文件是主界面 和游戏界面的背景图。两个音乐 文件是在游戏中添加的音乐。souFeiYa是一开始游戏的音乐,而chunYinYue是游戏界面的音乐。

以下是两幅基本背景图。

 

 

 

 

 

看到这两个原图片,大家是不是知道了它们的位置了。哈哈哈哈。

不同的是 , 第二幅是用paint方法画上去的,而第一幅是加到JFrame面板里去的。

 

 

介绍完了游戏所需基本资源,接下来我们一个一个介绍源文件。

 

我们先贴上JieMian.java的源代码。

package bao2048;


import java.awt.Color;
import java.awt.Image;
import java.io.FileNotFoundException;
import java.awt.event.*;


import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;


import sun.audio.AudioPlayer;


public class JieMian   implements  ActionListener {


static int  a=9;
static JFrame  jfJieMian;
ImageIcon  g;
JLabel  biaoQian;
JPanel  anNiuMianBan , jp;
MyThread  xianChen; 
    JButton  jtnKaiShi  , jtnWoMen , jtnBangZhu , jtnTuiChu;
public JieMian() throws Exception
{


   xianChen= new MyThread();
   xianChen.start();
jfJieMian = new  JFrame("2048游戏");
g = new ImageIcon("11.jpg");
biaoQian= new JLabel(g);
biaoQian.setBounds(0, 0, g.getIconWidth(), g.getIconHeight());  //设置标签大小是整个图片
jfJieMian.getLayeredPane().add(biaoQian , new Integer(Integer.MIN_VALUE));  //第2层面板
jp = (JPanel)jfJieMian.getContentPane();
jp.setOpaque(false);  //设置透明
anNiuMianBan = new JPanel();
anNiuMianBan.setOpaque(false);
anNiuMianBan.setLayout(null); //设置布局管理器为null


   jtnKaiShi = new JButton("开始游戏");
jtnKaiShi.setSize(100, 50);
jtnKaiShi.setLocation(20, 50);
jtnKaiShi.setBackground(Color.cyan);
jtnKaiShi.addActionListener(this);
anNiuMianBan.add(jtnKaiShi);



jtnWoMen = new JButton("关于我们");  
jtnWoMen.setSize(100 ,50);
jtnWoMen.setLocation(20 ,120);
jtnWoMen.setBackground(Color.orange);
jtnWoMen.addActionListener(this);
anNiuMianBan.add(jtnWoMen);



jtnBangZhu = new  JButton("帮助");
jtnBangZhu.setSize(100 ,50);
jtnBangZhu.setLocation(20 , 190);
jtnBangZhu.setBackground(Color.yellow);
jtnBangZhu.addActionListener(this);
anNiuMianBan.add(jtnBangZhu);


jtnTuiChu = new JButton("退出游戏");
jtnTuiChu.setSize(100 ,50);
jtnTuiChu.setLocation(20, 260);
jtnTuiChu.setBackground(Color.LIGHT_GRAY);
jtnTuiChu.setBorder(BorderFactory.createEtchedBorder());
jtnTuiChu.addActionListener(this);
anNiuMianBan.add(jtnTuiChu);



jp.add(anNiuMianBan);
jfJieMian.setSize(800, 500);
jfJieMian.setLocation(200, 150);
jfJieMian.setVisible(true);
jfJieMian.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub

if(e.getSource() == jtnKaiShi)
{
jfJieMian.setVisible(false);

try {
   My2048 My = new  My2048();
//MyThread.boola=false;
//if(t.isAlive() )  System.out.println("在运行这个线程");
//t.yield();
   AudioPlayer.player.stop(Music.suoFeiYa);
} catch (FileNotFoundException e1) {
// TODO 自动生成的 catch 块
e1.printStackTrace();
}

}


if(e.getSource() == jtnWoMen)
{
String  s1 = "团队名\n";
String s2 = "团队人员:***\n";
String s3 = "本游戏由我们团队协作完成,在原有代码基础上增加了部分功能\n本游戏用于发现杯项目作业,可供个人使用,由于代码并非原创\n禁止转出\n编不下去啦。。。。。可以自己改改。。。";
JOptionPane.showMessageDialog(jfJieMian, s1+s2+s3 , "团队介绍" , JOptionPane.INFORMATION_MESSAGE);

}


if(e.getSource() == jtnBangZhu)
{
String s1 = "开始时棋盘内随机出现两个数字,出现的数字仅可能为2或4\n";
String s2 ="玩家可以选择上下左右四个方向,若棋盘内的数字出现位移或合并,视为有效移动\n";
String s3 = "玩家选择的方向上若有相同的数字则合并,每次有效移动可以同时合并,但不可以连续合\n";
String s4="合并所得的所有新生成数字想加即为该步的有效得分\n";
String s5="玩家选择的方向行或列前方有空格则出现位移\n";
String s6="每有效移动一步,棋盘的空位(无数字处)随机出现一个数字(依然可能为2或4)\n";
String s7="棋盘被数字填满,无法进行有效移动,判负,游戏结束\n";
String s8="棋盘上出现2048,判胜,游戏结束\n";
JOptionPane.showMessageDialog(jfJieMian, s1+s2+s3+s4+s5+s6+s7+s8 ,"游戏规则" , JOptionPane.INFORMATION_MESSAGE);
}



if(e.getSource() == jtnTuiChu)
{
System.exit(0);

}
}

}

 

 

在这个源文件里,我们有四个按钮的构造,它们在JieMian的构造 方法里。还加了鼠标监听,用来监听点击按钮之后的操作。

 

帮助按钮和关于 我们按钮点击的结果是new了一个对话框,对话框里以字符串的形式列出了相关信息。退出按钮则调用了System.exit(0)方法来结束 程序,虽然有很多人不太喜欢这个粗暴的方法,但对于我等初学者还是很管用的,主要是播放游戏音乐的功能是放在线程里的,所以用这个方法可以很 直接的结束程序。在开始游戏按钮里,我们看到它new了一个My2048, 这是一个源文件,负责的是游戏的游戏界面。至于背景图片是怎么加上去的,我整了好久,最后才发现,JFrame的控制布局管理器不是只有ContPentpane。它有三层!而我们的图片应该加在它的第二层上面,并且把第一层的布局设置为透明,这样就可以看见图片了。

 

 

这个面板new了My2048,那么,接下来我们贴出它的源码,来看看它实现了什么功能。

 

以下是My2048的源码:

package bao2048;


import java.awt.*;
import java.io.FileNotFoundException;


import javax.swing.*;
public class My2048 extends JFrame 
{ 
static Operation  a ;
  public My2048() throws FileNotFoundException//构造函数 
  {

    setTitle("2048");//设置标题
    setSize(500, 670);//设定窗口大小
    setLocation(400, 50);//设定窗口起始位置
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    //getContentPane().setLayout(new GridLayout(4, 4, 5, 5));//设定布局方式为GridLayout型
    a =new Operation(this);
    this.setFocusable(true);
    this.requestFocus();
    this.setVisible(true);//设为可视
  }
  
  public static void main(String args[]) throws Exception //程序入口点
  {
   /* try
    {
      UIManager.setLookAndFeel("org.jvnet.substance.skin.SubstanceRavenGraphiteLookAndFeel");//设定UI界面风格
    } //接受抛出的异常
    catch (ClassNotFoundException | InstantiationException| IllegalAccessException | UnsupportedLookAndFeelException e)
    {
      e.printStackTrace();
    }*/
    //JFrame.setDefaultLookAndFeelDecorated(true);//设定Frame的缺省外观
    //new My2048();
 new  JieMian();
  }
}

 

 

这里,出现了主函数,我们把new   JieMian的代码放到了主函数里,程序一执行,就出现主界面。而这里主函数在My2048里。我们来看看My2048的构造函数里都有什么。

除了一些基本的JFrame的设置之外,出现了一个Operation类,这个类实现了2048游戏的主要功能。而其他代码则是构建出一个窗体,也就是内部游戏界面。

 

 

接下来我们看看Operation类。

 

以下是Operation的源代码。

 

package bao2048;


import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.*;
import javax.swing.*;
public   class Operation   implements   KeyListener   , MouseListener
{
static int  deFenShuJu=0;
   Block[] block;//用于储存16个数据
  static JPanel panel = new JPanel();
  
  public  boolean up;
  public  boolean down;
  public  boolean left;
  public  boolean right;
  static int moveFlag;//用于累计移动的次数
  static boolean numFlag;//用于判断是否还能加入新的数字
  public Operation(JFrame frame ) 
  {
 
 
 
   // this.panel = (JPanel)frame.getContentPane();//构造出panel
SouthMianBan southM = new SouthMianBan(frame,this);




//frame.add( southM);
frame.add(southM, BorderLayout.NORTH);
frame.add(panel , BorderLayout.CENTER);
panel.addMouseListener(this);
panel.addKeyListener(this);
//panel.setFocusable(true);  //设置面板处于激活状态,不然键盘监听用不了
//panel.requestFocus();
panel.setLayout(new GridLayout(4, 4, 5, 5));
    block = new Block[16];//构造出长度为16的数组
    numFlag = true;//初始化
    moveFlag = 0;
    up=true;down=true;left=true;right=true;
    addBlock();
    for (int i = 0; i < 2; i++)
    appearBlock();
    frame.addKeyListener(this);
   
    
  }
  
  private void addBlock() 
  {
    for (int i = 0; i < 16; i++) //往panel里加入block
    {
      block[i] = new Block();
      block[i].setHorizontalAlignment(JLabel.CENTER);// 设置文本对齐方式,CENTER显示居中对齐
      block[i].setOpaque(true);//设置控件是否透明
      panel.add(block[i]);  
    }
  } 
  public void appearBlock() 
  {
    while (numFlag) //当还能加入随机的一个新的值得时候
    {
      int index = (int) (Math.random() * 16);//取一个0到15的随机整数,这个数作为随机加入盘中的2或4的位置
      if (block[index].getValue() == 0)//如果这个数所在的block数组中值为0,即在为空的时候,加入一个2或4的数字
      {
        if (Math.random() < 0.5)
        {
          block[index].setValue(2);
        }
        else
        {
          block[index].setValue(4);
        }
        break;//跳出while
      }
    }
  }
  
  public void judgeAppear() //统计block数组中是否含有值为0的元素,若没有,则numFlag变为false  judge 判断
  {
    int sum = 0;
    for (int i = 0; i < 16; i++) 
    {
      if (block[i].getValue() != 0)
      {
        sum++;
      }
    }
    if (sum == 16)
      numFlag = false;
  
  }
  
  public int Find(int i,int j,int a,int b)
  {// i 0 j4   a 0 b 16 
    while(i<b&&i>=a)
    {
       if(block[i].getValue()!=0)
       {
        return i;
       }
       i=i+j;
    }
    return -1;
  }
  public void upBlock()
  {
    int i=0,j=0;int t=0;int valueJ=0;int valueI=0;int index=0;
    for(i=0;i<4;i++)  //管横向
    {
      index=i;
      for(j=i+4;j<16;j+=4)   //管纵向
      {  
        valueJ=0; valueI=0;
        if(block[index].getValue()==0)
        {
          t=Find(index,4,0,16);
          if(t!=-1)
          {
            block[index].setValue(block[t].getValue());
            block[t].setValue(0);
          }
          else
          {
            break;
          }
        }
        valueI=block[index].getValue();
        if(block[j].getValue()==0)
        {
          t=Find(j,4,0,16);
          if(t!=-1)
          {
            block[j].setValue(block[t].getValue());
            block[t].setValue(0);
          }
          else
          {
            break;
          }
        }
        valueJ=block[j].getValue();
        if(valueI==valueJ&&valueI!=0&&valueJ!=0) // 合并数字
        {
          block[index].setValue(valueI+valueJ);
          block[j].setValue(0);
          numFlag = true;
          deFenShuJu+=10;
          SouthMianBan.getFenShu(deFenShuJu);
          SouthMianBan.deFen.setText("得分: "+deFenShuJu);
        }
        index=j;  //这一句很重要
//index  始终在j的上一行,两个始终相邻
      }
        
    }
  }
  public void downBlock() {
  
 int i=0,j=0;int t=0;int valueJ=0;int valueI=0;int index=0;
 for(i=12 ; i<16 ; i++)
 {
 index = i;
 for(j=i-4; j>i-16 ;j-=4)  //j>i-12
 {
 valueJ=0; valueI=0;
 if(block[index].getValue()==0)
       {
         t=Find(index,-4, 0 , 16);//i-12    
         if(t!=-1)
         {
           block[index].setValue(block[t].getValue());
           block[t].setValue(0);
         }
         else
         {
           break;
         }
       }
 
 valueI=block[index].getValue();
 if(block[j].getValue()==0)
       {
         t=Find(j,-4,0,16);
         if(t!=-1)
         {
           block[j].setValue(block[t].getValue());
           block[t].setValue(0);
         }
         else
         {
           break;
         }
       }
 
 valueJ=block[j].getValue();
       if(valueI==valueJ&&valueI!=0&&valueJ!=0)
       {
         block[index].setValue(valueI+valueJ);
         block[j].setValue(0);
         numFlag = true;
         deFenShuJu+=10;
         SouthMianBan.getFenShu(deFenShuJu);
         SouthMianBan.deFen.setText("得分: "+deFenShuJu);
       }
       index=j;
     }
 }
 }
  
  
  public void rightBlock() 
  {
    int i=0,j=0;int t=0;int valueJ=0;int valueI=0;int index=0;
    for(i=3;i<16;i+=4)
    {
      index=i;
      for(j=i-1;j>i-4;j--)
      {  
        
        if(block[index].getValue()==0)
        {
          t=Find(index,-1,i-3,index+1);
          if(t!=-1)
          {
            block[index].setValue(block[t].getValue());
            block[t].setValue(0);
          }
          else
          {
            break;
          }
        }
        valueI=block[index].getValue();
        if(block[j].getValue()==0)
        {
          t=Find(j,-1,i-3,j+1);
          if(t!=-1)
          {
            block[j].setValue(block[t].getValue());
            block[t].setValue(0);
          }
          else
          {
            break;
          }
        }
        valueJ=block[j].getValue();
        if(valueI==valueJ&&valueI!=0&&valueJ!=0)
        {
          block[index].setValue(valueI+valueJ);
          block[j].setValue(0);
          numFlag = true;
          deFenShuJu+=10;
          SouthMianBan.getFenShu(deFenShuJu);
          SouthMianBan.deFen.setText("得分: "+deFenShuJu);
        }
        index=j;
      }
        
    }
  }
  public void leftBlock() 
  {
 int i=0,j=0;int t=0;int valueJ=0;int valueI=0;int index=0;
 for(i=0 ; i<16 ; i+=4)
 {
 index = i;
 for(j=i+1; j<i+4 ; j++)
 {
 if(block[index].getValue()==0)
       {
         t=Find(index,1,index-1,i+4);
         if(t!=-1)
         {
           block[index].setValue(block[t].getValue());
           block[t].setValue(0);
         }
         else
         {
           break;
         }
       }
 
 valueI=block[index].getValue();
 if(block[j].getValue()==0)
       {
         t=Find(j,1,j-1,i+4);
         if(t!=-1)
         {
           block[j].setValue(block[t].getValue());
           block[t].setValue(0);
         }
         else
         {
           break;
         }
       }
 
 valueJ=block[j].getValue();
       if(valueI==valueJ&&valueI!=0&&valueJ!=0)
       {
         block[index].setValue(valueI+valueJ);
         block[j].setValue(0);
         numFlag = true;
         deFenShuJu+=10;
         SouthMianBan.getFenShu(deFenShuJu);
         SouthMianBan.deFen.setText("得分: "+deFenShuJu);
       }
       index=j;
     } 
 }
 }
  
  public void over() 
  {
    if (numFlag ==false&& up==false&&down==false&&left==false&&right==false) //当不能添加元素,并且不可移动的步数超过36就输了,输了的时候在盘中央显示GAMEOVER
    {
      block[4].setText("G");
      block[5].setText("A");
      block[6].setText("M");
      block[7].setText("E");
      block[8].setText("O");
      block[9].setText("V");
      block[10].setText("E");
      block[11].setText("R"); 
      block[11].addMouseListener(new MouseAdapter() {public void mousePressed(MouseEvent e){reStart();}});
//给标签block[11]添加鼠标监听,单击鼠标,重新启动程序
    }
  }
    
  public void win() //同OVER
  { 
    block[0].setText("Y");
    block[1].setText("O");
    block[2].setText("U");
    block[13].setText("W");
    block[14].setText("I");
    block[15].setText("N");
    block[15].addMouseListener(new MouseAdapter() {
      public void mousePressed(MouseEvent e) {
        reStart();
      }
    });
  }
  public   void reStart()//重启游戏,和构造函数类似,不在累述
  {
    numFlag=true;
    moveFlag=0;
    up=true;down=true;left=true;right=true;
    for(int i=0;i<16;i++)
      block[i].setValue(0);
    for (int i = 0; i < 2; i++)
      appearBlock();
  }
  public void keyPressed(KeyEvent e) //判断按的上下左右键,并依次调用移动函数、判断函数、添加函数、判断是否输掉的函数
  {
 
 panel.setFocusable(true);  //设置面板处于激活状态,不然键盘监听用不了
 panel.requestFocus();
    switch (e.getKeyCode()) {
    case KeyEvent.VK_UP:
      if(up)
      {
     upBlock();
      }
      judgeAppear();
      appearBlock();
      over();
        
      if(numFlag==false)
      {
        up=false;
      }
      else
      {
        up=true;down=true;left=true;right=true;
      }
      break;
    case KeyEvent.VK_DOWN:
      if(down){
      downBlock();}
      judgeAppear();
      appearBlock();
      over();
      if(numFlag==false)
      {
        down=false;
      }
      else
      {
        up=true;down=true;left=true;right=true;
      }
      break;
    case KeyEvent.VK_LEFT:
      if(left){
      leftBlock();}
      judgeAppear();
      appearBlock();
      over();
        
      if(numFlag==false)
      {
        left=false;
      }
      else
      {
        up=true;down=true;left=true;right=true;
      }
      break;
    case KeyEvent.VK_RIGHT:
      if(right){
      rightBlock();}
      judgeAppear();
      appearBlock();
      over();
        
      if(numFlag==false)
      {
        right=false;
      }
      else
      {
        up=true;down=true;left=true;right=true;
      }
      break;
    }
  
  }
  public void keyTyped(KeyEvent e) {
  
  }
  public void keyReleased(KeyEvent e) {
  
  }


@Override
public void mouseClicked(MouseEvent e) {
// TODO 自动生成的方法存根

}


@Override
public void mousePressed(MouseEvent e) {
// TODO 自动生成的方法存根
panel.setFocusable(true);
panel.requestFocus();
}


@Override
public void mouseReleased(MouseEvent e) {
// TODO 自动生成的方法存根

}


@Override
public void mouseEntered(MouseEvent e) {
// TODO 自动生成的方法存根

}


@Override
public void mouseExited(MouseEvent e) {
// TODO 自动生成的方法存根

}
}

 

这段代码看着有点多,但不要怕,这段代码是最容易理解的。因为这一部分是大佬写的。

 

这段代码 结构非常明确,主要要说的是添加事件监听的那段代码。

也就是上下左右键的功能实现。

重新把它贴出来:(这里以上为例)

public void upBlock()
  {
    int i=0,j=0;int t=0;int valueJ=0;int valueI=0;int index=0;
    for(i=0;i<4;i++)  //管横向
    {
      index=i;
      for(j=i+4;j<16;j+=4)   //管纵向
      {  
        valueJ=0; valueI=0;
        if(block[index].getValue()==0)
        {
          t=Find(index,4,0,16);
          if(t!=-1)
          {
            block[index].setValue(block[t].getValue());
            block[t].setValue(0);
          }
          else
          {
            break;
          }
        }
        valueI=block[index].getValue();
        if(block[j].getValue()==0)
        {
          t=Find(j,4,0,16);
          if(t!=-1)
          {
            block[j].setValue(block[t].getValue());
            block[t].setValue(0);
          }
          else
          {
            break;
          }
        }
        valueJ=block[j].getValue();
        if(valueI==valueJ&&valueI!=0&&valueJ!=0) // 合并数字
        {
          block[index].setValue(valueI+valueJ);
          block[j].setValue(0);
          numFlag = true;
          deFenShuJu+=10;
          SouthMianBan.getFenShu(deFenShuJu);
          SouthMianBan.deFen.setText("得分: "+deFenShuJu);
        }
        index=j;  //这一句很重要
//index  始终在j的上一行,两个始终相邻
      }
        
    }
  }

代码中有两个for循环:外层for循环实现横向转移的功能,内层循环解决按列匹配的功能。也就是说,内层for循环先按列解决一列方块是否为零并且更换位置,而外层循环则是按每列 依次解决四 列的内层循环。

在内层循环 里,有 三个if语句,第一个是 index的匹配,找到index下是否有0的位置,返回,互换,第二个if语句是J从index下找有没有为0的位置互换,最后一个if是查找有没有值 相等的两个,如果 有,值相加,赋给位置居上的一个方块,另一个则 置值为0。

 

接下来贴上block的代码就是很有必要的了。

代码如下:

package bao2048;




import javax.swing.*;
import java.awt.*;
public class Block extends JLabel 
{
  private int value;
  public Block() 
  {
    value = 0;//初始化值为0
    setFont(new Font("font", Font.PLAIN, 40));//设定字体
    setBackground(Color.ORANGE);//设定初始颜色为灰色
  }
  
  public int getValue()//获取值
  {
    return value;
  }
  
  public void setValue(int value)
  {
    this.value = value;
    String text = String.valueOf(value);
    if (value != 0)
      setText(text);
    else
      setText("");//如果值为0则不显示
    setColor();
  }
  
  public void setColor() //根据值的不同设定不同的背景颜色、label字体
  {
    switch (value) 
      {
    case 0:
      setBackground(Color.orange);
      break;
    case 2:
      setBackground(new Color(238, 228, 218));
      break;
    case 4:
      setBackground(new Color(238, 224, 198));
      break;
    case 8:
      setBackground(new Color(243, 177, 116));
      break;
    case 16:
      setBackground(new Color(243, 177, 116));
      break;
    case 32:
      setBackground(new Color(248, 149, 90));
      break;
    case 64:
      setBackground(new Color(249, 94, 50));
      break;
    case 128:
      setBackground(new Color(239, 207, 108));
      break;
    case 256:
      setBackground(new Color(239, 207, 99));
      break;
    case 512:
      setBackground(new Color(239, 203, 82));
      break;
    case 1024:
      setBackground(new Color(239, 199, 57));
      break;
    case 2048:
      setBackground(new Color(239, 195, 41));
      break;
    case 4096:
      setBackground(new Color(255, 60, 57));
      break;
      }
  }
}

 

这里主要是给方块设置显示的值,并且不同的值设置不同的颜色。

 

 

接下来是音乐:Music.java

package bao2048;


import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;


import sun.audio.AudioPlayer;
import sun.audio.AudioStream;


public  class Music   implements  Runnable{
   static  AudioStream suoFeiYa ,chunYinYue;
public  Music()
{
try {
InputStream  in = new   FileInputStream("suoFeiYa.wav");
suoFeiYa= new  AudioStream(in);
AudioPlayer.player.start(suoFeiYa);
} catch (Exception e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}



public  Music(int  a)
{
try {
InputStream  in = new   FileInputStream("chunYinYue.wav");
chunYinYue = new  AudioStream(in);
AudioPlayer.player.start(chunYinYue);
} catch (Exception e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
@Override
public void run() {
new  Music();
}

}

这里是两个音乐,都用构造函数来创建,但以 参数的不同来区分。

 

音乐是放在线程里的,接下来 贴上线程的代码:

 

package bao2048;


public class MyThread extends  Thread{


public  MyThread()
{
super();
}
@SuppressWarnings("deprecation")
public void run()
{
Runnable r =new Music();

}
}

 

在线程里的run方法里跑音乐。

 

最后一个就是游戏界面的位于上部的效果了:

 

package bao2048;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.io.File;
import java.io.IOException;


import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;


import sun.audio.AudioPlayer;
public class SouthMianBan   extends   JPanel     implements  ActionListener{
 Operation op =null;
Image  image = null;
static int def; //用来显示得分数据
static JButton deFen;
JButton  fanHuiJieMian;
JButton  yinYue;
JButton  chonXin;
Font f ,f2 ,f3;
Thread t;
JLabel  one;
JFrame  jiaoDian;
public  SouthMianBan(JFrame frame, Operation op)
{
   this.op=op;
jiaoDian= frame;
Runnable r0 = new Music(0);
t = new Thread(r0);
f =  new  Font("dialog" , Font.CENTER_BASELINE , 12);
this.setLayout(null);
this.setPreferredSize(new Dimension(0 ,170));
this.setVisible(true);



deFen = new JButton("得分: 0 ");
deFen.setSize(120, 40);
deFen.setLocation(350, 15);
deFen.setBackground(Color.magenta);
deFen.setFont(f);
this.add(deFen);



fanHuiJieMian= new JButton("返回主界面 ");
fanHuiJieMian.setSize(120, 40);
fanHuiJieMian.setLocation(200, 15);
fanHuiJieMian.setBackground(Color.green);
fanHuiJieMian.setFont(f);
fanHuiJieMian.addActionListener(this);
this.add(fanHuiJieMian);



chonXin = new JButton("重新游戏");
chonXin.setSize(120, 40);
chonXin.setLocation(350, 110);
chonXin.setBackground(Color.GREEN);
chonXin.setFont(f);
chonXin.addActionListener(this);
this.add(chonXin);




yinYue= new JButton("关闭音乐 ");
yinYue.setSize(120, 40);
yinYue.setLocation(350, 60);
yinYue.setBackground(Color.pink);
yinYue.setFont(f);
yinYue.addActionListener(this);
this.add(yinYue);
yinYue.addFocusListener(new  FocusAdapter()
{
public  void  focusLost(FocusEvent e)
{
Operation.panel.getComponent(0).setFocusable(true);
Operation.panel.getComponent(0).requestFocus();
}
});


}

public static  void getFenShu(int m)
{
def = m;
}


public  void  paint(Graphics  g)
{
try {
f2 =  new  Font("楷体" , Font.CENTER_BASELINE , 32);
f3 =  new  Font("楷体" , Font.CENTER_BASELINE , 18);
image = ImageIO.read(new  File("12.jpg"));
g.drawImage(image , 0 ,0 , 500 ,170 ,null);
g.setColor(Color.red);
g.setFont(f2);
g.drawString("经典2048", 20, 30);
g.setColor(Color.BLUE);
g.setFont(f3);
g.drawString("快来试试你的水平!一起争夺2048", 20, 150);
} 
catch (IOException e)
{
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}



boolean  yinYuebool = true;  //决定是否 播放音乐
boolean  huiJieMian = true;  //决定是否 回到界面
@Override
public void actionPerformed(ActionEvent e) {
// TODO 自动生成的方法存根

if(e.getSource() == fanHuiJieMian)
{
if(huiJieMian)
{
JieMian.jfJieMian.setVisible(true);
AudioPlayer.player.stop(Music.chunYinYue);
fanHuiJieMian.setText("回到游戏");
if(yinYue.getText().equals("关闭音乐")) {}  else  { yinYue.setText("关闭音乐");yinYuebool = true;}
AudioPlayer.player.start(Music.suoFeiYa);
huiJieMian = false;
}
else
{
JieMian.jfJieMian.setVisible(false);
fanHuiJieMian.setText("返回主界面 ");
AudioPlayer.player.start(Music.chunYinYue);
AudioPlayer.player.stop(Music.suoFeiYa);
huiJieMian = true;
}
}


if(e.getSource() ==  chonXin)
{
op.reStart();
deFen.setText("得分:"+0);
Operation.deFenShuJu=0;
}
if(e.getSource() ==  yinYue)
{

if( yinYuebool)
{
AudioPlayer.player.stop(Music.chunYinYue);
yinYue.setText("继续播放");
yinYuebool = false;
}
else
{
AudioPlayer.player.start(Music.chunYinYue);
yinYue.setText("关闭音乐");
yinYuebool = true;
}

jiaoDian.setFocusable(true);
jiaoDian.requestFocus();
}
}



}

这里要注意参数传递,在构造方法里传了一个Operation的对象,用来调用report()方法。

 

说到这里,整个2048就结束 了。

码字不易,留个赞吧大笑

 

2048大家都玩过,说实话,我没完过,自己也不太感兴趣。如果不是这次学校有这样的一个项目落到我手上,可能我不太会去写这个游戏。现在整体写下来,代码风格并不整齐,也有一部分功能上的bug,但自己不怎么打算继续重构和维护下去,因为太耗时间,最近自己精力主要在算法上,而这一个项目确实耗费了我很多时间。由于这是一个团队项目,所以,我先要感谢我的团队,在他们的帮助下,我们成功做出来了。 虽然是团队项目,并有部分代码是已有的,但说原创并不为过。所以先在这里道个歉。
[2048 Dàjiā dōu wánguò, shuō shíhuà, wǒ méiwánguò, zìjǐ yě bù tài gǎn xìngqù. Rúguǒ bùshì zhè cì xuéxiào yǒu zhèyàng de yīgè xiàngmù luò dào wǒ shǒu shàng, kěnéng wǒ bù tài huì qù xiě zhège yóuxì. Xiànzài zhěngtǐ xiě xiàlái, dàimǎ fēnggé bìng bù zhěngqí, yěyǒu yībùfèn gōngnéng shàng de bug, dàn zìjǐ bù zě me dǎsuàn jìxù zhòng gòu hé wéihù xiàqù, yīnwèi tài hào shíjiān, zuìjìn zìjǐ jīnglì zhǔyào zài suànfǎ shàng, ér zhè yīgè xiàngmù quèshí hàofèile wǒ hěnduō shíjiān. Yóuyú zhè shì yīgè tuánduì xiàngmù, suǒyǐ, wǒ xiān yào gǎnxiè wǒ de tuánduì, zài tāmen de bāngzhù xià, wǒmen chénggōng zuò chūláile. Suīrán shì tuánduì xiàngmù, bìng yǒu bùfèn dàimǎ shì yǐ yǒu de, dàn shuō yuánchuàng bìng bù wéiguò. Suǒyǐ xiān zài zhèlǐ dào gè qiàn.]
Everyone has played in 2048. To be honest, I have never finished, and I am not very interested. If it wasn't for this school that had such a project falling into my hands, maybe I wouldn't write this game. Now the whole is written down, the code style is not neat, there are some functional bugs, but I don’t plan to continue to rebuild and maintain it. Because it is too time consuming, my energy is mainly on the algorithm recently, and this project really costs. I have a lot of time. Since this is a team project, I would like to thank my team first, and with their help, we have succeeded in making it.

Although it is a team project, and some of the code is already available, it is not too original. So first apologize here.
 
Android小项目--2048小游戏 Android小项目–2048小游戏 文章目录Android小项目--2048小游戏1.摘要2.实现的功能3.完成的界面展示4.功能具体实现的过程1. 游戏面板GameView的设计(采用GridLayout布局)2. Cell类的设计:Cell用来表示游戏中的小格子3. 将cell添加到GameView中:4. 游戏模式的改变:5. 记录当前分数及历史最高分数6. 判断游戏是否结束5. 总结 1.摘要 现如今,电子游戏已慢慢渗透进人们生活中,并在扮演着越来越重的角色。2048小游戏属于益智类小游戏,它做到 阅读详情

相关推荐

基于Java的2048小游戏设计

基于Java的2048小游戏设计 基于Java的2048小游戏设计的源代码 基于Java的2048小游戏设计的课程设计报告 package 小游戏; import javax.sound.sampled.; import javax.swing.; import java.io.; import java.awt.; import java.awt.event.; import java.util.; public class My2048Game extends JFrame{ //定义自己的面板 MyP

weixin_48864837的博客 1万+

C语言实训 easyx 2048小游戏(带背景音乐)

easyx2048小游戏 在使用前需要做的准备工作如下: 需要所用到的图片和歌曲移动到“配置”文件夹。 图片及歌曲(可根据自己的喜好进行歌曲的调整)的命名如下:(命名如需改变需要改变源代码) 1.主界面:如下 2.运行界面如下: 代码如下:(运行前需要下载easyx库 https://easyx.cn/ ) #include <stdio.h> #include <stdlib.h> #include <time.h> #include <con

LYE_LYE的博客 5007

2048小游戏的C语言实现方式

项目介绍:每次可以选择上下左右其中一个方向去滑动,每滑动一次,所有的数字方块都会往滑动的方向靠拢外,系统也会在空白的地方随机出现一个数字方块2或4,相同数字的方块在靠拢、相撞时会相加。不断的叠加最终拼凑出2048这个数字就算成功。 ...

T、的博客 4980

2048游戏规则及玩法技巧攻略

2048是一款非常有趣的益智游戏,可能有些小伙伴对游戏的具体规则还不是很清楚,下面小编就来为大家做个具体的游戏规则介绍以及分享一些玩法技巧攻略,喜欢的朋友千万不要错过哦!游戏规则:  每次控制所有方块向同一个方向运动,两个相同数字的方块撞在一起之后合并成为他们的和,每次操作之后会在空白的方格处随机生成一个2或者4,最终得到一个“2048”的方块就算胜利了。如果16个格子全部填满并且相邻的格子都不相...

u013252110的博客 2万+

基于C#的2048小游戏

项目简介 利用C#语言实现一个2048小游戏,通过编写程序实现游戏的操作过程。 2048游戏规则其实很简单,玩家每次可以选择上、下、左、右其中一个方向去移动,每移动一次,所有的数字方块都会往移动的方向靠拢。同时,系统也会在空白的地方随机出现新的数字方块,相同数字的方块在靠拢、相撞时会相加。系统给予的数字方块不是“2”就是“4”,玩家要想办法在这小小的16格范围中凑出“2048”这个数字方块,即为游...

小鲤的博客 4143

大一上C++课程设计——2048小游戏

1.需求分析 2048小游戏,需要在进行上下左右操作的时候合并相同的数字,并且向按动的方向进行移动,每次移动后,随机生成2或者4两个数字。要求合并后移动两个数字之间不可以出现0,当无法移动再一次进行移动或者合并出现数字2048的时候,游戏结束。无法移动,游戏判负,出现2048,游戏获胜。 2.基本求解思路 万事开头难。想要对一个问题进行求解,首先要理清楚完成事情的步骤。按照题目要求: “使用面向对象的方法来设计程序,即使用类和对象来完成设计,不运行使用面向过程的一堆函数来完成。 ” 需要注明的是,此处的N我

m0_51719971的博客 5212

2048小游戏C++板来啦!

C++板2048游戏实现,超详细有趣喔~~关注主播不迷路喔!

PingdiGuo_guo的博客 1万+

java2048小游戏源码及解析_200行java代码实现2048小游戏

本文实例为大家分享了java实现2048小游戏的具体代码,供大家参考,具体内容如下效果图:游戏介绍:1.2048是一款益智类小游戏,刚开始随机出现两个数字,可以上下左右控制数字的移动。2.当选择一个方向移动后,所有数字都会沿该方向移动到表格尽头,并且空余表格会随机出现2或4,当碰到相同的两个数字时,该两个数字会合并相加成一个数字,直到最大的数字变成2048游戏成功3.否则当数字填满表格且不能再移动...

weixin_30936907的博客 1690

JAVA实现2048小游戏

目录 一、效果 二、教程 三、代码 一、效果 2048小游戏是一款比较流行的数字游戏,游戏规则如下: 每次可以选择上下左右其中一个方向去滑动,每滑动一次,所有的数字方块都会往滑动的方向靠拢外,系统也会在空白的地方乱数出现一个数字方块,相同数字的方块在靠拢、相撞时会相加。不断的叠加最终拼凑出2048这个数字就算成功。 ps: 博主就没有添加成功的图片了,实在是因为技术不行,试完了几次均没有凑成 2048 ... 二、教程 1、使用IDEA搭建一个...

D77的博客 1万+

Docker实现上线部署2048小游戏

一.docker Docker 是一个开源的应用容器引擎,主要利用 linux 内核 namespace 实现沙盒隔离,用 cgroup 实现资源限制 二.docker实现上线部署2048小游戏 1.下载docker安装包和依赖包 [root@server1 ~]# ls ##安装包在docker目录内 docker [root@server1 ~]# cd docker/ [root@serv...

chaos_oper的博客 3135

C# Winform 2048小游戏源码

C# Winform 2048小游戏源码,2048小游戏源码,窗体应用程序2048小游戏源码,界面风格干净整洁,内容条理清晰,小游戏分为七个等级,3*3格子到10*10格子,格子越多越简单,格子越小越容易到最后无路可走,凑成2048即为成功,无路可走即为失败。这个源码让你快速搭建自己的应用程序,注释完整,代码规范,各种风格都有,代码上手简单,代码独立,可以直接用程序打开,运行使用。也可直接点击EXE运行程序。

不踩坑,不知道坑有多深;不总结,踩过的坑白踩。 5925

Python制作2048小游戏

这个2048小游戏我们使用的是画布模块,通过实现测试出位置进行更改每个画图的区域进行颜色的改变的文字的修改,四个方位的操作产生数字变换,每次变化都需要重新判断画图区域,进行变化,实现界面的状态的切换。

qq_44793283的博客 5949

C# 控制台版2048游戏 自定义配色 有音乐音效 高仿原作

使用C#语言开发的VS控制台应用程序,修改了控制台默认的颜色,使用自定义的配色,有背景音乐和音效,与原作相似度90%,编译无错,注释详细,和大家分享一下,一起学习进步!

js2048小游戏-全网详细解析2048源码-2048小游戏html讲解

js2048小游戏-全网详细解析2048源码-2048小游戏html讲解! 成功是留给有准备的人的。话不多多少我喜欢直接上代码,里面都有注释!如果大家有什么疑问可以留言交流。另外本人擅长:微信小程序、支付宝小程序、后台管理系统。如果大家有这边的项目也可以跟我合作。微信:wyk1364630428! <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>2048</ti

weixin_44959182的博客 2303

Unity简单操作:PostProcessing的使用

Postprocessing是一个unity2018版本的集成后期插件,有了这个插件,之后用unity开发时,后期调整颜色、光晕等会很方便。 目录 新建一个postprocessing profile(配置文件)。​ 参数说明: 新建一个postprocessing profile(配置文件)。 在主相机下加一个组件PostProcess Layer。 注意:需要将相机的Layer层设置与Post Process Layer 下的Volume blending(体积混合)...

weixin_39114763的博客 7369

Python解析骑行fit文件:从数据缺失到完整记录

本文详细介绍了如何利用Python解析和处理骑行FIT文件,解决数据缺失问题。通过使用fitparse库读取二进制FIT文件,并结合Garmin FitCSVTool进行格式转换,指导读者一步步诊断数据完整性、设计心率修补方案,并最终生成完整的骑行记录文件。该方法为骑行爱好者提供了灵活的数据修复与自定义分析能力。

weixin_29062255的博客 456

python快速实现2048小游戏

《2048》是一款比较流行的数字游戏,最早于2014年3月20日发行。原版2048首先在GitHub上发布,原作者是Gabriele Cirulli,后被移植到各个平台。这款游戏是基于《1024》和《小3传奇》的玩法开发而成的新型数字游戏。操作指南:每次可以选择上下左右其中一个方向去滑动,每滑动一次,所有的数字方块都会往滑动的方向靠拢外,系统也会在空白的地方乱数出现一个数字方块,相同数字的方块在靠拢、相撞时会相加。不断的叠加最终拼凑出2048这个数字就算成功。游戏技巧:最大数尽可能放在角落。数字按顺序紧邻排

ASS-ASH的博客 6104

Windows 下安装 ros2 的超详细教程

Windows11 ROS安装

2401_83363439的博客 3320

C语言简易小游戏实现—2048

2048小游戏是一款休闲益智类小游戏,游戏在一个4×4的方格上进行,在这里会不断产生方块,方块的初始值为2,玩家通过合并、消除方块使得方块的数值越来越大,最终合成2048的方块来获取胜利,游戏的核心功能逻辑简单易实现,通过C语言的函数功能,同时配合easyx图形库美化游戏界面,一个简单的2048小游戏即可实现。//创建游戏窗口,尺寸为500 * 700//创建游戏窗口//设置背景(方格间距)颜色//设置窗口为透明色//设置背景颜色//清除背景//打印游戏右侧区域并设置颜色//设置矩形填充颜色。

2301_77482503的博客 8563

Rocky Linux 9 离线环境Docker部署实战:从依赖打包到服务启动

本文提供了一份详尽的Rocky Linux 9离线环境Docker部署实战指南。针对无法访问外网的生产或内网服务器,文章详细讲解了如何在联网跳板机上下载Docker二进制包及所有系统依赖RPM包,并完成打包。随后,手把手指导在离线服务器上进行依赖安装、二进制文件部署、systemd服务配置、SELinux与防火墙调优,最终实现Docker服务的成功启动与验证。

weixin_28363123的博客 141

基于多智能体架构的AI自动化渗透测试平台PentAGI部署与实战

自动化渗透测试是网络安全领域提升效率、应对复杂威胁的关键技术。其核心原理在于通过程序化脚本或智能系统模拟攻击者行为,对目标系统进行安全评估。该技术能显著降低人工重复劳动,实现7x24小时持续监控与测试,其价值在于将安全专家从繁琐的扫描、信息收集中解放出来,专注于高级威胁分析与策略制定。在实际应用中,自动化渗透测试广泛应用于企业安全自评估、红蓝对抗演练及DevSecOps流程。本文聚焦于一个名为PentAGI的先进平台,它采用多智能体(Multi-Agent)系统架构,集成了知识图谱和向量数据库,实现了从侦察

weixin_30294295的博客 611

用js实现2048小游戏

用js实现2048小游戏笔记仓库:https://github.com/nnngu/LearningNotes 1、游戏简介2048是一款休闲益智类的数字叠加小游戏。(文末给出源代码和演示地址)2、游戏玩法在 4*4 的16宫格中,您可以选择上、下、左、右四个方向进行操作,数字会按方向移动,相邻的两个数字相同就会合并,组成更大的数字,每次移动或合并后会自动增加一个数字。当16宫格中没有空格子...

nnngu 1万+

基于YOLOv8深度学习的夜视行人检测系统:UI界面 + YOLOv8 + 数据集的实现

为了解决这一问题,基于深度学习的行人检测技术,尤其是YOLO(You Only Look Once)系列算法,成为了一个有效的解决方案。本文将详细介绍如何构建一个夜视行人检测系统,利用YOLOv8模型实现高效的行人检测,并设计用户友好的界面以便于使用。本博客详细介绍了如何构建一个基于深度学习的夜视行人检测系统,包括数据准备、YOLOv8模型训练、用户界面设计和实时监控模块的实现。为了将数据集转换为YOLO格式,我们需要确保每个图像文件都有一个对应的标签文件,标签文件中包含行人的边界框信息。

YOLO 1172
上一篇: 打字游戏
下一篇: 旋转 图片
libingbojava
博客等级 码龄9年 64粉丝 111原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值