The interface here doesn’t look very good, but I think it’s not a big problem, as long as the functions are in place!
Two canvases:
Canvas 1: Used to draw static things, such as game area borders, grids, Score area box, next area box, buttons, etc., parts that do not need to be refreshed.
Canvas 2: Used to draw dynamic parts of the game, such as grid model, grid movement, rotation deformation, elimination, points display, next graphic display, etc.
First create a game form class GameFrame, inherit it from JFrame, and use it to display on the screen (window object). Each game Each has a window, just set the window title, size, layout, etc.
/* * 游戏窗体类 */ public class GameFrame extends JFrame { public GameFrame() { setTitle("俄罗斯方块");//设置标题 setSize(488, 476);//设定尺寸 setLayout(new BorderLayout()); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//点击关闭按钮是关闭程序 setLocationRelativeTo(null); //设置居中 setResizable(false); //不允许修改界面大小 } }
Create the panel container BackPanel and inherit it from 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); } }
Create a Main class to start this window.
public class Main { //主类 public static void main(String[] args) { GameFrame frame = new GameFrame(); BackPanel panel = new BackPanel(frame); frame.add(panel); frame.setVisible(true);//设定显示 } }
Right-click to execute the Main class, and the window is created
Create menu
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"); }
Implement ActionListener and override the method actionPerformed
Implementation of actionPerformed method
Draw the game area border
//绘制边框 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); }
Draw the right auxiliary area (points, next, button, etc.)
//绘制右边区域边框 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); }
Override the paint method in BackPanel, And call the two area drawing methods just now.
Draw the scoring area and the next area
//绘制积分区域 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); }
Draw the grid (15 columns and 20 rows)
//绘制网格 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); } }
Call in the paint method
Create a pause button in the right area of the game
//初始化 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"); }
At this time The basic layout has been completed.
GamePanel inherits from JPanel and overrides the paint method
Modify the Main class and put Canvas 2 into the window
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);//设定显示 } }
Because the game area is divided into small grids, each small grid is a unit, and the entire grid is a two-dimensional array of 15, 20.
So the first element in the first line, expressed as an array subscript, is 0,0, and the second element in the first line is 0,1
This is easy, we Create a Block class, set the coordinates, width and height to draw the block (the width and height are fixed at 20, corresponding to the grid).
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; } }
Instantiate this class and call the draw method in the paint method
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); } }
Add the move method to the Block class
Two Parameters boolean xDir, int step
xDir Boolean value: true means horizontal movement, false means downward movement
step is the number of steps: when xDir is true, we set it to 1 and - 1 A horizontal movement of 1 means moving to the right, -1 means moving to the left; when xDir is true or false, moving downward is 1 (because it cannot move upward).
//移动 void move(boolean xDir, int step){ if(xDir){//X方向的移动,step 正数向右 负数向左 x += step; }else{//向下运动 y += step; } panel.repaint(); }
GamePanel adds keyboard event
//添加键盘监听 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); }
So I operated a wave
Seven graphics
As shown in the picture above, if we take the small square marked in red as the origin (0,0), then we analyze the positions of the other squares in the graphic.
For example, in the above figure, if the red box is (0,0), then is the front one (-1,0), because y is the same? , as long as x moves one position to the left.
By analogy, the third one should be (1,0), the fourth one should be (2,0).
此图形呢,标红的为(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个小方块的相对位置
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,于是:
图形创建好了,怎么去移动这个图形呢
很简单就是键盘移动的时候,改成调用Model类的move方法了,此方法里面就是循环模型的4个Block实例,每个小块调用自己的move方法即可:
效果如下:
旋转万能公式 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(); }
当图形触底或者接触往下接触到其他方块时,会累计在下面,并且创建新的图形出来。
public Block[][] blockStack = new Block[15][20];
这个二维数组用来存储累计的方块
图形触底后,会根据每个小block实例的位置一一对应插入到blockStack这个二维数组中。
在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分
显示下一个
这个其实不难:
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(); } } } }
最后加入积分、按键控制、游戏结束、重新开始等就完成了
The above is the detailed content of How to write code to implement Tetris game in Java. For more information, please follow other related articles on the PHP Chinese website!