?個(gè)人簡介
???????個(gè)人主頁:陳橘又青
?????♂?博客記錄心情,代碼編寫人生。
??如果文章對你有用,麻煩關(guān)注點(diǎn)贊收藏走一波,感謝支持!
??強(qiáng)力推薦我平時(shí)學(xué)習(xí)編程和準(zhǔn)備面試的刷題網(wǎng)站:點(diǎn)這里!
前言
大家好,今天用Java編程實(shí)現(xiàn)一個(gè)GUI界面的經(jīng)典俄羅斯方塊游戲,以下是完整的開發(fā)思路,供大家學(xué)習(xí)交流。
效果展示
目錄
一、效果展示
??1.游戲界面
??2.游戲結(jié)束
二、項(xiàng)目介紹
1.項(xiàng)目背景
2.總體需求
??①游戲邏輯
??②游戲過程
??③其它功能
三、代碼展示 ?
??1.主類和窗口設(shè)計(jì)
??2.子類和方法實(shí)現(xiàn)
四、項(xiàng)目總結(jié)
一、效果展示
1.游戲界面
2.游戲結(jié)束
二、項(xiàng)目介紹
1.項(xiàng)目背景
“俄羅斯方塊”是一個(gè)經(jīng)典的游戲,在游戲中,由小方塊組成的不同形狀的板塊陸續(xù)從屏幕上方落下來,玩家通過調(diào)整板塊的位置和方向,使它們在屏幕底部拼出完整的橫條。這些完整的橫條會隨即消失,給新落下來的板塊騰出空間,同時(shí),玩家得到分?jǐn)?shù)獎(jiǎng)勵(lì)。未被消除掉的方塊不斷堆積起來,一旦堆到屏幕頂端,玩家便告輸,游戲結(jié)束。
2.總體需求
①游戲邏輯
(1)方塊的誕生需要用隨機(jī)原理,另外,它需要初始化的被放置在游戲界面的頂部。
(2)方塊需要自動下降,在下降過程中,還需判斷它是否與周圍環(huán)境發(fā)生沖突,能否繼續(xù)下降。
(3)方塊本身可以變形,變形后的方塊具有不同的數(shù)據(jù),判斷的方式又會不一樣。
(4)當(dāng)用戶一直按住 ↓ 鍵的時(shí)候,方塊需要持續(xù)往下掉。
②游戲過程
(1) 左右操作。需要監(jiān)聽KeyEvent,讓方塊左右移動,直到碰到邊界。
(2) 變形操作。也要監(jiān)聽KeyEvent,讓方塊切換形狀。
(3) 下降操作。同樣監(jiān)聽KeyEvent,讓方塊快速的下降。
(4)當(dāng)誕生的方塊出世與其他方塊沖突時(shí),判定游戲結(jié)束。
③其它功能
(1)用戶可以通過單擊界面上提供的按鈕,隨時(shí)暫停與繼續(xù)游戲 。
(2)用戶可以通過單機(jī)界面上提供的按鈕,重新開始游戲。
三、代碼展示 ?
1.主類和窗口設(shè)計(jì)
設(shè)計(jì)游戲窗口的圖形化界面以及各功能按鈕。
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import javax.imageio.*;
import javax.swing.*;
import javax.swing.Timer;
public class MyGame extends JFrame {
public MyGame(){
GameBody gamebody=new GameBody();
gamebody.setBounds(5,10,500,600); //
gamebody.setOpaque(false);
gamebody.setLayout(null);
addKeyListener(gamebody);
add(gamebody);
int w=Toolkit.getDefaultToolkit().getScreenSize().width;
int h=Toolkit.getDefaultToolkit().getScreenSize().height;
final JButton login=new JButton(new ImageIcon("image/cxks.png"));
login.setContentAreaFilled(false);
login.setMargin(new Insets(0,0,0,0));
login.setBorderPainted(false);
login.setBounds(340,320,120,26);
gamebody.add(login);
login.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) { //登錄的按鈕
if(e.getSource()==login){
requestFocus(true); //獲得焦點(diǎn),不用失去焦點(diǎn)
gamebody.resetMap(); //重置地圖
gamebody.drawWall(); //沖重新繪制邊界墻體
gamebody.createshape(); //重新產(chǎn)生新的地圖
gamebody.setStart(false); //喚醒定時(shí)下落的線程
gamebody.score=0; //將分?jǐn)?shù)置為零
repaint();
}
}
});
final JButton pauselogin=new JButton(new ImageIcon("image/zt.png"));
pauselogin.setContentAreaFilled(false);
pauselogin.setMargin(new Insets(0,0,0,0));
pauselogin.setBorderPainted(false);
pauselogin.setBounds(340,370,120,26);
gamebody.add(pauselogin);
pauselogin.addMouseListener(new MouseListener(){ //暫停的按鈕
//鼠標(biāo)點(diǎn)擊事件,可以分別判斷不同的事件,做出不同的反應(yīng)
public void mouseClicked(MouseEvent e){
if(e.getButton()==e.BUTTON1 ){ //單擊左鍵暫停
gamebody.setStart(true); //將自動下落線程關(guān)閉
//requestFocus(true); //同時(shí)整個(gè)JFrame失去焦點(diǎn),無法操作,但可以點(diǎn)擊按鈕
}
else if(e.getButton()==e.BUTTON3 ){ //右擊暫停,繼續(xù)游戲
gamebody.setStart(false); //喚醒自動下落線程
requestFocus(true);
}
/* if(e.getClickCount()==2){ //左鍵雙擊,也可以繼續(xù)游戲
gamebody.setStart(false);
requestFocus(true);
}*/
}
public void mouseEntered(MouseEvent e){}
public void mouseExited(MouseEvent e){}
public void mousePressed(MouseEvent e){}
public void mouseReleased(MouseEvent e){}
});
setTitle("俄羅斯方塊");
setResizable(false);
setFocusable(true);
setBounds((w-500)/2,(h-600)/2,500,600);
setLayout(null);
setVisible(true);
setDefaultCloseOperation(3);
}
public static void main(String[] args) {
new MyGame();
}
2.子類和方法實(shí)現(xiàn)
①創(chuàng)建需要定義的局部變量和游戲GameBody類。
class GameBody extends JPanel implements KeyListener{
private int shapeType=-1; //定義方塊的類型 定義的為7種
private int shapeState=-1; //定義方塊為何種狀態(tài),每種都有四種狀態(tài)
private int nextshapeType=-1; //定義下一塊產(chǎn)生的類型
private int nextshapeState=-1; //定義下一塊的方塊的狀態(tài)
private final int CELL=25; //定義方格的大小
private int score=0; //定義顯示的成績
private int left; //定義初始圖形與兩邊的墻的距離
private int top; //定義初始圖形與上下墻的距離
private int i=0; //表示列
private int j=0; //表示行
public int flag=0;
public volatile boolean start=false; //暫停的判斷條件,為輕量鎖,保持同步的
Random randomcolor=new Random();
Random random=new Random();
②定義地圖的大小,初始化地圖并畫出圍墻 。
//定義地圖的大小,創(chuàng)建二位的數(shù)組
int[][] map=new int[13][23];
//初始化地圖
public void resetMap(){
for(i=0;i<12;i++){
for(j=0;j<22;j++){ //遍歷的范圍不能小
map[i][j]=0;
}
}
}
//畫圍墻的方法
public void drawWall(){
for(j=0;j<22;j++) //0到21行
{
map[0][j]=2;
map[11][j]=2; //第0行和第11行為墻
}
for(i=0;i<12;i++){ //0到11列
map[i][21]=2; //第21行劃墻
}
}
?③定義隨機(jī)的圖形種類和產(chǎn)生圖形的方法。
private final int[][][] shapes=new int[][][]{
// i
{ { 0, 0, 0, 0, 1, 1, 1, 1, 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, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 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
{ { 0, 1, 0, 0, 1, 1, 1, 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 },
{ 1, 1, 1, 0, 0, 1, 0, 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 } }
};
//產(chǎn)生新圖形的方法
public void createshape(){
if(shapeType==-1&&shapeState==-1){
shapeType = random.nextInt(shapes.length);
shapeState = random.nextInt(shapes[0].length);
}else{
shapeType=nextshapeType;
shapeState=nextshapeState;
}
nextshapeType = random.nextInt(shapes.length);
nextshapeState = random.nextInt(shapes[0].length);
//shapeType=(int)(Math.random()*1000)%7; //在7中類型中隨機(jī)選取
//shapeState=(int)(Math.random()*1000)%4; //在四種狀態(tài)中隨機(jī)選取
left=4; top=0; //圖形產(chǎn)生的初始位置為(4,0)
④判斷游戲?qū)崟r(shí)進(jìn)行狀態(tài) 。
if(gameOver(left,top)==1){
resetMap();
drawWall();
score=0;
JOptionPane.showMessageDialog(null, "GAME OVER");
}
}
//遍歷[4][4]數(shù)組產(chǎn)生的方塊并判斷狀態(tài)
public int judgeState(int left,int top,int shapeType,int shapeState){
for(int a=0;a<4;a++){
for(int b=0;b<4;b++){
if(((shapes[shapeType][shapeState][a*4+b]==1 && //遍歷數(shù)組中為1的個(gè)數(shù),即判斷是否有圖形
map[left+b+1][top+a]==1))|| //判斷地圖中是否還有障礙物
((shapes[shapeType][shapeState][a*4+b]==1 && //遍歷數(shù)組中為1的個(gè)數(shù),即判斷是否有圖形
map[left+b+1][top+a]==2))){ //判斷是否撞墻
return 0; //表明無法不能正常運(yùn)行
}
}
}
return 1;
}
⑤創(chuàng)建鍵盤事件監(jiān)聽。
public void keyPressed(KeyEvent e){
switch(e.getKeyCode()){
case KeyEvent.VK_LEFT:
leftMove();//調(diào)用左移的方法
repaint();
break;
case KeyEvent.VK_RIGHT:
rightMove();//調(diào)用右移的方法
repaint();
break;
case KeyEvent.VK_DOWN:
downMove();//調(diào)用左移的方法
repaint();
break;
case KeyEvent.VK_UP:
turnShape();//調(diào)用變形的方法
repaint();
break;
}
}
}
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
}
//創(chuàng)建左移的方法
public void leftMove(){
if(judgeState(left-1,top,shapeType,shapeState)==1){
left-=1;
}
}
//創(chuàng)建右移的方法
public void rightMove(){
if(judgeState(left+1,top,shapeType,shapeState)==1){
left+=1;
};
}
//創(chuàng)建下移的方法
public void downMove(){
if(judgeState(left,top+1,shapeType,shapeState)==1){ //判斷有圖形
top+=1;
deleteLine(); //判斷下移后是否有滿行
}
if(judgeState(left,top+1,shapeType,shapeState)==0){ //判斷沒有圖形
addshape(left,top,shapeType,shapeState);
createshape();
deleteLine();
}
}
//創(chuàng)建旋轉(zhuǎn)變形的方法
public void turnShape(){
int tempshape=shapeState;
shapeState=(shapeState+1)%4; //在四中的狀態(tài)中選取
if(judgeState(left,top,shapeType,shapeState)==1){
}
if(judgeState(left,top,shapeType,shapeState)==0){
shapeState=tempshape; //沒有圖形,不能進(jìn)行旋轉(zhuǎn),還原原來狀態(tài)
}
repaint();
}
⑥繪制界面中的各文字及圖形 。
public void paintComponent(Graphics g){
super.paintComponent(g);
int t=randomcolor.nextInt(5);
int count=randomcolor.nextInt(5);
Color[] color=new Color[]{Color.pink,Color.green,Color.red,Color.yellow,Color.blue};
//繪制圍墻
for(j=0;j<22;j++){
for(i=0;i<12;i++){
if(map[i][j]==2){//判斷是否為墻并繪制
g.setColor(Color.blue);
g.fill3DRect(i*CELL,j*CELL,CELL,CELL,true);
}
if(map[i][j]==0){//判斷是否為墻并繪制
g.setColor(Color.red);
g.drawRoundRect(i*CELL,j*CELL,CELL,CELL,6,6);}
}
}
//繪制正在下落的圖形
for(int k=0;k<16;k++){
if(shapes[shapeType][shapeState][k]==1){
g.setColor(Color.red);
g.fill3DRect((left+k%4+1)*CELL,(top+k/4)*CELL,CELL,CELL,true); //left\top為左上角的坐標(biāo)
}
}
//繪制落下的圖形
for(j=0;j<22;j++){
for(i=0;i<12;i++){
if(map[i][j]==1){
g.setColor(Color.green);
g.fill3DRect(i*CELL,j*CELL,CELL,CELL,true);
}
}
}
//顯示右邊預(yù)覽圖形
for(int i = 0; i < 4; i++) {
for(int j = 0; j < 4; j++){
if(shapes[nextshapeType][nextshapeState][i*4+j] == 1) {
g.setColor(Color.red);
g.fill3DRect(375+(j*(CELL-10)),190+(i*(CELL-10)), CELL-10, CELL-10,true);
}
}
}
//添加右邊預(yù)覽圖形方格
for(int i = 0; i < 5; i++) {
for(int j = 0; j < 5; j++){
g.setColor(Color.blue);
g.drawRoundRect(360+(j*(CELL-10)),175+(i*(CELL-10)),CELL-10, CELL-10,3,3);
}
}
g.setFont(new Font("楷書",Font.BOLD,20));
g.setColor(Color.BLACK);
g.drawString("游戲分?jǐn)?shù):", 310, 70);
g.setColor(Color.pink);
g.drawString(score+" ", 420, 70);
g.setColor(Color.BLACK);
g.drawString(" 分", 450, 70);
g.setColor(Color.BLACK);
g.setFont(new Font("黑體",Font.BOLD,14));
g.drawString("提示:左擊暫停,右擊繼續(xù)。", 305, 430);
g.setColor(Color.blue);
g.drawString("Next square", 358, 268);
}
//創(chuàng)建添加新圖形到地圖的方法
public void addshape(int left,int top,int shapeType,int shapeState){
int temp=0;
for(int a=0;a<4;a++){
for(int b=0;b<4;b++){ //對存儲方塊隊(duì)的[4][4]數(shù)組遍歷
if(map[left+b+1][top+a]==0){ //表明[4][4]數(shù)組沒有方塊
map[left+b+1][top+a]=shapes[shapeType][shapeState][temp];
}
temp++;
}
}
}
⑦創(chuàng)建監(jiān)聽器,消行方法等其它函數(shù)。
public void deleteLine(){
int tempscore=0; //定義滿行的列個(gè)數(shù)滿足1
for(int a=0;a<22;a++){ //對地圖進(jìn)行遍歷
for(int b=0;b<12;b++){
if(map[b][a]==1){ //表示找到滿行
tempscore++; // 記錄一行有多少個(gè)1
if(tempscore==10){
score+=10;
for(int k=a;k>0;k--){ //從滿行開始回歷
for(int c=1;c<12;c++){
map[c][k]=map[c][k-1]; //將圖形整體下移一行
}
}
}
}
}
tempscore=0;
}
}
//判斷游戲結(jié)束,1、判斷新塊的狀態(tài)是否不存在,即judgeState()==0
//2、判斷初始產(chǎn)生的位置是否一直為1;
public int gameOver(int left,int top){
if(judgeState(left,top,shapeType,shapeState)==0){
return 1;
}
return 0;
}
//創(chuàng)建構(gòu)造方法
public GameBody(){
resetMap();
drawWall();
createshape();
//Timer timer=new Timer(1000,new TimeListener());
Thread timer=new Thread(new TimeListener());
timer.start();
}
public void setStart(boolean start){ //改變start值的方法
this.start=start;
}
//創(chuàng)建定時(shí)下落的監(jiān)聽器
class TimeListener implements Runnable{
public void run(){
while(true){
if(!start){
try{
repaint();
if(judgeState(left,top+1,shapeType,shapeState)==1){
top+=1;
deleteLine();}
if(judgeState(left,top+1,shapeType,shapeState)==0){
if(flag==1){
addshape(left,top,shapeType,shapeState);
deleteLine();
createshape();
flag=0;
}
flag=1;
}
Thread.sleep(800);
}catch(Exception e){
e.getMessage();
}
}
}
}
}
}
}
四、項(xiàng)目總結(jié)
Java語言是當(dāng)今流行的網(wǎng)絡(luò)編程語言,它具有面向?qū)ο蟆⒖缙脚_、分布應(yīng)用等特點(diǎn)。而俄羅斯方塊游戲的設(shè)計(jì)工作復(fù)雜且富有挑戰(zhàn)性,它包含的內(nèi)容多,涉及的知識廣泛,與圖形界面聯(lián)系較大,包括界面的顯示與更新、數(shù)據(jù)收集等,在設(shè)計(jì)的過程中,必將運(yùn)用到各方面的知識,這對于設(shè)計(jì)者而言,是個(gè)很好的鍛煉機(jī)會。
俄羅斯方塊游戲的實(shí)現(xiàn)可以使開發(fā)者鞏固所學(xué)基本知識,深刻掌握J(rèn)ava語言的重要概念及其面向?qū)ο蟮奶匦?,增進(jìn)Java語言編輯基本功,拓寬常用類庫的應(yīng)用,培養(yǎng)熟練地應(yīng)用面向?qū)ο蟮乃枷牒驮O(shè)計(jì)方法解決實(shí)際問題的能力,為今后從事實(shí)際開發(fā)工作打下堅(jiān)實(shí)的基礎(chǔ)。文章來源:http://www.zghlxwxcb.cn/news/detail-430688.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-430688.html
到了這里,關(guān)于軟件設(shè)計(jì)實(shí)戰(zhàn):基于Java的俄羅斯方塊游戲【完整版】的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!