- 浏览: 80086 次
文章分类
最新评论
-
wodentt:
....为什么楼主都英文的博客
用java解决百度之星移动火柴的问题 part 1 -
wensonzhou86:
思路明确,代码清晰,通俗易懂
visit 淘宝面试题:如何充分利用多核CPU,计算很大的List中所有整数的和 -
carydeepbreathing:
代码比较干净
visit 淘宝面试题:如何充分利用多核CPU,计算很大的List中所有整数的和 -
meiowei:
yangguo 写道i don't think it is a ...
用java解决百度之星移动火柴的问题 part 2 -
yangguo:
i don't think it is a good way ...
用java解决百度之星移动火柴的问题 part 2
Recently, I've seen an interesting post: http://www.iteye.com/topic/595321 , it's a java implementation of Tetris. While being a long time game player (My first game was the gold digger on an IBM XT back in 1988, subsequently I played Koei's Romance of 3 kingdoms, from II - XI, among others), this is the first time I am trying a game.
Though I like this post a lot, the code there suffers multi-threading problem. While I don't claim I am a good game developer or an expert on multi-threading, I try to fix this problem. At the same time, I don't want to compete what-you-can-do-in-100-lines game, I am trying to write an easy-to-understand version.
Most of the code and the gif files are from the original author, I modified some of them.
The first class is the tetris block, for more details, check the wiki page: http://en.wikipedia.org/wiki/Tetris .
package my.test1; import java.awt.Image; import javax.swing.ImageIcon; public class TetrisBlock { public int type; public int orientation; public int color = 1; public int x; public int y; private static int[][][][] blockmeshs = { { {{0, 1, 0, 0}, {0, 1, 0, 0}, {0, 1, 0, 0}, {0, 1, 0, 0}},/* l */ {{0, 0, 0, 0}, {1, 1, 1, 1}, {0, 0, 0, 0}, {0, 0, 0, 0}}},/*-*/ { {{0, 0, 0, 0}, {1, 1, 0, 0}, {0, 1, 1, 0}, {0, 0, 0, 0}},/* z */ {{0, 0, 0, 0}, {0, 0, 1, 0}, {0, 1, 1, 0}, {0, 1, 0, 0}}},/* z| */ { {{0, 0, 0, 0}, {0, 1, 1, 0}, {1, 1, 0, 0}, {0, 0, 0, 0}},/* xz */ {{0, 1, 0, 0}, {0, 1, 1, 0}, {0, 0, 1, 0}, {0, 0, 0, 0}}},/* xz| */ { {{0, 0, 0, 0}, {0, 1, 1, 0}, {0, 1, 1, 0}, {0, 0, 0, 0}}},/** []*/ { {{0, 1, 1, 0}, {0, 1, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 0}}, {{0, 0, 0, 0}, {1, 1, 1, 0}, {0, 0, 1, 0}, {0, 0, 0, 0}}, {{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}}},/* f */ { {{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}}, {{0, 1, 0, 0}, {0, 1, 0, 0}, {0, 1, 1, 0}, {0, 0, 0, 0}}, {{0, 0, 0, 0}, {1, 1, 1, 0}, {1, 0, 0, 0}, {0, 0, 0, 0}}},/* xf */ { {{0, 1, 0, 0}, {1, 1, 1, 0}, {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, 0, 0, 0}, {1, 1, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 0}}, {{0, 1, 0, 0}, {1, 1, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 0}} /* t */ } }; private static Image[] images = { new ImageIcon("domaintest/pics/red.gif").getImage(), // this is just a place holder, not used. new ImageIcon("domaintest/pics/lightblue.gif").getImage(), new ImageIcon("domaintest/pics/pink.gif").getImage(), new ImageIcon("domaintest/pics/blue.gif").getImage(), new ImageIcon("domaintest/pics/orange.gif").getImage(), new ImageIcon("domaintest/pics/green.gif").getImage(), new ImageIcon("domaintest/pics/red.gif").getImage() // this is the real red image }; public static Image image(int color) { return images[color]; } public static int blocksize() { return 6; } // the length of the really used images. public boolean isOccupied(int row, int col) { return blockmeshs[type][orientation][row][col] != 0; } public void rotate() { orientation++; if (orientation >= blockmeshs[type].length) orientation = 0; } public void settle(TetrisBoard tetrisBoard) { int[][] b = blockmeshs[type][orientation]; for (int i=0; i<4; i++) { for (int j=0; j<4; j++) { int a = b[i][j]; if (a != 0) { tetrisBoard.settle(y+i, x+j, color); } } } for (int i=y+3; i>y; i--) { tetrisBoard.removeFilledRow(i); } } public boolean canMoveDown(TetrisBoard tetrisBoard) { int[][] b = blockmeshs[type][orientation]; int yy = y + 1; for (int i=0; i<4; i++) { for (int j=0; j<4; j++) { if (yy + i >= tetrisBoard.length() && b[i][j] != 0) return false; if (yy+i < tetrisBoard.length() && x+j < tetrisBoard.width() && tetrisBoard.isOccupied(yy+i, x+j) && b[i][j] != 0) return false; } } return true; } public boolean canMoveLeft(TetrisBoard tetrisBoard) { int[][] b = blockmeshs[type][orientation]; int xx = x - 1; for (int i=0; i<4; i++) { for (int j=0; j<4; j++) { if (xx + j <= -1 && b[i][j] != 0) return false; if (y+i < tetrisBoard.length() && xx+j < tetrisBoard.width() && tetrisBoard.isOccupied(y+i, xx+j) && b[i][j] != 0) return false; } } return true; } public boolean canMoveRight(TetrisBoard tetrisBoard) { int[][] b = blockmeshs[type][orientation]; int xx = x + 1; for (int i=0; i<4; i++) { for (int j=0; j<4; j++) { if (xx + j >= tetrisBoard.width() && b[i][j] != 0) return false; if (y+i < tetrisBoard.length() && xx+j < tetrisBoard.width() && tetrisBoard.isOccupied(y+i, xx+j) && b[i][j] != 0) return false; } } return true; } public boolean canRotate(TetrisBoard tetrisBoard) { int oo = orientation + 1; if (oo >= blockmeshs[type].length) oo = 0; int[][] b = blockmeshs[type][oo]; for (int i=0; i<4; i++) { for (int j=0; j<4; j++) { if (y+i < tetrisBoard.length() && x+j < tetrisBoard.width() && tetrisBoard.isOccupied(y+i, x+j) && b[i][j] != 0) return false; } } return true; } }
The tetris block is encoded in a 4X4 matrix (look at the 1's in the matrix). All rotations of each block form an array. All such arrays form a bigger array called blockmeshs. There should be 7 images for 7 different blocks, but I just use the six gifs from the original post.
Though this class has >200 line of code, half of them are static data, so I can live with it. This class can be unit-tested without Swing.
The TetrisBoard class is:
package my.test1; public class TetrisBoard { private int length = 21; private int width = 10; public int[][] board = new int[length][width]; public int width() { return width; } public int length() { return length;} public boolean isOccupied(int row, int col) { if (row < 0 || row >= length || col < 0 || col >= width) return false; return board[row][col] != 0; } public int colorOf(int row, int col) { return board[row][col]; } public void settle(int row, int col, int color) { if (row < length && col < width) board[row][col] = color; } public void removeFilledRow(int row) { if (row >= length) return; boolean notFilled = false; for (int j=0; j<board[0].length; j++) { if (board[row][j] == 0) { notFilled = true; break; } } if (!notFilled) { for (int j=row; j>0; j--) System.arraycopy(board[j-1], 0, board[j], 0, board[0].length); } } }
These two classes are tightly coupled. I choose to put some methods in one rather than another, the main motivation is performance (multi dimension array access can be slow, though it doesn't matter much in this case).
The next class is the composition of the above two:
package my.test1; /** * Other features, such as scores, next shape, stop/restart, change speed */ import java.util.Random; public class Tetris { public TetrisBlock movingBlock = new TetrisBlock(); public TetrisBoard board = new TetrisBoard(); public boolean finished = false; private Random generator = new Random(); public synchronized void play() { if (movingBlock.canMoveDown(board)) movingBlock.y++; else { if (movingBlock.y == 0) { finished = true; } else { movingBlock.settle(board); newBlock(); } } } private void newBlock() { movingBlock = new TetrisBlock(); movingBlock.x = 3; movingBlock.y = 0; movingBlock.type = generator.nextInt(TetrisBlock.blocksize()+1); movingBlock.color = generator.nextInt(TetrisBlock.blocksize()) + 1; // should be movingBlock.orientation = 0; } }
The method play() is synchronized because the data can be modified by this class and user input (arrow keys).
Now we are done with the game logic, it's time to work on the GUI. The first class is the drawing class:
package my.test1; import java.awt.Dimension; import java.awt.Graphics; import javax.swing.JPanel; public class TetrisPanel extends JPanel { public Tetris tetris; public TetrisPanel(Tetris tetris) { super(); this.setFocusable(true); this.setPreferredSize(new Dimension(150, 315)); this.tetris = tetris; addKeyListener(new TetrisGuiListener(tetris, this)); } public void paintComponent(Graphics g) { super.paintComponent(g); g.fillRect(0, 0, 150, 315); for (int i=0; i<tetris.board.length(); i++) { for (int j=0; j<tetris.board.width(); j++) { if (tetris.board.isOccupied(i, j)) g.drawImage(TetrisBlock.image(tetris.board.colorOf(i, j)), j * 15, i * 15 , null); } } for (int i=0; i<4; i++) { for (int j=0; j<4; j++) { if (tetris.movingBlock.isOccupied(i, j) && (i + tetris.movingBlock.y) <= 21 && !tetris.board.isOccupied(i + tetris.movingBlock.y, j + tetris.movingBlock.x)) g.drawImage(TetrisBlock.image(tetris.movingBlock.color), ((j + tetris.movingBlock.x) * 15), ((i + tetris.movingBlock.y) * 15) , null); } } } }
We just override the paintComponent() method with our logic. The second part of it is a little nonintuitive because we need a special logic to write the last block. My current design is to draw the partial image, but others could choose not to draw at all.
The listener class is as follows:
package my.test1; import java.awt.Component; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; public class TetrisGuiListener implements KeyListener { private Tetris tetris; private Component gui; public TetrisGuiListener(Tetris tetris, Component gui) { this.tetris = tetris; this.gui = gui; } public void keyPressed(KeyEvent e) { synchronized(tetris) { if (e.getKeyCode() == 65 || e.getKeyCode() == 37) { if (tetris.movingBlock.canMoveLeft(tetris.board)) tetris.movingBlock.x--; } else if (e.getKeyCode() == 68 || e.getKeyCode() == 39) { if (tetris.movingBlock.canMoveRight(tetris.board)) tetris.movingBlock.x++; } else if (e.getKeyCode() == 83 || e.getKeyCode() == 40) { if (tetris.movingBlock.canMoveDown(tetris.board)) tetris.movingBlock.y++; } else if (e.getKeyCode() == 87 || e.getKeyCode() == 38) { if (tetris.movingBlock.canRotate(tetris.board)) tetris.movingBlock.rotate(); } gui.repaint(); } } public void keyTyped(KeyEvent e) { } public void keyReleased(KeyEvent e) { } }
Again, we need to synchronize the data since this class is running on the EDT (event dispatching thread).
The next class is a wrapper of Tetris class, using SwingWorker:
package my.test1; /** * http://download.oracle.com/javase/tutorial/uiswing/concurrency/index.html * http://java.sun.com/developer/technicalArticles/javase/swingworker/ * http://www.javaworld.com/javaworld/jw-08-2007/jw-08-swingthreading.html?page=1 * http://developerlife.com/tutorials/?p=15 * http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html * http://java.sun.com/products/jfc/tsc/articles/threads/threads2.html * http://java.sun.com/products/jfc/tsc/articles/threads/threads3.html * http://stackoverflow.com/questions/1505427/multithreading-with-java-swing-for-a-simple-2d-animation * http://www.javaranch.com/journal/200410/JavaDesigns-SwingMultithreading.html * http://kenai.com/projects/trident/pages/SimpleSwingExample * http://java.sun.com/products/jfc/tsc/articles/painting/ */ import java.awt.Component; import java.util.List; import javax.swing.SwingWorker; public class TetrisGame extends SwingWorker<Tetris, Tetris> { private Tetris tetris; private Component gui; public TetrisGame(Tetris tetris, Component gui) { this.tetris = tetris; this.gui = gui; } @Override protected Tetris doInBackground() throws Exception { try { while (!tetris.finished) // && !isCancelled()) { tetris.play(); publish(tetris); try { Thread.sleep(200); } catch (Exception ex) { ex.printStackTrace(); } } return tetris; } catch (Throwable t) { t.printStackTrace(); return null; } } // This is called fro EDT @Override protected void process(List<Tetris> tetrisList) { Tetris t = tetrisList.get(tetrisList.size()-1); synchronized(t) { gui.repaint(); } } }
For more information on SwingWorker, check the links in the java doc section. This class, except the method marked, will run in a separate thread. The separate thread and EDT both modify the data in the Tetris class.
Finally, a window class to stitch everything together.
package my.test1; import java.awt.Container; import java.awt.Dimension; import java.awt.FlowLayout; import javax.swing.JFrame; import javax.swing.SwingUtilities; public class TetrisWindow extends JFrame { public TetrisWindow() { super("Tetris"); this.setDefaultCloseOperation(EXIT_ON_CLOSE); this.setPreferredSize(new Dimension(160, 355)); this.setResizable(false); Tetris tetris = new Tetris(); TetrisPanel tetrisPanel = new TetrisPanel(tetris); Container content = getContentPane(); content.setLayout(new FlowLayout()); content.add(tetrisPanel); this.pack(); TetrisGame game = new TetrisGame(tetris, tetrisPanel); game.execute(); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { new TetrisWindow().setVisible(true); } }); } }
Remember, we need to fire off the window from EDT too, so we use SwingUtilities.
Here is a screenshot.
Other references on this topic:
http://www.cs.unc.edu/~plato/COMP14/Assignments/tetris/tetris.html
http://www.ibm.com/developerworks/java/library/j-tetris/
http://gametuto.com/tetris-tutorial-in-c-render-independent/
http://zetcode.com/tutorials/javaswingtutorial/thetetrisgame/
发表评论
-
Excel Addin
2014-02-23 02:07 0When utilizing custom excell a ... -
Interview questions
2011-07-23 03:38 0Core: private int a; --> in ... -
How to design a bowling scoreboard
2011-07-22 23:26 1419I just saw this post: http:// ... -
obj xml
2011-05-27 23:13 0def record_parent(child, par ... -
obj xml 1
2011-05-27 23:11 0def obj2xml(obj, objname=Non ... -
visit 淘宝面试题:如何充分利用多核CPU,计算很大的List中所有整数的和
2010-07-16 00:11 2095I've seen these two: http://ww ... -
JSE links
2007-07-13 08:09 1942Tutorials: http://javaboutique. ... -
TCP, NIO, concurrency
2007-05-15 21:57 30Raw servers http://cindy.sour ... -
Exceptions
2007-03-24 22:52 31In batch modes, sometimes, we d ... -
Java enums in JDK 1.4
2007-03-04 05:59 1792Constantly, we need to define e ...
相关推荐
《 Tetris 游戏源码解析》 Tetris(俄罗斯方块)是一款经典且具有全球影响力的电子游戏,由Alexey Pajitnov在1984年使用BASIC编程语言设计。本篇将深入探讨使用Delphi 7开发的"Tetris_tetris_tetrisgame_源码",...
nand2tetris.zip 是 Nand2Tetris 课程 所需全部软件包,版本是 Version 2.6 软件源下载地址页面 https://www.nand2tetris.org/software Nand2Tetris 是一节面向所有受众,零计算机基础,基于动手实践的从最基本...
【Tetris MVC 代码包】是一个以Swing框架实现的、基于MVC设计模式的俄罗斯方块游戏项目。在本项目中,我们主要探讨以下几个关键知识点: 1. **MVC设计模式**: - **Model(模型)**:负责处理游戏的核心逻辑,包括...
《Tetris—Windows C语言实现》 Tetris,又称俄罗斯方块,是一款风靡全球的经典电子游戏,由阿列克谢·帕基特诺夫于1984年在苏联发明。这款游戏以其简单的规则、高度的策略性和无尽的乐趣,吸引了无数玩家。在本文...
"ASCII-Tetris-master_原始_tetris_"就是一个这样的项目,它提供了一种在Linux环境下用ASCII字符实现的俄罗斯方块代码,对于想要学习基础编程、理解控制台应用开发或者熟悉C语言的朋友来说,这是一个很好的起点。...
Qt for Tetris 是一个使用 Qt4 框架开发的经典游戏——俄罗斯方块的实现项目。这个项目特别适用于 CentOS 6.6 操作系统,并且它展示了如何利用 Qt 工具集来创建一个功能完备、用户友好的图形界面游戏。Qt 是一个跨...
【标题】"俄罗斯方块Tetris3D"是一款经典的3D版本的俄罗斯方块游戏,它将我们熟知的传统平面游戏提升到了一个新的维度。这款3D版本为玩家提供了更为丰富的视觉体验,使得游戏玩法和策略都有所变化。 【描述】"一个...
《Yet Another Tetris Implementation》是一款基于Java开发的开源游戏项目,它为玩家提供了一种全新的体验方式,将经典的俄罗斯方块游戏推向了新的高度。该项目不仅实现了基础的2D单人和多人游戏模式,还引入了创新...
《 Tetris俄罗斯方块:游戏背后的编程艺术与技术解析》 Tetris,这个由苏联程序员阿列克谢·帕基特诺夫于1984年创作的经典游戏,至今仍以其独特的魅力吸引着全球玩家。它不仅仅是一款游戏,更是编程世界中的一个...
《Tetris(俄罗斯方块)开发详解》 在IT领域,经典的Tetris(俄罗斯方块)游戏一直是程序员们热衷的项目之一,它以其简单而富有挑战性的玩法深受玩家喜爱。本篇文章将深入探讨如何在Windows XP平台上,利用Visual ...
game tetris 俄罗斯方块 shell bash
【标题】"陈广 俄罗斯方框 Tetris C#代码" 涉及的是一个使用C#编程语言实现的经典游戏——俄罗斯方块的项目。在这个项目中,开发者陈广运用了C#的基础语法、面向对象编程思想以及图形用户界面(GUI)设计来创建一个...
本项目名为"Tetris-master.zip",它是一个使用Java编程语言开发的俄罗斯方块游戏。这个大作业不仅包含了游戏的完整代码,还附带了作业报告,为开发者提供了深入理解游戏逻辑和Java编程技巧的机会。 一、Java语言...
【标题】:“tetris javascript 俄罗斯方块”指的是使用JavaScript编程语言实现的在线版俄罗斯方块游戏。这个项目展示了如何利用HTML5 Canvas或者DOM元素来创建一个动态的游戏界面,并通过JavaScript处理游戏逻辑,...
《 Tetris 俄罗斯方块小游戏开发详解》 俄罗斯方块,这款经典的益智游戏自诞生以来,便以其简单易上手、玩法深邃的特点深受玩家喜爱。本文将深入探讨使用Java语言开发俄罗斯方块小游戏的过程,包括游戏的核心逻辑、...
《 Tetris游戏的Java实现详解 》 Tetris,又称俄罗斯方块,是一款风靡全球的经典电子游戏,它的简洁规则和无穷魅力吸引了无数玩家。本文将深入探讨如何使用Java编程语言来实现这一经典游戏。 一、Java语言基础 在...
《 Tetris游戏编程实践:基于C语言的Windows实现》 在计算机科学的世界里,经典游戏“俄罗斯方块”(Tetris)不仅是娱乐的源泉,更是学习编程的绝佳案例。本项目提供了一个C语言编写的Tetris游戏,特别适用于...
本文档 归纳总结了 Nand2Tetris 的 Projects 的课件 lecture 和 课外阅读 book 其中课外阅读book只在第一部分Part1(前六章)有。课件lecture,包括Part1 和 Part2 共12章。 另外附带了 课外阅读 的完整 《The ...
《 Tetris(俄罗斯方块):JavaScript编程的艺术与实践》 俄罗斯方块,这款由Alexey Pajitnov在1984年创造的经典游戏,以其简洁的设计和无尽的挑战性,一直深受全球玩家的喜爱。在这个项目中,我们将探讨如何使用纯...
《 Tetris_WPF.zip_Sharp_WPF:C#与WPF技术在游戏开发中的应用解析》 在IT领域,游戏开发是一项技术含量高、创意丰富的任务。本项目“Tetris_WPF.zip_Sharp_WPF”是利用C#编程语言和Windows Presentation ...