本文主要是介绍俄罗斯方块(详解),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
几个重要的细节问题:
1、控件的布局问题:只有将布局方式设置为null,才能通过setBounds设置大小和位置
2、分层模式,内部类方式:各个类之间的相互的交流
3、事件的监听:键盘监听,失去焦点问题
4、碰壁问题:多画一行和一列
5、移动:判断是否碰壁方法不动,通过不同方式和所传的参数的不同,从而实现各种动作
package tetris3;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 javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.Timer;
import javax.swing.border.TitledBorder;import tetris2.NextBlock;public class Tetris extends JFrame {public static boolean isPlay=true;//设置是否点击暂停Model tPanel;//方块面板JMenuItem jitemPause ;//暂停菜单项int grade = 0, rank = 1,numb = 1;//初始化基本数据JLabel score, level;NextBlock nextBlockPanel;//下一块的面板Color bgColor = Color.black, blockColor = Color.yellow;//颜色public Tetris() {super("俄罗斯方块");this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置关闭控件this.setSize(378, 496);this.setLocationRelativeTo(null);//设置窗口居中setPanel();//布局addMenu();// 添加菜单this.setResizable(false);//设置窗口大小不能改变}//设置下一块的颜色public void setNextBlock(){this.nextBlockPanel.repaint();}//设置移动块的颜色public void setBlockColor(Color c){tPanel.setBlockColor(c);}//获取已经堆积块的颜色public Color getModelColor(){return tPanel.ModelColor;}//设置背景颜色public void setBGColor(Color c){bgColor=c;tPanel.setBackground(bgColor);}//设置已堆积块的颜色public void setModelColor(Color c){tPanel.setModelColor(c);}//设置分数public void setScore(int s){score.setText(""+s);}//设置下一块的颜色public void setNextBg(Color c){nextBlockPanel.setBg(c);}//设置等级public void setGrade(int s){level.setText(""+s);}//控制暂停菜单项中的暂停和继续间的切换public void setPause(boolean b){String str;if(b){str="continue";}else{str="pause";}jitemPause.setText(str);}//获取下一块的信息public NextBlock getNextBlock(){this.nextBlockPanel.repaint();return this.nextBlockPanel;}//<span style="color:#ff0000;">设置布局面板,这里要注意,要是添加按钮,文本编辑控件,都会失去焦点,使运行面板不能实现键盘监听,可以通过内部的类是其失去焦点,//为了避免不必要的错误,尽量不要用吧,这里慎重啊。还有记得设置这些面板布局方式为空,只有这样才能保证这些添加的控件能在规定的位置//正确的显示</span>.public void setPanel() {nextBlockPanel = new NextBlock();tPanel = new Model();tPanel.setBackground(bgColor);this.getContentPane().add(tPanel);this.addKeyListener(tPanel.listener);JPanel controlPanel = new JPanel();controlPanel.setLayout(null);controlPanel.setBorder(new TitledBorder("Control And Show"));JLabel nextBlockShape = new JLabel("Next Block Shape");nextBlockShape.setBounds(10, 20, 100, 20);controlPanel.add(nextBlockShape);nextBlockPanel.setBounds(0, 50, 120, 80);nextBlockPanel.repaint();controlPanel.add(nextBlockPanel);JLabel scoreLabel = new JLabel("Score");scoreLabel.setBounds(40, 120, 100, 60);controlPanel.add(scoreLabel);score = new JLabel("0");score.setBounds(15, 160, 108, 20);controlPanel.add(score);JLabel levelLabel = new JLabel("Level");levelLabel.setBounds(40, 160, 100, 100);controlPanel.add(levelLabel);level = new JLabel("1");level.setBounds(15, 220, 108, 20);controlPanel.add(level);JLabel jl1=new JLabel("注意自我保护,");jl1.setBounds(15, 280, 108, 30);JLabel jl2=new JLabel("适度游戏益脑,");jl2.setBounds(15, 310, 108, 30);JLabel jl3=new JLabel("合理安排时间,");jl3.setBounds(15, 340, 108, 30);JLabel jl4=new JLabel("享受健康生活。");jl4.setBounds(15, 370, 108, 30);controlPanel.add(jl1);controlPanel.add(jl2);controlPanel.add(jl3);controlPanel.add(jl4);this.getContentPane().add(controlPanel);JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, tPanel,controlPanel);// 水平分隔窗格,左右各添加一个面板split.setDividerLocation(241);// 设置水平分隔条的位置split.setEnabled(false);// 设置分隔条不能变动this.getContentPane().add(split);// 框架内容窗格添加分隔窗格}//添加菜单private void addMenu() {JMenuBar menubar = new JMenuBar();this.setJMenuBar(menubar);JMenu game = new JMenu("Game");JMenu information = new JMenu("Information");JMenuItem informationOne = new JMenuItem("Author:xjj");JMenuItem informationTwo = new JMenuItem("Blog:http://blog.csdn.net/u011479875");information.add(informationOne);information.add(informationTwo);JMenuItem jitemNew = new JMenuItem("New Game");jitemNew.setActionCommand("new");jitemNew.addActionListener(new MenuListener());jitemPause = new JMenuItem("Pause");jitemPause.setActionCommand("pause");jitemPause.addActionListener(new MenuListener());JMenuItem jitemSetBlcokGb = new JMenuItem("BlockColor");jitemSetBlcokGb.setActionCommand("blcokbg");jitemSetBlcokGb.addActionListener(new MenuListener());JMenuItem jitemBg = new JMenuItem("BackgroundColor");jitemBg.setActionCommand("bg");jitemBg.addActionListener(new MenuListener());JMenuItem jitemModel = new JMenuItem("ModelColor");jitemModel.setActionCommand("model");jitemModel.addActionListener(new MenuListener());JMenuItem jitemExit = new JMenuItem("Exit");jitemExit.setActionCommand("exit");jitemExit.addActionListener(new MenuListener());game.add(jitemNew);game.add(jitemPause);game.addSeparator();// 菜单里设置分隔线game.add(jitemSetBlcokGb);game.add(jitemBg);game.add(jitemModel);game.addSeparator();// 菜单里设置分隔线game.add(jitemExit);menubar.add(game);menubar.add(information);}//监听类,这里写成内部类的形式的好处是,能够调用直接调用上面的一下成员变量,和方法,避免传参。class MenuListener implements ActionListener {public MenuListener() {}public void actionPerformed(ActionEvent e) {if(e.getActionCommand().equalsIgnoreCase("exit")){System.exit(EXIT_ON_CLOSE);}if(e.getActionCommand().equalsIgnoreCase("pause")){if(Tetris.isPlay){tPanel.timer.stop();}else{tPanel.timer.start();}setPause(Tetris.isPlay);Tetris.isPlay=!Tetris.isPlay;}if(e.getActionCommand().equalsIgnoreCase("blcokbg")){Color c=JColorChooser.showDialog(tPanel, "颜色", Color.black);if(c.equals(bgColor)){JOptionPane.showMessageDialog(tPanel, "背景色不能和块色相同");return ;}setBlockColor(c);}if(e.getActionCommand().equalsIgnoreCase("bg")){Color c=JColorChooser.showDialog(tPanel, "颜色", Color.black);if(c.equals(blockColor) || c.equals(getModelColor())){JOptionPane.showMessageDialog(tPanel, "背景色不能和块色相同");return ;}setBGColor(c);}if(e.getActionCommand().equalsIgnoreCase("model")){Color c=JColorChooser.showDialog(tPanel, "颜色", Color.black);if(c.equals(bgColor)){JOptionPane.showMessageDialog(tPanel, "背景色不能和块色相同");return ;}setModelColor(c);}if(e.getActionCommand().equalsIgnoreCase("new")){grade = 0;rank = 1;numb = 1;setScore(0);setGrade(1);tPanel.newGame();tPanel.repaint();}}}//<span style="color:#ff0000;">方块面板。这里写成内部类一样是为了实现两个类之间的交流,而且不需要传参,这种结构很好用</span>class Model extends JPanel{public TimeListener listener = new TimeListener();//监听器public int blockType=0,turnState=0,x=0,y,score=0,delay=1000,level=1;Timer timer;//定时器Color ModelColor=Color.red,BlockColor=Color.blue;NextBlock nextBlock;//下一块块public int[][] map = new int[100][100];//地图int[][][] shapes = new int[][][] {//几种图形,这里也可以使用二维数组,只不过要换成16进制形式,然后通过位运算计算// I{ { 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },{ 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 },{ 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },{ 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 } },// S{ { 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },{ 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },{ 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },{ 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 } },// Z{ { 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },{ 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 },{ 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },{ 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 } },// J{ { 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0 },{ 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },{ 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 },{ 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },// O{ { 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },{ 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },{ 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },{ 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },// L{ { 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0 },{ 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },{ 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },{ 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },// T{ { 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },{ 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },{ 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },{ 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 } } };public Model() {nextBlk();newGame();timer = new Timer(delay, listener);timer.start();}//设置堆积块颜色public void setModelColor(Color c) {this.ModelColor=c;repaint();}//设置移动块颜色public void setBlockColor(Color c) {this.BlockColor=c;repaint();}//初始化数据public void newGame() {blockType=0;turnState=0;x=4;y=0;score=0;delay=1000;level=1;map = new int[100][100];//这里将框架向为扩充一行和一列,这样对后面判断是否越界很方便for(int i=0;i<12;i++){map[i][22]=1;}for(int i=0;i<22;i++){map[12][i]=1;}}private void nextBlk() {this.x=4;this.y=0;this.nextBlock=getNextBlock();setNext();if(crash(x, y, blockType, turnState)==0){//如果一出来就撞了,游戏结束JOptionPane.showMessageDialog(null, "Game Over!!!");timer.stop();}}//设置下一块信息public void setNext(){this.blockType=this.nextBlock.getBlockType();this.turnState=this.nextBlock.getTurnState();}//画图protected void paintComponent(Graphics g) {super.paintComponent(g);
// g.fill3DRect(220, 420, 20, 20, true);//画俄罗斯方块已经“堆积的方块”g.setColor(ModelColor);for(int i=0;i<12;i++){for(int j=0;j<22;j++){if(map[i][j]==1){g.fill3DRect(i*20, j*20, 20, 20, true);}}}//画移动的块g.setColor(BlockColor);for(int j=0;j<16;j++){if(shapes[blockType][turnState][j]==1){g.fill3DRect((j%4+x)*20, (j/4+y)*20, 20, 20,true);}}}//向下运行,如果碰撞,则结束这个块的运行,并生成下一个块,否则继续向下走private void down(){System.out.println(x+" "+y);if(crash(x,y+1,blockType,turnState)==0){add(x,y,blockType,turnState);setNextBlock();nextBlk();}else{y++;}repaint();}//向左移动public void left() {if(x>=0){x -= crash(x-1,y,blockType,turnState);repaint();}}//向右移动public void right() {if(x<12){x += crash(x+1,y,blockType,turnState);repaint();}}//旋转,为了保证在左右靠墙是都能转动,所以要对下一状态进行预判,要是下一块不安全,则将4*4的块模型整体向后退一不,然后再//旋转,但是要注意,要是横条是 要退3步public void turn() {if( crash(x,y,blockType,(turnState+1)%4)==0 ){if(blockType==0&&crash(x-3,y,blockType,(turnState+1)%4)==1){x-=3;turnState =(turnState+1)%4;}else if(crash(x-1,y,blockType,(turnState+1)%4)==1){x-=1;turnState =(turnState+1)%4;}}else{turnState =(turnState+1)%4;}repaint();}//判断是否碰撞,要是返回0,否则返回1,这里返回1和0是有讲究的,是为了左右运行时,可以直接加上这个返回值就行了,碰撞加0//没有,加1.private int crash(int x, int y, int blockType, int turnState) {for(int a=0;a<4;a++){for(int b=0;b<4;b++){if(x==-1){//这里是判断左移是是否碰壁return 0;}if((shapes[blockType][turnState][a*4+b] & map[x+b][y+a]) ==1){//这里通过位运算可以很快速度得到结果,必须同时为1才算碰撞,即,//是块和块碰撞return 0;}}}return 1;}//向已堆积块添加块private void add(int x, int y, int blockType, int turnState) {for(int a=0;a<4;a++){for(int b=0;b<4;b++){if(shapes[blockType][turnState][a*4+b]==1){map[x+b][y+a]=1;}}}tryDelLine();//分数和等级}//控制分数和等级,同时消的越多,分数加的越多,等差数列,等级的升高,伴随着速度的加快public void tryDelLine() {int count=1;for(int b=0;b<22;b++){int c=1;for(int a=0;a<13;a++){c&=map[a][b];}if(c==1){if(count==1){score +=10;}else{score +=10*count;}count++;setScore(score);for(int d=b;d>0;d--){for(int e=0;e<12;e++){map[e][d]=map[e][d-1];}}//※更改游戏难度(加快下落速度)if(score>100*numb){level++;setGrade(level);delay -= delay;if(delay==100){delay=100;}timer.setDelay(delay);}}}}//这个内部类是实现事件的监听,也是方便类与类之间的联系class TimeListener extends KeyAdapter implements ActionListener{public void actionPerformed(ActionEvent e) {if(!Tetris.isPlay){return ;}down();}public void keyPressed(KeyEvent e) {if(!Tetris.isPlay){return ;}switch(e.getKeyCode()){case KeyEvent.VK_DOWN:down();break;case KeyEvent.VK_LEFT:left();break;case KeyEvent.VK_RIGHT:right();break;case KeyEvent.VK_UP:turn(); break;}}}}public static void main(String[] args) {Tetris te = new Tetris();te.setVisible(true);}}
这篇关于俄罗斯方块(详解)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!