欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

小游戏 2048的实现

程序员文章站 2022-04-07 19:47:31
...

2048,2016年最火的一款小游戏,其规则简单,容易实现,耐玩。

下面先说其游戏规则:

                  游戏本身由4*4的方格组成,可以上下左右四个方向滑动给,每次滑动后可以将同方向,同直线的数字进行加和或移动。每次操作后会,都有新的数字生成,生成数字为2或4经过实践,2与4的个数比例为:9:1。游戏开始时将产生2个数字。

 

2048的制作流程:自行实现,根据游戏的玩法自行定义,规划好后就可以实现。

            首先,这个游戏身主要为逻辑运算,先做逻辑。

            建立二维数组,设计随机产生算法。随机产生算法中需要注意:

                                                                1、随机产生的位置

                                                                2、随机的数字是几

             上下左右的运算:

                                                                 1、运算有四种:上下左右。要注意的是这里是传入的参数是键盘的监听还是其他,                                                                        (建议是其他)。为了后面得实现分块,跟好的修改。

                                                                  2、运算对数组的修改,这个是算法的核心

                                                                  3、何时游戏结束

                这里将数组全部放在一个类中,更容易修改。

下面是逻辑层的代码,重点的四步操作逻辑基本一致。

package java_2048;
import java.util.Random;
public class SuanFa extends ShuJu{
    public SuanFa(){
        suiji();suiji();
        for(int i=0;i<16;i++)
            C[i]=i;
        for(int i=0;i<4;i++)
            for(int j=0;j<4;j++)
                B[i][j]=A[i][j];
    }//建立对象,初始右两个数
    public void  suiji(){
        Random random=new Random();
        int x=random.nextInt(16-jishu)+1;
        for(int i=0;i<16;i++){
                if(A[i/4][i%4]==0)
                    if(--x==0){
                        C[i]=17;
                        A[i/4][i%4]=(byte)(random.nextInt(10)==0?2:1);//9:1出现4
                        break;
                    }
        }
        jishu++;
    }//出现数字的位置与大小
//移动算法
    public boolean Left(){
        byte s;
        int sw;
        boolean X=false;
        for(int i=0;i<4;i++){
            s=A[i][0];//记录末端
            sw=0;
            for(int j=1;j<4;j++){
                if(s!=0){//如果末端不为零操作
                    if(A[i][j]==s){
                        X=true;
                        A[i][j]=0;
                        A[i][sw]+=1;
                        Score+=1<<A[i][sw];
                        s=0;jishu--;
                    }
                    else if(A[i][j]!=0){
                        s=A[i][j];
                        sw=j;
                    }
                }
                else if(A[i][j]!=0){
                    s=A[i][j];
                    sw=j;
                }
            }
            s=0;
            for(int j=0;j<4;j++){
                if(A[i][j]!=0){
                    if(s!=j){
                        X=true;
                    }
                    A[i][s++]=A[i][j];
                }
            }
            for(int j=s;j<4;j++)
                A[i][j]=0;
        }
        return X;
    }
    public boolean Right(){
        int  s,sw;
        boolean X=false;
        for(int i=0;i<4;i++){
            s=A[i][3];//记录末端
            sw=3;
            for(int j=2;j>=0;j--){
                if(s!=0){//如果末端不为零操作
                    if(A[i][j]==s){
                        X=true;
                        A[i][j]=0;
                        A[i][sw]+=1;
                        Score+=1<<A[i][sw];
                        s=0;jishu--;
                    }
                    else if(A[i][j]!=0){
                        s=A[i][j];
                        sw=j;
                    }
                }
                else if(A[i][j]!=0){
                    s=A[i][j];
                    sw=j;
                }
            }
            s=3;
            for(int j=3;j>=0;j--){
                if(A[i][j]!=0){
                    if(s!=j){
                        X=true;
                    }
                    A[i][s--]=A[i][j];
                }
            }
            for(int j=s;j>=0;j--)
                A[i][j]=0;
        }
        return X;
    }
    public boolean Up(){
        int  s,sw;
        boolean X=false;
        for(int i=0;i<4;i++){
            s=A[0][i];//记录末端
            sw=0;
            for(int j=1;j<4;j++){
                if(s!=0){//如果末端不为零操作
                    if(A[j][i]==s){
                        X=true;
                        A[j][i]=0;
                        A[sw][i]+=1;
                        Score+=1<<A[sw][i];
                        s=0;jishu--;
                    }
                    else if(A[j][i]!=0){
                        s=A[j][i];
                        sw=j;
                    }
                }
                else if(A[j][i]!=0){
                    s=A[j][i];
                    sw=j;
                }
            }
            s=0;
            for(int j=0;j<4;j++){
                if(A[j][i]!=0){
                    if(s!=j){
                        X=true;
                    }
                    A[s++][i]=A[j][i];
                }
            }
            for(int j=s;j<4;j++)
                A[j][i]=0;
        }
        return X;
    }
    public boolean Down(){
        int  s,sw;
        boolean X=false;
        for(int i=0;i<4;i++){
            s=A[3][i];//记录末端
            sw=3;
            for(int j=2;j>=0;j--){
                if(s!=0){//如果末端不为零操作
                    if(A[j][i]==s){
                        X=true;
                        A[j][i]=0;
                        A[sw][i]+=1;
                        Score+=1<<A[sw][i];
                        s=0;jishu--;
                    }
                    else if(A[j][i]!=0){
                        s=A[j][i];
                        sw=j;
                    }
                }
                else if(A[j][i]!=0){
                    s=A[j][i];
                    sw=j;
                }
            }
            s=3;
            for(int j=3;j>=0;j--){
                if(A[j][i]!=0){
                    if(s!=j){
                        X=true;
                    }
                    A[s--][i]=A[j][i];
                }
            }
            for(int j=s;j>=0;j--)
                A[j][i]=0;
        }
        return X;
    }

    public boolean IsJieShu(){
        if(jishu<16) return true;//个数小于16还有空间
        for(int i=0;i<4;i++)
            for(int j=0;j<3;j++)
                if(A[i][j]==A[i][j+1])//横向加和
                    return true;
        for(int i=0;i<4;i++)
            for(int j=0;j<3;j++)
                if(A[j][i]==A[j+1][i])//纵向加和
                    return true;
        return false;
    }//判断游戏是否结束

    public void QingKongandFuZhi(){
        for(int i=0;i<4;i++){
            for(int j=0;j<4;j++)
                B[i][j]=A[i][j];
        }
        for(int i=0;i<16;i++){
            C[i]=(byte) i;
        }
    }//为动态准备

    public void DuiBi(int a){
        int x,j;
        boolean N;
        switch(a){
            case 1://左
            {
                for(int i=0;i<4;i++){
                    j=0;//实际     真矩阵
                    x=0;//最端点  假矩阵
                    N=false;
                    while(j<4&&A[i][j]!=0){
                        while(x<4&&B[i][x]==0)
                        {
                            x++;//找到非零元素
                        }
                        if(B[i][x]==A[i][j]) {
                            C[4*i+x]=C[4*i+x]-x+j;//对实际改值
                            if(N) {
                                A[i][j]++;
                                N=false;
                                C[4*i+j]=17;
                            }
                            x++;j++;
                        }
                        else {
                            N=true;
                            A[i][j]--;
                            C[4*i+x]=C[4*i+x]-x+j;
                            x++;
                        }
                    }
                }
                break;
            }
            case 3://右
            {
                for(int i=0;i<4;i++){
                    j=3;//实际     真矩阵
                    x=3;//最端点  假矩阵
                    N=false;
                    while(j>0&&A[i][j]!=0){
                        while(x>0&&B[i][x]==0)
                        {
                            x--;//找到非零元素
                        }
                        if(B[i][x]==A[i][j]) {
                            C[4*i+x]=C[4*i+x]-x+j;//对实际改值
                            if(N) {
                                A[i][j]++;
                                N=false;
                                C[4*i+j]=17;
                            }
                            x--;j--;
                        }
                        else {
                            N=true;
                            A[i][j]--;
                            C[4*i+x]=C[4*i+x]-x+j;
                            x--;
                        }
                    }
                }
                break;
            }
            case 5://上
            {
                for(int i=0;i<4;i++){
                    j=0;//实际     真矩阵
                    x=0;//最端点  假矩阵
                    N=false;
                    while(j<4&&A[j][i]!=0){
                        while(x<4&&B[x][i]==0)
                        {
                            x++;//找到非零元素
                        }
                        if(B[x][i]==A[j][i]) {
                            C[4*x+i]=C[4*x+i]-4*(x-j);//对实际改值
                            if(N) {
                                A[j][i]++;
                                N=false;
                                C[4*j+i]=17;
                            }
                            x++;j++;
                        }
                        else {
                            N=true;
                            A[j][i]--;
                            C[4*x+i]=C[4*x+i]-4*(x-j);
                            x++;
                        }
                    }
                }
                break;
            }
            case 2://下
            {
                for(int i=0;i<4;i++){
                    j=3;//实际     真矩阵
                    x=3;//最端点  假矩阵
                    N=false;
                    while(j>0&&A[j][i]!=0){
                        while(x>0&&B[x][i]==0)
                        {
                            x--;//找到非零元素
                        }
                        if(B[x][i]==A[j][i]) {
                            C[4*x+i]=C[4*x+i]-4*(x-j);//对实际改值
                            if(N) {
                                A[j][i]++;
                                N=false;
                                C[4*j+i]=17;
                            }
                            x--;j--;
                        }
                        else {
                            N=true;
                            A[j][i]--;
                            C[4*x+i]=C[4*x+i]-4*(x-j);
                            x--;
                        }
                    }
                }
                break;
            }
        }
    }//为动态准备

}

然后是数据层

package java_2048;
public class ShuJu{
    public int Score=0;//分数
    public int jishu; //个数
    public byte A[][]=new byte[4][4];//矩阵
    public byte B[][]=new byte[4][4];//假的矩阵
    public int C[]=new int[16];
    public Boolean x;
}

           由于后面为了加入动态的效果,中间加入一些其他的数据。

           有句题外话,如果一些需求在一开始没有加入。后面加入就很困难(把要加需求的打死,很烦0.0)。这话这么说也是有问题。好的代码是由很好的扩展性的,健壮性的。

          对于2048,一个逻辑游戏,如果不做美工,不做动态,实现很容易。只需要加入面板,键盘的监听。在进行每次的按键后调用repaint()来重画这个面板。

         如果要加入动态。这不得不提一个概念:帧数,FPS。也就是每一秒的画面数,然后加入定时器,将坐标位移。

        面板类的实现,有IO流,异常抛出。这里针对图片的加载。

        还有面板的一些基本操作,键盘的监听等。

package java_2048;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
//定时器
import java.util.Timer;
import java.util.TimerTask;
public class Move extends JFrame implements KeyListener{
    public Image img1;public Image img2;
    public Image img3;public Image img4;
    public Image img5;public Image img6;
    public Image img7;public Image img8;
    public Image img9;public Image img10;
    public Image img11;public Image img12;
    public Image img0;

    private SuanFa s;
    @Override
    public void keyTyped(KeyEvent e) {
        // TODO Auto-generated method stub
    }

    @Override
    public void keyPressed(KeyEvent e) {
        s.QingKongandFuZhi();
        int j=0;
        if(e.getKeyCode()==KeyEvent.VK_DOWN)
        {
            j=2;
            s.x=s.Down();
        }else if(e.getKeyCode()==KeyEvent.VK_UP)
        {
            j=5;
            s.x=s.Up();
        }else if(e.getKeyCode()==KeyEvent.VK_LEFT)
        {
            j=1;
            s.x=s.Left();
        }else if(e.getKeyCode()==KeyEvent.VK_RIGHT)
        {
            j=3;
            s.x=s.Right();
        }
        if(s.x){
            s.DuiBi(j);
            s.suiji();
            timer();
        }
        if(!s.IsJieShu()){
            dispose();//游戏结束 直接关闭窗体
        }
    }
    @Override
    public void keyReleased(KeyEvent e) {

    }
    public Move(){
        super("2048");
        s=new SuanFa();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//同关闭
        setResizable(false);//禁止放缩
        setVisible(true);//显示
        setBounds(200,100,498,700);//左上坐标和窗口大小
        try{
            img0= ImageIO.read(new File("C:\\Users\\Lenovo\\Desktop\\Code\\code\\src\\java_2048\\imgs\\0.png"));
            img1= ImageIO.read(new File("C:\\Users\\Lenovo\\Desktop\\Code\\code\\src\\java_2048\\imgs\\1.png"));
            img2= ImageIO.read(new File("C:\\Users\\Lenovo\\Desktop\\Code\\code\\src\\java_2048\\imgs\\2.png"));
            img3= ImageIO.read(new File("C:\\Users\\Lenovo\\Desktop\\Code\\code\\src\\java_2048\\imgs\\3.png"));
            img4= ImageIO.read(new File("C:\\Users\\Lenovo\\Desktop\\Code\\code\\src\\java_2048\\imgs\\4.png"));
            img5= ImageIO.read(new File("C:\\Users\\Lenovo\\Desktop\\Code\\code\\src\\java_2048\\imgs\\5.png"));
            img6= ImageIO.read(new File("C:\\Users\\Lenovo\\Desktop\\Code\\code\\src\\java_2048\\imgs\\6.png"));
            img7= ImageIO.read(new File("C:\\Users\\Lenovo\\Desktop\\Code\\code\\src\\java_2048\\imgs\\7.png"));
            img8= ImageIO.read(new File("C:\\Users\\Lenovo\\Desktop\\Code\\code\\src\\java_2048\\imgs\\8.png"));
            img9= ImageIO.read(new File("C:\\Users\\Lenovo\\Desktop\\Code\\code\\src\\java_2048\\imgs\\9.png"));
            img10= ImageIO.read(new File("C:\\Users\\Lenovo\\Desktop\\Code\\code\\src\\java_2048\\imgs\\10.png"));
            img11= ImageIO.read(new File("C:\\Users\\Lenovo\\Desktop\\Code\\code\\src\\java_2048\\imgs\\11.png"));
            img12= ImageIO.read(new File("C:\\Users\\Lenovo\\Desktop\\Code\\code\\src\\java_2048\\imgs\\12.png"));
        }
        catch (IOException e){
            e.printStackTrace();
        }
        this.addKeyListener(this);
    }
    public void paint(Graphics g){
        super.paint(g);
        g.setColor(Color.red);
        g.drawString("目标: 2048",60,60);
        g.drawString("分数: "+s.Score,60,120);
//        g.drawString("占格数: "+s.jishu,120,120);
        g.setColor(Color.black);
        g.drawLine(0,195,498,195);
        Huatu1(g);
        Huatu2(g);
        System.out.println(DS+" "+ZD);
    }
    public int ZTimes=1000;//总时间 一次的时间200+10+200ms
    public int Times=100;//每帧的时间间隔 10ms
    public int ZD=(int)(ZTimes/Times);//总段数分段
    public int DS=0;


    public void Huatu1(Graphics g){
        int a,b;
        int width=112,hight=112;
        for(int i=0;i<4;i++)
            for(int j=0;j<4;j++){
                a=122*j+10;b=122*i+10+195;
                if(s.C[4*i+j]!=(4*i+j)){
                    continue;
                }
                switch (s.B[i][j]){
                    case 0:
                    {
                        g.drawImage(img0,a,b,width,hight,null);
                        break;
                    }
                    case 1:
                    {
                        g.drawImage(img1,a,b,width,hight,null);
                        break;
                    }
                    case 2:
                    {
                        g.drawImage(img2,a,b,width,hight,null);
                        break;
                    }
                    case 3:
                    {
                        g.drawImage(img3,a,b,width,hight,null);
                        break;
                    }
                    case 4:
                    {
                        g.drawImage(img4,a,b,width,hight,null);
                        break;
                    }
                    case 5:
                    {
                        g.drawImage(img5,a,b,width,hight,null);
                        break;
                    }
                    case 6:
                    {
                        g.drawImage(img6,a,b,width,hight,null);
                        break;
                    }
                    case 7:
                    {
                        g.drawImage(img7,a,b,width,hight,null);
                        break;
                    }
                    case 8:
                    {
                        g.drawImage(img8,a,b,width,hight,null);
                        break;
                    }
                    case 9:
                    {
                        g.drawImage(img9,a,b,width,hight,null);
                        break;
                    }
                    case 10:
                    {
                        g.drawImage(img10,a,b,width,hight,null);
                        break;
                    }
                    case 11:
                    {
                        g.drawImage(img11,a,b,width,hight,null);
                        break;
                    }
                    case 12:
                    {
                        g.drawImage(img12,a,b,width,hight,null);
                        break;
                    }

                }
            }
    }//静态绘图
    public int shengcheng_wAh(){
        if(DS<ZD) return 112;
        else{
            return (DS-ZD)*(112/ZD);
        }
    }
    public int shengcheng_aAb(){
        if(DS<ZD) return 0;
        else{
            return (DS-ZD)*(56/ZD);
        }
    }
    public int yidong_x(int m,int n){//m为目的 n为当前
        if((m-n)%4==0) return 122*(n%4)+10;//纵向平移x不变
        else{
            return (122*(n%4)+10+(m-n)*122*DS/ZD);//(m-n)*122*DS/ZD
            //横向平移
        }
    }
    public int yidong_y(int m,int n){
        if((m-n)%4!=0) return 122*(n/4)+205;
        else{
            return 122*(n/4)+205+(m-n)*122*DS/(ZD*4);//(m-n)/4*122*DS/ZD
        }
    }
    public void Huatu2(Graphics g){
        int a,b;
        int width=112,hight=112;
        for(int i=0;i<4;i++)
            for(int j=0;j<4;j++){
                if(s.C[4*i+j]!=(4*i+j)){
                    if(s.C[4*i+j]==17){
                        hight=width=shengcheng_wAh();
                        a=122*j+10+56-shengcheng_aAb();
                        b=122*i+10+195+56-shengcheng_aAb();
                    }
                    else{
                        a=yidong_x(s.C[4*i+j],4*i+j);
                        b=yidong_y(s.C[4*i+j],4*i+j);
                    }
                }
                else
                    continue;
                switch (s.B[i][j]){
                    case 0:
                    {
                        g.drawImage(img0,a,b,width,hight,null);
                        break;
                    }
                    case 1:
                    {
                        g.drawImage(img1,a,b,width,hight,null);
                        break;
                    }
                    case 2:
                    {
                        g.drawImage(img2,a,b,width,hight,null);
                        break;
                    }
                    case 3:
                    {
                        g.drawImage(img3,a,b,width,hight,null);
                        break;
                    }
                    case 4:
                    {
                        g.drawImage(img4,a,b,width,hight,null);
                        break;
                    }
                    case 5:
                    {
                        g.drawImage(img5,a,b,width,hight,null);
                        break;
                    }
                    case 6:
                    {
                        g.drawImage(img6,a,b,width,hight,null);
                        break;
                    }
                    case 7:
                    {
                        g.drawImage(img7,a,b,width,hight,null);
                        break;
                    }
                    case 8:
                    {
                        g.drawImage(img8,a,b,width,hight,null);
                        break;
                    }
                    case 9:
                    {
                        g.drawImage(img9,a,b,width,hight,null);
                        break;
                    }
                    case 10:
                    {
                        g.drawImage(img10,a,b,width,hight,null);
                        break;
                    }
                    case 11:
                    {
                        g.drawImage(img11,a,b,width,hight,null);
                        break;
                    }
                    case 12:
                    {
                        g.drawImage(img12,a,b,width,hight,null);
                        break;
                    }

                }
            }
    }//动态绘图

    public void timer(){
        DS=0;
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            public void run() {
                if(DS==ZD)
                    ZhuanHua();
                repaint();
                DS++;
                if(DS==2*ZD){
//                    DS=0;
                    timer.cancel();
                }
            }
        }, 0, Times);// 这里设定将延时每天固定执行
    }//动生成的
    public void ZhuanHua(){
        for(int i=0;i<16;i++)
            if(s.C[i]!=17)
                s.C[i]=i;
        for(int i=0;i<4;i++)
            for(int j=0;j<4;j++)
                s.B[i][j]=s.A[i][j];
    }
}

写这个项目的目的是为练手,能解到面板操作,以及面向对象的一些内容。还有一些逻辑的实现。从截图到逻辑的是实现,中间也有许多困难,也有一些新的知识,查资料,调代码。过程不可谓不艰辛,收获不可谓不大。

效果图:

                     小游戏 2048的实现