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

JAVA小游戏----俄罗斯方块

程序员文章站 2024-03-18 19:50:34
...

一、画墙

import javax.swing.JFrame;

public class Tetris extends JFrame {
	TetrisPanel TP = new TetrisPanel();

	public static void main(String[] args) {
		new Tetris().launchFrame();
	}
	
	public void launchFrame(){
		setBounds(1000, 100, 405, 570);  
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        setVisible(true);
        setResizable(false);
        
        add(TP);//添加TetrisPanel面板
	}
}
import java.awt.Color;
import java.awt.Graphics;

import javax.swing.JPanel;

public class TetrisPanel extends JPanel{
	/*
	 *思想是使用二维数组表示平面,用其中的值标记哪里是墙壁哪里是方块
	 * 
	 */
	private int[][] map = new int[27][20];
	
	//初始化数组,即构造墙壁
		public TetrisPanel(){
		for(int i=0;i<map.length;i++){
			for(int j=0;j<map[i].length;j++){
				if(j==0||j==map[i].length-1||i==map.length-1){
					map[i][j]=3;
				}
				else map[i][j]=0;
			}
		  }
		}
		
		@Override
		public void paint(Graphics g) {		
			super.paint(g);
			//画墙壁
			for(int i=0;i<map.length;i++){
				for(int j=0;j<map[i].length;j++){
					if(map[i][j]==3){
						g.setColor(Color.gray);
						g.fillRect( j*20, i*20, 20, 20);
					}
					if(map[i][j]==1){
						g.setColor(Color.blue);
						g.fillRect(j*20, i*20, 20, 20);
					}
				}
			}
		}
}
JAVA小游戏----俄罗斯方块

二、画俄罗斯方块

1.定义方块

//方块形状定义
	public static int[][][][] shapes=new int[][][][]{  //四维数组,七种形状,每种形状四个方块组成
    	
    	//用多维数组表示方块,在为1的地方填色
            //T  四种形态
            {{{0,1,0}, {1,1,1}, {0,0,0}},  //第一行,第二行,第三行
             {{0,0,1}, {0,1,1}, {0,0,1}}, //朝左
             {{1,1,1}, {0,1,0}, {0,0,0}}, //朝下 
             {{1,0,0}, {1,1,0}, {1,0,0}}},
            //这是两种形态,不能通过旋转得到
            //L  四种形态
            {{{0,1,0}, {0,1,0}, {0,1,1}},  
             {{0,0,1}, {1,1,1}, {0,0,0}},  
             {{1,1,0}, {0,1,0}, {0,1,0}},  
             {{0,0,0}, {1,1,1}, {1,0,0}}},  
            //J  四种形态
            {{{0,1,0}, {0,1,0}, {1,1,0}},  
             {         {1,1,1}, {0,0,1}},  
             {{0,1,1}, {0,1,0}, {0,1,0}},  
             {{1,0,0}, {1,1,1}        }},  
            //S  两种形态
            {{{0,1,1}, {1,1,0} ,{0,0,0}},  
             {{0,1,0}, {0,1,1}, {0,0,1}}},  
             //Z  两种形态
            {{{1,1,0}, {0,1,1}, {0,0,0}},  
             {{0,1,0}, {1,1,0}, {1,0,0}}},  
             //田  一种形态
            {{{1,1}, {1,1}}},  
            //I  两种形态
            {{{1,1,1,1}		 },  //填0会造成无法靠近边框
             {{1},{1},{1},{1}}}  
    };

2.生成方块(TetrisPanel中添加)

        //新方块的初始位置
	int Block_x ;
	int Block_y ;
	
	private int blockType; // 方块类型(第一维数组,共七种)
	private int turnState; // 方块形态(第二维数组,I是两种,L是四种...)
//生成方块
	public void newBlock(){
		//初始在顶部中间位置
		Block_x = 9;
		Block_y = 0;
		
		blockType = (new Random()).nextInt(Tetris.shapes.length); // 不大于方块种类的随机数
		turnState = (new Random()).nextInt(Tetris.shapes[blockType].length); // 不大于方块形态的随机数
		
		repaint();
	}

在paint函数中添加绘制方块的代码

//画方块
for(int i=0;i<Tetris.shapes[blockType][turnState].length;i++){
    for(int j=0;j<Tetris.shapes[blockType][turnState][i].length;j++){
	if(Tetris.shapes[blockType][turnState][i][j]==1){
	    g.setColor(Color.cyan);
	/*
	 * 为什么加j和i??
	 * 第i行第j个方块(空的也算,只是不画)
	 */
	g.drawRect((Block_x+j)*20, (Block_y+i)*20, 20, 20);
	}			
      }
   }

3.使方块向下移动

Timer timer; 
int delay=300;
TimerListener timerListener=new TimerListener();
public void newGame(){//使用Timer来定时执行
    newBlock();
    timer = new Timer(delay, timerListener);
    timer.start();
}
public class TimerListener implements ActionListener{
	@Override
	public void actionPerformed(ActionEvent e) {
		down();
	}
}
private void down(){
    Block_y ++;//未撞底部墙向下
    repaint();
}

在LaunchFrame()中调用TP.newGame(),即可启动timer,使方块下落

JAVA小游戏----俄罗斯方块

三、判断是否触底

//判断是否撞到底部墙
private int is_crash(int blockType, int turnState, int local_x, int local_y){
    for(int i=0;i<Tetris.shapes[blockType][turnState].length;i++){
        for(int j=0;j<Tetris.shapes[blockType][turnState][i].length;j++){
            //在生成newBlock之前,Block_x,Block_y一直变化
            if((Tetris.shapes[blockType][turnState][i][j] & map[local_y+i][local_x+j])==1){
		return 1;
	    }
        }
    }
    return 0;
}

这里使用数字而不是boolean型来记录,因为有原始墙和新生成的墙两种墙,用数字更易表示

触底后墙即增长

//墙增加
	private void addWall(){
		for(int i=0;i<Tetris.shapes[blockType][turnState].length;i++){
			for(int j=0;j<Tetris.shapes[blockType][turnState][i].length;j++){
				//直接这样会覆盖以前的为1的地方
				//map[i+Block_y][j+Block_x]=Tetris.shapes[blockType][turnState][i][j];
				if(Tetris.shapes[blockType][turnState][i][j]==1){
					map[i+Block_y][j+Block_x]=1;
				}				
			}
		}
	}
改写down()函数,下落时同时判断是否撞墙
private void down(){
	if(is_crash(blockType,turnState,Block_x,Block_y+1)==0){
		Block_y ++;//未撞底部墙向下
	}else{
		//撞底部墙则墙增加,生成新方块
		addWall();
		newBlock();
	}
	repaint();
}

四、键盘控制移动及变形

private class KeyControl extends KeyAdapter{
		@Override
		public void keyPressed(KeyEvent e) {			
			super.keyPressed(e);
			int key = e.getKeyCode();
			switch(key){
			case KeyEvent.VK_DOWN:down();break;
			case KeyEvent.VK_LEFT:left();break;
			case KeyEvent.VK_RIGHT:right();break;
			case KeyEvent.VK_UP:turn();break;
			}
		}
	}
private void left(){
	if(is_crash(blockType,turnState,Block_x-1,Block_y)==0){
	    Block_x--;
	}
	repaint();
}
	
private void right(){
	if(is_crash(blockType,turnState,Block_x+1,Block_y)==0){
	    Block_x++;
	}
	repaint();
}
	
private void turn(){
	turnState=((turnState+1)%Tetris.shapes[blockType].length);
	repaint();
}

实例化键盘控制对象

KeyControl kc = new KeyControl();

在LaunchFrame()中添加addKeyListener(TP.kc);实现键盘控制

五、方块的消除

//消除方块
private void Deline(){
	for(int i=map.length-2;i>0;i--){
		//判断是否满行
		int c=1;
		for(int j=1;j<map[i].length-2;j++){
			c=map[i][j]&c;//只要有一个为0,c就为0
		}
		//消除
		if(c==1){
			for(int k=i;k>0;k--){
				for(int l=1;l<map[i].length-2;l++){
				    map[k][l]=map[k-1][l];//将上一行复制到下一行,最顶上单独处理
				}
			}
			for(int l=1;l<map[i].length-2;l++)
			    map[0][l]=0;//最顶上一行清零
			}
		}
	}

在down()添加Deline(),即可实现消除功能

JAVA小游戏----俄罗斯方块


完整代码如下:

import javax.swing.JFrame;

public class Tetris extends JFrame {
	TetrisPanel TP = new TetrisPanel();
	
	//方块形状定义
		public static int[][][][] shapes=new int[][][][]{  //四维数组,七种形状,每种形状四个方块组成
	    	
	    	//用多维数组表示方块,在为1的地方填色
	            //T  四种形态
	            {{{0,1,0}, {1,1,1}, {0,0,0}},  //第一行,第二行,第三行
	             {{0,0,1}, {0,1,1}, {0,0,1}}, //朝左
	             {{1,1,1}, {0,1,0}, {0,0,0}}, //朝下 
	             {{1,0,0}, {1,1,0}, {1,0,0}}},
	            //这是两种形态,不能通过旋转得到
	            //L  四种形态
	            {{{0,1,0}, {0,1,0}, {0,1,1}},  
	             {{0,0,1}, {1,1,1}, {0,0,0}},  
	             {{1,1,0}, {0,1,0}, {0,1,0}},  
	             {{0,0,0}, {1,1,1}, {1,0,0}}},  
	            //J  四种形态
	            {{{0,1,0}, {0,1,0}, {1,1,0}},  
	             {         {1,1,1}, {0,0,1}},  
	             {{0,1,1}, {0,1,0}, {0,1,0}},  
	             {{1,0,0}, {1,1,1}        }},  
	            //S  两种形态
	            {{{0,1,1}, {1,1,0} ,{0,0,0}},  
	             {{0,1,0}, {0,1,1}, {0,0,1}}},  
	             //Z  两种形态
	            {{{1,1,0}, {0,1,1}, {0,0,0}},  
	             {{0,1,0}, {1,1,0}, {1,0,0}}},  
	             //田  一种形态
	            {{{1,1}, {1,1}}},  
	            //I  两种形态
	            {{{1,1,1,1}		 },  //填0会造成无法靠近边框
	             {{1},{1},{1},{1}}}  
	    };

	public static void main(String[] args) {
		new Tetris().launchFrame();
	}
	
	public void launchFrame(){
	setBounds(1000, 100, 405, 570);  
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        setVisible(true);
        setResizable(false);
        
        add(TP);//添加TetrisPanel面板
        addKeyListener(TP.kc);
        TP.newGame();
	}
}
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.Random;

import javax.swing.JPanel;
import javax.swing.Timer;

public class TetrisPanel extends JPanel {
	/*
	 * 思想是使用二维数组表示平面,用其中的值标记哪里是墙壁哪里是方块
	 * 
	 */
	private int[][] map = new int[27][20];

	// 新方块的初始位置
	int Block_x;
	int Block_y;

	private int blockType; // 方块类型(第一维数组,共七种)
	private int turnState; // 方块形态(第二维数组,I是两种,L是四种...)

	Timer timer;
	int delay = 300;
	public TimerListener timerListener = new TimerListener();

	KeyControl kc = new KeyControl();

	// 初始化数组,即构造墙壁
	public TetrisPanel() {
		for (int i = 0; i < map.length; i++) {
			for (int j = 0; j < map[i].length; j++) {
				if (j == 0 || j == map[i].length - 1 || i == map.length - 1) {
					map[i][j] = 3;
				} else
					map[i][j] = 0;
			}
		}
	}

	// 生成方块
	public void newBlock() {
		// 初始在顶部中间位置
		Block_x = 9;
		Block_y = 0;

		blockType = (new Random()).nextInt(Tetris.shapes.length); // 不大于方块种类的随机数
		turnState = (new Random()).nextInt(Tetris.shapes[blockType].length); // 不大于方块形态的随机数

		repaint();
	}

	public void newGame() {// 使用Timer来定时执行
		newBlock();
		timer = new Timer(delay, timerListener);
		timer.start();
	}

	public class TimerListener implements ActionListener {
		@Override
		public void actionPerformed(ActionEvent e) {
			down();
		}
	}

	private void down() {
		if (is_crash(blockType, turnState, Block_x, Block_y + 1) == 0) {
			Block_y++;// 未撞底部墙向下
		} else {
			// 撞底部墙则墙增加,生成新方块
			addWall();
			newBlock();
		}
		Deline();
		repaint();
	}

	// 判断是否撞到底部墙
	private int is_crash(int blockType, int turnState, int local_x, int local_y) {
		for (int i = 0; i < Tetris.shapes[blockType][turnState].length; i++) {
			for (int j = 0; j < Tetris.shapes[blockType][turnState][i].length; j++) {
				// 在生成newBlock之前,Block_x,Block_y一直变化
				if ((Tetris.shapes[blockType][turnState][i][j] & map[local_y + i][local_x + j]) == 1) {
					return 1;
				}
			}
		}
		return 0;
	}

	// 墙增加,在撞到前生成新墙
	private void addWall() {
		for (int i = 0; i < Tetris.shapes[blockType][turnState].length; i++) {
			for (int j = 0; j < Tetris.shapes[blockType][turnState][i].length; j++) {
				// 直接这样会覆盖以前的为1的地方
				// map[i+Block_y][j+Block_x]=Tetris.shapes[blockType][turnState][i][j];
				if (Tetris.shapes[blockType][turnState][i][j] == 1) {
					map[i + Block_y][j + Block_x] = 1;
				}
			}
		}
	}

	// 消除方块
	private void Deline() {
		for (int i = map.length - 2; i > 0; i--) {
			// 判断是否满行
			int c = 1;
			for (int j = 1; j < map[i].length - 2; j++) {
				c = map[i][j] & c;// 只要有一个为0,c就为0
			}
			// 消除
			if (c == 1) {
				for (int k = i; k > 0; k--) {
					for (int l = 1; l < map[i].length - 2; l++) {
						map[k][l] = map[k - 1][l];// 将上一行复制到下一行,最顶上单独处理
					}
				}
				for (int l = 1; l < map[i].length - 2; l++)
					map[0][l] = 0;// 最顶上一行清零
			}
		}
	}

	@Override
	public void paint(Graphics g) {
		super.paint(g);
		// 画墙壁
		for (int i = 0; i < map.length; i++) {
			for (int j = 0; j < map[i].length; j++) {
				if (map[i][j] == 3) {
					g.setColor(Color.gray);
					g.fillRect(j * 20, i * 20, 20, 20);
				}
				if (map[i][j] == 1) {
					g.setColor(Color.blue);
					g.fillRect(j * 20, i * 20, 20, 20);
				}
			}
		}
		// 画方块
		for (int i = 0; i < Tetris.shapes[blockType][turnState].length; i++) {
			for (int j = 0; j < Tetris.shapes[blockType][turnState][i].length; j++) {
				if (Tetris.shapes[blockType][turnState][i][j] == 1) {
					g.setColor(Color.cyan);
					/*
					 * 为什么加j和i?? 第i行第j个方块(空的也算,只是不画)
					 */
					g.drawRect((Block_x + j) * 20, (Block_y + i) * 20, 20, 20);
				}
			}
		}
	}

	private class KeyControl extends KeyAdapter {
		@Override
		public void keyPressed(KeyEvent e) {
			super.keyPressed(e);
			int key = e.getKeyCode();
			switch (key) {
			case KeyEvent.VK_DOWN:
				down();
				break;
			case KeyEvent.VK_LEFT:
				left();
				break;
			case KeyEvent.VK_RIGHT:
				right();
				break;
			case KeyEvent.VK_UP:
				turn();
				break;
			}
		}
	}

	private void left() {
		if (is_crash(blockType, turnState, Block_x - 1, Block_y) == 0) {
			Block_x--;
		}
		repaint();
	}

	private void right() {
		if (is_crash(blockType, turnState, Block_x + 1, Block_y) == 0) {
			Block_x++;
		}
		repaint();
	}

	private void turn() {
		turnState = ((turnState + 1) % Tetris.shapes[blockType].length);
		repaint();
	}
}
点击下载源码