Java swing寫一個貪喫蛇

學習Java的時候,我用swing寫了一個貪喫蛇。運行效果如圖:

 

 

 

下面給出程序完整源代碼,可以直接複製粘貼到你的機子上運行(注意文件名要改成Main.java)。

關於代碼有任何問題可以留言我,我會盡力解答。

 

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;


public class Main extends JFrame{
	private static GamePanel p1 = new GamePanel();//創建遊戲面板
	private static InformationPanel p2 = new InformationPanel();//創建信息面板

	public Main(){//配置框架的佈局
		setLayout(new BorderLayout());
		add(p1,BorderLayout.CENTER);
		add(p2,BorderLayout.EAST);
	}

	public static void main(String[] args){
		JFrame frame = new Main();//新建框架

		//配置框架
		frame.setTitle("貪喫蛇");
		frame.setSize(1100, 800);
		frame.setVisible(true);
		frame.setResizable(false);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setLocationRelativeTo(null);

		//新建線程
		Thread thread1 = new Thread(p1);
		thread1.start();
		Thread thread2 = new Thread(p2);
		thread2.start();
	}
}

class GamePanel extends JPanel implements Runnable{
	public static final int PER_UNIT_LENGTH = 20;//單位長度
	public static final int MULTIPLE = 15;//倍數
	public static final int HALF_SIDE = MULTIPLE * PER_UNIT_LENGTH;//遊戲邊框的一半長 = 倍數 * 單位長度

	private boolean isFirstRun = true;//判斷是否需要初始化
	private boolean isStarted = false;//判斷是否開始
	private boolean isPaused = false;//判斷是否暫停

	private static int score = 0;//遊戲分數
	private static int information = 0;//傳遞遊戲信息

	private Snake snake = new Snake();//新建一條蛇
	private Dot dessert = new Dot();//新建一個點心

	protected void paintComponent(Graphics g){
		super.paintComponent(g);

		//獲取中點座標
		int xCentre = getWidth() / 2;
		int yCentre = getHeight() / 2;

		//獲取一個隨機點座標
		int xRandom, yRandom;

		if(isFirstRun){
			isFirstRun = false;

			//初始化遊戲邊框
			g.drawRect(xCentre - HALF_SIDE, yCentre - HALF_SIDE, HALF_SIDE * 2, HALF_SIDE * 2);

			//初始化蛇頭
			snake.getHead().setX(xCentre);
			snake.getHead().setY(yCentre);
			g.setColor(Color.ORANGE);
			g.fillOval(snake.getHead().getX(), snake.getHead().getY(), PER_UNIT_LENGTH, PER_UNIT_LENGTH);

			//初始化點心
			do{
				xRandom = xCentre - HALF_SIDE + ((int)(Math.random() * MULTIPLE * 2)) * PER_UNIT_LENGTH;
				yRandom = yCentre - HALF_SIDE + ((int)(Math.random() * MULTIPLE * 2)) * PER_UNIT_LENGTH;
			}while(xRandom == snake.getHead().getX() && yRandom == snake.getHead().getY());
			dessert.setX(xRandom);
			dessert.setY(yRandom);
			g.setColor(Color.DARK_GRAY);
			g.fillOval(dessert.getX(), dessert.getY(), PER_UNIT_LENGTH, PER_UNIT_LENGTH);
		}
		else{
			//繪畫遊戲邊框
			g.drawRect(xCentre - HALF_SIDE, yCentre - HALF_SIDE, HALF_SIDE * 2, HALF_SIDE * 2);

			//繪畫蛇身
			g.setColor(Color.MAGENTA);
			for(int i = 0;i < snake.getBody().size();i++){
				g.fillOval(snake.getBody().get(i).getX(), snake.getBody().get(i).getY(), PER_UNIT_LENGTH, PER_UNIT_LENGTH);
			}

			//繪畫蛇頭
			g.setColor(Color.ORANGE);
			g.fillOval(snake.getHead().getX(), snake.getHead().getY(), PER_UNIT_LENGTH, PER_UNIT_LENGTH);

			//如果蛇喫到點心,則更新點心
			if(isEncountered()){
				do{
					xRandom = xCentre - HALF_SIDE + ((int)(Math.random() * MULTIPLE * 2)) * PER_UNIT_LENGTH;
					yRandom = yCentre - HALF_SIDE + ((int)(Math.random() * MULTIPLE * 2)) * PER_UNIT_LENGTH;
				}while(xRandom == snake.getHead().getX() && yRandom == snake.getHead().getY());
				dessert.setX(xRandom);
				dessert.setY(yRandom);
			}
			g.setColor(Color.DARK_GRAY);
			g.fillOval(dessert.getX(), dessert.getY(), PER_UNIT_LENGTH, PER_UNIT_LENGTH);

			//如果遊戲結束,則追加繪畫GAME OVER
			if(isCrushed()){
				g.setColor(Color.BLACK);
				FontMetrics fm = g.getFontMetrics();
				int stringWidth = fm.stringWidth("GAME OVER");
				int stringAscent = fm.getAscent();
				int xCoordinate = xCentre - stringWidth / 2;
				int yCoordinate = yCentre - stringAscent / 2;
				g.drawString("GAME OVER", xCoordinate, yCoordinate);
			}
		}
	}

	public GamePanel(){
		//配置面板屬性
		setFocusable(true);
		setFont(new Font("Californian FB", Font.BOLD, 80));

		//註冊鍵盤監聽器
		addKeyListener(new KeyAdapter(){
			public void keyPressed(KeyEvent e){
				int direction = snake.getDirection();
				switch(e.getKeyCode()){
					case KeyEvent.VK_UP:
						if(isStarted && !isPaused && !isCrushed()){
							if(direction != Snake.DIRECTION_UP && direction != Snake.DIRECTION_DOWN){
							snake.setDirection(Snake.DIRECTION_UP);
							changeSnakeLocation();
							}
						}
						break;
					case KeyEvent.VK_DOWN:
						if(isStarted && !isPaused && !isCrushed()){
							if(direction != Snake.DIRECTION_UP && direction != Snake.DIRECTION_DOWN){
							snake.setDirection(Snake.DIRECTION_DOWN);
							changeSnakeLocation();
							}
						}
						break;
					case KeyEvent.VK_LEFT:
						if(isStarted && !isPaused && !isCrushed()){
							if(direction != Snake.DIRECTION_LEFT && direction != Snake.DIRECTION_RIGHT){
							snake.setDirection(Snake.DIRECTION_LEFT);
							changeSnakeLocation();
							}
						}
						break;
					case KeyEvent.VK_RIGHT:
						if(isStarted && !isPaused && !isCrushed()){
							if(direction != Snake.DIRECTION_LEFT && direction != Snake.DIRECTION_RIGHT){
							snake.setDirection(Snake.DIRECTION_RIGHT);
							changeSnakeLocation();
							}
						}
						break;
					case KeyEvent.VK_ENTER:
						if(isCrushed()){//如果遊戲結束,則重置數據重新開始遊戲
							snake.setDirection(Snake.DIRECTION_RIGHT);
							snake.setSpeed(Snake.SPEED_3);
							snake.setBody(new LinkedList<Dot>());

							isFirstRun = true;
							isStarted = false;
							isPaused = false;

							score = 0;
							information = 0;

							repaint();
						}
						else{
							isStarted = true;
						}
						break;
					case KeyEvent.VK_SPACE:
						if(isStarted && !isCrushed())
							isPaused = !isPaused;
						break;
					case KeyEvent.VK_F1:snake.setSpeed(Snake.SPEED_1);break;
					case KeyEvent.VK_F2:snake.setSpeed(Snake.SPEED_2);break;
					case KeyEvent.VK_F3:snake.setSpeed(Snake.SPEED_3);break;
					case KeyEvent.VK_F4:snake.setSpeed(Snake.SPEED_4);break;
					case KeyEvent.VK_F5:snake.setSpeed(Snake.SPEED_5);break;
					default:
				}
			}
		});
	}

	public void run(){//控制蛇自動前進
		while(true){
			if(isStarted && !isPaused && !isCrushed()){
				changeSnakeLocation();
			}
			try{
				Thread.sleep(snake.getSpeed());
			}
			catch(InterruptedException ex){
				ex.printStackTrace();
			}
		}
	}

	public synchronized void changeSnakeLocation(){//改變蛇的位置信息並重畫
		//存儲蛇頭先前的信息,爲蛇身的更新提供依據
		int xPrevious = snake.getHead().getX();
		int yPrevious = snake.getHead().getY();

		//更新蛇頭位置
		switch(snake.getDirection()){
			case Snake.DIRECTION_UP:snake.getHead().setY(yPrevious - PER_UNIT_LENGTH);break;
			case Snake.DIRECTION_DOWN:snake.getHead().setY(yPrevious + PER_UNIT_LENGTH);break;
			case Snake.DIRECTION_LEFT:snake.getHead().setX(xPrevious - PER_UNIT_LENGTH);break;
			case Snake.DIRECTION_RIGHT:snake.getHead().setX(xPrevious + PER_UNIT_LENGTH);break;
			default:
		}

		//根據蛇頭信息和是否喫到點心更新蛇身位置
		if(isEncountered()){
			score++;
			snake.getBody().addFirst(new Dot(xPrevious, yPrevious));
		}
		else{
			snake.getBody().addFirst(new Dot(xPrevious, yPrevious));
			snake.getBody().removeLast();
		}

		//重畫並獲取焦點
		repaint();
		requestFocus();
	}

	public boolean isEncountered(){//判斷是否喫到點心
		if(snake.getHead().getX() == dessert.getX() 
		&& snake.getHead().getY() == dessert.getY()){
			return true;
		}
		else{
			return false;
		}
	}

	public boolean isCrushed(){//判斷遊戲是否結束
		//先判斷是否碰觸邊框
		boolean isCrushedByBorder = snake.getHead().getX() >= getWidth() / 2 + HALF_SIDE  
		|| snake.getHead().getX() < getWidth() / 2 - HALF_SIDE 
		|| snake.getHead().getY() >= getHeight() / 2 + HALF_SIDE 
		|| snake.getHead().getY() < getHeight() / 2 - HALF_SIDE;
		if(isCrushedByBorder){
			information = 1;
			return true;
		}

		//再判斷是否碰觸自身
		boolean isCrushedByItself = false;
		for(int i = 0;i < snake.getBody().size();i++){
			if(snake.getHead().getX() == snake.getBody().get(i).getX() 
			&& snake.getHead().getY() == snake.getBody().get(i).getY() && !isCrushedByItself)
				isCrushedByItself = true;
		}
		if(isCrushedByItself){
			information = 2;
			return true;
		}
		else{
			return false;
		}
	}

	public static int getScore(){
		return score;
	}

	public static int getInformation(){
		return information;
	}
}

class InformationPanel extends JPanel implements Runnable{
	private Box box = Box.createVerticalBox();//創建一個垂直盒子容器
	private JLabel[] help = new JLabel[5];//顯示幫助信息
	private JLabel score = new JLabel("分數:");//顯示分數
	private JLabel show = new JLabel();//顯示信息

	public InformationPanel(){
		//初始化數組
		for(int i = 0;i < help.length;i++)
			help[i] = new JLabel();

		//配置字體
		Font font1 = new Font("DialogInput", Font.BOLD, 20);
		Font font2 = new Font("DialogInput", Font.BOLD + Font.ITALIC, 25);
		for(int i = 0;i < help.length;i++)
			help[i].setFont(font1);
		score.setFont(font2);
		score.setForeground(Color.GREEN);
		show.setFont(font2);
		show.setForeground(Color.RED);

		//配置幫助信息
		help[0].setText("回車鍵開始遊戲");
		help[1].setText("方向鍵移動蛇");
		help[2].setText("空格鍵暫停遊戲");
		help[3].setText("按鍵F1-F5調節蛇速");
		help[4].setText("按回車鍵可以重新開始遊戲");

		//配置信息面板
		add(box);
		box.add(Box.createVerticalStrut(150));
		for(int i = 0;i < help.length;i++){
			box.add(help[i]);
			box.add(Box.createVerticalStrut(10));
		}
		box.add(Box.createVerticalStrut(90));
		box.add(score);
		box.add(Box.createVerticalStrut(150));
		box.add(show);		
	}

	public void run(){//更新遊戲信息
		while(true){
			String string1 = "分數:" + Integer.toString(GamePanel.getScore());
			score.setText(string1);
			String string2 = null;
			switch(GamePanel.getInformation()){
				case 0:break;
				case 1:string2 = "你撞穿牆壁了!";break;
				case 2:string2 = "你喫到自己了!";break;
				default:
			}
			show.setText(string2);
		}
	}
}

class Snake{//蛇類
	public static final int DIRECTION_UP = 1;
	public static final int DIRECTION_DOWN = 2;
	public static final int DIRECTION_LEFT = 3;
	public static final int DIRECTION_RIGHT = 4;
	public static final int SPEED_1 = 300;
	public static final int SPEED_2 = 200;
	public static final int SPEED_3 = 150;
	public static final int SPEED_4 = 100;
	public static final int SPEED_5 = 30;
	private int direction = DIRECTION_RIGHT;
	private int speed = SPEED_3;
	private Dot head = new Dot();
	private LinkedList<Dot> body = new LinkedList<Dot>();

	public Snake(){
	}

	public Dot getHead(){
		return head;
	}

	public LinkedList<Dot> getBody(){
		return body;
	}

	public int getDirection(){
		return direction;
	}

	public int getSpeed(){
		return speed;
	}

	public void setBody(LinkedList<Dot> body){
		this.body = body;
	}

	public void setDirection(int direction){
		this.direction = direction;
	}

	public void setSpeed(int speed){
		this.speed = speed;
	}
}

class Dot{//點類
	private int x = 0;
	private int y = 0;

	public Dot(){
	}

	public Dot(int x, int y){
		this.x = x;
		this.y = y;
	}

	public int getX(){
		return x;
	}

	public int getY(){
		return y;
	}

	public void setX(int x){
		this.x = x;
	}

	public void setY(int y){
		this.y = y;
	}
}

 

參考:Java語言程序設計(基礎篇)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章