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);
}
}
}
}
}
二、画俄罗斯方块
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,使方块下落
三、判断是否触底
//判断是否撞到底部墙
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(),即可实现消除功能
完整代码如下:
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();
}
}
点击下载源码