小游戏 2048的实现
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];
}
}
写这个项目的目的是为练手,能解到面板操作,以及面向对象的一些内容。还有一些逻辑的实现。从截图到逻辑的是实现,中间也有许多困难,也有一些新的知识,查资料,调代码。过程不可谓不艰辛,收获不可谓不大。
效果图:
上一篇: 跃锦成龙之表白流星