01-Swing相关
- Swing 包下的类、方法—>GUI开发---->图形用户界面
- 用到三个类JFrame、JPanel、JButtom
- JFrame:顶层容器,所有的元素都放在他的里面
- JPanel:中层容器
02-SnakeFrame
- 容器分成两个部分(属性):SnakePanel(放蛇的容器)、ButtonPanel(放按钮的容器)
- SnakeFrame顶层容器进行两个操作(方法):initFrame()(初始化)、addComponent()(添加元素)
- 属性和方法在初始化的时候就应该被调用,初始化方法、添加元素方法和组件显示方法可以放在构造方法内部(程序运一开始运行的时候顶层容器、放蛇的容器、放按钮的容器就应该都显示和添加好)
- initFrame()—初始化方法
//设置顶层容器的标题
this.setTitle(“贪吃蛇”);
//设置顶层容器的大小
this.setBounds(int初始位置x, 初始位置y, 706, 500);
//设置顶层容器大小无法更改
this.setResizable(false);
//设置顶层容器关闭界面后关闭进程
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- addComponent()—添加元素组件
//添加组件
this.add(snakePanel);
this.add(buttonPanel);
- SnakeFrame()—重写无参构造方法
//初始化
initFrame();
//添加组件
addComponent();
//设置顶层容器内容可见
this.setVisible(true);
03-SnakePanel
- 中层容器里面包含Snake(蛇)、Food(食物)、SnakeThread(多线程)
- SnakePanel中层容器进行两个操作(方法):initPanel()(初始化)、addComponent()(添加元素)、paint()(前期画上网格线)
- 初始化方法和添加组件方法在对象被创建的时候就应该被调用,将这两个方法放在初始化方法中
- paint(Graphics g)—画线拼网格
重写父类实现的方法:- g.setColor(Color.gray);//改变画笔的颜色
- for (int i = 0; i < Config.ROWS; i++) {
g.drawLine(0, Config.SPANi, Config.SPANConfig.COLS, Config.SPAN*i);
}
这个地方画线的控制搞不清楚,只需要记住:- ROWS-行
- COLS-列
画横线必须小于行 i<Config.ROWS
画竖线必须小于列 i<Config.COLS- drawLine(x1,y1,x2,y2);
如果不会想就看第二条线(横竖都是)
- initpanel()—初始化操作
//设置大小
this.setSize(700,440);
//设置背景染色
this.setBackground(Color.black);
- SnakePanel()—重写无参构造方法
initPanel();
addCompoent();
//启动线程
snakeThread.start();
- paint(Graphics g)—重写父类的画线方法
super.paint(g);
//画笔
g.setColor(Color.gray);
//划线 横线 竖线
for (int i = 0; i < Config.ROWS; i++) {
g.drawLine(0, Config.SPANi, Config.SPANConfig.COLS, Config.SPANi);
}
for (int i = 0; i < Config.COLS; i++) {
g.drawLine(Config.SPANi, 0, Config.SPANi, Config.SPANConfig.ROWS);
}
04-Config
- 常量配置
(1)行、列、间距
(2)上、下、左、右四个方向
(3)死亡检测的判断
(4)暂停继续的控制
(5)速度的设置
(6)得分
(7)valid(暂时还没用到)
public static final int ROWS = 22;//行
public static final int COLS = 35;//列
public static final int SPAN = 20;//间距
public static final String U = "u";//方向-上
public static final String D = "d";//方向-下
public static final String L = "l";//方向-左
public static final String R = "r";//方向-右
public static boolean islive = true;//死亡检测的标识
public static int speed = 400;//速度,间隔多少毫秒移动一次
public static boolean valid = false;//当前的 单位时间内 有无 有效按键事件false代表没有
public static boolean isgone = true;//暂停 继续
public static int score = 0;//得分
// public static User user;//当前登陆人
//控制条件初始化
public static void reload() {
islive = true;
valid = false;
score = 0;
speed = 400;
isgone = true;
}
05-Food
- 食物类
- 食物是一个方格所以他也是由坐标构成
定义两个属性:col—列、row—行- 食物可以进行操作:draw(Graphics g)(画食物)、reGen()(重新生成食物)
- 食物在被创建的时候就需要生成,同样是沿用之前的重写无参构造方法
draw(Graphics g)—画食物
//设置食物为红颜色
g.setColor(Color.red);//画填充颜色的矩形
g.fillRect(colConfig.SPAN, rowConfig.SPAN, Config.SPAN, Config.SPAN);
//画矩形框
g.drawRect(0, 0, Config.SPAN, Config.SPAN);
- void reGen()—重新生成食物坐标
this.col=new Random().nextInt(Config.COLS);
this.row = new Random().nextInt(Config.ROWS);
- Food()—重写无参构造方法
this.reGen();
- 在SnakePanel中画线的地方调用画食物方法
- 蛇吃食物需要确定食物所在的位置
Rectangle getFoodRctangle()—获取食物所在的矩形—直接返回食物所在的矩形
return new Rectangle(
this.colConfig.SPAN,
this.rowConfig.SPAN,
Config.SPAN,
Config.SPAN);
}
06-Snake
- 蛇类
- 蛇有自己的头、躯干、尾巴对应属性
Node head(头)、Node body(躯干)、Node tail;(尾巴)- 蛇在初始化的时侯定义为头、身、尾三段,并且还要让三个节点关联起来
- 关联好点之后要将三个节点画出来也就是画蛇
draw(Graphics g)- 蛇要移动起来
addHead()(添加头)、remove()(去掉尾)
Snake()—蛇的初始化
this.head = new Node(17, 11, Config.R);
this.body = new Node(16, 11, Config.R);
this.tail = new Node(15, 11, Config.R);//让三个节点关联,创建一个双向链表
head.pre=null;
head.next=body;
body.pre=head;
body.next=tail;
tail.pre=body;
tail.next=null;
- draw(Graphics g)—画蛇
//画蛇调用了节点的draw();方法- //画每个节点
for(Node n = head; n!=null; n=n.next){
n.draw(g);
}
- 在SnakePanel中调用画蛇的方法
- addHead()—添加头
//判断蛇当前往哪个方向走,然后在头相应的位置添加一个元素
Node node = null;
//根据蛇当前移动的方向,创建要添加的节点
switch (this.head.getDir()) {
case Config.U:
node = new Node(this.head.getCol(), this.head.getRow()-1, this.head.getDir());
break;
case Config.D:
node = new Node(this.head.getCol(), this.head.getRow()+1, this.head.getDir());
break;
case Config.L:
node = new Node(this.head.getCol()-1, this.head.getRow(), this.head.getDir());
break;
case Config.R:
node = new Node(this.head.getCol()+1, this.head.getRow(), this.head.getDir());
break;
default:
break;
}
node.pre=null;
node.next=head;
head.pre=node;
head=node;
- 注意:
向上和向下走的时候不会改变节点所在的列
向左和向右走的时候不会改变焦点所在的行- 要将新的头节点和之前的头节点关联起来
removeTali()—去掉尾
//保存下来最后一个节点
Node temp = tail;
tail = tail.pre;
tail.next = null;
temp.pre = null;
- void move()—蛇移动的方法
//添加头
addHead();
//删去尾
removeTail();
- 在画蛇的地方调用蛇移动的方法
- 因为让蛇移动的方法是不断地画蛇
所以需要开启线程
SnakePanel里边
//多线程(2种实现方式)
//内部类 可以访问外部类的所有成员变量和方法
class SnakeThread extends Thread{
@Override
public void run() {
super.run();
while(Config.islive){
try {
Thread.sleep(400);
} catch (InterruptedException e) {
e.printStackTrace();
}
//System.out.println("****************");
//重新绘制SnakePanel
repaint();
}
}
}
*蛇类需要被键盘控制移动的方向
dirControl(keyEvent e)—键盘控制蛇移动的方向
需要注意的是,蛇在运动的时候指定与现在运动相反的方向是无效的
- dirControl(keyEvent e)—键盘控制蛇移动的方向
//获取键盘按键的编码
switch (e.getKeyCode()) {
case KeyEvent.VK_W:
//不能向相反的方向转换方向
if(!this.head.getDir().equals(Config.D)){
//设置他的方向
this.head.setDir(Config.U);
}
break;
case KeyEvent.VK_S:
//不能向相反的方向转换方向
if(!this.head.getDir().equals(Config.U)){
//设置他的方向
this.head.setDir(Config.D);
}
break;
case KeyEvent.VK_A:
//不能向相反的方向转换方向
if(!this.head.getDir().equals(Config.R)){
//设置他的方向
this.head.setDir(Config.L);
}
break;
case KeyEvent.VK_D:
//不能向相反的方向转换方向
if(!this.head.getDir().equals(Config.L)){
//设置他的方向
this.head.setDir(Config.R);
}
break;
//上下左右键
case KeyEvent.VK_UP:
//不能向相反的方向转换方向
if(!this.head.getDir().equals(Config.D)){
//设置他的方向
this.head.setDir(Config.U);
}
break;
case KeyEvent.VK_DOWN:
//不能向相反的方向转换方向
if(!this.head.getDir().equals(Config.U)){
//设置他的方向
this.head.setDir(Config.D);
}
break;
case KeyEvent.VK_LEFT:
//不能向相反的方向转换方向
if(!this.head.getDir().equals(Config.R)){
//设置他的方向
this.head.setDir(Config.L);
}
break;
case KeyEvent.VK_RIGHT:
//不能向相反的方向转换方向
if(!this.head.getDir().equals(Config.L)){
//设置他的方向
this.head.setDir(Config.R);
}
break;
default:
break;
}
- e.getKeyCode()—该方法可以获取键盘按键的编码
- KeyEvent.VK_*—可以获取键盘上的按键
- 设置好之后蛇还是不能通过键盘进行控制,因为需要Snakepanel绑定键盘监听器:
this.addKeyListener(this);- 这个时候需要SnakePanel实现一个接口KeyListener
- KeyEvent类是表示键盘点击事件
- 实现了KeyListener接口之后实现方法
@Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub}
@Override
public void keyPressed(KeyEvent e) {
// 在按键点击的时候调用蛇的方向控制方法
this.snake.dirControl(e);}
@Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub}
- SnakePanel需要获取焦点
SnakeFrame中设置SnakePanel获取焦点
this.setLayout(null);
this.snakePanel.setFoucusable(true);
this.snakePanel.requestFoucus();
- 蛇可以吃食物
void eatOrNot()—蛇吃食物检测
思想:蛇头所在的方格和食物所在的方格重合了即是吃到食物了- 获取蛇头所在的方格
- 获取食物所在的方格
Rectangle类可以定义一个矩形
- Rctangle getHeadRactangle()—获取蛇头所在的矩形—直接返回蛇头所在的矩形
return new Rectangle(
this.head.getCol()*Config.SPAN,
this.head.getRow()*Config.SPAN,
Config.SPAN,
Config.SPAN);
- 这里的蛇头的横坐标是列x间距
- 这里的蛇头的纵坐标是行x间距
- eatOrNot()—判断蛇是否吃了食物
如果蛇吃了食物就添加蛇头、重新生成食物
方法().contains()判断字符串中是否含有字符串
if(this.getHeadRctangle().contains(this.food.getFoodRctangle())){
//添加头
this.addHead();
//重新生成食物
this.food.reGen();
}
- 为了比较蛇和食物所在的空格,需要调用食物的获取方格方法
- Food food在snake初始化的时候就被作为参数传入
- Snakepanel中调用蛇吃食物的方法
- 蛇需要进行死亡检测
蛇死亡了存在一下情况:
(1)头碰到了边界
(2)头碰到了身体
如果蛇死亡了则不再重复进行画蛇
islive控制画蛇
deadCheck()—死亡检测
//head碰到了边界
if(
this.head.getCol()<0||
this.head.getCol()>Config.COLS ||
this.head.getRow()<0 ||
this.head.getRow()>Config.ROWS){
Config.islive = false;
return;
}
判断头是否超出了边界的框框,四个边,如果发现超出了就将isalive设置为false
- head碰到了body
遍历身体上的节点,如果哪个节点和头节点重合了isalive设置为false
//head碰到了body
for(Node node = head.next;node!=null;node=node.next){
Rectangle nodeRect = new Rectangle(
node.getCol()*Config.SPAN,
node.getRow()*Config.SPAN,
Config.SPAN,
Config.SPAN);
if(this.getHeadRctangle().contains(nodeRect)){
Config.islive = false;
return;
}
}
07-Node
- 节点类
- 节点用横纵坐标来表示属性:
col—横坐标
row—纵坐标- 节点具有方向
dir—方向- 节点具有双向指针
pre—指向前一个元素
next—指向后一个元素- 节点在被创建的时候就应该具有坐标和方向
Node(int col, int row, String dir)(无参构造方法)- 画节点的时候节点要区分头节点和身体节点的颜色
void draw(Graphics g)
- Node(int col,int row,String dir)—重写构造方法
super();
this.col = col;
this.row = row;
this.dir = dir;
- void draw(Graphics g)
if(this.pre==null){//判断是否为头节点
g.setColor(Color.yellow);
}else{
g.setColor(Color.blue);
}
//该方法可以画填充的圆圈
g.fillOval(colConfig.SPAN, rowConfig.SPAN, Config.SPAN, Config.SPAN);
8-ButtonPanel
- 按钮容器
为了实现监听按钮需要实现一个接口
ActionListener
按钮容器需要有四个按钮(属性):
btn_pause(暂停游戏)
btn_continue(继续游戏)
btn_restart(重新开始)
btn_rank(游戏排行)- 按钮容器的动作(方法)
- void init()—初始化方法
设置大小
添加按钮监听- void addComponent()—添加元素方法
添加四个按钮元素
- ButtonPanel(SnakePanel)—重写初始化方法
this.snakePanel = snakePanel;
init();
addComponent();
- init()—初始化方法
this.setBounds(0, 435, 706, 60);
btn_continue.addActionListener(this);
btn_pause.addActionListener(this);
btn_rank.addActionListener(this);
btn_restart.addActionListener(this);
- addComponent()—添加元素
this.add(btn_pause);
this.add(btn_continue);
this.add(btn_restart);
this.add(btn_rank);
- void pauseGame()—暂停游戏方法
设置一个标识符控制调用重新画蛇的方法
isgone—控制画蛇标识符
Config.isgone = false;
void contiueGame()—继续游戏方法
Config.isgone = true;
void actionPerformed(ActionEvent e)实现接口的方法
//暂停
if(e.getSource() == btn_pause){
pauseGame();
}//继续
if(e.getSource()== btn_continue){
this.setLayout(null);
this.snakePanel.setFocusable(true);
this.snakePanel.requestFocus();
continueGame();
}