最近游戏 Baba is You 很火,虽然我是云玩家,但是看了感觉:“应该不太难实现?”“那不如自己动手做一个?”
最近也正好在学做线程游戏,说干就干。
先说一下,这个只是在学习 Java 过程中的一次编程实战,并不是搞什么山寨抄袭,就和写飞机大战、坦克大战、推箱子的小游戏一样,仅供参考学习。(这不就是个高配推箱子么?
能力有限(我太菜辽),到写这篇博客为止,我才实现基本功能。因为全都是按自己的想法实现的,我掌握的方法不算多,很多地方做得比较简陋,可能有更高效的解决方法。欢迎大家指导批评,未来将不断完善。
打算是分三到四篇博客来写,第一篇主要写游戏界面的编写和我最开始用的一套比较复杂没达到期望最后被放弃的规则。
想要实现的效果
游戏界面
首先主界面要有游戏名,开始游戏按钮、选关按钮、界面要显示现在选择的关卡。
点击开始游戏要打开游戏界面,选关要打开选关栏。
游戏界面基本元素:Baba(也就是能移动的玩家You)、Wall(遇到要Stop的墙)、Rock(能Push的石头)、Flag(踩到就Win的旗帜),以及能推动形成不同组合的字:Baba、is、You、Wall、Rock、Flag、Stop、Push、Win。
为了界面美观,全部使用贴图画在界面上。
贴图上传到百度网盘供大家参考使用:
链接点这里,提取码: krqa
基本规则
第一篇博客主要针对实现这些基本规则,尽量给进阶规则留方便。
玩家 You 可以移动
遇到地图边缘要 Stop;
遇到 Wall 要 Stop;
遇到 Rock 可以 Push;
遇到 Flag 就算 Win。
进阶规则
进阶规则暂不求实现,慢慢来。
界面上的文字可以以 is 为中心推动随意组合,默认为:
Baba is You, Rock is Push, Wall is Stop, Flag is Win
You 就是可以移动的玩家,Push 是可以推动的东西、Stop 是遇到要停的东西、Win 是遇到就胜利的东西。
也就是只要 is 前面有字,这个字代表的东西就可以实现 is 后面的功能。
实现思路
- 游戏开始界面,继承于JPanel,要有背景,中间写了游戏名 BABA IS YOU,下面有开始按钮 PLAY、选关按钮 LEVELS。这些都用
g.drawImage
画上去。使用MouseListener
监听主界面的操作,主要用到mouseClicked
方法,获取点击处的 x 和 y,如果此处在 PLAY 和 LEVELS 范围类触发相应的操作。开始界面使用重绘方法使界面元素始终显示。 - 用一个接口类
imageInterface
放所有要画的图片,需要用到画图的类直接继承这个接口就行。 - 左下角显示当前选择的关卡。使用
g.drawString
画上去。 - 点击 LEVELS 打开选关界面,新弹出一个窗体,上面放用
JComboBox
实现选择关卡,用ActionListener
记录监听操作记录选择的关卡。 - 点击 PLAY 开始游戏,打开游戏界面,一个新窗体,去掉边框,右上角有 EXIT 按钮,使用
MouseListener
和KeyListener
监听界面。点击 EXIT 时退出当前界面回到主界面,使用.setVisible()
进行界面的关闭和打开。游戏界面的元素(baba、墙、石头、旗帜)都用g.drawImage
画上去。 - 键盘监听器里主要使用
keyPressed
和keyReleased
方法,监听上下左右和 WSAD 键,主界面定义四个布尔类型的变量(后面要调用),按下时是true
,松开时是false
。 - 基本规则分为 move、stop、push、win 四种规则,为了方便以后实现进阶规则,将这四种基本规则封装成类,可以重复调用。
- 刚开始写的时候我是直接通过元素在面板中的坐标位置和与其他元素的距离来判断实现基本规则的。也就是这篇博客中使用的方法,后来发现实现复杂,而且不太方便之后进阶规则的实现,就放弃了再采用另一种方法实现。如果大家有更好的想法一定要积极交流啊~
具体代码
项目结构
主界面
package gameui; //我把界面类都放在gameui包下
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import data.*; //这两个是导入这个project下的其他package
import listenners.*;
public class mainUI extends JPanel implements imageInterface{ //继承JPanel父类和图片接口
public int level = 1; //选择的关卡,默认为1
public JFrame frame = new JFrame("BaBa is You!"); //要在其他类调用的定义为public
//重绘主界面
public void paint(Graphics g) {
super.paint(g); //先调用一次JPanel父类中的paint方法
//画图
g.drawImage(backPic, 0, 0, frame.getWidth(), frame.getHeight(), null);
g.drawImage(babaisYou, 100, 100, 800, 250, null);
g.drawImage(play, 350, 450, 300, 100, null);
g.drawImage(levels, 430, 600, 140, 50, null);
//左下角画关卡名
String strlevel = "level "+level ;
g.setColor(Color.white);
g.drawString(strlevel, 10, 750);
}
public void showUI() {
//设置容器JFrame的参数
frame.setSize(1000,800); //大小
frame.setLocationRelativeTo(null); //默认居中
frame.setDefaultCloseOperation(3); //关闭后结束所有进程
frame.add(this,BorderLayout.CENTER); //将Panel添加到frame中,放在中间位置
frame.setVisible(true); //显示frame
frame.setResizable(false); //禁止改变大小
mainListeners listeners = new mainListeners(this); //实例化listeners并将这个继承于JPanel的类传过去
this.addMouseListener(listeners); //给panel加监听器
}
//主函数
public static void main(String[] args) {
mainUI ui = new mainUI();
ui.showUI(); //显示主界面
}
}
主界面监听器
package listeners; //我把监听器都放在listeners包下
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import gameui.*;
public class mainListeners implements MouseListener{
mainUI ui;
int clickx,clicky;
public mainListeners(mainUI ui) {
this.ui = ui; //通过构造器传入主界面,方便调用主界面定义的参数
}
public void mouseClicked(MouseEvent e) {
clickx = e.getX(); //获得x,y坐标
clicky = e.getY();
System.out.println(clickx+" "+clicky); //这里加了输出方便debug
//当鼠标点击到PLAY范围时,隐藏主界面,打开游戏界面
if (clickx >350 && clickx < 650 && clicky > 450 && clicky < 550) {
ui.frame.setVisible(false);
gameUI ui1 = new gameUI(ui); //实例化gameUI并传入ui
ui1.showLevel_1();
}
//当鼠标点击到LEVELS范围时,锁定主界面,打开关卡选择界面
else if (clickx >430 && clickx < 570 && clicky > 600 && clicky < 650) {
ui.frame.setEnabled(false); //让主窗体不可操作
chooseLevelUI cLevelUI = new chooseLevelUI(ui); //实例化chooselevelUI并传入ui
}
}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
}
关卡选择界面
package gameui;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JWindow;
import listeners.*;
public class chooseLevelUI{
mainUI ui;
//使用下拉列表组件JComboBox实现选择关卡
String[] boxName = {"Level 1", "Level 2", "Level 3", "Level 4", "Level 5"};
public JComboBox levelBox = new JComboBox(boxName);
public JWindow levelWin = new JWindow(); //这里使用JWindow作为选关界面容器,用JFrame也行
//这里直接在构造器里实现选关界面,再定义一个方法调用也行
public chooseLevelUI(mainUI ui) {
this.ui = ui; //构造器传入主界面ui
//设置levelWin的参数
levelWin.setSize(200,150);
levelWin.setLocationRelativeTo(null);
//给levelWin添加边框布局的levelPanel
JPanel levelPanel = new JPanel();
levelPanel.setLayout(new BorderLayout());
levelWin.add(levelPanel);
//给levelPanel再添加三个Panel放在北、中、南三个位置
JPanel noPanel = new JPanel();
JPanel cePanel = new JPanel();
JPanel soPanel = new JPanel();
noPanel.setPreferredSize(new Dimension(0,40)); //设置大小
soPanel.setPreferredSize(new Dimension(0,50));
noPanel.setBackground(new Color(67,69,72)); //设置颜色
cePanel.setBackground(new Color(67,69,72));
soPanel.setBackground(new Color(67,69,72));
//使用Jlabel显示文字
JLabel noLabel = new JLabel("请选择关卡");
noLabel.setFont(new Font("黑体",Font.BOLD,18)); //设置字体
noLabel.setForeground(Color.white); //设置前景色
//添加按钮
JButton sureButton = new JButton("确定");
JButton cancelButton = new JButton("取消");
//给控件添加选关监听器
levelListener ll = new levelListener(this,ui); //实例化监听器并传入选关界面和主界面
sureButton.addActionListener(ll);
cancelButton.addActionListener(ll);
levelBox.addActionListener(ll);
//将控件放进相应位置
noPanel.add(noLabel);
soPanel.add(sureButton);
soPanel.add(cancelButton);
levelPanel.add(noPanel,BorderLayout.NORTH);
levelPanel.add(cePanel,BorderLayout.CENTER);
levelPanel.add(soPanel,BorderLayout.SOUTH);
cePanel.add(levelBox);
levelWin.setVisible(true); //显示选关界面
}
}
选关监听器
package listeners;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import gameui.chooseLevelUI;
import gameui.mainUI;
public class levelListener implements ActionListener{ //继承于ActionListener
String cmd;
String strlevel;
mainUI ui;
chooseLevelUI levui;
//通过构造器传入选关界面和主界面,方便调用里面的参数
public levelListener(chooseLevelUI levui, mainUI ui) {
this.levui = levui;
this.ui = ui;
}
public void actionPerformed(ActionEvent e) {
cmd = e.getActionCommand(); //获取点击的按钮名
strlevel = (String) levui.levelBox.getSelectedItem(); //获取下拉列表组件选择的关卡
//如果点击了确定按钮
if (cmd.equals("确定")) {
//判断选择了第几关,将选择的关卡赋给主界面ui的level
for (int i = 1; i <= 5; i++) {
if (strlevel.equals("Level "+i)) {ui.level = i;}
}
ui.repaint(); //重绘主界面
levui.levelFrame.setVisible(false); //隐藏选关窗体
ui.frame.setEnabled(true); //让主界面窗体可以操作
}
//如果点击了取消按钮
else if (cmd.equals("取消")) {
levui.levelFrame.setVisible(false); //隐藏选关窗体
ui.frame.setEnabled(true); //让主界面窗体可以操作
}
}
}
游戏界面
package gameui;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import action.thread_Lv1;
import listeners.*;
public class gameUI {
public mainUI ui;
public Graphics gg;
public JFrame gFrame = new JFrame();
public boolean key_W, key_S, key_A, key_D; //判断有没有按下键盘对应按键
public int distance = 25; //每次移动的距离
public gameUI(mainUI ui) {
this.ui = ui; //通过构造器传入主界面ui
}
public void showLevel_1() {
//设置窗体容器
gFrame.setUndecorated(true); //JFrame去掉边框
gFrame.setSize(1000,800);
gFrame.setLocationRelativeTo(null);
gFrame.setDefaultCloseOperation(3);
gFrame.setResizable(false); //禁止改变大小
//给gFrame添加JPanel
JPanel cenPanel = new JPanel();
cenPanel.setBackground(new Color(28,31,34)); //设置背景颜色
gFrame.add(cenPanel);
//加游戏监听器
gameListener gl = new gameListener(this); //实例化gameListener并传入游戏界面这个类
cenPanel.addKeyListener(gl);
cenPanel.addMouseListener(gl);
gFrame.setVisible(true); //显示游戏界面
cenPanel.requestFocus(); //获得焦点
gg = cenPanel.getGraphics(); //获取画笔
gg.setColor(new Color(28,31,34));
gg.fillRect(0, 0, 1000, 1000); //画背景
this.startGame(); //调用开始游戏方法
}
//开始游戏
public void startGame() {
//判断现在是第几关,启动对应的线程类
if (ui.level == 1) {
System.out.println("lv1 Start");
thread_Lv1 draw = new thread_Lv1(this); //实例化第一关的线程类并传入游戏界面类
draw.start(); //启动线程
}
//先从第一关实现基本规则,后面的关卡慢慢再实现
else if (ui.level == 2) {
System.out.println("lv2 Start");
}
else if (ui.level == 3) {
System.out.println("lv3 Start");
}
else if (ui.level == 4) {
System.out.println("lv4 Start");
}
else if (ui.level == 5) {
System.out.println("lv5 Start");
}
}
}
游戏监听器
package listeners;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import gameui.gameUI;
//继承于键盘监听器和鼠标监听器
public class gameListener implements KeyListener, MouseListener{
gameUI gui;
Graphics gg;
//使用构造器传入游戏ui,方便使用其中的参数
public gameListener(gameUI gui) {
this.gui = gui;
this.gg = gui.gg;
}
//按下对应方向按键时让gui中定义的对应的布尔类型为true
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_W || e.getKeyCode() == KeyEvent.VK_UP) {
gui.key_W = true;
}
else if (e.getKeyCode() == KeyEvent.VK_S || e.getKeyCode() == KeyEvent.VK_DOWN) {
gui.key_S = true;
}
else if (e.getKeyCode() == KeyEvent.VK_A || e.getKeyCode() == KeyEvent.VK_LEFT) {
gui.key_A = true;
}
else if (e.getKeyCode() == KeyEvent.VK_D || e.getKeyCode() == KeyEvent.VK_RIGHT){
gui.key_D = true;
}
}
//抬起对应方向按键时让gui中定义的对应的布尔类型为false
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_W || e.getKeyCode() == KeyEvent.VK_UP) {
gui.key_W = false;
}
else if (e.getKeyCode() == KeyEvent.VK_S || e.getKeyCode() == KeyEvent.VK_DOWN) {
gui.key_S = false;
}
else if (e.getKeyCode() == KeyEvent.VK_A || e.getKeyCode() == KeyEvent.VK_LEFT) {
gui.key_A = false;
}
else if (e.getKeyCode() == KeyEvent.VK_D || e.getKeyCode() == KeyEvent.VK_RIGHT){
gui.key_D = false;
}
}
//判断有没有点击EXIT(EXIT在另一个画画类中,在线程中调用画在右上角)
public void mouseClicked(MouseEvent e) {
int clickx = e.getX();
int clicky = e.getY();
if (clickx >900 && clickx < 990 && clicky > 0 && clicky < 40) {
gui.gFrame.setVisible(false);
gui.ui.frame.setVisible(true);
}
}
public void keyTyped(KeyEvent e) {}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
}
画图片的接口
package data;
import java.awt.Image;
import javax.swing.ImageIcon;
//图片都保存在当前project下的ressourse文件夹下
public interface imageInterface {
Image backPic = new ImageIcon("ressources\\BackgroundMenu.PNG").getImage();
Image babaisYou = new ImageIcon("ressources\\BabaIsYou.png").getImage();
Image play = new ImageIcon("ressources\\play.png").getImage();
Image levels = new ImageIcon("ressources\\levels.png").getImage();
Image exit = new ImageIcon("ressources\\exit.png").getImage();
Image succes = new ImageIcon("ressources\\succes.png").getImage();
Image wall = new ImageIcon("ressources\\Wall.png").getImage();
Image flag = new ImageIcon("ressources\\Flag.png").getImage();
Image baba = new ImageIcon("ressources\\Baba.png").getImage();
Image rock = new ImageIcon("ressources\\Rock.png").getImage();
Image textBaba = new ImageIcon("ressources\\TextBaba.png").getImage();
Image textWall = new ImageIcon("ressources\\TextWall.png").getImage();
Image textFlag = new ImageIcon("ressources\\TextFlag.png").getImage();
Image textRock = new ImageIcon("ressources\\TextRock.png").getImage();
Image textWin = new ImageIcon("ressources\\TextWin.png").getImage();
Image textYou = new ImageIcon("ressources\\TextYou.png").getImage();
Image textStop = new ImageIcon("ressources\\TextStop.png").getImage();
Image textPush = new ImageIcon("ressources\\TextPush.png").getImage();
Image textIs = new ImageIcon("ressources\\TextIs.png").getImage();
}
画图类
package data;
import java.awt.Graphics;
//就是继承图片接口后把图片画出来的方法,后来我把这个类和关卡中画图的类合并了
public class drawImg implements imageInterface{
public void drawExit(Graphics g, int x, int y) {
g.drawImage(exit, x, y, 90, 40, null);
}
public void drawWall(Graphics g, int x, int y) {
g.drawImage(wall, x, y, 50, 50, null);
}
public void drawFlag(Graphics g, int x, int y) {
g.drawImage(flag, x, y, 50, 50, null);
}
public void drawBaba(Graphics g, int x, int y) {
g.drawImage(baba, x, y, 50, 50, null);
}
public void drawRock(Graphics g, int x, int y) {
g.drawImage(rock, x, y, 50, 50, null);
}
public void drawTbaba(Graphics g, int x, int y) {
g.drawImage(textBaba, x, y, 50, 50, null);
}
public void drawTflag(Graphics g, int x, int y) {
g.drawImage(textFlag, x, y, 50, 50, null);
}
public void drawTis(Graphics g, int x, int y) {
g.drawImage(textIs, x, y, 50, 50, null);
}
public void drawTpush(Graphics g, int x, int y) {
g.drawImage(textPush, x, y, 50, 50, null);
}
public void drawTrock(Graphics g, int x, int y) {
g.drawImage(textRock, x, y, 50, 50, null);
}
public void drawTstop(Graphics g, int x, int y) {
g.drawImage(textStop, x, y, 50, 50, null);
}
public void drawTwall(Graphics g, int x, int y) {
g.drawImage(textWall, x, y, 50, 50, null);
}
public void drawTwin(Graphics g, int x, int y) {
g.drawImage(textWin, x, y, 50, 50, null);
}
public void drawTyou(Graphics g, int x, int y) {
g.drawImage(textYou, x, y, 50, 50, null);
}
}
到这里主要的几个界面都已经完成。图片接口、画图类、主界面、选关界面、游戏界面,以及对应的监听器代码如上所示。
也就是这个游戏的基本框架完成了,下面就是要实现这个游戏的规则逻辑了。
这一套规则是我最开始采用的,后来发现实现复杂,而且不利于之后进阶规则的实现,还没完全完成的时候就被放弃了,大家有兴趣可以继续看看,如果有什么好的想法或者建议批评欢迎提出来一起交流。
如果没兴趣可以看看我之后用另外方法实现的博客。(到目前为止还没写hhh不过会尽快写的
第一关的线程类
package action; //我把这些动作都放在了action包下
import java.awt.Graphics;
import data.imageInterface;
import data.pushRules;
import data.stopRules;
import gameui.*;
import data.*;
//继承于Thread
public class thread_Lv1 extends Thread{
gameUI gui;
Graphics gg;
/**
* 我将每个元素的初始位置坐标都保存在数组里了,
* 数组是传引用,后面在其他类可以直接更改这里的数据.
* 忽略有点混乱的命名
*/
public int[] Lv1_wallX = new int[40];
public int[] Lv1_wallY = new int[40];
public int[] Lv1_rockX = new int[4];
public int[] Lv1_rockY = new int[4];
public int[] Lv1_flagX = {700};
public int[] Lv1_flagY = {400};
public int[] Lv1_babaX = {300};
public int[] Lv1_babaY = {350};
public int[] tbabaX = {300};
public int[] tbabaY = {50};
public int[] tisX = {300, 400, 800};
public int[] tisY = {100, 650, 650};
public int[] tyouX = {300};
public int[] tyouY = {150};
public int[] trockX = {250};
public int[] trockY = {100};
public int[] tpushX = {350};
public int[] tpushY = {100};
public int[] tflagX = {400};
public int[] tflagY = {600};
public int[] twinX = {400};
public int[] twinY = {700};
public int[] twallX = {750};
public int[] twallY = {650};
public int[] tstopX = {850};
public int[] tstopY = {650};
//使用构造器传入游戏界面gui,方便调用里面定义的参数
public thread_Lv1(gameUI gui) {
this.gui = gui;
this.gg = gui.gg;
//wall初始坐标
for (int i = 0; i<20; i++) {
Lv1_wallX[i] = i*50;
Lv1_wallX[i+20] = i*50;
Lv1_wallY[i] = 250;
Lv1_wallY[i+20] = 500;
}
//rock初始坐标
for (int i = 0; i<4; i++) {
Lv1_rockX[i] = 500;
Lv1_rockY[i] = 300 + i*50;
}
}
//线程的run方法
public void run() {
draw_Lv1 dLv1 = new draw_Lv1(gui, this); //第一关的画图类
moveRules mo = new moveRules(gui); //移动规则
stopRules stopRules = new stopRules(gui); //停止规则
pushRules pushRules = new pushRules(gui); //推动规则
//这里理应还有个胜利规则winRules,但是中途放弃了这个方法就没有写
//死循环
for(;;) {
//传入参数调用规则后画图
stopRules.stopRule(Lv1_babaX,Lv1_babaY,Lv1_wallX,Lv1_wallY);
pushRules.pushRule(Lv1_babaX,Lv1_babaY,Lv1_rockX,Lv1_rockY, Lv1_wallX, Lv1_wallY);
mo.move(Lv1_babaX,Lv1_babaY);
dLv1.draw(); //画图
//循环一次休眠60ms
try {
Thread.sleep(60);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
第一关的画图类
package action;
import java.awt.Graphics;
import gameui.gameUI;
import data.*;
//这个类后来优化把它独立封装和画图类合并了
public class draw_Lv1 {
gameUI gui;
Graphics gg;
thread_Lv1 t1;
//构造器传入游戏界面gui、第一关线程类t1
public draw_Lv1(gameUI gui, thread_Lv1 t1) {
this.gui = gui;
this.gg = gui.gg;
this.t1 = t1;
}
public void draw() {
drawImg img = new drawImg();
//画EXIT
img.drawExit(gg, 900, 10);
//画wall
for(int i = 0; i<40; i++) {
img.drawWall(gg, t1.Lv1_wallX[i], t1.Lv1_wallY[i]);
}
//画rock
for(int i = 0; i<4; i++) {
img.drawRock(gg, t1.Lv1_rockX[i], t1.Lv1_rockY[i]);
}
//画flag
img.drawFlag(gg, t1.Lv1_flagX[0], t1.Lv1_flagY[0]);
//画baba
img.drawBaba(gg, t1.Lv1_babaX[0], t1.Lv1_babaY[0]);
//画text
img.drawTbaba(gg, t1.tbabaX[0], t1.tbabaY[0]);
img.drawTflag(gg, t1.tflagX[0], t1.tflagY[0]);
img.drawTpush(gg, t1.tpushX[0], t1.tpushY[0]);
img.drawTrock(gg, t1.trockX[0], t1.trockY[0]);
img.drawTstop(gg, t1.tstopX[0], t1.tstopY[0]);
img.drawTwall(gg, t1.twallX[0], t1.twallY[0]);
img.drawTwin(gg, t1.twinX[0], t1.twinY[0]);
img.drawTyou(gg, t1.tyouX[0], t1.tyouY[0]);
for(int i = 0; i<3; i++) {
img.drawTis(gg, t1.tisX[i], t1.tisY[i]);
}
}
}
移动规则
package data;
import java.awt.Color;
import gameui.gameUI;
public class moveRules {
gameUI gui;
//构造器传入游戏界面gui,方便调用里面的参数
public moveRules(gameUI gui) {
this.gui = gui;
}
//传入要移动的元素的坐标数组
public void move(int[] x, int[] y) {
//循环遍历数组,这是为以后进阶规则打基础,移动的元素可能不止一个
for (int i = 0; i<x.length;i++) {
//擦除原来位置的元素,去除残影
gui.gg.setColor(new Color(28,31,34));
gui.gg.fillRect(x[i], y[i], 50, 50);
//判断键盘按下什么键,元素往相应的方向移动
if (gui.key_W == true) {
y[i] -= gui.distance;
}
else if (gui.key_S == true) {
y[i] += gui.distance;
}
else if (gui.key_A == true) {
x[i] -= gui.distance;
}
else if (gui.key_D == true) {
x[i] += gui.distance;
}
}
}
}
停止规则
package data;
import gameui.*;
public class stopRules {
gameUI gui;
//构造器传入游戏界面gui,方便调用里面的参数
public stopRules (gameUI gui) {
this.gui = gui;
}
/**
* @param x 控制移动的物体的x坐标
* @param y 控制移动的物体的y坐标
* @param stopX 遇到要停止的物体的x坐标
* @param stopY 遇到要停止的物体的y坐标
*/
public void stopRule (int[] x, int[] y ,int[] stopX, int[] stopY) {
for (int j = 0; j < x.length; j++) {
//墙判断,通过墙和移动元素的距离判断是否需要停止
for(int i = 0; i<stopX.length;i++) {
if(stopX[i] - x[j] >=-50 && stopX[i] - x[j] <=0 && stopY[i] - y[j]>-50 && stopY[i] - y[j]<50) {
gui.key_A = false; //让按键失效,即不可往那个方向移动
}
else if (stopX[i] - x[j] <=50 && stopX[i] - x[j] >=0 && stopY[i] - y[j]>-50 && stopY[i] - y[j]<50) {
gui.key_D = false;
}
else if (stopY[i] - y[j] >=-50 && stopY[i] - y[j] <=0 && stopX[i]-x[j]>-50 && stopX[i]-x[j]<50) {
gui.key_W = false;
}
else if (stopY[i] - y[j] <=50 && stopY[i] - y[j] >=0 && stopX[i]-x[j]>-50 && stopX[i]-x[j]<50) {
gui.key_S = false;
}
}
//边框判断,通过坐标判断是否到达界面边框
if(x[0]<=0) {gui.key_A = false;}
else if (x[0]>=950) {gui.key_D = false;}
if(y[0]<=0) {gui.key_W = false;}
else if (y[0]>=750) {gui.key_S = false;}
}
}
//这个停止规则2方法是后面推动方法里判断推动的元素是否需要停止的
public void stopRule2 (int x, int y ,int[] stopX, int[] stopY) {
//墙判断
for(int i = 0; i<stopX.length;i++) {
if(stopX[i] - x >=-50 && stopX[i] - x <=0 && stopY[i] - y>-50 && stopY[i] - y<50) {
gui.key_A = false;
}
else if (stopX[i] - x <=50 && stopX[i] - x >=0 && stopY[i] - y>-50 && stopY[i] - y<50) {
gui.key_D = false;
}
else if (stopY[i] - y >=-50 && stopY[i] - y <=0 && stopX[i]-x>-50 && stopX[i]-x<50) {
gui.key_W = false;
}
else if (stopY[i] - y<=50 && stopY[i] - y >=0 && stopX[i]-x>-50 && stopX[i]-x<50) {
gui.key_S = false;
}
}
//边框判断
if(x<=0) {gui.key_A = false;}
else if (x>=950) {gui.key_D = false;}
if(y<=0) {gui.key_W = false;}
else if (y>=750) {gui.key_S = false;}
}
}
推动规则
package data;
import java.awt.Color;
import gameui.*;
//难点在于距离判断
public class pushRules {
gameUI gui;
//构造器传入游戏界面gui,方便调用里面的参数
public pushRules(gameUI gui) {
this.gui = gui;
}
/**
* @param x 控制移动的物体的x坐标
* @param y 控制移动的物体的y坐标
* @param pushX 遇到要推动的物体的x坐标
* @param pushY 遇到要推动的物体的y坐标
* @param stopX 遇到要停止的物体的x坐标
* @param stopY 遇到要停止的物体的y坐标
*/
public void pushRule(int[] x, int[] y ,int[] pushX, int[] pushY, int[] stopX, int[] stopY) {
stopRules sRules = new stopRules(gui);
for (int j = 0; j < x.length; j++) {
for(int i = 0; i<pushX.length;i++) {
//同样是通过距离判断是否需要推动
if(pushX[i] - x[j] >=-50 && pushX[i] - x[j] <=0 && pushY[i] - y[j]>-50 && pushY[i] - y[j]<50) {
sRules.stopRule2(pushX[i], pushY[i], stopX, stopY);//推动的石头是否需要停止
//如果对应键盘按键生效,先抹掉原来的元素图案在移动后的新位置画上图案
if (gui.key_A == true) {
gui.gg.setColor(new Color(28,31,34));
gui.gg.fillRect(pushX[i], pushY[i], 50, 50);
pushX[i] -= 25;
}
}
else if (pushX[i] - x[j] <=50 && pushX[i] - x[j] >=0 && pushY[i] - y[j]>-50 && pushY[i] - y[j]<50) {
sRules.stopRule2(pushX[i], pushY[i], stopX, stopY);
if (gui.key_D == true) {
gui.gg.setColor(new Color(28,31,34));
gui.gg.fillRect(pushX[i], pushY[i], 50, 50);
pushX[i] += 25;
}
}
if (pushY[i] - y[j] >=-50 && pushY[i] - y[j] <=0 && pushX[i]-x[j]>-50 && pushX[i]-x[j]<50) {
sRules.stopRule2(pushX[i], pushY[i], stopX, stopY);
if (gui.key_W == true) {
gui.gg.setColor(new Color(28,31,34));
gui.gg.fillRect(pushX[i], pushY[i], 50, 50);
pushY[i] -= 25;
}
}
else if (pushY[i] - y[j] <=50 && pushY[i] - y[j] >=0 && pushX[i]-x[j]>-50 && pushX[i]-x[j]<50) {
sRules.stopRule2(pushX[i], pushY[i], stopX, stopY);
if (gui.key_S == true) {
gui.gg.setColor(new Color(28,31,34));
gui.gg.fillRect(pushX[i], pushY[i], 50, 50);
pushY[i] += 25;
}
}
}
}
}
}
还应该有个比较简单的胜利规则,不过写到这里我就放弃了这个实现规则的方法,原因是:
- 判断条件复杂,嵌套太多,代码混乱,debug 困难,效率低下;
- 部分功能仍未实现,虽然有思路,不过理由同上一条;
- 不利于进阶规则的实现,进阶规则实现时需要将 move、stop、push、win 的元素相互转换,此方法难以实现。
所以至此我开始从头换一种方法实现功能。具体内容将在我的下一篇博客中介绍。