本文章是【狂神說Java】一小時開發貪吃蛇游戲的學習筆記,
文章目錄
- 影片幀的概念
- 繪制靜態視窗
- 繪制游戲面板
- 畫靜態小蛇
- 讓小蛇動起來
- 小蛇的上下左右移動
- 吃食物、積分系統和失敗判定
- 打包發布
影片幀的概念
影片幀——就是影像影片中最小單位的單幅影像畫面,相當于電影膠片上的每一格鏡頭, 一幀就是一副靜止的畫面,連續的幀就形成影片,如電視圖象等,
我們通常說幀數,簡單地說,就是在1秒鐘時間里傳輸的圖片的幀數,也可以理解為圖形處理器每秒鐘能夠重繪幾次,通常用fps(Frames Per Second)表示,
每一幀都是靜止的圖象,快速連續地顯示幀便形成了運動的假象,高的幀率可以得到更流暢、更逼真的影片,每秒鐘幀數 (fps) 愈多,所顯示的動作就會愈流暢,
——來自百度知道
繪制靜態視窗
作者這里在IDEA里新建專案時就遇到問題了:.java檔案的左下角有個橙色小圖示,滑鼠移動到上面時顯示"Java file outside of source root",并且main方法前也沒有執行的綠色按鈕,這里以作者之前做的一個學校作業為例,

這里參考了一篇博客解決了這個問題
關于IDEA中突然出現java file outside of source root的問題解決

按步驟點擊后,界面恢復如下:

運行結果也正常

看完可能出現的問題后,我們回到正題,
我們新建一個專案,并建好包,并把圖片資源匯入,

作者并沒有學過GUI的相關知識,所以這部分直接跟著視頻敲就完事了,
//StartGame.java
package snakegames;
import javax.swing.*;
public class StartGame {
public static void main(String[] args) {
//1.繪制一個靜態視窗 JFrame
JFrame frame=new JFrame("Snake Game");
frame.setBounds(10,10,900,720);//設定界面大小
frame.setResizable(false); //視窗大小則不可改變
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//設定關閉界面,游戲可以關閉了
frame.setVisible(true);//讓視窗能夠顯示出來
}
}
運行效果

繪制游戲面板
接下來我們新建一個類,用來撰寫畫板,
//GamePanel.java
package snakegames;
import javax.swing.*;
import java.awt.*;
public class GamePanel extends JPanel {
//畫板:畫界面,畫蛇
@Override
//Graphics 畫筆
protected void paintComponent(Graphics g){
super.paintComponent(g);//清屏
this.setBackground(Color.WHITE);//設定背景的顏色
//繪制頭部的廣告欄
Data.header.paintIcon(this,g,25,11);
//繪制游戲區域
g.fillRect(25,75,850,600);
}
}
我們新建一個類Data來存放外部資料,
匯入圖片時要尋找好路徑,
//Data.java
package snakegames;
import javax.swing.*;
import java.net.URL;
//存放外部資料
public class Data {
//頭部的圖片 URL:定位圖片地址 ImageIcon:圖片
public static URL headerURL=Data.class.getResource("/statics/header.png");
public static ImageIcon header=new ImageIcon(headerURL);
//把圖片全部匯入
public static URL upURL=Data.class.getResource("/statics/up.png");
public static URL downURL=Data.class.getResource("/statics/down.png");
public static URL leftURL=Data.class.getResource("/statics/left.png");
public static URL rightURL=Data.class.getResource("/statics/right.png");
public static URL bodyURL=Data.class.getResource("/statics/body.png");
public static URL foodURL=Data.class.getResource("/statics/food.png");
public static ImageIcon up=new ImageIcon(upURL);
public static ImageIcon down=new ImageIcon(downURL);
public static ImageIcon left=new ImageIcon(leftURL);
public static ImageIcon right=new ImageIcon(rightURL);
public static ImageIcon body=new ImageIcon(bodyURL);
public static ImageIcon food=new ImageIcon(foodURL);
}
然后我們在StartGame類里添加上畫板
//StartGame.java
package snakegames;
import javax.swing.*;
public class StartGame {
public static void main(String[] args) {
//1.繪制一個靜態視窗 JFrame
JFrame frame=new JFrame("Snake Game");
frame.setBounds(10,10,900,720);//設定界面大小
frame.setResizable(false); //視窗大小則不可改變
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//設定關閉界面,游戲可以關閉了
//2.面板JPanel 可以加入到Frame
frame.add(new GamePanel());
frame.setVisible(true);//讓視窗能夠顯示出來
}
}
效果圖

畫靜態小蛇
package snakegames;
import javax.swing.*;
import java.awt.*;
public class GamePanel extends JPanel {
int length;//蛇的長度
int[] snakeX=new int[600];//蛇的坐標X
int[] snakeY=new int[500];//蛇的坐標Y
String fx;//R:right L:left U:up D:down
//初始化
public void init(){
length=3;
snakeX[0]=100;snakeY[0]=100;//頭部坐標
snakeX[1]=75;snakeY[1]=100;//第一個身體坐標
snakeX[2]=50;snakeY[2]=100;//第二個身體坐標
fx="R";
}
//構造器
public GamePanel(){
init();
}
//畫板:畫界面,畫蛇
@Override
//Graphics 畫筆
protected void paintComponent(Graphics g){
super.paintComponent(g);//清屏
this.setBackground(Color.WHITE);//設定背景的顏色
//繪制頭部的廣告欄
Data.header.paintIcon(this,g,25,11);
//繪制游戲區域
g.fillRect(25,75,850,600);
//畫一條靜態的小蛇
if(fx.equals("R")){
Data.right.paintIcon(this,g,snakeX[0],snakeY[0]);
}else if(fx.equals("L")){
Data.left.paintIcon(this,g,snakeX[0],snakeY[0]);
}else if(fx.equals("U")){
Data.up.paintIcon(this,g,snakeX[0],snakeY[0]);
}else if(fx.equals("D")){
Data.down.paintIcon(this,g,snakeX[0],snakeY[0]);
}
for(int i=1;i<length;i++){
Data.body.paintIcon(this,g,snakeX[i],snakeY[i]);//蛇的身體長度通過length來控制
}
}
}
效果圖

讓小蛇動起來
接收鍵盤的輸入:監聽
package snakegames;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class GamePanel extends JPanel implements KeyListener {
int length;//蛇的長度
int[] snakeX=new int[600];//蛇的坐標X
int[] snakeY=new int[500];//蛇的坐標Y
String fx;//R:right L:left U:up D:down
boolean isStart=false;//游戲是否開始
//初始化
public void init(){
length=3;
snakeX[0]=100;snakeY[0]=100;//頭部坐標
snakeX[1]=75;snakeY[1]=100;//第一個身體坐標
snakeX[2]=50;snakeY[2]=100;//第二個身體坐標
fx="R";
}
//構造器
public GamePanel(){
init();
//獲取鍵盤的監聽事件
this.setFocusable(true);
this.addKeyListener(this);
}
@Override
public void keyTyped(KeyEvent e) {/*鍵盤按下,彈起:敲擊*/}
@Override
public void keyReleased(KeyEvent e) {/*釋放某個鍵*/}
//接收鍵盤的輸入:監聽
@Override
public void keyPressed(KeyEvent e) {
//鍵盤按下未釋放
//接收鍵盤的輸入,獲取按下的鍵盤是哪個鍵
int keyCode=e.getKeyCode();
if(keyCode==KeyEvent.VK_SPACE){//如果按下的是空格鍵
isStart=!isStart;//如果是啟動,就暫停;如果是不啟動,則啟動
repaint();//重繪界面
}
}
//畫板:畫界面,畫蛇
@Override
//Graphics 畫筆
protected void paintComponent(Graphics g){
super.paintComponent(g);//清屏
this.setBackground(Color.WHITE);//設定背景的顏色
//繪制頭部的廣告欄
Data.header.paintIcon(this,g,25,11);
//繪制游戲區域
g.fillRect(25,75,850,600);
//畫一條靜態的小蛇
if(fx.equals("R")){
Data.right.paintIcon(this,g,snakeX[0],snakeY[0]);
}else if(fx.equals("L")){
Data.left.paintIcon(this,g,snakeX[0],snakeY[0]);
}else if(fx.equals("U")){
Data.up.paintIcon(this,g,snakeX[0],snakeY[0]);
}else if(fx.equals("D")){
Data.down.paintIcon(this,g,snakeX[0],snakeY[0]);
}
for(int i=1;i<length;i++){
Data.body.paintIcon(this,g,snakeX[i],snakeY[i]);//蛇的身體長度通過length來控制
}
//游戲提示是否開始
if(isStart==false){
//畫一個文字 String
g.setColor(Color.WHITE);//設定畫筆的顏色
g.setFont(new Font("微軟雅黑",Font.BOLD,40));//設定字體
g.drawString("按下空格開始游戲",300,300);
}
}
}
效果圖

按下空格后

再次按下空格

定時器,監聽時間流動
我們想讓小蛇動起來,最核心的部分就在下面,
直觀地看:
- 腦袋右移一格,第一個身體跟著腦袋右移到剛才腦袋的位置,第二個身體跟著移動到剛才第一個身體的位置
- 腦袋下移一格,第一個身體跟著腦袋移動到剛才腦袋的位置,第二個身體跟著移動到剛才第一個身體的位置,以此類推
代碼實作:
- 除了腦袋,身體從最后一個開始,每個都向前移動,覆寫前一個身體
我認為actionPerformed方法中的內容,是整個專案的核心,
package snakegames;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.sql.Time;
public class GamePanel extends JPanel implements KeyListener, ActionListener {
int length;//蛇的長度
int[] snakeX=new int[600];//蛇的坐標X
int[] snakeY=new int[500];//蛇的坐標Y
String fx;//R:right L:left U:up D:down
boolean isStart=false;//游戲是否開始
Timer timer=new Timer(100,this);//定時器
//初始化
public void init(){
length=3;
snakeX[0]=100;snakeY[0]=100;//頭部坐標
snakeX[1]=75;snakeY[1]=100;//第一個身體坐標
snakeX[2]=50;snakeY[2]=100;//第二個身體坐標
fx="R";
}
//構造器
public GamePanel(){
init();
//獲取鍵盤的監聽事件
this.setFocusable(true);
this.addKeyListener(this);
timer.start();//讓時間動起來
}
@Override
public void keyTyped(KeyEvent e) {/*鍵盤按下,彈起:敲擊*/}
@Override
public void keyReleased(KeyEvent e) {/*釋放某個鍵*/}
//接收鍵盤的輸入:監聽
@Override
public void keyPressed(KeyEvent e) {
//鍵盤按下未釋放
//接收鍵盤的輸入,獲取按下的鍵盤是哪個鍵
int keyCode=e.getKeyCode();
if(keyCode==KeyEvent.VK_SPACE){//如果按下的是空格鍵
isStart=!isStart;//如果是啟動,就暫停;如果是不啟動,則啟動
repaint();//重繪界面
}
}
//畫板:畫界面,畫蛇
@Override
//Graphics 畫筆
protected void paintComponent(Graphics g){
super.paintComponent(g);//清屏
this.setBackground(Color.WHITE);//設定背景的顏色
//繪制頭部的廣告欄
Data.header.paintIcon(this,g,25,11);
//繪制游戲區域
g.fillRect(25,75,850,600);
//畫一條靜態的小蛇
if(fx.equals("R")){
Data.right.paintIcon(this,g,snakeX[0],snakeY[0]);
}else if(fx.equals("L")){
Data.left.paintIcon(this,g,snakeX[0],snakeY[0]);
}else if(fx.equals("U")){
Data.up.paintIcon(this,g,snakeX[0],snakeY[0]);
}else if(fx.equals("D")){
Data.down.paintIcon(this,g,snakeX[0],snakeY[0]);
}
for(int i=1;i<length;i++){
Data.body.paintIcon(this,g,snakeX[i],snakeY[i]);//蛇的身體長度通過length來控制
}
//游戲提示是否開始
if(isStart==false){
//畫一個文字 String
g.setColor(Color.WHITE);//設定畫筆的顏色
g.setFont(new Font("微軟雅黑",Font.BOLD,40));//設定字體
g.drawString("按下空格開始游戲",300,300);
}
}
//定時器,監聽時間,幀:執行定時操作
@Override
public void actionPerformed(ActionEvent e) {
//如果游戲處于開始狀態
if(isStart){
//右移
/*直觀地看:
* 腦袋右移一格,第一個身體跟著腦袋右移到剛才腦袋的位置,第二個身體跟著移動到剛才第一個身體的位置
* 腦袋下移一格,第一個身體跟著腦袋移動到剛才腦袋的位置,第二個身體跟著移動到剛才第一個身體的位置,以此類推
* 代碼實作:
* 除了腦袋,身體從最后一個開始,每個都向前移動,覆寫前一個身體
* */
for(int i=length-1;i>0;i--){
//除了腦袋,身體都向前移動
snakeX[i]=snakeX[i-1];
snakeY[i]=snakeY[i-1];
}
snakeX[0]=snakeX[0]+25;//頭部移動
//邊界判斷
if(snakeX[0]>850){
snakeX[0]=25;
}
//每一次移動完成就重繪一次界面
repaint();
}
timer.start();//讓時間動起來
}
}
效果動圖

小蛇的上下左右移動
我們在keyPressed方法里添加一些操作
//接收鍵盤的輸入:監聽
@Override
public void keyPressed(KeyEvent e) {
//鍵盤按下未釋放
//接收鍵盤的輸入,獲取按下的鍵盤是哪個鍵
int keyCode=e.getKeyCode();
if(keyCode==KeyEvent.VK_SPACE){//如果按下的是空格鍵
isStart=!isStart;//如果是啟動,就暫停;如果是不啟動,則啟動
repaint();//重繪界面
}
//鍵盤控制走向
if(keyCode==KeyEvent.VK_LEFT){
fx="L";
}else if(keyCode==KeyEvent.VK_RIGHT){
fx="R";
}else if(keyCode==KeyEvent.VK_UP){
fx="U";
}else if(keyCode==KeyEvent.VK_DOWN){
fx="D";
}
}
//定時器,監聽時間,幀:執行定時操作
@Override
public void actionPerformed(ActionEvent e) {
//如果游戲處于開始狀態
if(isStart){
for(int i=length-1;i>0;i--){
//除了腦袋,身體都向前移動
snakeX[i]=snakeX[i-1];
snakeY[i]=snakeY[i-1];
}
//通過控制方向讓頭部移動
if(fx.equals("R")){
snakeX[0]=snakeX[0]+25;//頭部移動
if(snakeX[0]>850){snakeX[0]=25;}//邊界判斷
}else if(fx.equals("L")){
snakeX[0]=snakeX[0]-25;
if(snakeX[0]<25){snakeX[0]=850;}
}else if(fx.equals("U")){
snakeY[0]=snakeY[0]-25;
if(snakeY[0]<75){snakeY[0]=650;}
}else if(fx.equals("D")){
snakeY[0]=snakeY[0]+25;
if(snakeY[0]>600){snakeY[0]=75;}
}
//每一次移動完成就重繪一次界面
repaint();
}
timer.start();//讓時間動起來
}
效果動圖

吃食物、積分系統和失敗判定
我們做到這里,已經把程式的大框架做出來了,剩下的東西就不那么困難了,
//GamePanel.java
package snakegames;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.sql.Time;
import java.util.Random;
public class GamePanel extends JPanel implements KeyListener, ActionListener {
int length;//蛇的長度
int[] snakeX=new int[600];//蛇的坐標X
int[] snakeY=new int[500];//蛇的坐標Y
String fx;//R:right L:left U:up D:down
boolean isStart=false;//游戲是否開始
Timer timer=new Timer(100,this);//定時器
//1.定義一個食物
int foodX;
int foodY;
Random random=new Random();
//死亡判斷
boolean isFail=false;
//積分系統
int score;
//初始化
public void init(){
length=3;
snakeX[0]=100;snakeY[0]=100;//頭部坐標
snakeX[1]=75;snakeY[1]=100;//第一個身體坐標
snakeX[2]=50;snakeY[2]=100;//第二個身體坐標
fx="R";
foodX=25+25*random.nextInt(34);
foodY=75+25*random.nextInt(24);
score=0;
}
//構造器
public GamePanel(){
init();
//獲取鍵盤的監聽事件
this.setFocusable(true);
this.addKeyListener(this);
timer.start();//讓時間動起來
}
@Override
public void keyTyped(KeyEvent e) {/*鍵盤按下,彈起:敲擊*/}
@Override
public void keyReleased(KeyEvent e) {/*釋放某個鍵*/}
//接收鍵盤的輸入:監聽
@Override
public void keyPressed(KeyEvent e) {
//鍵盤按下未釋放
//接收鍵盤的輸入,獲取按下的鍵盤是哪個鍵
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_SPACE) {//如果按下的是空格鍵
if(isFail){
//失敗,游戲再來一遍
isFail=false;
init();//重新初始化游戲
}else{
isStart = !isStart;//如果是啟動,就暫停;如果是不啟動,則啟動
}
repaint();//重繪界面
}
//鍵盤控制走向
if (keyCode == KeyEvent.VK_LEFT) {
fx = "L";
} else if (keyCode == KeyEvent.VK_RIGHT) {
fx = "R";
} else if (keyCode == KeyEvent.VK_UP) {
fx = "U";
} else if (keyCode == KeyEvent.VK_DOWN) {
fx = "D";
}
}
//畫板:畫界面,畫蛇
@Override
//Graphics 畫筆
protected void paintComponent(Graphics g){
super.paintComponent(g);//清屏
this.setBackground(Color.WHITE);//設定背景的顏色
//繪制頭部的廣告欄
Data.header.paintIcon(this,g,25,11);
//繪制游戲區域
g.fillRect(25,75,850,600);
//畫一條靜態的小蛇
if(fx.equals("R")){
Data.right.paintIcon(this,g,snakeX[0],snakeY[0]);
}else if(fx.equals("L")){
Data.left.paintIcon(this,g,snakeX[0],snakeY[0]);
}else if(fx.equals("U")){
Data.up.paintIcon(this,g,snakeX[0],snakeY[0]);
}else if(fx.equals("D")){
Data.down.paintIcon(this,g,snakeX[0],snakeY[0]);
}
for(int i=1;i<length;i++){
Data.body.paintIcon(this,g,snakeX[i],snakeY[i]);//蛇的身體長度通過length來控制
}
//畫積分
g.setColor(Color.WHITE);
g.setFont(new Font("微軟雅黑",Font.BOLD,18));
g.drawString("長度:"+length,750,35);
g.drawString("分數:"+score,750,50);
//畫食物
Data.food.paintIcon(this,g,foodX,foodY);
//游戲提示是否開始
if(isStart==false){
//畫一個文字 String
g.setColor(Color.WHITE);//設定畫筆的顏色
g.setFont(new Font("微軟雅黑",Font.BOLD,40));//設定字體
g.drawString("按下空格開始游戲",300,300);
}
//失敗提醒
if(isFail==true){
g.setColor(Color.RED);
g.setFont(new Font("微軟雅黑",Font.BOLD,40));
g.drawString("游戲失敗,按下空格重新開始",200,300);
}
}
//定時器,監聽時間,幀:執行定時操作
@Override
public void actionPerformed(ActionEvent e) {
//如果游戲處于開始狀態,并且游戲沒有Fail
if(isStart && isFail==false){
//右移
for(int i=length-1;i>0;i--){
//除了腦袋,身體都向前移動
snakeX[i]=snakeX[i-1];
snakeY[i]=snakeY[i-1];
}
//通過控制方向讓頭部移動
if(fx.equals("R")){
snakeX[0]=snakeX[0]+25;//頭部移動
//邊界判斷
if(snakeX[0]>850){snakeX[0]=25;}
}else if(fx.equals("L")){
snakeX[0]=snakeX[0]-25;
if(snakeX[0]<25){snakeX[0]=850;}
}else if(fx.equals("U")){
snakeY[0]=snakeY[0]-25;
if(snakeY[0]<75){snakeY[0]=650;}
}else if(fx.equals("D")){
snakeY[0]=snakeY[0]+25;
if(snakeY[0]>600){snakeY[0]=75;}
}
//如果小蛇的頭和食物坐標重合了
if(snakeX[0]==foodX && snakeY[0]==foodY){
//長度加一
length++;
//分數加一
score+=10;
//重新生成食物
foodX=25+25*random.nextInt(34);
foodY=75+25*random.nextInt(24);
}
//結束判斷
for(int i=1;i<length;i++){
if(snakeX[0]==snakeX[i] && snakeY[0]==snakeY[i]){
isFail=true;
}
}
//每一次移動完成就重繪一次界面
repaint();
}
timer.start();//讓時間動起來
}
}
效果動圖

打包發布
點擊Project Structure下的Artifacts

依次點擊


點擊Apply

選擇最上方工具列的Build->Build Artifacts


我們對這個jar包右鍵,選擇在檔案夾中打開


我們雙擊它,就可以打開了

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/336318.html
標籤:其他
