Java小游戏之贪吃蛇

本程序是结合自己对于面向对象的理解,线程和GUI做的小游戏

游戏运行如图:
运行时的图片游戏结束的图片
首先我定义了两个类,一个用于存储蛇的信息,一个用于存储食物的信息

蛇的类

package SnakeGame;

public class Snake {
//	设置蛇的身体位置坐标,最大可达到34*24
	public int[] snakex=new int[750];
	public int[] snakey=new int[750];
//	蛇身体的长度
	int len=0;
//	蛇的方向
	String direction=null;
//	初始化
	public Snake() {
		setup();
	}
//	初始化
	public void setup() {
		len=3;
		direction="R";
		snakex[0]=100; snakex[1]=75; snakex[2]=50;
		snakey[0]=100; snakey[1]=100; snakey[2]=100;
	}
	
}

食物的类

package SnakeGame;
import java.util.*;

public class food {
	Random r=new Random();
//	定义食物的数量
	public int len=4;
//	定义食物的位置
	int[] footx=new int[len];
	int[] footy=new int[len];
	
	public food() {
		for(int i=0;i<len;i++) {
			footx[i]=r.nextInt(34)*25+25;
			footy[i]=r.nextInt(24)*25+75;
		}
	}
	
//	用于初始化
	public void setup() {
		for(int i=0;i<len;i++) {
			footx[i]=r.nextInt(34)*25+25;
			footy[i]=r.nextInt(24)*25+75;
		}
	}
	
//	用于重新生成食物
	public void produce(int i) {
		footx[i]=r.nextInt(34)*25+25;
		footy[i]=r.nextInt(24)*25+75;
	}
}

*说明:如上图运行界面所示的,因为我黑色背景定义的宽高为850px650px,蛇的身体节点和食物的宽高都是25px,x轴空出的间距有25px,y轴空出75px,所以footx[i]=r.nextInt(34)*25+25;footy[i]=r.nextInt(24)*25+75;(其他情况以此类推)

创建窗口

package SnakeGame;
import javax.swing.*;

public class SnakeFrame {
	
	public static void main(String[] args) {
		JFrame frame=new JFrame();
		SnakePanel sp=new SnakePanel();
		
		frame.add(sp);
		frame.setBounds(400,50,900,720);
		frame.setIconImage(new ImageIcon("Images/logo.png").getImage());
		frame.setResizable(false);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setTitle("贪吃蛇");
		frame.setVisible(true);
		
	}
	
}

在画板中画出

package SnakeGame;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.*;

@SuppressWarnings("serial")
public class SnakePanel extends JPanel implements ActionListener,KeyListener{
	ImageIcon title=new ImageIcon("Images/title.jpg");
	ImageIcon body=new ImageIcon("Images/body.png");
	ImageIcon up=new ImageIcon("Images/up.png");
	ImageIcon down=new ImageIcon("Images/down.png");
	ImageIcon right=new ImageIcon("Images/right.png");
	ImageIcon left=new ImageIcon("Images/left.png");
	ImageIcon food=new ImageIcon("Images/food.png");
	
//	蛇
	Snake s;
//	食物
	food f;
//	是否开始
	boolean isStarted=false;
//	是否结束
	boolean isFailed=false;
//	计时器
	Timer timer=new Timer(110, this);
	
	public SnakePanel() {
//		获取焦点
		this.setFocusable(true);
		s=new Snake();
		f=new food();
//		计时器运行
		timer.start();
//		加入监听事件
		this.addKeyListener(this);
	}
	
	@Override
	public void paint(Graphics g) {
		super.paintComponent(g);
		title.paintIcon(this, g, 25, 11);
		g.fillRect(25, 75, 850, 650);
		
//		判断蛇的方向,画蛇头
		if (s.direction.equals("R")) 
            right.paintIcon(this, g, s.snakex[0], s.snakey[0]);
        else if (s.direction.equals("L")) 
            left.paintIcon(this, g, s.snakex[0], s.snakey[0]);
        else if (s.direction.equals("U")) 
            up.paintIcon(this, g, s.snakex[0], s.snakey[0]);
        else if (s.direction.equals("D")) 
            down.paintIcon(this, g, s.snakex[0], s.snakey[0]);
//		画蛇的身体
		for(int i=1;i<s.len;i++) {
			body.paintIcon(this, g, s.snakex[i], s.snakey[i]);
		}
//		画食物
		for(int i=0;i<f.len;i++) {
			food.paintIcon(this, g,f.footx[i], f.footy[i]);
		}
		
//		判断游戏是否开始,没开始就显示
		if(!isStarted) {
			g.setColor(Color.WHITE);
			g.setFont(new Font("arial",Font.BOLD,30));
			g.drawString("Press Space to start/pause", 200, 200);
		}
//		判断游戏是否结束,结束就显示
		if(isFailed) {
			g.setColor(Color.WHITE);
			g.setFont(new Font("arial",Font.BOLD,30));
			g.drawString("Game Over,Press Space to restart", 200, 200);
		}
		
//		画分数和蛇的长度
		g.setColor(Color.WHITE);
		g.setFont(new Font("arial",Font.BOLD,15));
		g.drawString("Length: "+(s.len), 650, 35);
		g.drawString("Score : "+(s.len-3)*10, 650, 55);
	}

	@Override
	public void actionPerformed(ActionEvent e) {
//		timer.start();
		
//		如果开始并且未结束执行
		if(isStarted&&!isFailed) {
//			存储的坐标会比蛇的长度多一,因为如果蛇吃到食物就能马上获取最后一个的坐标
//			如果没有多一,蛇的身体会画在(0,0)位置
//			从后往前,给最后一个赋值前一个的坐标
			for(int i=s.len;i>0;i--) {
				s.snakex[i]=s.snakex[i-1];
				s.snakey[i]=s.snakey[i-1];
			}
//			判断蛇头的方向,每次加减25px(图片大小25px)
			if(s.direction.equals("R")) {
				s.snakex[0]+=25;
				if(s.snakex[0]>850) s.snakex[0]=25;
			}else if(s.direction.equals("L")) {
				s.snakex[0]-=25;
				if(s.snakex[0]<25) s.snakex[0]=850;
			}else if(s.direction.equals("U")) {
				s.snakey[0]-=25;
				if(s.snakey[0]<75) s.snakey[0]=650;
			}else if(s.direction.equals("D")) {
				s.snakey[0]+=25;
				if(s.snakey[0]>650) s.snakey[0]=75;
			}
			
//			判断是否吃到食物,吃到就调用food的produce重新生成食物位置,蛇长度+1
			for(int i=0;i<f.len;i++) {
				if(s.snakex[0]==f.footx[i]&&s.snakey[0]==f.footy[i]) {
					s.len++;
					f.produce(i);
				}
			}
			
//			判断是否撞到自己身体,撞到结束游戏
			for(int i=1;i<s.len;i++) {
				if(s.snakex[0]==s.snakex[i]&&s.snakey[0]==s.snakey[i]) {
					isFailed=true;
				}
			}
			
		}
		
//		重绘
		repaint();
		
	}
	
//	用于蛇,食物位置,判断条件初始化
	public void setup() {
		s.setup();
		f.setup();
		isStarted=false;
		isFailed=false;
	}
	
	
	@Override
	public void keyPressed(KeyEvent e) {
		
//		如果按键为空格执行
		if(e.getKeyCode()==KeyEvent.VK_SPACE) {
//			判断是否结束,结束初始化
			if(isFailed) {
				setup();
			}else {
//				没有结束就就为暂停/开始,当前状态取反
				isStarted=!isStarted;
			}
		}else if(isStarted&&!isFailed) {//当游戏开始时才能按
			if(e.getKeyCode()==KeyEvent.VK_UP&&s.direction!="D") {//按下向上,且当前蛇的方向不能是向下,防止往回跑
				s.direction="U";
			}else if(e.getKeyCode()==KeyEvent.VK_DOWN&&s.direction!="U") {//
				s.direction="D";
			}else if(e.getKeyCode()==KeyEvent.VK_RIGHT&&s.direction!="L") {//
				s.direction="R";
			}else if(e.getKeyCode()==KeyEvent.VK_LEFT&&s.direction!="R") {//
				s.direction="L";
			}
		}
		
	}

	@Override
	public void keyReleased(KeyEvent arg0) {
		
	}

	@Override
	public void keyTyped(KeyEvent arg0) {
		
	}
	
	
}




可以运行! (以下代码只是其中的一个类) package chy.snake.entities; import java.awt.Color; import java.awt.Graphics; import java.awt.Point; import java.util.HashSet; import java.util.LinkedList; import java.util.Set; import chy.snake.listener.SnakeListener; import chy.snake.util.Global; public class Snake { public static final int up = 1; public static final int down = -1; public static final int left = -2; public static final int right = 2; private int oldDirection,newDirection; //newDirection:一次时间 间隔内输入的最后方向 private Point oldTail; private boolean life; //life 为 true或者false,初始为true, 用于118行 private LinkedList<Point> body = new LinkedList<Point> (); //需要经常访问蛇的第一个和最后一个节点,使用链表LinkedList存放蛇的身体节点,因为它有getFirst(),getLast(),removeLast(),方法 private Set<SnakeListener> listeners = new HashSet<SnakeListener>(); public Snake(){ init(); } public void init(){ //初始化 int x = Global.WIDTH/2; int y = Global.HEIGHT/2; for(int i=0;i<3;i++){ //初始长度3 body.addLast(new Point(x-i,y)); //是addLast } oldDirection = newDirection = right; //初始方向 右 life = true; } public void die(){ life = false; } public void move(){ System.out.println("Snake's move"); if (!(oldDirection + newDirection == 0)){ oldDirection = newDirection; } //1.去尾 oldTail = body.removeLast(); int x = body.getFirst().x; int y = body.getFirst().y; //蛇头的x,y坐标 switch(oldDirection){ case up: y--; break; case down: y++; break; case left: x--; break; case right: x++; break; } Point newHead = new Point(x,y); //2.加头 body.addFirst(newHead); } public void changeDirection(int direction){ /*无效方向:在蛇的这一次移动之后和下一次移动之前的 这个时间间隔内输入了多个方向,只有最后一个方向 是 有效方向,其余的都为无效方向*/ System.out.println("Snake's changeDirection"); newDirection = direction; //将一个时间间隔内按得最后方向,赋给 newDirection } public void eatFood(){ System.out.println("Snake's eatFood"); body.addLast(oldTail); //后面的节点不去掉 } public boolean isEatFood(){ System.out.println("Snake's isEatFood"); return false; } public boolean isEatBody(Snake snake){ //比较蛇是否吃到身体 System.out.println("snake's isEatBody"); for(int i= 1;i<body.size();i++){ //i 从蛇头结点的下一个节点开始,排除蛇头结点 if(body.get(i).equals(this.getHead())){ //如果i 的节点 和 头结点 相同 return true; } } return false; } public void drawMe(Graphics g){ System.out.println("Snake's drawMe"); g.setColor(Color.GREEN); //设置蛇的颜色 for(Point p : body){ g.fill3DRect(p.x * Global.CELL_SIZE, p.y * Global.CELL_SIZE, Global.CELL_SIZE, Global.CELL_SIZE, true); } } public Point getHead(){ //得到蛇头节点,判断吃食物 return body.getFirst(); } private class SnakeDriver implements Runnable{ //线程,不停的调用move方法 @Override public void run() { // TODO 自动生成的方法存根 while(life){ // 42和46行,life为true 或者false move(); for(SnakeListener l : listeners){ l.snakeMoved(Snake.this); //循环,依次调用SnakeMoved方法 } try { Thread.sleep(300); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } } public void start(){ new Thread(new SnakeDriver()).start(); //启动线程的方法 } public void addSnakeListener(SnakeListener l){ if(l != null){ this.listeners.add(l); } } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值