目录
效果图
实现思路
代码实现
创建窗口
画布2
创建菜单及菜单选项
绘制游戏区域
画布2绘制一个小方块
创建图形
创建模型类
模型旋转变形
方块累计
方块消除和积分
加入自动向下线程,并启动
首页 Java java教程 Java实现俄罗斯方块游戏的代码怎么写

Java实现俄罗斯方块游戏的代码怎么写

May 03, 2023 pm 08:55 PM
java

效果图

这里界面做的感觉不是很好看,但我觉得问题不大,功能到位就好!

Java实现俄罗斯方块游戏的代码怎么写

实现思路

两块画布:

画布1: 用来绘制静态东西,比如游戏区边框、网格、得分区域框、下一个区域框、按钮等,无需刷新的部分。

画布2: 用来绘制游戏动态的部分,比如 方格模型、格子的移动、旋转变形、消除、积分显示、下一个图形显示 等。

代码实现

创建窗口

首先创建一个游戏窗体类GameFrame,继承至JFrame,用来显示在屏幕上(window的对象),每个游戏都有一个窗口,设置好窗口标题、尺寸、布局等就可以。

/*
 * 游戏窗体类
 */
public class GameFrame extends JFrame {
	
	public GameFrame() {
		setTitle("俄罗斯方块");//设置标题
		setSize(488, 476);//设定尺寸
		setLayout(new BorderLayout());
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//点击关闭按钮是关闭程序
        setLocationRelativeTo(null);   //设置居中
    	setResizable(false); //不允许修改界面大小
	}
}
登录后复制

画布1

创建面板容器BackPanel继承至JPanel

/*
 * 背景画布类
 */
public class BackPanel extends JPanel{
	BackPanel panel=this;
	private JFrame mainFrame=null;
	//构造里面初始化相关参数
	public BackPanel(JFrame frame){
		this.setLayout(null);
		this.setOpaque(false);
		this.mainFrame = frame;
		mainFrame.setVisible(true);
	}
}
登录后复制

再创建一个Main类,来启动这个窗口。

public class Main {
	//主类
	public static void main(String[] args) {
		GameFrame frame = new GameFrame();
		BackPanel panel = new BackPanel(frame);
		frame.add(panel);
		frame.setVisible(true);//设定显示
	}
}
登录后复制

右键执行这个Main类,窗口建出来了

Java实现俄罗斯方块游戏的代码怎么写

创建菜单及菜单选项

创建菜单

private void  initMenu(){
		// 创建菜单及菜单选项
		jmb = new JMenuBar();
		JMenu jm1 = new JMenu("游戏");
		jm1.setFont(new Font("仿宋", Font.BOLD, 15));// 设置菜单显示的字体
		JMenu jm2 = new JMenu("帮助");
		jm2.setFont(new Font("仿宋", Font.BOLD, 15));// 设置菜单显示的字体
		
		JMenuItem jmi1 = new JMenuItem("开始新游戏");
		JMenuItem jmi2 = new JMenuItem("退出");
		jmi1.setFont(new Font("仿宋", Font.BOLD, 15));
		jmi2.setFont(new Font("仿宋", Font.BOLD, 15));
		
		JMenuItem jmi3 = new JMenuItem("操作说明");
		jmi3.setFont(new Font("仿宋", Font.BOLD, 15));
		JMenuItem jmi4 = new JMenuItem("失败判定");
		jmi4.setFont(new Font("仿宋", Font.BOLD, 15));
		
		jm1.add(jmi1);
		jm1.add(jmi2);
		
		jm2.add(jmi3);
		jm2.add(jmi4);
		
		jmb.add(jm1);
		jmb.add(jm2);
		mainFrame.setJMenuBar(jmb);// 菜单Bar放到JFrame上
		jmi1.addActionListener(this);
		jmi1.setActionCommand("Restart");
		jmi2.addActionListener(this);
		jmi2.setActionCommand("Exit");
		
		jmi3.addActionListener(this);
		jmi3.setActionCommand("help");
		jmi4.addActionListener(this);
		jmi4.setActionCommand("lost");
	}
登录后复制

实现ActionListener并重写方法actionPerformed

Java实现俄罗斯方块游戏的代码怎么写

actionPerformed方法的实现

Java实现俄罗斯方块游戏的代码怎么写

Java实现俄罗斯方块游戏的代码怎么写

绘制游戏区域

绘制游戏区域边框

//绘制边框
private void drawBorder(Graphics g) {
	BasicStroke bs_2=new BasicStroke(12L,BasicStroke.CAP_ROUND,BasicStroke.JOIN_MITER);
	Graphics2D g_2d=(Graphics2D)g;
	g_2d.setColor(new Color(128,128,128));
	g_2d.setStroke(bs_2);

	RoundRectangle2D.Double rect = new RoundRectangle2D.Double(6, 6, 313 - 1, 413 - 1, 2, 2);
	g_2d.draw(rect);
}
登录后复制

绘制右边辅助区域(积分、下一个、按钮等)

//绘制右边区域边框
private void drawBorderRight(Graphics g) {
	BasicStroke bs_2=new BasicStroke(12L,BasicStroke.CAP_ROUND,BasicStroke.JOIN_MITER);
	Graphics2D g_2d=(Graphics2D)g;
	g_2d.setColor(new Color(128,128,128));
	g_2d.setStroke(bs_2);
	
	RoundRectangle2D.Double rect = new RoundRectangle2D.Double(336, 6, 140 - 1, 413 - 1, 2, 2);
	g_2d.draw(rect);
	//g_2d.drawRect(336, 6, 140, 413);
}
登录后复制

在BackPanel 中重写paint 方法,并调用刚才两个区域绘制方法。

Java实现俄罗斯方块游戏的代码怎么写

Java实现俄罗斯方块游戏的代码怎么写

绘制得分区域和下一个区域

//绘制积分区域
private void drawCount(Graphics g) {
	BasicStroke bs_2=new BasicStroke(2L,BasicStroke.CAP_ROUND,BasicStroke.JOIN_MITER);
	Graphics2D g_2d=(Graphics2D)g;
	g_2d.setColor(new Color(0,0,0));
	g_2d.setStroke(bs_2);
	g_2d.drawRect(350, 17, 110, 80);
	
	//得分
	g.setFont(new Font("宋体", Font.BOLD, 20));
	g.drawString("得分:",380, 40);
}

//绘制下一个区域
private void drawNext(Graphics g) {
	BasicStroke bs_2=new BasicStroke(2L,BasicStroke.CAP_ROUND,BasicStroke.JOIN_MITER);
	Graphics2D g_2d=(Graphics2D)g;
	g_2d.setColor(new Color(0,0,0));
	g_2d.setStroke(bs_2);
	g_2d.drawRect(350, 120, 110, 120);
	
	//得分
	g.setFont(new Font("宋体", Font.BOLD, 20));
	g.drawString("下一个:",360, 140);
}
登录后复制

绘制网格(15列 20行)

//绘制网格
private void drawGrid(Graphics g) {
	Graphics2D g_2d=(Graphics2D)g;
	g_2d.setColor(new Color(255,255,255,150));
	int x1=12;
	int y1=20;
	int x2=312;
	int y2=20;
	for (int i = 0; i <= ROWS; i++) {
		y1 = 12 + 20*i;
		y2 = 12 + 20*i;
		g_2d.drawLine(x1, y1, x2, y2);		
	}
	
	y1=12;
	y2=412;
	for (int i = 0; i <= COLS; i++) {
		x1 = 12 + 20*i;
		x2 = 12 + 20*i;
		g_2d.drawLine(x1, y1, x2, y2);		
	}
}
登录后复制

在paint方法中调用

Java实现俄罗斯方块游戏的代码怎么写

创建游戏右边区域的一个暂停按钮

//初始化
private void init() {
	// 开始/停止按钮
	btnStart = new JButton();
	btnStart.setFont(new Font("黑体", Font.PLAIN, 18));
	btnStart.setFocusPainted(false);
	btnStart.setText("暂停");
	btnStart.setBounds(360, 300, 80, 43);
	btnStart.setBorder(BorderFactory.createRaisedBevelBorder());
	this.add(btnStart);
	btnStart.addActionListener(this);
	btnStart.setActionCommand("start");
}
登录后复制

Java实现俄罗斯方块游戏的代码怎么写

此时基本布局已经完成了。

画布2

GamePanel 继承至 JPanel 并重写 paint 方法

修改Main类,将画布2也放到窗口中

public class Main {
	//主类
	public static void main(String[] args) {
		GameFrame frame = new GameFrame();
		BackPanel panel = new BackPanel(frame);
		frame.add(panel);
		GamePanel gamePanel = new GamePanel(frame);
		panel.setGamePanel(gamePanel);
		frame.add(gamePanel);
		frame.setVisible(true);//设定显示
	}
}
登录后复制

画布2绘制一个小方块

因为游戏区域被分成了一个个的小格子,每个小格子就是一个单位,整个网格就是一个15,、20的二维数组。

于是第一行第一个元素,用数组下标来表示就是 0,0 、第一行第二个元素就是0、1

这样就好办了,我们创建一个Block类,设置坐标和宽高即可绘制方块(宽高为固定20,与网格对应)。

package main;
import java.awt.Graphics;
public class Block {
	private int x=0;//x坐标
	private int y=0;//y坐标
	private GamePanel panel=null;
	
	public Block(int x,int y,int mX,int mY,GamePanel panel){
		this.x=x;
		this.y=y;
		this.panel=panel;
	}
	//绘制
	void draw(Graphics g){
		g.fillRect(12+x*20, 12+y*20, 20, 20);
	}
	
	public int getX() {
		return x;
	}
	public void setX(int x) {
		this.x = x;
	}
	public int getY() {
		return y;
	}
	public void setY(int y) {
		this.y = y;
	}
}
登录后复制

实例化这个类,并在paint方法中调用draw绘制方法

private void init() {
	x=0;
	y=0;
	curBlock = new Block(x, y,this);
}
登录后复制
@Override
public void paint(Graphics g) {
	super.paint(g);
	
	if(curBlock!=null){
		curBlock.draw(g);	
	}
}
登录后复制

Java实现俄罗斯方块游戏的代码怎么写

在Block类加入移动方法

两个参数 boolean xDir, int step

xDir 布尔值:true表示横向移动,false表示向下移动

step是步数:当xDir为true,我们设定为 1 和 -1 横向移动1表示向右,-1表示向左移动;当xDir为true为false,向下移动为1(因为不能向上移动)。

//移动
	void move(boolean xDir, int step){
		if(xDir){//X方向的移动,step 正数向右 负数向左
			x += step;
		}else{//向下运动
			y += step;
		}
		panel.repaint();
	}
登录后复制

GamePanel添加键盘事件

//添加键盘监听
private void createKeyListener() {
	KeyAdapter l = new KeyAdapter() {
		//按下
		@Override
		public void keyPressed(KeyEvent e) {
			int key = e.getKeyCode();
			switch (key) {
				//空格
				case KeyEvent.VK_SPACE:
					break;
					
				//向上
				case KeyEvent.VK_UP:
				case KeyEvent.VK_W:
					break;
					
				//向右	
				case KeyEvent.VK_RIGHT:
				case KeyEvent.VK_D:
					if(curBlock!=null) curBlock.move(true, 1);
					break;
					
				//向下
				case KeyEvent.VK_DOWN:
				case KeyEvent.VK_S:
					if(curBlock!=null) curBlock.move(false, 1);
					break;
					
				//向左
				case KeyEvent.VK_LEFT:
				case KeyEvent.VK_A:
					if(curBlock!=null) curBlock.move(true, -1);
					break;
			}
		
		}
		//松开
		@Override
		public void keyReleased(KeyEvent e) {
		}
		
	};
	//给主frame添加键盘监听
	mainFrame.addKeyListener(l);
}
登录后复制

于是我操作一波

Java实现俄罗斯方块游戏的代码怎么写

创建图形

七种图形

Java实现俄罗斯方块游戏的代码怎么写

如上图,如果我们以标红的小方块为原点(0,0)那我们分析一下图形其他几个方块的位置。

Java实现俄罗斯方块游戏的代码怎么写

比如上面图形,红色框住的为(0,0)的话,那最前面的那个是不是(-1,0),因为 y 他们是一样的,只要 x 往左边移动一个位置。

以此类推,第3个应该是(1,0),第4个是(2,0)。

Java实现俄罗斯方块游戏的代码怎么写

此图形呢,标红的为(0,0),它正下方的那个应该是(0,1),它右边那个是(1,0),它右下角的那个应该是(1,1)
于是我们可以设计一个Data类,专门存储7种图形的位置信息,分别对应前面图的7种模型

public class Data {
	public static List datas = new ArrayList(); 
	static void init(){
		int[][] data1 = {{-1,0},{0,0},{1,0},{1,1}}; 
		datas.add(data1);
		
		int[][] data2 = {{-1,0},{0,0},{1,0},{2,0}}; 
		datas.add(data2);
		
		int[][] data3 = {{-1,0},{-1,1},{0,0},{1,0}}; 
		datas.add(data3);
		
		int[][] data4 = {{-1,0},{0,0},{0,1},{1,1}}; 
		datas.add(data4);
		
		int[][] data5 = {{0,0},{0,1},{1,0},{1,1}}; 
		datas.add(data5);
		
		int[][] data6 = {{-1,1},{0,0},{0,1},{1,0}}; 
		datas.add(data6);
		
		int[][] data7 = {{-1,0},{0,0},{0,1},{1,0}}; 
		datas.add(data7);
	}
}
登录后复制

创建模型类

其中创建的时候,随机从Data类里面7个数据里面取到一个,生成一个图形,根据对应二维数组作为下标来创建小方块。

public class Model {

	private int x=0;
	private int y=0;
	private GamePanel panel=null;
	private List blocks = new ArrayList();
	boolean moveFlag=false;

	public Model(int x,int y,GamePanel panel){
		this.x=x;
		this.y=y;
		this.panel=panel;
		
		createModel();
	}
	
	private void createModel() {
		Random random = new Random();
		int type = random.nextInt(7);//1-7种模型
		int[][] data= (int[][])Data.datas.get(type);
		
		Block block=null;
		int mX=0;
		int mY=0;
		for (int i = 0; i < 4; i++) {
			mX = data[i][0];
			mY = data[i][1];
			block = new Block(x, y, mX , mY, panel);
			blocks.add(block);
		}
	}
}
登录后复制

Block也要稍微做些变动

需要加入偏移坐标值,来设定4个小方块的相对位置

Java实现俄罗斯方块游戏的代码怎么写

GamePanel类中实例化的就是Model类了,同时绘制的也是

curModel = new Model(x,y,this);
登录后复制
@Override
public void paint(Graphics g) {
	super.paint(g);
	
	//当前模型
	if(curModel!=null){
		List blocks = curModel.getBlocks();
		Block block=null;
		for (int i = 0; i < blocks.size(); i++) {
			block = (Block)blocks.get(i);
			block.draw(g);
		}
	}
}
登录后复制

我这里设定创建Model的时候x为7,y为3,于是:

Java实现俄罗斯方块游戏的代码怎么写

图形创建好了,怎么去移动这个图形呢

很简单就是键盘移动的时候,改成调用Model类的move方法了,此方法里面就是循环模型的4个Block实例,每个小块调用自己的move方法即可:

Java实现俄罗斯方块游戏的代码怎么写

效果如下:

Java实现俄罗斯方块游戏的代码怎么写

模型旋转变形

旋转万能公式 x=-y y=x 这里的x、y指的是Data类里面二维数组的值,也就是 Block中的偏移值

在Block中添加变形方法

	//变形
	public void rotate() {
		//旋转万能公式 x=-y y=x
		int x = mX;
		mX = -mY;
		mY = x;
	}
登录后复制

Model中添加变形方法,就是循环4个Block实例

这里加入了预变形方法,就是要先判断能否变形,比如变形会出边界,会碰到别的方块,则不让变形。

//旋转
void rotate(){
	boolean flag = true;//允许变形
	Block block=null;
	for (int i = 0; i < blocks.size(); i++) {
		block = (Block)blocks.get(i);
		if(!block.preRotate()){ //有一个不让变形就不能变形
			flag = false;//不能变形
			break;
		}
	}
	if(flag){
		for (int i = 0; i < blocks.size(); i++) {
			block = (Block)blocks.get(i);
			block.rotate();
		}
	}
	panel.repaint();
}
登录后复制

Java实现俄罗斯方块游戏的代码怎么写

方块累计

当图形触底或者接触往下接触到其他方块时,会累计在下面,并且创建新的图形出来。

public Block[][] blockStack = new Block[15][20];

这个二维数组用来存储累计的方块

图形触底后,会根据每个小block实例的位置一一对应插入到blockStack这个二维数组中。

Java实现俄罗斯方块游戏的代码怎么写

在paint方法中加入累积块的绘制

	//累计块
		Block bott = null;
		for (int i = 0; i < 15; i++) {
			for (int j = 0; j < 20; j++) {
				bott = (Block)blockStack[i][j];
				if(bott!=null ){
					bott.draw(g);
				}
			}
		}
登录后复制

方块消除和积分

1.从当前撞击的模型中取出y坐标(注意去重)。

2.将y进行排序,让位置小的排在前面,也就是如果消除两行的话要先消上面的那行。

3.消除当前行采用的是数据替换,从当前行开始,上一行的数据往下一行赋值,当前行就等于被消除了。

4.积分处理。

//消除处理
private void clear() {
	Block block = null ;
	int num=0;
	int y=0;
	List hasDoList=new ArrayList(); 
	List clearList=new ArrayList();
	for (int i = 0; i < blocks.size(); i++) {
		block = (Block)blocks.get(i);
		y = block.getY() + block.getmY();
		if(y<0 || y>19) continue;
		
		if(!hasDoList.contains(y)){
			hasDoList.add(y);
			if(block.clear()){
				clearList.add(y);
				num++;
			}
		}
	}
	if(num==1){
		panel.curCount+=100;
	}else if(num==2){
		panel.curCount+=300;
	}else if(num==3){
		panel.curCount+=600;
	}else if(num==4){
		panel.curCount+=1000;
	}
	//执行格子的消除动作
	if(num>0){
		Collections.sort(clearList);
		doClear(clearList);
	}
}
//执行消除
void doClear(List l){
	int y=0;
	for (int i = 0; i < l.size(); i++) {
		y = Integer.parseInt(String.valueOf(l.get(i)));
		clearClock(y);
	}
}

void clearClock(int y){
	Block[][] stack = panel.blockStack;
	Block block=null;
	for (int i = 0; i < 15; i++) {
		for (int j = 19; j >= 0; j--) {//从最下面往上
			if(y>=j&&j>0){//消除行和上方的行,全部往下移动,即这行等于上一行的数据
				block = stack[i][j-1];
				if(block!=null){
					block.setY(block.getY()+1);
				}
				stack[i][j]=block;
			}else if(j==0){//第一行,清空
				stack[i][j]=null;
			}
		}
	}
}
登录后复制

积分规则:1行100分、2行300分、3行600分、4行1000分

Java实现俄罗斯方块游戏的代码怎么写

显示下一个

这个其实不难:

1.创建好当前模型的时候,同时创建好下一个模型,并绘制出来;

2.当前模型触底累计后,把下一个模型设置为当前模型。

3.同时创建一个新模型做为下一个模型。

//创建模型
	public void createModel(int type) {
		if(type==0){//游戏刚开始时
			curModel = new Model(x,y,this);
			nextModel = new Model(x,y,this);
		}else{//游戏运行中
			curModel = nextModel;
			nextModel = new Model(x,y,this);
		}
	}
登录后复制

在paint方法中绘制‘下一个’,在右边的下一个区域显示

		//下一个模型
		if(nextModel!=null){
			List blocks = nextModel.getBlocks();
			Block block=null;
			for (int i = 0; i < blocks.size(); i++) {
				block = (Block)blocks.get(i);
				block.drawNext(g);
			}
		}
登录后复制

加入自动向下线程,并启动

//游戏线程,用来自动下移
private class GameThread implements Runnable {
	@Override
	public void run() {
		while (true) {
			if("start".equals(gameFlag)){
				curModel.move(false, 1);
			}
			try {
				Thread.sleep(300);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}
登录后复制

Java实现俄罗斯方块游戏的代码怎么写

最后加入积分、按键控制、游戏结束、重新开始等就完成了

以上是Java实现俄罗斯方块游戏的代码怎么写的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
4 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

Java 中的完美数 Java 中的完美数 Aug 30, 2024 pm 04:28 PM

Java 完美数指南。这里我们讨论定义,如何在 Java 中检查完美数?,示例和代码实现。

Java 中的随机数生成器 Java 中的随机数生成器 Aug 30, 2024 pm 04:27 PM

Java 随机数生成器指南。在这里,我们通过示例讨论 Java 中的函数,并通过示例讨论两个不同的生成器。

Java中的Weka Java中的Weka Aug 30, 2024 pm 04:28 PM

Java 版 Weka 指南。这里我们通过示例讨论简介、如何使用weka java、平台类型和优点。

Java 中的史密斯数 Java 中的史密斯数 Aug 30, 2024 pm 04:28 PM

Java 史密斯数指南。这里我们讨论定义,如何在Java中检查史密斯号?带有代码实现的示例。

Java Spring 面试题 Java Spring 面试题 Aug 30, 2024 pm 04:29 PM

在本文中,我们保留了最常被问到的 Java Spring 面试问题及其详细答案。这样你就可以顺利通过面试。

突破或从Java 8流返回? 突破或从Java 8流返回? Feb 07, 2025 pm 12:09 PM

Java 8引入了Stream API,提供了一种强大且表达力丰富的处理数据集合的方式。然而,使用Stream时,一个常见问题是:如何从forEach操作中中断或返回? 传统循环允许提前中断或返回,但Stream的forEach方法并不直接支持这种方式。本文将解释原因,并探讨在Stream处理系统中实现提前终止的替代方法。 延伸阅读: Java Stream API改进 理解Stream forEach forEach方法是一个终端操作,它对Stream中的每个元素执行一个操作。它的设计意图是处

Java 中的时间戳至今 Java 中的时间戳至今 Aug 30, 2024 pm 04:28 PM

Java 中的时间戳到日期指南。这里我们还结合示例讨论了介绍以及如何在java中将时间戳转换为日期。

创造未来:面向零基础的 Java 编程 创造未来:面向零基础的 Java 编程 Oct 13, 2024 pm 01:32 PM

Java是热门编程语言,适合初学者和经验丰富的开发者学习。本教程从基础概念出发,逐步深入讲解高级主题。安装Java开发工具包后,可通过创建简单的“Hello,World!”程序实践编程。理解代码后,使用命令提示符编译并运行程序,控制台上将输出“Hello,World!”。学习Java开启了编程之旅,随着掌握程度加深,可创建更复杂的应用程序。

See all articles