java学年设计小游戏——俄罗斯方块 java小游戏俄罗斯方块的模块设计和代码实现
大一学年完成的java小游戏——俄罗斯方块,实现功能如下:
一、游戏背景
(一)欢迎界面
在游戏窗口弹出前创立一个欢迎界面的对话框,并为此窗口设置背景图片和开始按钮,主要使用JDialog类、JButton类及JPanel类和JLabel类,创建出图片标签,将按钮放在图片标签上,最后将标签放在创建的面板上,将面板添加到窗口中,实现面版层次的组合。
(背景图片及开始按钮的添加代码如下:)
(二)游戏窗口
定义Tereris类继承使用JFrame类的创建窗口的方法 initWindow,设置窗口的属性等,窗口大小设置为600*850,标题为“趣味俄罗斯方块游戏”,窗口位置为居中,窗口大小设置不可变。
(三)背景方格
使用JPanel类创建对象初始化游戏面板:设置背景方格为30行15列,为背景方格添加文本域和键盘监听事件,方格颜色为白色,游戏边界。在困难模式下,背景方格中会随机产生不能被消除的方块为固定障碍物。
(对游戏面板划分背景方格的代码如下:)
(四)背景音乐
背景音乐主要使用音频输入流AudioInputStream来读取wave.格式的音频并通过Clip接口实现播放,背景音乐的播放与暂停均作为单独函数以便在游戏暂停、继续和游戏停止时实现相应的播放和暂停效果。
(背景音乐的播放和暂停代码如下:)
(五)说明面板
1、创建说明面板:创建JPanel对象即左说明面板,设置左说明面板的行数列数为24行1列,在说明面板中添加游戏说明:
按空格键,方格变形
按左箭头,方格左移
按右箭头,方格右移
按下箭头,方格下移(即加速)
添加游戏状态说明、游戏分数说明,设置游戏状态、游戏分数的字体颜色为蓝色,将左说明面板添加到游戏窗口中。
交互式设计:在左说明面板添加了普通模式、困难模式、游戏暂停(继续)和音乐开启和关闭、退出游戏按钮,采取了由一个焦点连接的键盘按键和鼠标按钮的设计对俄罗斯方块的旋转变形、游戏模式的选择和游戏状态的改变进行控制。注意:由于javaGUI界面的监听KeyListener接口的焦点问题,添加按钮之后默认按钮为焦点。因此,键盘的控制需要先用鼠标点击游戏窗口申请焦点,每次点击按钮之后也需要点击窗口重新申请,申请一次即可连续使用键盘控制。
(六)障碍物
方格由于触碰底部边框或其他方格而停止移动后成为障碍物,形成障碍物之后,将方格对应的背景方格的文本域数字改为1(默认为零),并将背景方格更改为方块颜色。
(七)障碍物的消除
规定当背景方格中障碍物填满整行时,将此行的背景方格消除,上方障碍物按行下落覆盖。即将该行背景方格及以上行方格的文本数字改为上一行的方格的数字,并对该行及以上行按照数字重新涂色。
二、俄罗斯方块
(一)方块绘制
1、方块形状的设计及绘制原理:通过十六进制的数字来设计出4*4的16宫格,通过16宫格中每一位数字(0或1)来对方格进行涂色,将1的方格涂为蓝色,用一维数组存储22种已设计的方块形状(即相应的16进制数字),使用Random类来随机出方块的索引,绘制出对应的方块。
(二)方块下移原理
设置方块下移的初始位置,即16宫格中第一个方格的位置坐标,先判断是否可以下落,即是否触碰障碍物,若可以下落,则每隔一定时间对代表方块的16宫格进行颜色清除(变为白色),再根据下移一行后的方块坐标,遍历16宫格和对应的背景方格重新绘制出方块。
(三)方块移动速度控制
方块下移速度由线程休眠时间控制,可以解释为更新方块的时间间隔,对方块下移速度的控制也为对线程休眠时间的控制。
(四)键盘控制方块的左右移动、向下加速移动
左移则遍历16宫格中的每一个方格,将数字为1的方格左边的背景方格的文本更改为1,全部更改后,进行方格的清除和重新绘制,右移、下移原理同上。
(五)键盘控制方块的变形(即旋转)
键盘监听到“space”即将方块进行旋转,旋转原理为当前方块索引值的更改,根据当前方块旋转后的形状进行分类讨论,将旋转后对应的方块的索引值赋值给当前方块,再按索引对应的方块形状对16宫格进行清除和重新绘制。
(六)方块停止移动
遍历16宫格中的每一个方块,判断其对应的背景方格下面的一行的方块是否为障碍物,若为障碍物则将对应的背景方格改为1并重新绘制16宫格对应的背景方格。同时重新生成方块并且下落。
三、游戏状态
(一)开始游戏
变量isrunning用来判断游戏是否结束,设立永真循环,若游戏为非结束状态,继续运行游戏。
(二)暂停游戏(继续游戏)
设置布尔类型的变量game_pause,若键盘监听到暂停键或动作监听按下“游戏暂停(继续)”按钮就更改一次game_pause的值(继续游戏也即监听到两次)。
(三)困难模式
当点击困难模式的按钮时会在游戏面板上随机生成几个不可被消除的固定障碍物,这些障碍物会阻挡方块的下落,同时也会加快方块下落的速度,让玩家的思考时间减少,从而提高游戏的难度。
(四)游戏结束
当背景方格中第三行出现文本为1的方格时,将isrunning的值改为false,此时跳出游戏开始的永真循环,游戏结束。
(五)再来一局
当游戏结束之后会弹出一个再来一局的界面,此时玩家可自行选择重新进行游戏和退出游戏,若玩家选择重新进行游戏,玩家重玩一局时可刷新自己的最高分记录。
四、游戏分数
(一)消除加分机制(详见上障碍物消除)
当满足障碍物可消除时,游戏分数增加100分,相应的方块下落速度增加即减少线程休眠时间time。
(二)最高分机制
当玩家没有退出游戏,而是在游戏结束后选择再来一局时,玩家可多次进行游戏且之前的产生的最终游戏分数最高者保存到最高分里面。
五、游戏设计界面
(一)欢迎界面:
(二)游戏界面(游戏边界设置为无色):
全部代码如下:使用IDE:IDEA(设计不易,拿代码吱一声)
(声明:本作业为博主本人与另一名同学共同设计完成)
#大一学年设计作业--俄罗斯方块小游戏--java语言
package com.sxt;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import javax.sound.sampled.*;
import java.io.*;
public class Tertris extends JFrame implements KeyListener{
//游戏的行数30,列数15
private static final int game_x = 30;
private static final int game_y = 15;
//存放背景音乐
private Clip bgMusic;
private Clip effMusic;
//文本域数组,背景方格
JTextArea[][] text;
//二维数组,记录方格的属性,0或1
int[][] data;
//显示游戏分数的标签
JLabel label;
//显示游戏状态的标签
JLabel label1;
//当前游戏得分最高记录
JLabel label3;
String label4= "开始游戏!";
String label5= "再来一局!";
String label6= "普通模式";
String label7= "困难模式";
//困难模式状态码
int state=0;
//音效状态码
int stateMusic=0;
String label8= "游戏暂停(继续)";
String label9= "退出游戏";
String label10= "关闭(开启)所有音效";
//用于判断游戏是否结束
static boolean isrunning=true;
//用于存储所有的方块的数组
int[] allRect;
//用于存储当前方块的变量
int rect;
//存储下一个方块的变量
int next;
//线程的休眠时间
int time = 1000;
//表示方块坐标
int x, y;
//该变量用于计算得分
int score = 0;
//静态常量记录最高得分
public static int maxscore;
//定义一个标志变量,用于判断游戏是否暂停
boolean game_pause = false;
//定义一个变量用于记录按下暂停键的次数
int pause_times = 0;
//游戏欢迎界面 窗口类 包括开始按钮和背景图片
private class WelcomeWindow extends JDialog {
private JButton startButton;
public WelcomeWindow(Frame owner,String label) {
super(owner, "欢迎来到俄罗斯方块游戏!", true); // 模态对话框
setSize(360, 478);
setLocationRelativeTo(owner);
setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); // 阻止默认的关闭操作
// 创建启动按钮
startButton = new JButton(label);
startButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// 关闭欢迎窗口
dispose();
}
});
// 创建背景图片标签
JLabel backgroundLabel = new JLabel();
ImageIcon backgroundImage = new ImageIcon("resources/001.jpg"); // 设置背景图片
backgroundLabel.setIcon(backgroundImage);
// 设置背景标签的布局为绝对布局,以便精确放置按钮
backgroundLabel.setLayout(null);
backgroundLabel.setBounds(0, 0, backgroundImage.getIconWidth(), backgroundImage.getIconHeight());
// 设置按钮在背景图片上的位置
int buttonWidth = 100;
int buttonHeight = 30;
int buttonX = 130;
int buttonY = 300;
// 设置按钮的位置和大小
startButton.setBounds(buttonX, buttonY, buttonWidth, buttonHeight);
// 将按钮添加到背景图片标签上
backgroundLabel.add(startButton);
// 设置背景标签为不透明
backgroundLabel.setOpaque(true); // 设置为true以便看到背景图片
// 创建内容面板并添加背景图片标签
JPanel contentPanel = new JPanel();
contentPanel.setLayout(new BorderLayout());
contentPanel.add(backgroundLabel, BorderLayout.CENTER);
// 将内容面板添加到对话框中
this.add(contentPanel);
//关闭窗口则关闭程序
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
// 当窗口关闭时,结束程序
System.exit(0);
}
});
}
}
//
//游戏窗口
public void initWindow() {
//设置窗口大小
this.setSize(450,650);
//设置窗口可见
requestFocus();
this.setVisible(true);
//设置窗口居中
this.setLocationRelativeTo(null);
//设置释放窗体
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//设置窗口大小不可变
this.setResizable(false);
//设置标题
this.setTitle("我的俄罗斯方块游戏");
}
//初始化游戏界面
public void initGamePanel() {
//划分背景方格
JPanel game_main = new JPanel();
game_main.setLayout(new GridLayout(game_x,game_y,5,5));
requestFocus();
game_main.setVisible(true);
//初始化面板
for (int i = 0 ; i < text.length ; i++) {
for (int j = 0 ; j < text[i].length ;j++) {
//设置文本域的行列数
text[i][j] = new JTextArea(game_x,game_y);
//设置文本域的背景颜色
text[i][j].setBackground(Color.white);
//添加键盘监听事件
text[i][j].addKeyListener(this);
//初始化游戏边界
if (j == 0 || j == text[i].length-1 || i == text.length-1) {
text[i][j].setBackground(Color.getColor("#ccffff"));
data[i][j] = 1;//背景方格的属性,障碍物
}
//设置文本区域不可编辑
text[i][j].setEditable(false);
//文本区域添加到主面板上
game_main.add(text[i][j]);
}
}
game_main.requestFocusInWindow();
//添加到窗口中
this.setLayout(new BorderLayout());
this.add(game_main,BorderLayout.CENTER);
}
//初始化游戏的左说明面板
public void initExplainPanel() {
//创建游戏的左说明面板
JPanel explain_left = new JPanel();
explain_left.setLayout(new GridLayout(25, 1));
//初始化左说明面板
//在左说明面板,添加说明文字
explain_left.add(new JLabel("------------------------------------"));
explain_left.add(new JLabel("游戏说明:"));
explain_left.add(new JLabel("按空格键,方块变形"));
explain_left.add(new JLabel("按左箭头,方块左移"));
explain_left.add(new JLabel("按右箭头,方块右移"));
explain_left.add(new JLabel("按下箭头,方块下落"));
explain_left.add(new JLabel("按下键盘p键,游戏暂停!"));
//设置标签的内容为蓝色字体
label1.setForeground(Color.BLUE);
label.setForeground(Color.BLUE);
label3.setForeground(Color.BLUE);
//把游戏状态标签,游戏分数标签,添加到左说明面板
explain_left.add(new JLabel("------------------------------------"));
explain_left.add(label);
explain_left.add(label3);
explain_left.add(new JLabel("------------------------------------"));
explain_left.add(label1);
explain_left.add(new JLabel("------------------------------------"));
//将左说明面板添加到窗口的左侧
this.add(explain_left, BorderLayout.WEST);
// 创建普通模式按钮按钮
JButton commonButton = new JButton(label6);
commonButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (time < 1000){
time += 500;
}
}
});
// 创建困难模式按钮按钮
JButton difficultyButton = new JButton(label7);
difficultyButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
state++;
time-=500;
if(state==1){
Random random = new Random();
for(int i=0;i>= 1;
}
m++;
n = n - 4;
}
}
//移除某一行的所有方块,令以上方块掉落的方法
public void removeRow(int row) {
int temp = 100;
for (int i = row;i >= 1;i--) {
for (int j = 1;j temp) {
time -= (temp/4);
}
score += temp;
//显示变化后的分数
label.setText("游戏得分为: " + score);
if(score>maxscore){
maxscore=score;
}
label3.setText("当前游戏最高得分为: " + maxscore);
}
//刷新移除某一行后的游戏界面的方法
public void reflesh(int row) {
//遍历row行以上的游戏区域
for (int i = row;i >= 1;i--) {
for (int j = 1;j 0) {
//清除上一层方块
clear(m-1,n);
}
//重新绘制方块
draw(m,n);
}
//清除方块掉落后,上一层有颜色的地方的方法
public void clear(int m,int n) {
//定义变量
int temp = 0x8000;
for (int i = 0;i < 4;i++) {
for (int j = 0;j < 4;j++) {
if ((temp & rect) != 0) {
text[m][n].setBackground(Color.WHITE);
}
n++;
temp >>= 1;
}
m++;
n = n - 4;
}
}
//重新绘制掉落后方块的方法
public void draw(int m,int n) {
//定义变量
int temp = 0x8000;
for (int i = 0;i < 4;i++) {
for (int j = 0;j < 4;j++) {
if ((temp & rect) != 0) {
text[m][n].setBackground(Color.ORANGE);
}
n++;
temp >>= 1;
}
m++;
n = n - 4;
}
}
//暂停和旋转的键盘监听
@Override
public void keyTyped(KeyEvent e) {
//控制游戏开始
//控制游戏暂停
if (e.getKeyChar() == 'p') {
//判断游戏是否结束
if (!isrunning) {
return;
}
pause_times++;
//判断按下一次,暂停游戏
if (pause_times == 1) {
game_pause = true;
stopMusic();
label1.setText("游戏状态: 暂停中!");
}
//判断按下两次,继续游戏
if (pause_times == 2) {
game_pause = false;
pause_times = 0;
openMusic();
label1.setText("游戏状态: 正在进行中!");
}
}
//控制方块进行变形
if (e.getKeyChar() == KeyEvent.VK_SPACE) {
//判断游戏是否结束
if (!isrunning) {
return;
}
//判断游戏是否暂停
if (game_pause) {
return;
}
//定义变量,存储目前方块的索引
int old;
for (old = 0;old < allRect.length;old++) {
//判断是否是当前方块
if (rect == allRect[old]) {
break;
}
}
//定义变量,存储变形后方块
int next;
//判断是方块
if (old == 0 || old == 7 || old == 8 || old == 9) {
return;
}
//清除当前方块
clear(x,y);
if (old == 1 || old == 2) {
next = allRect[old == 1 ? 2 : 1];
if (canTurn(next,x,y)) {
rect = next;
}
}
if (old >= 3 & old 6 ? 3 : old + 1];
if (canTurn(next,x,y)) {
rect = next;
}
}
if (old == 10 || old == 11) {
next = allRect[old == 10 ? 11 : 10];
if (canTurn(next,x,y)) {
rect = next;
}
}
if (old == 12 || old == 13) {
next = allRect[old == 12 ? 13 : 12];
if (canTurn(next,x,y)) {
rect = next;
}
}
if (old >= 14 & old 17 ? 14 : old + 1];
if (canTurn(next,x,y)) {
rect = next;
}
}
if (old == 18 || old == 19) {
next = allRect[old == 18 ? 19 : 18];
if (canTurn(next,x,y)) {
rect = next;
}
}
if (old == 20 || old == 21) {
next = allRect[old == 20 ? 21 : 20];
if (canTurn(next,x,y)) {
rect = next;
}
}
//重新绘制变形后方块
draw(x,y);
}
}
//判断方块此时是否可以变形的方法
public boolean canTurn(int a,int m,int n) {
//创建变量
int temp = 0x8000;
//遍历整个方块
for (int i = 0;i < 4;i++) {
for (int j = 0;j < 4;j++) {
if ((a & temp) != 0) {
if (data[m][n] == 1) {
return false;
}
}
n++;
temp >>= 1;
}
m++;
n = n -4;
}
//可以变形
return true;
}
@Override
public void keyPressed(KeyEvent e) {
//方块进行左移
if (e.getKeyCode() == 37) {
//判断游戏是否结束
if (!isrunning) {
return;
}
//判断游戏是否暂停
if (game_pause) {
return;
}
//方块是否碰到左墙壁
if (y >= 1;
}
}
//首先清除目前方块
clear(x,y);
y--;
draw(x,y);
}
//方块进行右移
if (e.getKeyCode() == 39) {
//判断游戏是否结束
if (!isrunning) {
return;
}
//判断游戏是否暂停
if (game_pause) {
return;
}
//定义变量
int temp = 0x8000;
int m = x;
int n = y;
//存储最右边的坐标值
int num = 1;
for (int i = 0;i < 4;i++) {
for (int j = 0;j < 4;j++) {
if ((temp & rect) != 0) {
if (n > num) {
num = n;
}
}
n++;
temp >>= 1;
}
m++;
n = n - 4;
}
//判断是否碰到右墙壁
if (num >= (game_y-2)) {
return;
}
//方块右移途中是否碰到别的方块
temp = 0x8000;
for (int i = x;i < x + 4;i++) {
for (int j = y;j < y + 4;j++) {
if ((temp & rect) != 0) {
if (data[i][j+1] == 1) {
return;
}
}
temp >>= 1;
}
}
//清除当前方块
clear(x,y);
y++;
draw(x,y);
}
//方块进行下落
if (e.getKeyCode() == 40) {
//判断游戏是否结束
if (!isrunning) {
return;
}
//判断游戏是否暂停
if (game_pause) {
return;
}
//判断方块是否可以下落
if (!canFall(x,y)) {
return;
}
clear(x,y);
//改变方块的坐标
x++;
draw(x,y);
}
}
@Override
public void keyReleased(KeyEvent e) {
}
}