贪吃蛇和俄罗斯方块

贪吃蛇

一、创建新项目

创建一个新的项目,并命名。

创建一个名为images的文件夹用来存放游戏相关图片。

然后再在项目的src文件下创建一个com.xxx.view的包用来存放所有的图形界面类,

创建一个com.xxx.controller的包用来存放启动的入口类(控制类)

二、游戏界面 

package com.snake.view;
 
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
 
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.border.EmptyBorder;
 
 
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
 
public class SnakeJPanel extends JPanel implements ActionListener{
	
	private boolean start;//当前游戏状态
	
	private int speed;//速度
	
    private boolean exist;//当前是否存在食物
    
    private int foodType;//食物种类
    
    private int x;//豆子的横坐标
    private int y;//豆子的纵坐标
	
	private ArrayList<int[]> localList;//蛇
	
	
	public String direction;//方向
	
	private String direction2;//引导方向
	
	public boolean flag;
	
	Random rand = new Random();
 
	private ImageIcon up;
 
	private ImageIcon down;
 
	private ImageIcon right;
 
	private ImageIcon left;
 
	private ImageIcon body;
 
	private ImageIcon food;
 
	private ImageIcon title;
	
	Timer time;
	
	private int score;//当前得分情况
	
	private int num;//吃到的食物个数
	
//    private Image offScreenImage;  //图形缓存
	
	
	//图片绘制
	@Override
	public void paint(Graphics g) {
		
		direction = direction2;
		
		g.setColor(Color.WHITE);
		g.fillRect(0, 0, 900, 700);
		
		//绘制游戏框
		//标题框
//		g.drawRect(25, 30, 800, 75);
		title.paintIcon(this, g, 25, 10);
		
		//内容框
		g.setColor(Color.black);
		g.fillRect(25, 75, 850, 600);
		
		//绘制食物的坐标位置
		if(!exist) {//如果当前不存在豆子,随机绘制一个豆子	
			if(num % 5 == 0) {
				foodType = 1;
			}else {
				foodType = 0;
			}
			boolean isProduce = true;
			while(isProduce) {
				isProduce = false;
				x = rand.nextInt(33) * 25 + 25;		
				y = rand.nextInt(23) * 25 + 75;			
				for (int[] arr:localList) {
					if(x == arr[0] && y == arr[1]) {	
						isProduce = true;
						break;	
					}
				}
				
			}			
			System.out.println(x + "---" + y);
		}
		
		 if(eat()) {
	        exist = false;
		 }else {
			exist = true;
		 }
		 
		 
		if(foodType == 0) {
			//绘制食物
			g.setColor(Color.blue);
//			g.fillRect(x, y, 25, 25);
			g.drawImage(food.getImage(),x, y, 25, 25,null);
		}else {
			//绘制食物
			g.setColor(Color.WHITE);
			g.fillRect(x, y, 25, 25);
//			g.drawImage(food.getImage(),x, y, 25, 25,null);
		}
	
			
		//绘制头
		g.setColor(Color.red);
//		g.fillRect(localList.get(0)[0], localList.get(0)[1], 25, 25);	
		ImageIcon head = null;
		//判断当前方向
		if(direction.equals("R")) {
			 head = right;
		}else if(direction.equals("L")) {
			 head = left;
		}else if(direction.equals("U")) {
			 head = up;
		}else if(direction.equals("D")) {
			 head = down;
		}		
//		g.drawImage(head.getImage(), localList.get(0)[0], localList.get(0)[1], 25, 25,null);
		head.paintIcon(this, g,localList.get(0)[0], localList.get(0)[1]);
		
		//绘制身体
		g.setColor(Color.white);
		for (int i = 1; i < localList.size(); i++) {
//			g.fillRect(localList.get(i)[0], localList.get(i)[1], 25, 25);
//			g.drawImage(body.getImage(), localList.get(i)[0], localList.get(i)[1], 25, 25,null);
			body.paintIcon(this, g, localList.get(i)[0], localList.get(i)[1]);
		}
//		g.fillRect(localList.get(1)[0], localList.get(1)[1], 25, 25);
//		g.fillRect(localList.get(2)[0], localList.get(2)[1], 25, 25);
			
		
		//绘制分数和长度
		//长度
		g.setColor(Color.GREEN);
		g.setFont(new Font("宋体", Font.BOLD, 18));
		g.drawString("长度:" + (localList.size() - 1), 25, 30);
		
		//分数
		g.drawString("分数:" + score, 25, 48);
		
		if(!start) {//如果游戏未启动,结束移动和重绘
			g.setColor(Color.white);
			g.setFont(new Font("宋体", Font.BOLD, 30));
			g.drawString("暂停/开始(请按任意键开始,空格键暂停)", 150, 300);
			time.stop();
			
		}else {
			time.start();
		}
			
//		speed();
		//移动后进行下一次绘制		
//      move();//移动
//		repaint();//重新绘制		
	}
	
//	//解决闪烁问题
//	//如果为JFrame 为重量级  程序不会调用update()方法
//	//如果为Frame 为轻量级  重写update()方法 做双缓冲
//	//如果为JPanel 不会闪烁
//	  @Override
//	    public void update(Graphics g)
//	    {
//	    	System.out.println("update");
//	           if(offScreenImage == null)
//	              offScreenImage = this.createImage(900, 700);  //新建一个图像缓存空间,这里图像大小为800*600
//	              Graphics gImage = offScreenImage.getGraphics();  //把它的画笔拿过来,给gImage保存着
//	              paint(gImage);                                   //将要画的东西画到图像缓存空间去
//	              g.drawImage(offScreenImage, 0, 0, null);         //然后一次性显示出来
//	    }
	
 
	@Override
	public void actionPerformed(ActionEvent e) {	    
	    //移动后进行下一次绘制		
        move();//移动
	    repaint();//重新绘制		
		
	}
 
	/**
	 * 绘制速度
	 */
//	private void speed() {
//		try {//按一定速度进行移动
//			Thread.sleep(speed);//控制移动速度
//		} catch (InterruptedException e) {
//			// TODO 自动生成的 catch 块
//			e.printStackTrace();
//		}
//	}
 
	/**
	 * 初始化图片
	 */
	private void drawImage() {
		up = new ImageIcon("images/up.png");
		down = new ImageIcon("images/down.png");
		right = new ImageIcon("images/right.png");
		left = new ImageIcon("images/left.png");
		body = new ImageIcon("images/body.png");
		food = new ImageIcon("images/food.png");
		title = new ImageIcon("images/title.jpg");
	}
	
	private boolean eat() {
		if(localList.get(0)[0] == x && localList.get(0)[1] == y) {//如果当前蛇头吃到了豆子
			System.out.println("eat");
			num++;
			if(foodType == 0) {
				score += 10;
			}else {
				score += (rand.nextInt(5) * 10 + 10);
			}
			int last = localList.size() - 1;//蛇尾			
			//在蛇尾后面添加一节身体
			localList.add(new int[] {localList.get(last)[0],localList.get(last)[1]});
			return true;
		}
		return false;
	}
 
	//移动方法
	public void move() {
		//判断是否游戏结束
		if(isbody()) {
			System.out.println("game over");
			start = false;//结束游戏移动
			JOptionPane.showMessageDialog(null,"游戏已结束!");
			time.stop();
			init();		
		}
			
		if(flag && localList != null) {//如果长度不为空且游戏未结束				
			int last = localList.size() - 1;//记录蛇尾
			
			for (int i = last; i > 0; i--) {//从蛇尾开始,每节身体移动到前一节身体的位置上
				localList.set(i,new int[] {localList.get(i - 1)[0],localList.get(i - 1)[1]});
			}
			
			//记录头位置
			int[] local = localList.get(0);
			//判断当前方向,并进行模拟移动,判断是否与边界重合
			if(direction.equals("R")) {
				if(local[0] >= 850) {
					local[0] = 25;
				}else {
					local[0] += 25;
				}
				
			}else if(direction.equals("L")) {
				if(local[0] <= 25) {
					local[0] = 850;
				}else {
					local[0] -= 25;
				}
				
			}else if(direction.equals("U")) {
				
				if(local[1] <= 75) {
					local[1] = 650;
				}else {
					local[1] -= 25;
				}
				
			}else if(direction.equals("D")) {
				if(local[1] >= 650) {
					local[1] = 75;
				}else {
					local[1] += 25;
				}
				
			}			
						
			//更改头的位置
			localList.set(0, local);		
		}	
	}
	
	//判断下一步是否为蛇身
	private boolean isbody() {
		// TODO 自动生成的方法存根
		//记录头位置
		int x = localList.get(0)[0];
		int y = localList.get(0)[1];
 
		//判断当前方向,并进行模拟移动,判断是否与边界重合
		if(direction.equals("R")) {
			x += 25;
		}else if(direction.equals("L")) {
			x -= 25;
		}else if(direction.equals("U")) {
			y -= 25;
		}else if(direction.equals("D")) {
			y += 25;
		}			
		
		for (int i = 1; i < localList.size(); i++) {
			if(localList.get(i)[0] == x && localList.get(i)[1] == y) {
				return true;
			}
		}
		return false;
 
	}
	
//	//判断下一步是否为边界
//	private boolean isborder() {
//		// TODO 自动生成的方法存根
//		//记录头位置
//		// TODO 自动生成的方法存根
//		//记录头位置
//		int x = localList.get(0)[0];
//		int y = localList.get(0)[1];
//
//		//判断当前方向,并进行模拟移动,判断是否与边界重合
//		if(direction.equals("R")) {
//			x += 25;
//		}else if(direction.equals("L")) {
//			x -= 25;
//		}else if(direction.equals("U")) {
//			y -= 25;
//		}else if(direction.equals("D")) {
//			y += 25;
//		}	
//				
//		if(x < 25 || x > (33 * 25 + 25)) {
//			return true;//当x坐标超出边界,则返回true
//		}
//		if(y < 105 || y > (23 * 25 + 105)) {
//			return true;//当y坐标超出边界,则返回true
//		}
//		return false;//蛇头移动后未超出边界,返回false
//		
//	}
 
	/**
	 * Create the frame.
	 */
	public SnakeJPanel(int speed) {
		
		this.speed = speed; //初始化速度
		
		//初始化游戏面板的基本信息
		this.setSize(900, 700);
		this.setLocation(0, 30);
		this.setFocusable(true);
		
		init();//初始化界面
		drawImage();//绘制图片
		moveByKey();//给界面添加一个键盘监听
				
	}
 
	/*
	 * 键盘监听
	 * 通过键盘输入上下左右来控制当前蛇头移动的方向
	 * 先判断当前蛇头方向,再来改变引导方向
	 * 当进行绘制时再修改蛇的方向
	 * 保证不会因为在短时间内快速变换方向导致蛇头逆向转向
	 */
	private void moveByKey() {
		addKeyListener(new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent e) {
				int key = e.getKeyCode();
				//边界值判断
				switch(key) {
				case 65:
				case 37:{//向左走
					if(!direction.equals("R")) {
						direction2 = "L";
						
					}
					break;
				}				
				case 87:
				case 38:{//向上走
					if(!direction.equals("D")) {
						direction2 = "U";
					}				
					break;
				}				
				case 68:
				case 39:{//向右走
					if(!direction.equals("L")) {
						direction2 = "R";
					}
					break;
				}
				case 83:
				case 40:{//向下走
					if(!direction.equals("U")) {
						direction2 = "D";
					}					
					break;
				}
				case KeyEvent.VK_SPACE:{//如果当前键盘输入为空格
					start = !start;//调整游戏状态
					System.out.println("暂停/开始");
					repaint();//重绘
				}
				}
				
				//任意键开始
				if(!start && key != KeyEvent.VK_SPACE) {//如果当前状态为暂停状态,且键盘输入不是空格
					start = true;
					repaint();//重绘
					
				}				
			}
		});
	}
 
	/**
	 * 初始化游戏基本信息
	 */
	private void init() {
		start = false;
 
		exist = true;
 
		direction2 = "U";
 
		flag = true;
 
		localList = new ArrayList<int[]>();
 
		localList.add(0,new int[] {75,125});//蛇头
		localList.add(1,new int[] {75,150});//蛇身1
		localList.add(2,new int[] {75,175});//蛇身2
 
		//创建第一个食物的位置
		//通过循环保证当前生成的食物不在身体所在的坐标上
		boolean isProduce = true;
		while(isProduce) {//循环生成食物坐标
			isProduce = false;//结束本次循环
			x = rand.nextInt(33) * 25 + 25;		
			y = rand.nextInt(23) * 25 + 75;			
			for (int[] arr:localList) {//循环遍历蛇头及蛇身的坐标
				if(x == arr[0] && y == arr[1]) {//如果食物坐标和蛇的某一节坐标重合
					isProduce = true;//跳转循环状态,继续下一次食物生成
					break;	
				}
			}
			//蛇身遍历完成,没有重合坐标,结束食物坐标生成
							
		}
 
		time = new Timer(speed, this);
		setLayout(null);
 
		score = 0;
 
		num = 0;
		
		foodType = 0;
		
//		repaint();
		
	}
 
 
}

三、构造启动类

package com.snake.controller;
 
import javax.swing.JFrame;
import javax.swing.JOptionPane;
 
import com.snake.view.SnakeJPanel;
 
public class SnakeStart {
	public static void main(String[] args) {
		
		int speed = 0;
		String showInputDialog = null;//初始化时间
		//得到速度
		while(true) {
			showInputDialog = JOptionPane.showInputDialog("蛇移动速度(1 - 5)","3");
			
			if(showInputDialog == null) {
				showInputDialog = "3";//默认速度
				break;
			}
			if(showInputDialog.length() > 1) {
				continue;
			}
			char[] a = showInputDialog.toCharArray();
			if(a[0] >= '1' && a[0] <= '5') {
				break;
			}
		}
			
		speed = Integer.parseInt(showInputDialog) * 50;
		
		
		SnakeJPanel snakeJPanel = new SnakeJPanel(speed);
		
		//创建一个JFrame窗口,将游戏面板添加进行窗口中
		JFrame jFrame = new JFrame();
		//设置窗口的某些属性
		jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		jFrame.setSize(920, 750);
	    jFrame.add(snakeJPanel);
	    jFrame.setLocationRelativeTo(null);
	    jFrame.setVisible(true);
	}
 
}

四、游戏启动

设置游戏速度

游戏界面

 俄罗斯方块

游戏规则:

由小方块组成的不同形状的板块陆续从屏幕上方落下来,玩家通过调整板块的位置和方向,使它们在屏幕底部拼出完整的一条或几条。这些完整的横条会随即消失,给新落下来的板块腾出空间,与此同时,玩家得到分数奖励。没有被消除掉的方块不断堆积起来,一旦堆到屏幕顶端,玩家便告输,游戏结束。

整体代码分为三个模块:方格模块,七种图形模块,俄罗斯方块主模块。

小方块类:Cell

package com.zhao.demo.block;

import java.awt.image.BufferedImage;
import java.util.Objects;

/**
 * @author xiaoZhao
 * @date 2022/5/7
 * @describe
 *  小方块类
 *   方法: 左移、右移、下落
 */
public class Cell {
    // 行
    private int row;
    // 列
    private int col;
    private BufferedImage image;

    public Cell() {
    }

    public Cell(int row, int col, BufferedImage image) {
        this.row = row;
        this.col = col;
        this.image = image;
    }

    public int getRow() {
        return row;
    }

    public void setRow(int row) {
        this.row = row;
    }

    public int getCol() {
        return col;
    }

    public void setCol(int col) {
        this.col = col;
    }

    public BufferedImage getImage() {
        return image;
    }

    public void setImage(BufferedImage image) {
        this.image = image;
    }

    @Override
    public String toString() {
        return "Cell{" +
                "row=" + row +
                ", col=" + col +
                ", image=" + image +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }

        if (!(o instanceof Cell)) {
            return false;
        }
        Cell cell = (Cell) o;
        return getRow() == cell.getRow() &&
                getCol() == cell.getCol() &&
                Objects.equals(getImage(), cell.getImage());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getRow(), getCol(), getImage());
    }

    //左移动一格
    public void left(){
        col--;
    }

    //右移动一格
    public void right(){
        col++;
    }

    //下移动一格
    public void down(){
        row++;
    }
}

 四方格图形的父类:Tetromino

package com.zhao.demo.block;

import com.zhao.demo.shape.*;

/**
 * @author xiaoZhao
 * @date 2022/5/11
 * @describe 编写四方格父类
 */
public class Tetromino {

    public Cell[] cells = new Cell[4];

    //旋转的状态
    protected State[] states;
    //声明旋转次数
    protected int count = 10000;


    //左移方法
    public void moveLeft() {
        for (Cell cell : cells) {
            cell.left();
        }
    }

    //右移方法
    public void moveRight() {
        for (Cell cell : cells) {
            cell.right();
        }
    }

    //单元格下落
    public void moveDrop() {
        for (Cell cell : cells) {
            cell.down();
        }
    }

    //编写随机生成四方格
    public static Tetromino randomOne() {
        int num = (int) (Math.random() * 7);
        Tetromino tetromino = null;
        switch (num) {
            case 0:
                tetromino = new I();
                break;
            case 1:
                tetromino = new J();
                break;
            case 2:
                tetromino = new L();
                break;
            case 3:
                tetromino = new O();
                break;
            case 4:
                tetromino = new S();
                break;
            case 5:
                tetromino = new T();
                break;
            case 6:
                tetromino = new Z();
                break;
        }

        return tetromino;
    }

    //顺时针旋转的方法
    public void rotateRight() {
        if (states.length == 0) {
            return;
        }

        //旋转次数+1
        count++;
        State s = states[count % states.length];
        Cell cell = cells[0];
        int row = cell.getRow();
        int col = cell.getCol();
        cells[1].setRow(row + s.row1);
        cells[1].setCol(col + s.col1);
        cells[2].setRow(row + s.row2);
        cells[2].setCol(col + s.col2);
        cells[3].setRow(row + s.row3);
        cells[3].setCol(col + s.col3);
    }

    //逆时针旋转的方法
    public void rotateLeft() {
        if (states.length == 0) {
            return;
        }

        //旋转次数+1
        count--;
        State s = states[count % states.length];
        Cell cell = cells[0];
        int row = cell.getRow();
        int col = cell.getCol();
        cells[1].setRow(row + s.row1);
        cells[1].setCol(col + s.col1);
        cells[2].setRow(row + s.row2);
        cells[2].setCol(col + s.col2);
        cells[3].setRow(row + s.row3);
        cells[3].setCol(col + s.col3);
    }

    //四方格旋转状态的内部类
    protected class State {
        //存储四方格各元素的位置
        int row0, col0, row1, col1, row2, col2, row3, col3;

        public State() {
        }

        public State(int row0, int col0, int row1, int col1, int row2, int col2, int row3, int col3) {
            this.row0 = row0;
            this.col0 = col0;
            this.row1 = row1;
            this.col1 = col1;
            this.row2 = row2;
            this.col2 = col2;
            this.row3 = row3;
            this.col3 = col3;
        }

        public int getRow0() {
            return row0;
        }

        public void setRow0(int row0) {
            this.row0 = row0;
        }

        public int getCol0() {
            return col0;
        }

        public void setCol0(int col0) {
            this.col0 = col0;
        }

        public int getRow1() {
            return row1;
        }

        public void setRow1(int row1) {
            this.row1 = row1;
        }

        public int getCol1() {
            return col1;
        }

        public void setCol1(int col1) {
            this.col1 = col1;
        }

        public int getRow2() {
            return row2;
        }

        public void setRow2(int row2) {
            this.row2 = row2;
        }

        public int getCol2() {
            return col2;
        }

        public void setCol2(int col2) {
            this.col2 = col2;
        }

        public int getRow3() {
            return row3;
        }

        public void setRow3(int row3) {
            this.row3 = row3;
        }

        public int getCol3() {
            return col3;
        }

        public void setCol3(int col3) {
            this.col3 = col3;
        }

        @Override
        public String toString() {
            return "State{" +
                    "row0=" + row0 +
                    ", col0=" + col0 +
                    ", row1=" + row1 +
                    ", col1=" + col1 +
                    ", row2=" + row2 +
                    ", col2=" + col2 +
                    ", row3=" + row3 +
                    ", col3=" + col3 +
                    '}';
        }
    }
}

 

七种图形类:I、J、L、O、S、T、Z

 I

package com.zhao.demo.shape;

import com.zhao.demo.App.Tetris;
import com.zhao.demo.block.Cell;
import com.zhao.demo.block.Tetromino;

/**
 * @author xiaoZhao
 * @date 2022/5/11
 * @describe
 */
public class I extends Tetromino {

    public I() {
        cells[0] = new Cell(0,4, Tetris.I);
        cells[1] = new Cell(0,3, Tetris.I);
        cells[2] = new Cell(0,5, Tetris.I);
        cells[3] = new Cell(0,6, Tetris.I);

        //共有两种旋转状态
        states =new State[2];
        //初始化两种状态的相对坐标
        states[0]=new State(0,0,0,-1,0,1,0,2);
        states[1]=new State(0,0,-1,0,1,0,2,0);
    }

}

J

package com.zhao.demo.shape;

import com.zhao.demo.App.Tetris;
import com.zhao.demo.block.Cell;
import com.zhao.demo.block.Tetromino;

/**
 * @author xiaoZhao
 * @date 2022/5/11
 * @describe
 */
public class J extends Tetromino {
    public J() {
        cells[0] = new Cell(0,4, Tetris.J);
        cells[1] = new Cell(0,3, Tetris.J);
        cells[2] = new Cell(0,5, Tetris.J);
        cells[3] = new Cell(1,5, Tetris.J);

        states=new State[4];
        states[0]=new State(0,0,0,-1,0,1,1,1);
        states[1]=new State(0,0,-1,0,1,0,1,-1);
        states[2]=new State(0,0,0,1,0,-1,-1,-1);
        states[3]=new State(0,0,1,0,-1,0,-1,1);
    }
}

 L

package com.zhao.demo.shape;

import com.zhao.demo.App.Tetris;
import com.zhao.demo.block.Cell;
import com.zhao.demo.block.Tetromino;

/**
 * @author xiaoZhao
 * @date 2022/5/11
 * @describe
 */
public class L extends Tetromino {
    public L() {
        cells[0] = new Cell(0,4, Tetris.L);
        cells[1] = new Cell(0,3, Tetris.L);
        cells[2] = new Cell(0,5, Tetris.L);
        cells[3] = new Cell(1,3, Tetris.L);

        states=new State[4];
        states[0]=new State(0,0,0,-1,0,1,1,-1);
        states[1]=new State(0,0,-1,0,1,0,-1,-1);
        states[2]=new State(0,0,0,1,0,-1,-1,1);
        states[3]=new State(0,0,1,0,-1,0,1,1);
    }
}

O

package com.zhao.demo.shape;

import com.zhao.demo.App.Tetris;
import com.zhao.demo.block.Cell;
import com.zhao.demo.block.Tetromino;

/**
 * @author xiaoZhao
 * @date 2022/5/11
 * @describe
 */
public class O extends Tetromino {
    public O() {
        cells[0] = new Cell(0, 4, Tetris.O);
        cells[1] = new Cell(0, 5, Tetris.O);
        cells[2] = new Cell(1, 4, Tetris.O);
        cells[3] = new Cell(1, 5, Tetris.O);

        //无旋转状态
        states = new State[0];
    }
}

package com.zhao.demo.shape;

import com.zhao.demo.App.Tetris;
import com.zhao.demo.block.Cell;
import com.zhao.demo.block.Tetromino;

/**
 * @author xiaoZhao
 * @date 2022/5/11
 * @describe
 */
public class S extends Tetromino {
    public S() {
        cells[0] = new Cell(0,4, Tetris.S);
        cells[1] = new Cell(0,5, Tetris.S);
        cells[2] = new Cell(1,3, Tetris.S);
        cells[3] = new Cell(1,4, Tetris.S);

        //共有两种旋转状态
        states =new State[2];
        //初始化两种状态的相对坐标
        states[0]=new State(0,0,0,1,1,-1,1,0);
        states[1]=new State(0,0,1,0,-1,-1,0,-1);
    }
}

package com.zhao.demo.shape;

import com.zhao.demo.App.Tetris;
import com.zhao.demo.block.Cell;
import com.zhao.demo.block.Tetromino;

/**
 * @author xiaoZhao
 * @date 2022/5/11
 * @describe
 */
public class T extends Tetromino {
    public T() {
        cells[0] = new Cell(0,4, Tetris.T);
        cells[1] = new Cell(0,3, Tetris.T);
        cells[2] = new Cell(0,5, Tetris.T);
        cells[3] = new Cell(1,4, Tetris.T);

        states=new State[4];
        states[0]=new State(0,0,0,-1,0,1,1,0);
        states[1]=new State(0,0,-1,0,1,0,0,-1);
        states[2]=new State(0,0,0,1,0,-1,-1,0);
        states[3]=new State(0,0,1,0,-1,0,0,1);
    }
}

package com.zhao.demo.shape;

import com.zhao.demo.App.Tetris;
import com.zhao.demo.block.Cell;
import com.zhao.demo.block.Tetromino;

/**
 * @author xiaoZhao
 * @date 2022/5/11
 * @describe
 */
public class Z extends Tetromino {
    public Z() {
        cells[0] = new Cell(1,4, Tetris.Z);
        cells[1] = new Cell(0,3, Tetris.Z);
        cells[2] = new Cell(0,4, Tetris.Z);
        cells[3] = new Cell(1,5, Tetris.Z);

        //共有两种旋转状态
        states =new State[2];
        //初始化两种状态的相对坐标
        states[0]=new State(0,0,-1,-1,-1,0,0,1);
        states[1]=new State(0,0,-1,1,0,1,1,0);
    }
}

俄罗斯方块游戏主类:Tetris

package com.zhao.demo.App;

import com.zhao.demo.block.Cell;
import com.zhao.demo.block.Tetromino;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.security.cert.Certificate;

/**
 * @author xiaoZhao
 * @date 2022/5/11
 * @describe 俄罗斯方块游戏主类
 */
public class Tetris extends JPanel {

    //正在下落的方块
    private Tetromino currentOne = Tetromino.randomOne();
    //将要下落的方块
    private Tetromino nextOne = Tetromino.randomOne();
    //游戏主区域
    private Cell[][] wall = new Cell[18][9];
    //声明单元格的值
    private static final int CELL_SIZE = 48;

    //游戏分数池
    int[] scores_pool = {0, 1, 2, 5, 10};
    //当前游戏的分数
    private int totalScore = 0;
    //当前消除的行数
    private int totalLine = 0;

    //游戏三种状态 游戏中、暂停、结束
    public static final int PLING = 0;
    public static final int STOP = 1;
    public static final int OVER = 2;
    //当前游戏状态值
    private int game_state;
    //显示游戏状态
    String[] show_state = {"P[pause]", "C[continue]", "S[replay]"};


    //载入方块图片
    public static BufferedImage I;
    public static BufferedImage J;
    public static BufferedImage L;
    public static BufferedImage O;
    public static BufferedImage S;
    public static BufferedImage T;
    public static BufferedImage Z;
    public static BufferedImage background;

    static {
        try {
            I = ImageIO.read(new File("images/I.png"));
            J = ImageIO.read(new File("images/J.png"));
            L = ImageIO.read(new File("images/L.png"));
            O = ImageIO.read(new File("images/O.png"));
            S = ImageIO.read(new File("images/S.png"));
            T = ImageIO.read(new File("images/T.png"));
            Z = ImageIO.read(new File("images/Z.png"));
            background = ImageIO.read(new File("images/background.png"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void paint(Graphics g) {
        g.drawImage(background, 0, 0, null);
        //平移坐标轴
        g.translate(22, 15);
        //绘制游戏主区域
        paintWall(g);
        //绘制正在下落的四方格
        paintCurrentOne(g);
        //绘制下一个将要下落的四方格
        paintNextOne(g);
        //绘制游戏得分
        paintSource(g);
        //绘制当前游戏状态
        paintState(g);
    }

    public void start() {
        game_state = PLING;
        KeyListener l = new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                int code = e.getKeyCode();
                switch (code) {
                    case KeyEvent.VK_DOWN:
                        sortDropActive();
                        break;
                    case KeyEvent.VK_LEFT:
                        moveleftActive();
                        break;
                    case KeyEvent.VK_RIGHT:
                        moveRightActive();
                        break;
                    case KeyEvent.VK_UP:
                        rotateRightActive();
                        break;
                    case KeyEvent.VK_SPACE:
                            hadnDropActive();
                        break;
                    case KeyEvent.VK_P:
                        //判断当前游戏状态
                        if (game_state == PLING) {
                            game_state = STOP;
                        }
                        break;
                    case KeyEvent.VK_C:
                        if (game_state == STOP) {
                            game_state = PLING;
                        }
                        break;
                    case KeyEvent.VK_S:
                        //重新开始
                        game_state = PLING;
                        wall = new Cell[18][9];
                        currentOne = Tetromino.randomOne();
                        nextOne = Tetromino.randomOne();
                        totalScore = 0;
                        totalLine = 0;
                        break;
                }
            }
        };
        //将窗口设置为焦点
        this.addKeyListener(l);
        this.requestFocus();

        while (true) {
            if (game_state == PLING) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (camDrop()) {
                    currentOne.moveDrop();
                } else {
                    landToWall();
                    destroyLine();
                    if (isGameOver()) {
                        game_state = OVER;
                    } else {
                        //游戏没有结束
                        currentOne = nextOne;
                        nextOne = Tetromino.randomOne();
                    }
                }
            }
            repaint();
        }
    }

    //创建顺时针旋转
    public void rotateRightActive() {
        currentOne.rotateRight();
        if (outOFBounds() || coincide()) {
            currentOne.rotateLeft();
        }
    }

    //瞬间下落
    public void hadnDropActive() {
        while (true) {
            //判断能否下落
            if (camDrop()) {
                currentOne.moveDrop();
            } else {
                break;
            }
        }
        //嵌入到墙中
        landToWall();
        destroyLine();
        if (isGameOver()) {
            game_state = OVER;
        } else {
            //游戏没有结束
            currentOne = nextOne;
            nextOne = Tetromino.randomOne();
        }
    }

    //按键一次,下落一格
    public void sortDropActive() {
        if (camDrop()) {
            //当前四方格下落一格
            currentOne.moveDrop();
        } else {
            landToWall();
            destroyLine();
            if (isGameOver()) {
                game_state = OVER;
            } else {
                //游戏没有结束
                currentOne = nextOne;
                nextOne = Tetromino.randomOne();
            }
        }
    }

    //单元格嵌入墙中
    private void landToWall() {
        Cell[] cells = currentOne.cells;
        for (Cell cell : cells) {
            int row = cell.getRow();
            int col = cell.getCol();
            wall[row][col] = cell;
        }
    }

    //判断四方格能否下落
    public boolean camDrop() {
        Cell[] cells = currentOne.cells;
        for (Cell cell : cells) {
            int row = cell.getRow();
            int col = cell.getCol();
            //判断是否到达底部
            if (row == wall.length - 1) {
                return false;
            } else if (wall[row + 1][col] != null) {
                return false;
            }
        }
        return true;
    }

    //消除行
    public void destroyLine() {
        int line = 0;
        Cell[] cells = currentOne.cells;
        for (Cell cell : cells) {
            int row = cell.getRow();
            if (isFullLine(row)) {
                line++;
                for (int i = row; i > 0; i--) {
                    System.arraycopy(wall[i - 1], 0, wall[i], 0, wall[0].length);
                }
                wall[0] = new Cell[9];
            }
        }
        //分数池获取分数,累加到总分
        totalScore += scores_pool[line];
        //总行数
        totalLine += line;
    }

    //判断当前行是否已经满了
    public boolean isFullLine(int row) {
        Cell[] cells = wall[row];
        for (Cell cell : cells) {
            if (cell == null) {
                return false;
            }
        }
        return true;
    }

    //判断游戏是否结束
    public boolean isGameOver() {
        Cell[] cells = nextOne.cells;
        for (Cell cell : cells) {
            int row = cell.getRow();
            int col = cell.getCol();
            if (wall[row][col] != null) {
                return true;
            }
        }
        return false;
    }

    private void paintState(Graphics g) {
        if (game_state == PLING) {
            g.drawString(show_state[PLING], 500, 660);
        } else if (game_state == STOP) {
            g.drawString(show_state[STOP], 500, 660);
        } else {
            g.drawString(show_state[OVER], 500, 660);
            g.setColor(Color.RED);
            g.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 60));
            g.drawString("GAME OVER!", 30, 400);
        }
    }

    private void paintSource(Graphics g) {
        g.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 30));
        g.drawString("分数: " + totalScore, 500, 250);
        g.drawString("行数: " + totalLine, 500, 430);
    }

    private void paintNextOne(Graphics g) {
        Cell[] cells = nextOne.cells;
        for (Cell cell : cells) {
            int x = cell.getCol() * CELL_SIZE + 370;
            int y = cell.getRow() * CELL_SIZE + 25;
            g.drawImage(cell.getImage(), x, y, null);
        }
    }

    private void paintCurrentOne(Graphics g) {
        Cell[] cells = currentOne.cells;
        for (Cell cell : cells) {
            int x = cell.getCol() * CELL_SIZE;
            int y = cell.getRow() * CELL_SIZE;
            g.drawImage(cell.getImage(), x, y, null);
        }
    }

    private void paintWall(Graphics g) {
        for (int i = 0; i < wall.length; i++) {
            for (int j = 0; j < wall[i].length; j++) {
                int x = j * CELL_SIZE;
                int y = i * CELL_SIZE;
                Cell cell = wall[i][j];
                //判断是否有小方块
                if (cell == null) {
                    g.drawRect(x, y, CELL_SIZE, CELL_SIZE);
                } else {
                    g.drawImage(cell.getImage(), x, y, null);
                }
            }
        }
    }

    //判断是否出界
    public boolean outOFBounds() {
        Cell[] cells = currentOne.cells;
        for (Cell cell : cells) {
            int col = cell.getCol();
            int row = cell.getRow();
            if (row < 0 || row > wall.length - 1 || col < 0 || col > wall[0].length-1) {
                return true;
            }
        }
        return false;
    }

    //按键一次,左移一次
    public void moveleftActive() {
        currentOne.moveLeft();
        //判断是否越界或重合
        if (outOFBounds() || coincide()) {
            currentOne.moveRight();
        }
    }

    //按键一次,右移一次
    public void moveRightActive() {
        currentOne.moveRight();
        //判断是否越界或重合
        if (outOFBounds() || coincide()) {
            currentOne.moveLeft();
        }
    }

    //判断是否重合
    public boolean coincide() {
        Cell[] cells = currentOne.cells;
        for (Cell cell : cells) {
            int row = cell.getRow();
            int col = cell.getCol();
            if (wall[row][col] != null) {
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        JFrame jFrame = new JFrame("俄罗斯方块");
        //创建游戏界面
        Tetris panel = new Tetris();
        jFrame.add(panel);
        //设置可见
        jFrame.setVisible(true);
        //设置窗口大小
        jFrame.setSize(810, 940);
        //设置剧中
        jFrame.setLocationRelativeTo(null);
        //设置窗口关闭时停止
        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //游戏主要开始逻辑
        panel.start();
    }
}

三、效果展示

游戏开始,方快下落,右边区域展示即将下落的方块图、分数、消除的行数以及游戏切换的状态。

按下空格键,方块瞬间下落, 按下P键游戏暂停,消除一行分数为1(此处由分数池进行控制)

  按下C键游戏继续。

方块占满,游戏结束,此时可以按下S键重新开始游戏。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/141489.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Clickhouse学习笔记(14)—— Clickhouse监控

ClickHouse 运行时会将一些个自身的运行状态记录到众多系统表中&#xff0c;如下所示&#xff1a; 为了直观方便地监控ck的运行情况&#xff0c;使用Prometheus Grafana 的组合来进行监控 Prometheus 负责收集各类系统的运行指标&#xff1b;Grafana 负责可视化 Prometheus&a…

ASD光谱仪使用

ASD光谱仪使用 光谱仪机器和电脑用来实时查看光谱曲线&#xff0c;以及控制光谱仪采集的时间、条数等各项参数。 在采集时&#xff0c;需要面向太阳&#xff0c;将待测的对象完全暴露于阳光下&#xff08;下图站位是错误的挡住光线了&#xff09;。探头放置于对象正上方50cm处…

Android launchWhenXXX 和 repeatOnLifecycle

文章目录 Android launchWhenXXX 和 repeatOnLifecyclelifecycleScope和viewModelScopelaunchWhenXXXrepeatOnLifecycleflowWithLifecycle总结 Android launchWhenXXX 和 repeatOnLifecycle lifecycleScope和viewModelScope LiveData优点&#xff1a; 避免内存泄露风险&…

小程序中如何设置多门店/多人/多商品价格库存等复杂场景设置

有些商家希望打造小程序平台&#xff0c;在这个平台上有多个商家入驻&#xff0c;他们分别售卖自己的商品。而有些商家有多个连锁店&#xff0c;连锁店的商品都是一样的&#xff0c;但不同的连锁店有不同的库存和价格。这些业务在采云小程序中是怎么支持的呢&#xff1f;下面具…

2023数字科技生态展,移远通信解锁新成就

11月10日&#xff0c;以“数字科技&#xff0c;焕新启航”为主题的中国电信2023数字科技生态大会暨2023数字科技生态展在广州盛大启幕。作为物联网行业的龙头标杆&#xff0c;同时更与中国电信连续多年维持稳定友好的合作关系&#xff0c;移远通信受邀参加本次展会。 在本次展会…

CSRF和XSS漏洞结合实战案例

文章目录 CSRF和XSS漏洞结合实战案例实验原理实验步骤信息收集构造CSRF和XSS代码xss注入 CSRF和XSS漏洞结合实战案例 实验环境为csm 实验原理 攻击者利用JavaScript可以构造请求的功能在留言面板构造一个存储型xss注入&#xff0c;里面的内容为js请求。请求新添加用户&…

行业寒冬下,给软件测试工程师or功能测试的一些建议

​行业寒冬下&#xff0c;给软件测试工程师的一些建议 【文章末尾给大家留下了大量的福利】 国内的互联网行业发展较快&#xff0c;所以造成了技术研发类员工工作强度比较大&#xff0c;同时技术的快速更新又需要员工不断的学习新的技术。因此淘汰率也比较高&#xff0c;超过…

Oracle 11g安装教程

下载并安装Oracle数据库 首先&#xff0c;需要到Oracle官方网站下载Oracle数据库&#xff0c;在这里我们将下载Oracle 11g第2版的Microsoft Windows(x64)版本。由于安装文件过大&#xff0c;可以分两个文件下载或一次性下载&#xff0c;如下图所示 下载完成后&#xff0c;将下…

你知道调试一个 Web 的 Android 应用有多麻烦吗 AndroidStudio uniapp Capacitor

你知道调试一个 Web 的 Android 应用有多麻烦吗 AndroidStudio uniapp Capacitor 用的 uniapp 写的页面&#xff0c;全是坑&#xff0c;各种坑&#xff0c;生命周期不触发等。但由于已经做完大部分内容了&#xff0c;也不好换了。 我用的是 capacitor h5 > Android 的方式…

paypal第三方支付==沙盒,js

学习地址 https://developer.paypal.com/dashboard/ 创建沙盒已经得到商户和用户账号 得到clientid和client secret 得到买家账户和密码 查看沙盒内的所有账号&#xff0c;我这有一个卖家&#xff0c;两个买家账号 DEMO代码 GitHub - paypaldev/PayPal-Standard-Checkout-Tu…

基于51单片机的出租车计价器设计【程序+proteus仿真+参考论文+AD原理图】

一、项目功能简介 整个设计系统由STC89C52单片机DS1302时钟模块LCD1602显示模块DS18B20温度模块24C02存储模块L298电机驱动模块里按键模块蜂鸣器模块组成。 具体功能&#xff1a; 1、LCD1602显示日期时间、温度&#xff0c;计费开始显示起步价、里程价、总路程和总费用。 2、…

【无标题】通用工作站设计方案:ORI-D3R600服务器-多路PCIe3.0的双CPU通用工作站

ORI-D3R600服务器-多路PCIe3.0的双CPU通用工作站 一、机箱功能和技术指标&#xff1a; 系统 系统型号 ORI-SR630 主板支持 EEB(12*13)/CEB(12*10.5)/ATX(12*9.6)/Micro ATX 前置硬盘 最大支持8个3.5寸(兼容25寸)SATA硬盘 2*2.5(后置) 电源类型 CRPS元余电源&#xff0…

月入8.3k,新传文科生转行5G网络优化工程师,张雪峰:这专业,报考就打晕…

新闻传播专业的就业是什么样子的&#xff1f; 考研名师张雪峰说&#xff1a;如果我是家长的话&#xff0c;孩子非要报新闻学&#xff0c;我一定会干一个事&#xff0c;就是把他打晕&#xff0c;然后给他报个别的。 新闻传播专业似乎已经成了一个备受争议的话题&#xff0c;就业…

优秀智慧园区案例 - 珠海华发智慧园区,万字长文解析先进智慧园区建设方案经验

一、项目背景 珠海华发产业园运营管理有限公司&#xff08;简称“产业园公司”&#xff09;是2016年起连续五年跻身“中国企业500强”、国务院国企改革“双百企业”的珠海华发集团旗下的实体产业发展载体运营平台&#xff0c;依托“四园一基地”&#xff1a;中以国际产业园、信…

Spring中Bean的作用域

2023.11.8 默认情况下&#xff0c;Spring的IoC容器创建的Bean对象是单例的。下面测试一下&#xff1a; 创建一个bean类&#xff1a; package spring6.beans;public class SpringBean { }配置一下xml文件&#xff1a; <?xml version"1.0" encoding"UTF-8&…

软件测试每一个阶段需要掌握的基础知识

以下为大家介绍在每一个阶段需要掌握的基础知识以及对应的实践文档 1 测试方法与理论 名称相关知识点1.1 软件开发生命周期SCRUM/XP、持续集成/持续交付/DevOps1.2 测试流程体系传统测试流程、测试左移、测试右移1.3 测试技术体系分层测试体系、单元测试、UI 测试、接口测试、…

jetsonTX2 nx配置tensorRT加速yolov5推理

环境说明 Ubuntu 18conda环境python3.9cuda10.2&#xff0c;硬件平台是Jetson tx2 nx 前提你已经能运行YOLOV5代码后&#xff0c;再配置tensorRT进行加速。 目前只试了图片检测和C打开USB摄像头进行视频检测&#xff0c;希望是使用python配合D435i深度相机来实现检测&#xff…

前端---CSS的样式汇总

文章目录 CSS的样式元素的属性设置字体设置文字的粗细设置文字的颜色文本对齐文本修饰文本缩进行高设置背景背景的颜色背景的图片图片的属性平铺位置大小 圆角矩形 元素的显示模式行内元素和块级元素的转化弹性布局水平方向排列方式&#xff1a;justify-content垂直方向排序方式…

中断处理机制解析

要处理中断&#xff0c;需要有一个中断处理函数。定义如下&#xff1a; irqreturn_t (*irq_handler_t)(int irq, void * dev_id);/*** enum irqreturn* IRQ_NONE interrupt was not from this device or was not handled* IRQ_HANDLED interrupt was handled by this de…

【PG】PostgreSQL 目录结构

目录 1 软件安装目录 2 数据文件目录 base/&#xff1a;存储每个数据库的基本数据文件 global/&#xff1a;包含了全局性质的系统表空间文件 pg_tblspc/&#xff1a;包含了表空间的符号链接 pg_twophase/&#xff1a;包含了两阶段提交中使用的文件 pg_stat_tmp/&#xff…