Java贪吃蛇小游戏

Posted 想成为大师啊

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java贪吃蛇小游戏相关的知识,希望对你有一定的参考价值。

在用Java做一个贪吃蛇小游戏之前,先要了解一下JFrame

JFrame是指一个计算机语言-java的GUI程序的基本思路是以JFrame为基础,它是屏幕上window的对象,能够最大化、最小化、关闭。

构造方法

  • JFrame():构造一个初始时不可见的新窗体。
  • JFrame(GraphicsConfiguration gc):以屏幕设备的指定GraphicsConfiguration和空白标题创建一个 Frame。
  • JFrame(String title):创建一个新的、初始不可见的、具有指定标题的 Frame。
  • JFrame(String title, GraphicsConfiguration gc):创建一个具有指定标题和指定屏幕设备的GraphicsConfiguration 的 JFrame。

Snake

new一个JFrame对象,设置一个标题,可以选择有参构造函数

JFrame j = new JFrame("贪吃蛇小游戏");

也可以选择无参构造函数

JFrame j = new JFrame();
j.setTitle("贪吃蛇小游戏");

导入一个包:import java.awt.Toolkit;

import java.awt.Toolkit;

得到电脑屏幕的宽度和高度,方便设置对应的窗口大小和窗口打开时会出现在电脑的什么位置

int width = Toolkit.getDefaultToolkit().getScreenSize().width;
System.out.println(width);
int height =Toolkit.getDefaultToolkit().getScreenSize().height;
System.out.println(height);

这个时候就需要一点数学功底了,我设置好窗口大小,并将窗口居中显示在电脑屏幕上

setBounds()有四个参数:

  • 第一个参数改组件在JFrame中的x坐边(指定左上角的新位置)
  • 第二个参数改组件在JFrame中的y坐标(指定左上角的新位置)
  • 第三个参数改组件在JFrame中的组件宽度
  • 第四个参数改组件在JFrame中的组件高度
j.setBounds((width-800)/2, (height-800)/2, 800, 800);

方法setsetResizable(boolean resizable)参数为boolean类型,

  • resizeable值为true时,表示在生成的窗体可以自由改变大小;
  • resizeable值为false时,表示生成的窗体大小是由程序员决定的,用户不可以自由改变该窗体的大小。
j.setResizable(false);

通过方法setVisible(),参数为Boolean类型,将窗体打印出来

j.setVisible(true);

当我们关闭窗口时,程序不一定关闭了,所以要使用setDefaultCloseOperation(3); 这里的‘3’是默认关闭

j.setDefaultCloseOperation(3);
package com.blb.game;

import java.awt.Toolkit;

import javax.swing.JFrame;

public class Snake {
	public static void main(String[] args) {
		
		// 创建窗体对象,实例化,JFrame
		JFrame j = new JFrame();// 可在里面给名字,有参构造
		// 设置窗体的名字,无参构造
		j.setTitle("贪吃蛇小游戏");
		
		// 得到当前电脑屏幕的宽度
		int width = Toolkit.getDefaultToolkit().getScreenSize().width;
		System.out.println(width);
		// 得到当前电脑屏幕的高度
		int height = Toolkit.getDefaultToolkit().getScreenSize().height;
		System.out.println(height);
		j.setBounds((width-800)/2,(height-800)/2, 800, 800);// 使得界面居中
		
		j.setResizable(false);// 设置窗体的大小不可变
		
		j.setDefaultCloseOperation(3);// 游戏关闭,程序结束
		
		// 创建面板对象
		SnakePanel s = new SnakePanel();
		j.add(s);
		
		j.setVisible(true);// 设置窗体可见
		
	}
}

创建一个新类(在里面可以对创好的边框进行填充颜色和放置相应的图片),需继承JPanel

JPanel 是 Java图形用户界面(GUI)工具包swing中的面板容器类,包含在javax.swing 包中,可以进行嵌套,功能是对窗体中具有相同逻辑功能的组件进行组合。

SnakePanel

当Swing中的paint方法被调用时,paintComponent,paintBorder、paintChildren这三个方法也会被按顺序调用,之所以要按这个顺序调用是为了保证子组件能正确地显示在目前这个组件之上。

所以paintComponent就是本身这个容器自己画出自己组件的方法了。如果只是为了改变本身这个容器中的组件,只需要改写paintComponent方法就可以了,如果还要保留容器中的原本组件就别忘了调用super.paintComponent(g)如果要改写paint方法来改变本身这个容器的组件,那么也别忘了要调用super.paint(g),不然出来的东西是不包含原组件、原边框和子组件的

paint是系统回调函数,用户不能主动调用,需要进行重绘时,要使用repaint方法,当用户调用repaint方法后,实际上,系统后台调用了paint方法,所以,你的问题,要画一条直线,或者清除一条直线,那么你的绘制代码或清除代码要写到paint方法中,然后需要绘制或清除的时候,调用repaint方法,就可以了

package com.blb.game;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Iterator;
import java.util.Random;
import javax.swing.Timer;
import javax.swing.JButton;

import javax.swing.JPanel;

public class SnakePanel extends JPanel implements KeyListener,ActionListener{
	
	// 定义两个数组,表示蛇的坐标
	int[] snakeX = new int[10000];
	int[] snakeY = new int[10000];
	Timer timer;
	
	// 定义蛇的长度
	int length;
	int score;
	//定义小蛇状态,初始化小蛇状态
	boolean isDied = false;
	// 定义游戏初始化状态
	boolean isStart = false;
	//定义字符串变量表示小蛇移动方向,初始化小蛇方向
	String direction = "R";
	//创建随机数
	Random rd = new Random();
	//定义食物的横坐标
	int foodX;
	//定义食物的纵坐标
	int foodY;
	/*
	 * 构造方法
	 */
	public SnakePanel() {
		// 将当前的焦点定位在面板上(锁定焦点)
		this.setFocusable(true);
		// 添加监听事件
		this.addKeyListener(this);
		timer = new Timer(400,this);
		timer.start();
		init();
	}
	
	/*
	 * 初始化游戏
	 */

	private void init() {
		 //初始化小蛇长度
	    length = 3;

	    //初始分数为0
	    score = 0;

	    //初始化小蛇方向
	    direction = "R";

	    //初始化小蛇头部坐标
	    snakeX[0] = 175;
	    snakeY[0] = 225;

	    //初始化第一家身子坐标
	    snakeX[1] = 150;
	    snakeY[1] = 225;

	    //初始化第二节身子坐标
	    snakeX[2] = 125;
	    snakeY[2] = 225;
	    
	    //定义食物的横坐标
		foodX = rd.nextInt(30) * 25 + 25;
		//定义食物的纵坐标
		foodY = rd.nextInt(28) * 25 + 50;
	}
	/*
	 * 绘画方法
	 * Graphics g :画笔
	 * 
	 * */
	@Override
	protected void paintComponent(Graphics g) {
		// TODO Auto-generated method stub
		super.paintComponent(g);
		
		// 设置背景颜色
		this.setBackground(new Color(245, 239, 239));
		
		g.setColor(new Color(225,212,212));
		// 填充矩形,作为游戏区域
		g.fillRect(25, 25, 750, 700);
		
		//画出背景头部图像(this是当前面板,g是画笔,后面2个数据是图片在窗口的坐标值)
		Images.header.paintIcon(this, g, 25, 0);
		//这里你画了向右的蛇头
		//Images.right.paintIcon(this, g, snakeX[0], snakeY[0]);
//		Images.body.paintIcon(this, g, snakeX[1], snakeY[1]);
//		Images.body.paintIcon(this, g, snakeX[2], snakeY[2]);
		Images.food.paintIcon(this,g,foodX,foodY);
		
		for(int i=1;i<length;i++) {
			Images.body.paintIcon(this, g, snakeX[i], snakeY[i]);
		}
		
		if (isStart == false) {
			// 画笔颜色
			g.setColor(new Color(146, 193, 111));
			// 字体格式(字体样式,加粗,字体大小)
			g.setFont(new Font("微软雅黑", Font.BOLD, 35));
			// 文字内容以及坐标位置
			g.drawString("点击空格开始游戏", 250, 300);
		}
		//根据蛇头方向画小蛇的头部
		//Images.right.paintIcon(this,g,snakeX[0],snakeY[0]);
		if ("R".equals(direction)){
			//这里也画了,上面那个注释掉就行了
		    Images.right.paintIcon(this,g,snakeX[0],snakeY[0]);
		}else if ("L".equals(direction)){
		    Images.left.paintIcon(this,g,snakeX[0],snakeY[0]);
		}else if ("U".equals(direction)){
		    Images.up.paintIcon(this,g,snakeX[0],snakeY[0]);
		}else if ("D".equals(direction)){
		    Images.down.paintIcon(this,g,snakeX[0],snakeY[0]);
		}
		
		
	}

	@Override
	public void keyTyped(KeyEvent e) {
		// TODO Auto-generated method stub
		
	}

	/*
	 * 按键方法:实现按键监听
	 */
	@Override
	public void keyPressed(KeyEvent e) {
		// TODO Auto-generated method stub
		
		// 获取玩家按键的键盘码
		int keyCode = e.getKeyCode();
		if (keyCode == KeyEvent.VK_SPACE){

	        isStart = !isStart;
	        //重绘(这个很关键,保证了每一次按键都能进行)
	        repaint();
	    }
		
		/* 这个方法比较麻烦,不如上面直接反转
		 * 		if (keyCode == KeyEvent.VK_SPACE && isStart == false) {
					isStart = true;
					// 重绘
					repaint();
				}else if (isStart == true && keyCode == KeyEvent.VK_SPACE) {
					isStart = false;
					// 重绘
					repaint();
				}
		 */
		/*
		if语句判断按键码对应小蛇的蛇头方向;
		当按键向右,且蛇头方向不向左时,更改蛇头的方向向右;
		当按键向左,且蛇头方向不向右时,更改蛇头的方向向左;
		当按键向下,且蛇头方向不向上时,更改蛇头的方向向下;
		当按键向上,且蛇头方向不向下时,更改蛇头的方向向上;
		 */
		if (keyCode == KeyEvent.VK_RIGHT && !("L".equals(direction))){
		    direction = "R";
		}else if (keyCode == KeyEvent.VK_LEFT &&!("R".equals(direction))){

		    direction = "L";
		}else if (keyCode == KeyEvent.VK_DOWN && !("U".equals(direction))){
		    direction = "D";
		}else if (keyCode == KeyEvent.VK_UP && !("D".equals(direction))){
		    direction = "U";
		}
		
		
	}

	@Override
	public void keyReleased(KeyEvent e) {
		// TODO Auto-generated method stub
		
	}
	/*
	 * 行为事件:小蛇移动,吃食物
	 */
	@Override
	public void actionPerformed(ActionEvent e) {
//		//判断游戏的开始状态,为true,则小蛇移动
//	    if (isStart){
//	        //for循环改变小蛇身体坐标,把前一节坐标赋值给后一节,形成移动
//	        for (int i = length-1;i>0;i--){
//
//	            snakeX[i] = snakeX[i-1];
//	            snakeY[i] = snakeY[i-1];
//	        }
//
//	        //小蛇头部移动,横坐标加25
//	        snakeX[0] = snakeX[0] + 25;
//
//	        //当小蛇头部横坐标超过750,则初始为25
//	        if (snakeX[0]>750){
//	            snakeX[0] = 25;
//	        }
//	        //重绘
//	        repaint();
//	    }
		
		//这一段重复了,这是演示向右的代码
	  //判断游戏的开始状态,为true,则小蛇移动
	  		if (isStart){
	  		    //for循环改变小蛇身体坐标,把前一节坐标赋值给后一节,形成移动
	  		    for (int i = length - 1; i > 0; i--) {

	  		        snakeX[i] = snakeX[i - 1];
	  		        snakeY[i] = snakeY[i - 1];
	  		    }
	  		    //如果蛇头方向向右,则头部横坐标加25,当超过750,则初始化为25
	  		    if ("R".equals(direction)){

	  		        snakeX[0] = snakeX[0] + 25;
	  		        if (snakeX[0]>750){
	  		            snakeX[0] = 25;
	  		        }
	  		    }

	  		    //如果蛇头方向向左,则头部横坐标减25,当小于25,则初始化为750
	  		    if ("L".equals(direction)){

	  		        snakeX[0] = snakeX[0] - 25;
	  		        if (snakeX[0]<25){
	  		            snakeX[0] = 750;
	  		        }
	  		    }

	  		    //如果蛇头方向向下,则头部纵坐标加25,当大于725,则初始化为50
	  		    if ("D".equals(direction)){

	  		        snakeY[0] = snakeY[0] + 25;
	  		        if (snakeY[0]>725){
	  		            snakeY[0] = 50;
	  		        }
	  		    }

	  		    //如果蛇头方向向上,则头部纵坐标减25,当小于50,则初始化为725
	  		    if ("U".equals(direction)){

	  		        snakeY[0] = snakeY[0] - 25;
	  		        if (snakeY[0]<50){
	  		            snakeY[0] = 725;
	  		        }
	  		    }
	  		}
	  		//重绘
	  		repaint();
	  	//如果小蛇蛇头坐标等于食物坐标,则代表小蛇吃到食物,小蛇长度加1,食物坐标重新随机生成
			if (snakeX[0] == foodX && snakeY[0] == foodY) {

			    length++;
			    foodX = rd.nextInt(30) * 25 + 25;
			    foodY = rd.nextInt(28) * 25 + 25;
			}
			//如果小蛇蛇头坐标等于食物坐标,则代表小蛇吃到食物,小蛇长度加1,分数加10
			// 食物坐标重新随机生成
			if (snakeX[0] == foodX && snakeY[0] == foodY) {

			    length++;
			    score = score + 10;
			    foodX = rd.nextInt(30) * 25 + 25;
			    foodY = rd.nextInt(28) * 25 + 25;
			}
			//for循环遍历小蛇身子坐标,当头部坐标与任意身子坐标重合,游戏结束,更改小蛇状态
			for (int i = 1; i <length ; i++) {

			    if (snakeX[0] == snakeX[i] &&snakeY[0] == snakeY[i]){
			        isDied = true;
			    }
			}
	}
	
}


Images

用于封装图片对象

package com.blb.game;

import java.net.URL;

import javax.swing.ImageIcon;

/*
 * 封装图片对象
 */

public class Images {
	
	// 找到图片路径
	public static URL url1 = Images.class.getResource("/com/blb/img/header.png");
	// 封装成图片对象
	public static ImageIcon header = new以上是关于Java贪吃蛇小游戏的主要内容,如果未能解决你的问题,请参考以下文章

Java实现贪吃蛇游戏代码

求大神改一下贪吃蛇C++代码

Java贪吃蛇小游戏

贪吃蛇的java代码分析

JAVA GUI贪吃蛇小游戏「内含代码」

利用c# 编写贪吃蛇游戏如何修改label的属性