【Java】【MySnake】仿贪吃蛇小游戏开源代码(持续更新)

本文分享了一款基于Java开发的贪吃蛇小游戏,玩家控制的蛇可以穿越屏幕边缘,吃到不同颜色的蛋会变色。游戏代码由作者在暑假期间自学Java一个月后独立完成,旨在通过实际项目加深对Java知识点的理解和记忆。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

宣传视频在这里:https://www.bilibili.com/video/av62633283#reply1843437215
仿贪吃蛇小游戏,可以穿过边缘,吃到蛋可以变色。
这个是暑假学了一个月java做的。java很多知识点太枯燥,很难有学下去的动力,因此决定从一个小项目开始用实例了解知识点,更容易记住,也更容易学习。因此里面的注释有部分是比较乱的,涉及到一些基础知识点…
目前游戏有一些bug,还不知道怎么处理,不过应该不痛不痒。
跟着b站“马士兵”的视频做的,在此表示感谢0.0
将代码开源给大家,仅供学习参考,严禁用于各种商业用途,转载注明出处。

Yard

import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

public class Yard extends Frame {
	
	public final static int ROWS = 30;	//先定义院子的行和列,设置为常量,变量名大写
	public final static int COLS = 30;
	public static final int BLOCK_SIZE = 20;	//每个小格的长度
	public static int flag = 1;			//线程开关
	public static int speed = 50;		//fps
	public static int init_size = 30;	//蛇的初始长度
	
	
	Image offScreenImage = null;	//解决缓冲闪烁问题(刷背景)
	
	public void launch()	//将程序运行写在一个函数,里面的this是在调用继承的Frame,不用this也可以
	{
		this.setLocation(350, 10);	//方框位置
		this.setSize(COLS*BLOCK_SIZE, ROWS*BLOCK_SIZE );	//方框大小
		setResizable(false);	//不可调整大小
		this.addWindowListener(new WindowAdapter() {		//按钮可关闭

			@Override
			public void windowClosing(WindowEvent e) {
				super.windowClosing(e);
				System.exit(0);
			}
		});
				
		this.setVisible(true);	//可显示
		
		new Thread(new PaintThread()).start();	//启动线程
		this.addKeyListener(new KeyMonitor());	//加入键盘监听事件
	}
	
	Snake s = new Snake();	//生成蛇
	Egg e = new Egg();		//生成蛋
	
	public static void main(String[] args) {
		new Yard().launch();		//launch为非静态,因此需要创建实例对象Yard访问该方法(因为main是静态方法),此为匿名类
		
	}
	
	
	@Override
	public void update(Graphics g) {	//双缓冲
		if(offScreenImage == null) {
			offScreenImage = this.createImage(COLS*BLOCK_SIZE, ROWS*BLOCK_SIZE);
		}
		Graphics gOff = offScreenImage.getGraphics();
		paint(gOff);
		g.drawImage(offScreenImage, 0, 0, null);
	}
	
	private class KeyMonitor extends KeyAdapter{

		@Override
		public void keyPressed(KeyEvent e) {
			s.keyPressed(e);
		}
		
	}
	
	private class PaintThread implements Runnable{

		@Override
		public void run() {
			while(flag == 1) {
				repaint();
				try {
					Thread.sleep(speed);	//每speed毫秒一帧
					
				}	catch (InterruptedException e) {
						e.printStackTrace();
				}
			}
		}
	}
	
	public void paint(Graphics g) {	//画面板	paint方法不需要调用,只需要重写
		g.setColor(Color.BLACK);	//背景色
		g.fillRect(0, 0, COLS*BLOCK_SIZE, ROWS*BLOCK_SIZE);	//填充整个矩形区域
		g.setColor(Color.RED);		//画红线
		for(int i = 1;i < ROWS;i++)	//画出横线
		{
			g.drawLine(0, BLOCK_SIZE*i, COLS*BLOCK_SIZE, BLOCK_SIZE*i);	
		}
		for(int i = 1;i < COLS;i++)	//画出竖线
		{
			g.drawLine(BLOCK_SIZE*i, 0, BLOCK_SIZE*i, ROWS*BLOCK_SIZE);
		}
		s.eat(e);	//调用蛇吃蛋函数
		s.draw(g);	//调用画出蛇的函数
		e.draw(g);	//调用画出蛋的函数
	}
}

Snake

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;


//蛇是由头,尾 + n个结点拼接而成,因此蛇的成员变量应该有头结点,尾结点,蛇长度,蛇移动方向。
//结点又作为一个类,是蛇的内部类。结点成员变量有x坐标,y坐标,长度,宽度(表示出结点的位置和大小), 蛇吃蛋后新增结点的方向dir, 结点尾指针(类似于链表).方向有4个,新建一个方向枚举类Dir型表示.
public class Snake {
	public Node head = null;
	Node tail = null;
	int size = 0;	//蛇节数
	Color snake_color;
	Node color_n = head;
	private Node n = new Node(20, 20, Dir.L);	//初始化Snake
	
	public Snake(Node node) {	//在外部类中可以直接访问内部类,但不能直接访问内部类成员
		head = node;
		tail = node;
		size = 1;
	}
	
	public Snake() {	//初始化Snake
		head = n;
		tail = n;
		head.next = null;
		tail.next = null;
		size = 1;
		for(int i = 1;i <= Yard.init_size;i++) AddToHead();	//设置初始长度
	}
	
	public void AddToTail() {	//双向链表实现
		Node node = null;
		switch(tail.dir) {
		case U:
			node = new Node(tail.row+1, tail.col, tail.dir);
			break;
		case D:
			node = new Node(tail.row-1, tail.col, tail.dir);
			break;
		case L:
			node = new Node(tail.row, tail.col+1, tail.dir);
			break;
		case R:
			node = new Node(tail.row, tail.col-1, tail.dir);
			break;
		}
		tail.next = node;
		tail = node;
		size++;
	}
	
	public void AddToHead() {	//双向链表实现
		Node node = null;
		switch(head.dir) {
		case D:
			node = new Node(head.row+1, head.col, head.dir);
			break;
		case U:
			node = new Node(head.row-1, head.col, head.dir);
			break;
		case R:
			node = new Node(head.row, head.col+1, head.dir);
			break;
		case L:
			node = new Node(head.row, head.col-1, head.dir);
			break;
		}
		node.next = head;
		head.pre = node;
		head = node;
		size++;
	}
	
	public void draw(Graphics g) {	//画出整条蛇
		if(size <= 0) return;
		move();
		ChangeColor();	//改变蛇的颜色
		for(Node n = head;n != null;n = n.next)	//循环画出结点
		{
			n.draw(g, n);
		}
	}
	
	private void ChangeColor() {	//蛇颜色变化
		int speed = 2;	//颜色变化速度调节
		for(int i = 1;i <= speed;i++)
		{
			if(color_n == null) return;
			color_n.node_color = snake_color;
			color_n = color_n.next;
		}
	}

	private void move() {	//蛇移动
		AddToHead();	//增加头部结点
		checkEdge();	//检测边缘
//		checkDead();	//检测是否死亡
		deleteFromTail();	//删除尾巴结点
	}
	
	public void checkEgg() {	//检测鸡蛋位置是否与蛇冲突
//		int limit = 5; 
//		if(Egg.row - limit<= 0 || Egg.row+ limit >= Yard.ROWS || Egg.col - limit <= 0 && Egg.col+ limit >= Yard.COLS)
//		{
//			System.out.println(1);
//			Egg.hush = true;
//			return;
//		}
		if(Egg.egg_color == snake_color)
		{
			Egg.hush = true;
			return;
		}
		for(Node n = head.next;n != null;n = n.next)
		{
			if(n.row == Egg.row && n.col == Egg.col)
			{
				Egg.hush = true;
				return;
			}
		}
		Egg.hush = false;
	}
	
	private void checkDead() {	//检验蛇是否死亡
		for(Node n = head.next;n != null;n = n.next) {
			if(head.row == n.row && head.col == n.col) {
				deleteFromHead();	
				AddToTail();
				Yard.flag = 0;	//停止线程
			}
		}
	}

	private void checkEdge() {	//检验边缘
		if(head.row > Yard.ROWS) head.row = 0;
		else if(head.row < 0) head.row = Yard.ROWS;
		else if(head.col > Yard.COLS-1) head.col = 0;
		else if(head.col < 0) head.col = Yard.COLS-1;
	}

	public void eat(Egg e) {	//蛇吃掉鸡蛋
		if(this.getRect(head).intersects(e.getRect())) {	//intersects判断矩形是否重合
			snake_color = Egg.egg_color;
			color_n = head;
			e.reAppear();			//重置鸡蛋位置
			checkEgg();				//检测鸡蛋位置是否位于蛇身
			while(Egg.hush)
			{
				e.reAppear();
				checkEgg();
			}
			AddToHead();
		}
	}
	
	private Rectangle getRect(Node tmp) {	//返回头结点的矩形对象(用于碰撞)
		return new Rectangle(Yard.BLOCK_SIZE * tmp.col, Yard.BLOCK_SIZE * tmp.row, tmp.w, tmp.h);
	}
	
	private void deleteFromTail() {
		if(size == 0) return;
		tail = tail.pre;
		tail.next = null;
	}
	
	private void deleteFromHead() {
		if(size == 0) return;
		head = head.next;
	}



	private class Node{
		
		int w = Yard.BLOCK_SIZE;	//结点宽度
		int h = Yard.BLOCK_SIZE;	//结点高度
		int row, col;	//结点所在位置
		int fix_shape = 3;	//画结点的修补参数,让结点有立体感
		Color node_color = snake_color;
		Dir dir = Dir.L;	//默认方向
		Node next = null;
		Node pre = null;
		
		Node(int row, int col, Dir dir) {	//结点构造函数(可以用Alt+Shift引用source里面选Constructor生成构造函数)
			this.row = row;
			this.col = col;
			this.dir = dir;
		}
		
		void draw(Graphics g, Node n) {		//画出结点
			g.setColor(node_color);
			if(n == head)
			{
				g.drawOval(Yard.BLOCK_SIZE*col, Yard.BLOCK_SIZE*row, w-2, h-2);
			}
			else
			{
				g.fillRect(Yard.BLOCK_SIZE*col+fix_shape, Yard.BLOCK_SIZE*row+fix_shape, w-2*fix_shape, h-2*fix_shape);	//矩形填充结点的x,y坐标和宽度高度

			}
		}
	}
	
	public void keyPressed(KeyEvent e) {	//响应键盘
		int key = e.getKeyCode();
		switch(key) {
		case KeyEvent.VK_LEFT:
			if(head.dir != Dir.R) head.dir = Dir.L;
			break;
		case KeyEvent.VK_UP:
			if(head.dir != Dir.D) head.dir = Dir.U;
			break;
		case KeyEvent.VK_DOWN:
			if(head.dir != Dir.U) head.dir = Dir.D;
			break;
		case KeyEvent.VK_RIGHT:
			if(head.dir != Dir.L) head.dir = Dir.R;
			break;
		}
	}
}

Egg

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.util.Random;

public class Egg {
	public static int row, col;
	int w = Yard.BLOCK_SIZE;	//结点宽度
	int h = Yard.BLOCK_SIZE;	//结点高度
	int seed = 0;	//随机数种子
	public static Color egg_color;
	public static boolean hush = false;
	
	private static Random r = new Random();	//随机出生点
	private static Random r1 = new Random();//随机数
	
	public Egg(int row, int col, int seed) {
		this.row = row;
		this.col = col;
		this.seed = seed;
	}
	
	public Egg() {		//r.nextInt(i)表示(0-i)的随机数, r1随机6个数表示6种颜色
		this(r.nextInt(Yard.ROWS-5) + 5, r.nextInt(Yard.COLS-5) + 5, r1.nextInt(6));	//产生1~边界-1的随机坐标,去除出生点在边界可能性
	}
	
	public void reAppear() {	//Egg重新出现,采用随机坐标
		int limit = 6;	//limit用于限制鸡蛋出现的范围
//		this.row = row + r.nextInt(limit) - limit;
//		this.col = col + r.nextInt(limit) - limit;
		Egg.row = r.nextInt(Yard.ROWS-1);
		Egg.col = r.nextInt(Yard.COLS-1);
		if(row < limit) row += limit;
		if(row > Yard.ROWS-limit) row -= limit;
		if(col < limit) col += limit;
		if(col > Yard.COLS-limit) col -= limit;
		
		this.seed = r1.nextInt(6);
		switch(seed)	//随机颜色
		{
			case 0: egg_color = Color.green; break;
			case 1: egg_color = Color.BLUE;break;
			case 2: egg_color = Color.MAGENTA;break;
			case 3: egg_color = Color.ORANGE;break;
			case 4: egg_color = Color.PINK;break;
			case 5: egg_color = Color.RED;break;
			default: break;
		}
	}
	
	public Rectangle getRect() {	//返回头结点的矩形对象(用于碰撞)
		return new Rectangle(Yard.BLOCK_SIZE * this.col, Yard.BLOCK_SIZE * this.row, this.w, this.h);
	}
	
	void draw(Graphics g) {		//画出结点
		
		g.setColor(egg_color);
		g.fillOval(Yard.BLOCK_SIZE*col, Yard.BLOCK_SIZE*row, w, h);	//椭圆填充结点的x,y坐标和宽度高度
	}
	
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值