创建一个MainFrame类继承JFrame,表示主窗体。
使用继承,提高了代码的复用性,多个类相同的成员可放在同一个类中
public class MainFrame extends JFrame {
//创建窗体对象时,初始化窗体参数
public MainFrame(){
initFrame();
}
}
MainFrame类中写一个initFrame方法,初始化窗体参数
//1.初始化窗体参数
private void initFrame() {
//设置窗体标题
setTitle("贪吃蛇小游戏");
//设置窗体大小
setSize(610, 640);
//设置窗体位置
setLocation(400, 400);
//设置窗体大小不能改变
setResizable(false);
//设置窗体关闭按钮的作用,是正常退出
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
在MainFrame中添加main方法,创建窗体并运行,即可看到窗体
//主方法
public static void main(String[] args) {
//创建窗体,并显示在屏幕上
new MainFrame().setVisible(true);
}
在JFrame类中写一个initGamePanel方法,初始化游戏棋盘
//初始化游戏面板
private void initGamePanel() {
JPanel jPanel = new JPanel() {
//paint是用来绘制面板中的内容
@Override
public void paint(Graphics g) {
//设置画笔颜色
g.setColor(Color.black); //设置画笔颜色是黑色
//绘制40条横线
for(int i=1;i<=40;i++){
g.drawLine(0,15*i,600,15*i);
}
//绘制40条竖线
for (int i=1;i<=40;i++){
g.drawLine(15*i,0,15*i,600);
}
}
};
add(jPanel);
}
创建一个Node类表示节点,一条蛇有若干个节点组成,每一个节点其实就是一个小方格,我们用行和列来定义节点在棋盘中的位置。
public class Node {
private int x; //横坐标
private int y; //纵坐标
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
创建Snake类表示蛇,一条蛇有多个节点使用LinkedList集合存储Node节点,蛇出生的时候 有3个节点。
public class Snake {
//body
private LinkedList<Node> body;
//创建蛇对象时,初始化蛇身
public Snake() {
initSnake();
}
//初始化蛇身 3个节点
public void initSnake(){
//创建蛇的躯体
body=new LinkedList<>();
//给蛇添加3个节点,在棋盘的中心位置横向排列
body.add(new Node(20,20));
body.add(new Node(21,20));
body.add(new Node(22,20));
}
//获取蛇蛇身
public LinkedList<Node> getBody(){
return body;
}
}
在MainFrame中添加一个创建Snake的成员变量,表示窗体中有一条蛇
public class MainFrame extends JFrame {
//定义成员变量
private Snake snake=new Snake();
....
}
在绘制棋盘的paint方法中,添加下面的代码绘制把蛇绘制到棋盘上
//绘制横线
...
//绘制竖线
...
//绘制蛇,根据每一个节点的坐标在棋盘上绘制矩形
for (Node node : snake.getBody()) {
//每一个节点的宽度和高度是15
g.fillRect(node.getX()*15,node.getY()*15,15,15);
}
再次运行,就可以看到有一条蛇静静的躺在棋盘中间了
定义一个枚举类型,表示运动的4个方向
public enum Direction {
UP,DOWN,LEFT,RIGHT
}
在Snake类中定义一个移动方向的变量成员变量,和move方法。
public class Snake{
//蛇头运动的方向
private Direction direction;
//移动的方法: 每次移动在蛇头运动方向添加一个节点,再删除最后一个节点
public void move(){
//获取蛇头的节点
Node head = body.getFirst();
//新的蛇头节点
Node newHead=null;
//检查移动的方向
switch (direction){
//如果向左移动
case LEFT:
//创建一个新的蛇头节点
newHead=new Node(head.getX()-1,head.getY());
//把新的蛇头节点添加到body的第一个元素
body.addFirst(newHead);
//删除蛇尾
body.removeLast();
break;
case RIGHT:
//创建一个新的蛇头节点
newHead=new Node(head.getX()-1,head.getY());
//把新的蛇头节点添加到body的第一个元素
body.addFirst(newHead);
//删除蛇尾
body.removeLast();
break;
case UP:
//创建一个新的蛇头节点
newHead=new Node(head.getX(),head.getY()-1);
//把新的蛇头节点添加到body的第一个元素
body.addFirst(newHead);
//删除蛇尾
body.removeLast();
break;
case DOWN:
//创建一个新的蛇头节点
newHead=new Node(head.getX(),head.getY()+1);
//把新的蛇头节点添加到body的第一个元素
body.addFirst(newHead);
//删除蛇尾
body.removeLast();
break;
}
}
}
在MainFrame类中,创建一个定时器每200毫秒,重绘游戏棋盘
public class MainFrame extends JFrame {
...
//游戏棋盘面板
JPanel jPanel=null;
//定义定时器
private Timer timer;
public MainFrame() throws HeadlessException {
//初始化窗体参数
initFrame();
//初始化窗体中的游戏面板
initGamePanel();
//初始化定时器
initTimer();
}
//2.初始化游戏棋盘
private void initGamePanel() {
jPanel = new JPanel() {
//paint是用来绘制面板中的内容
@Override
public void paint(Graphics g) {
//每次重绘时清空面板中的内容
g.clearRect(0,0,getWidth(),getHeight());
....
}
};
add(jPanel);
}
//3.初始化定时器
private void initTimer() {
TimerTask task=new TimerTask() {
@Override
public void run() {
//调用蛇移动的方法
snake.move();
//每次移动重绘游戏棋盘,会自动调用jPanel的paint方法
jPanel.repaint();
}
};
timer=new Timer();
timer.scheduleAtFixedRate(task,0,200);
}
}
在MainFrame类中,添加一个setKeyListener的方法,监听上下左右四个键
public class MainFrame extends JFrame {
...
public MainFrame() throws HeadlessException {
...
//添加键盘监听
setkeyListener();
}
//添加键盘监听
private void setkeyListener() {
//对上下左右键进行监听
addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()){
//按下上键
case KeyEvent.VK_UP:
//蛇头方向改为上
snake.setDirection(Direction.UP);
break;
//按下下键
case KeyEvent.VK_DOWN:
//蛇头方向改为下
snake.setDirection(Direction.DOWN);
break;
//按下左键
case KeyEvent.VK_LEFT:
//蛇头方向改为左
snake.setDirection(Direction.LEFT);
break;
//按下右键
case KeyEvent.VK_RIGHT:
//蛇头方向改为右
snake.setDirection(Direction.RIGHT);
break;
}
}
});
}
}
到此蛇就可以跟随键盘移动了
当蛇移动到边界处就游戏结束,把标记改为false
public class Snake{
...
//定义一个标记true表示蛇是活的,可以运动
private boolean isLive=true;
//检查蛇是否碰壁
private void checkOver() {
//获取蛇头
Node head = body.getFirst();
int x=head.getX();
int y=head.getY();
if(x<0||y<0||x>=40||y>=40){
isLive=false;
}
}
}
在定时器中调用checkOver()方法
//3.初始化定时器
private void initTimer() {
TimerTask task=new TimerTask() {
@Override
public void run() {
//移动
snake.move();
//检查是否碰壁
snake.checkOver();
}
};
timer=new Timer();
timer.scheduleAtFixedRate(task,0,100);
}
创建一个Food类表示食物,其实食物也是一个节点,所以让Food继承Node
public class Food extends Node {
//创建食物的时候,随机产生一个位置
public Food() {
reset();
}
//随产生位置
public void reset(){
//随机生成食物的坐标
Random r=new Random();
setX(r.nextInt(40));
setY(r.nextInt(40));
}
}
在MainFrame中定义一个成员Food表示食物,并把食物绘制到棋盘上
public class MainFrame extends JFrame {
//定义食物
private Node food=new Node();
public MainFrame() throws HeadlessException {
//初始化窗体参数
initFrame();
//初始化窗体中的游戏面板
initGamePanel();
//初始化定时器
initTimer();
//添加键盘监听
setkeyListener();
}
//2.初始化游戏棋盘
private void initGamePanel() {
jPanel = new JPanel() {
//paint是用来绘制面板中的内容
@Override
public void paint(Graphics g) {
//清空棋盘
g.clearRect(0,0,600,600);
//绘制横线、绘制竖线、绘制蛇
//绘制食物
g.fillRect(food.getX()*15,food.getY()*15,15,15);
}
};
add(jPanel);
}
}
在Snake类中添加一个eatFood方法
//吃食物
public void eatFood() {
//获取蛇头
Node head = body.getFirst();
//如果蛇头和食物的位置重合,就在蛇头添加一个元素
if(head.getX()==MainFrame.food.getX()
&&head.getY()==MainFrame.food.getY()){
//沿着蛇头运动方向创建一个节点
//新的蛇头节点
Node newHead=null;
//检查移动的方向
switch (direction){
//如果向左移动
case LEFT:
//创建一个新的蛇头节点
newHead=new Node(head.getX()-1,head.getY());
//把心得蛇头节点添加到body的第一个元素
body.addFirst(newHead);
break;
case RIGHT:
//创建一个新的蛇头节点
newHead=new Node(head.getX()+1,head.getY());
//把心得蛇头节点添加到body的第一个元素
body.addFirst(newHead);
break;
case UP:
//创建一个新的蛇头节点
newHead=new Node(head.getX(),head.getY()-1);
//把心得蛇头节点添加到body的第一个元素
body.addFirst(newHead);
break;
case DOWN:
//创建一个新的蛇头节点
newHead=new Node(head.getX(),head.getY()+1);
//把心得蛇头节点添加到body的第一个元素
body.addFirst(newHead);
break;
}
//每次吃完食物,让食物重置
MainFrame.food.reset();
}
}
在定时器中调用吃食物的方法
//2.初始化游戏棋盘
private void initGamePanel() {
jPanel = new JPanel() {
//paint是用来绘制面板中的内容
@Override
public void paint(Graphics g) {
//清空棋盘
g.clearRect(0,0,600,600);
//绘制横线、绘制竖线、绘制蛇
//绘制食物
g.fillRect(food.getX()*15,food.getY()*15,15,15);
}
};
add(jPanel);
}