2048小游戏Java实现(含图片压缩包资源,对付上级要求的一般课设)
这是我的第一篇关于java的博客,所以大多数java细节都会解释,当然大佬可以避开小菜的博客了,对新手还是友好的,来不及解释了,快上车。
不看过程的可以直接拿干净的代码和图片哦~~
package h1; //这一步要取决于你实际上在哪个包下的名字
import javax.swing.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;
import java.util.Scanner;
public class h1_2 extends JFrame implements KeyListener {
JFrame screen=this;
Scanner Scanf=new Scanner(System.in);
int InPut;
int[][] arr=new int[4][4];
Random r=new Random();
public static void main(String[] args){
h1_2 obj=new h1_2();
obj.events();
}
public void init(){
this.addKeyListener(this);
screen.setSize(890,914);
screen.setLocationRelativeTo(null);
screen.setLocation(320,90);
screen.getContentPane().setLayout(null);
screen.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
screen.setVisible(true);
for(int i=0;i<4;i++){
for (int j =0;j<4;j++){
arr[i][j]=0;}}
int i=r.nextInt(4);
int j = r.nextInt(4);
arr[i][j]=2;
PainShow();}
int[] col=new int[16];
int[] row=new int[16];
public void RandGener(){
int lenth=-1;
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
if (arr[i][j]==0){
row[++lenth]=i;
col[lenth]=j;
}
}
}
if (lenth==-1){
return;}
int i=r.nextInt(++lenth);
arr[row[i]][col[i]]=2;
}
public void Upper(){
int point=0;
for(int j=0;j<4;j++){
point=0;
for(int i=1;i<4;i++) {
if(arr[i][j]==0){
continue;
}
if(arr[point][j]==0){
arr[point][j]=arr[i][j];
arr[i][j]=0;
}
else{
if(arr[i][j]==arr[point][j]){
arr[point][j]=arr[point][j]*2;
point++;
arr[i][j]=0;
}
else{
arr[++point][j]=arr[i][j];
if (point!=i){
arr[i][j]=0;
}}}}}}
public void Doward(){
int point;
for(int j=0;j<4;j++){
point=3;
for(int i=2;i>-1;i--) {
if(arr[i][j]==0){
continue;
}
if(arr[point][j]==0){
arr[point][j]=arr[i][j];
arr[i][j]=0;
}
else{
if(arr[i][j]==arr[point][j]){
arr[point][j]=arr[point][j]*2;
point--;
arr[i][j]=0;
}
else{
arr[--point][j]=arr[i][j];
if (point!=i){
arr[i][j]=0;
}}}}}}
public void Left(){
int point;
for(int i=0;i<4;i++){
point =0;
for(int j=1;j<4;j++){
if(arr[i][j]==0){
continue;
}
if(arr[i][point]==0){
arr[i][point]=arr[i][j];
arr[i][j]=0;
}
else{
if(arr[i][j]==arr[i][point]){
arr[i][point]=arr[i][point]*2;
point++;
arr[i][j]=0;
}
else{
arr[i][++point]=arr[i][j];
if(point!=j){
arr[i][j]=0;
}
}
}
}
}
}
public void Right(){
int point;
for(int i=0;i<4;i++){
point =3;
for(int j=2;j>-1;j--){
if(arr[i][j]==0){
continue;
}
if(arr[i][point]==0){
arr[i][point]=arr[i][j];
arr[i][j]=0;
}
else{
if(arr[i][j]==arr[i][point]){
arr[i][point]=arr[i][point]*2;
point--;
arr[i][j]=0;
}
else{
arr[i][--point]=arr[i][j];
if(point!=j){
arr[i][j]=0;
}
}
}
}
}
}
public void OutPut(){
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
System.out.print(arr[i][j]+" ");
}
System.out.println();
}
System.out.println();
}
public void events(){
init();
OutPut();
}
public void PainShow(){
screen.getContentPane().removeAll();
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
if(arr[i][j]!=0){
JLabel img=new JLabel(new ImageIcon(arr[i][j]+".png"));
System.out.println(arr[i][j]+".png");
img.setBounds(56+j*200,61+i*200,160,160);
screen.getContentPane().add(img);
}
}
}
JLabel back=new JLabel(new ImageIcon("棋盘.png"));
back.setBounds(0,0,890,886);
screen.getContentPane().add(back);
repaint();
}
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyPressed(KeyEvent e) {
System.out.println(e.getKeyCode());
if(e.getKeyChar()=='w'||e.getKeyCode()==38){
Upper();
RandGener();
PainShow();
OutPut();
}
if(e.getKeyChar()=='a'||e.getKeyCode()==37){
Left();
RandGener();
PainShow();
OutPut();
}
if(e.getKeyChar()=='d'||e.getKeyCode()==39){
Right();
RandGener();
PainShow();
OutPut();
}
if(e.getKeyChar()=='s'||e.getKeyCode()==40){
Doward();
RandGener();
PainShow();
OutPut();
}
}
@Override
public void keyReleased(KeyEvent e) {
}
}
代码开头
package h1; //package后面跟什么取决于当前在什么包下,我这里包叫h1
import javax.swing.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;
import java.util.Scanner;
public class h1_2 extends JFrame implements KeyListener { //继承JFrame同时调用KeyListener接口,使该对象具有窗口能力和键盘输入监听能力
// int isFail=1; //初始状态默认为输
// public h1_2(){super();};
// public h1_2(String e){super(e);};
JFrame screen=this; //可以不用这样多次一举,后面的screen其实都可以省略,因为在下面的所有的screen.方法和变量都是这个this环境下的,可以省略变量screen不写
Scanner Scanf=new Scanner(System.in); //System.in为控制台的输入流,代表创建一个从控制台读取的读取器对象,后来会学到其他流,比如说InputStream(输入字节流),InputStreamReader(输入字符流).....
int InPut; //成员变量,记录每一次你所按下的键是什么,然后再执行对应的操作
int[][] arr=new int[4][4]; //创建2048棋盘的布局为4*4方格
Random r=new Random(); //创建Random对象,为了产生随机数,来随机确定下一个2产生的位置
public static void main(String[] args){
h1_2 obj=new h1_2();
obj.events(); //所有的执行操作打包到了events函数里了
}
棋盘初始化
public void init(){ //初始化局内数组变量,以及gui
this.addKeyListener(this);
screen.setSize(890,914); //创建窗口大小,第一个是宽,第二个是高 上面说过,如果没有定义screen在这也是可以省略screen.的。
screen.setLocationRelativeTo(null); // null为空值,在该方法里取消窗口居中显示的默认布局
screen.setLocation(320,90); //既然上面取消了1默认布局,就要创建默认布局,第一个参数代表距离屏幕左侧的距离(单位:像素点),第二个参数代表距离屏幕上侧的距离(单位:像素点)
screen.getContentPane().setLayout(null); //这段代码的作用是将JFrame的内容面板的布局管理器设置为空(null),即在内容面板上添加的组件将具有绝对定位和尺寸。通常不建议使用此方法,因为在调整应用程序的大小或缩放时,它可能会导致不可预测的行为。建议使用布局管理器来处理组件的位置和大小。
screen.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //设置当屏幕关闭时,不执行其他操作
screen.setVisible(true); //设置屏幕是可见的
for(int i=0;i<4;i++){ //对棋盘初始化,把他们的值都变成0
for (int j =0;j<4;j++){
arr[i][j]=0; //由于该方法里也没有放置与this.arr重名的变量,所以默认调用this环境变量下的成员arr,但是如果该方法函数里创建了arr变量,就会默认调用新建的arr,如果想再次调用成员变量arr,就需要this.arr来调用
}
}
//为了定位行和列来随机生成第一个2
int i=r.nextInt(4); //r为创建的成员变量Random随机数生成器,i为随机定位的行
int j = r.nextInt(4); //j为随机生成的列
arr[i][j]=2;
PainShow(); //该方法为下面定义的方法,其作用是把当前棋盘状态更新到屏幕
}
//每当移动一次,创建下一个随机的2的生成位置
int[] col=new int[16]; //记录空闲位置的横坐标
int[] row=new int[16]; //记录空闲坐标的列位置
随机位置产生新值方法
public void RandGener(){ //每移动一次,就生成一个随机位置的2
int lenth=-1;
for(int i=0;i<4;i++){ //以下方法是判断哪些位置为0,因为只有0的位置为空闲位置,可以生成2 没什么特别讲的,看看吧
for(int j=0;j<4;j++){
if (arr[i][j]==0){
row[++lenth]=i;
col[lenth]=j;
}
}
}
if (lenth==-1){
return;
}
int i=r.nextInt(++lenth);
arr[row[i]][col[i]]=2;
}
向上,下,左,右方向移动代码方法实现
//向上移动,改变棋盘布局 这一部分是我自己想的方法,可以参考一下思路,,虽然挺菜的吧,不过感觉普通的两次偏移数字,还是比较优雅的了
//以下所有i,j分别都按着一个标准,i代表行,j代表列
public void Upper(){ //向上移动,肯定是一列一列的遍历,所以外层遍历变量是列,内层是行
int point=0;
for(int j=0;j<4;j++){
point=0; //point代表偏移地址,因为是列合并,所以这里的遍历的行,point代表第二个偏移地址i
//以下代码有个细节就是,i是从1开始的,不是从0开始的,看完以下代码,过来看看为什么~~~
for(int i=1;i<4;i++) { //好吧这里,我真的不想讲了,哈哈~~讲一下基本思路,一定看看这个,再看下面吧 2222-->4022-->4202-->4400,这个展示的是横向左合并,以下代码就是这么实现的
if(arr[i][j]==0){ //归为两大类,下一个遍历数是0和不是0的,是0的就可以跳过了
continue;
} //接下来不是0的情况,又分为两类
if(arr[point][j]==0){ //point停留的地址为0的话,则执行了4022-->4202 而point指向的地址就是第二个0,所以把2替换过来
arr[point][j]=arr[i][j];
arr[i][j]=0;
}
else{ //如果point停留的地方不为0,又分为两类,好吧,我承认我有套娃的嫌疑,蛤蛤蛤
if(arr[i][j]==arr[point][j]){ //point指向的地方和当前的数值相同,就合并,执行了2222--->4022或者是4202--->4400
arr[point][j]=arr[point][j]*2;
point++;
arr[i][j]=0;
}
else{ //point指向的地方和当前的数值不相同,此时point指向的是4,当前遍历的是第三个数值2,就执行4022--->4202。
arr[++point][j]=arr[i][j];
if (point!=i){ //为什么要有这一步,大佬,请您也想一想,好吧~~
arr[i][j]=0;
}
}
}
}
}
}
//向下移动 思路和Upper()函数思路大同小异
public void Doward(){
int point;
for(int j=0;j<4;j++){
point=3; //point代表偏移地址,代表第二个偏移地址i
for(int i=2;i>-1;i--) {
if(arr[i][j]==0){
continue;
}
if(arr[point][j]==0){
arr[point][j]=arr[i][j];
arr[i][j]=0;
}
else{
if(arr[i][j]==arr[point][j]){
arr[point][j]=arr[point][j]*2;
point--;
arr[i][j]=0;
}
else{
arr[--point][j]=arr[i][j];
if (point!=i){
arr[i][j]=0;
}
}
}
}
}
}
//向左偏移 思路和Upper()函数思路大同小异
public void Left(){
int point; //偏移地址为i
for(int i=0;i<4;i++){
point =0;
for(int j=1;j<4;j++){
if(arr[i][j]==0){
continue;
}
if(arr[i][point]==0){
arr[i][point]=arr[i][j];
arr[i][j]=0;
}
else{
if(arr[i][j]==arr[i][point]){
arr[i][point]=arr[i][point]*2;
point++;
arr[i][j]=0;
}
else{
arr[i][++point]=arr[i][j];
if(point!=j){
arr[i][j]=0;
}
}
}
}
}
}
//向右偏移 思路和Upper()函数思路大同小异
public void Right(){
int point; //偏移地址为i
for(int i=0;i<4;i++){
point =3;
for(int j=2;j>-1;j--){ //从右向左遍历
if(arr[i][j]==0){ //当前为0跳过
continue;
} //当前都不为0的遍历
if(arr[i][point]==0){ //point为0则替换
arr[i][point]=arr[i][j];
arr[i][j]=0;
}
else{
if(arr[i][j]==arr[i][point]){
arr[i][point]=arr[i][point]*2;
point--;
arr[i][j]=0;
}
else{
arr[i][--point]=arr[i][j];
if(point!=j){
arr[i][j]=0;
}
}
}
}
}
}
public void OutPut(){ //这个函数纯属在调试台测试结果的正确性
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
System.out.print(arr[i][j]+" ");
}
System.out.println();
}
System.out.println();
}
public void events(){ //运行的总函数
init(); //初始化棋盘
OutPut(); //输出棋盘列表,可以忽略这一布
}
把棋盘状态映射到真实棋盘上
public void PainShow(){ //一个一个把对应的棋映射到棋盘上面
screen.getContentPane().removeAll(); //把原来的棋移除,重新更新棋盘
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
if(arr[i][j]!=0){
JLabel img=new JLabel(new ImageIcon(arr[i][j]+".png"));
System.out.println(arr[i][j]+".png");
img.setBounds(56+j*200,61+i*200,160,160);
screen.getContentPane().add(img);
}
}
}
JLabel back=new JLabel(new ImageIcon("棋盘.png"));
back.setBounds(0,0,890,886);
screen.getContentPane().add(back);
repaint();
}
@Override
public void keyTyped(KeyEvent e) {
}
事件监听器重构方法
@Override
public void keyPressed(KeyEvent e) { //调用KeyListener重写监听器的方法
System.out.println(e.getKeyCode());
if(e.getKeyChar()=='w'||e.getKeyCode()==38){ //按下w或上键执行 上键的代码为38 下面同理
Upper();
RandGener();
PainShow();
OutPut();
}
if(e.getKeyChar()=='a'||e.getKeyCode()==37){
Left();
RandGener();
PainShow();
OutPut();
}
if(e.getKeyChar()=='d'||e.getKeyCode()==39){
Right();
RandGener();
PainShow();
OutPut();
}
if(e.getKeyChar()=='s'||e.getKeyCode()==40){
Doward();
RandGener();
PainShow();
OutPut();
}
}
@Override
public void keyReleased(KeyEvent e) { //上面定义的是键盘按下的反应,也可以把keyPressed的方法放置到这里,代表键盘按下,松开后执行
}
}
路漫漫兮其修远兮 吾将上下而求索
在此,我想真诚地表示,虽然我对Java有一定的掌握和了解,但仍然有很多需要学习和探索的地方。 Java是一个复杂而有趣的编程语言,我感到非常荣幸能够在其中学习和成长。我希望能够和其他对Java或python以及虚拟机感兴趣的人共同交流,同时也期待着在未来的学习中不断地拓展自己的知识面。 收尾~