1 绘制窗口
public class GameWin extends JFrame {
// 窗口绘制方法
void launch(){
this.setVisible(true);
this.setSize(500,500);
// 设置窗口位置,居中
this.setLocationRelativeTo(null);
this.setTitle("黄金矿工");
// 关闭窗口
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
GameWin gameWin = new GameWin();
gameWin.launch();
}
}
2 背景绘制
背景中需要添加人物,矿山,天空
创建背景类来接收,在主类中调用
- 创建Bg类
public class Bg {
Image bg = Toolkit.getDefaultToolkit().getImage("imgs/bg.jpg"); //背景图
Image bg1 = Toolkit.getDefaultToolkit().getImage("imgs/bg1.jpg"); //天空
Image peo = Toolkit.getDefaultToolkit().getImage("imgs/peo.png"); // 人物
void paintSelf(Graphics g){
g.drawImage(bg,0,200,null);
g.drawImage(bg1,0,0,null);
g.drawImage(peo,310,50,null);
}
}
- 主类中调用(GameWin.java)
创建Bg类对象,
创建绘制图片的方法,调用paintSelf
Bg bg = new Bg();
// 绘制图片
public void paint(Graphics g){
bg.paintSelf(g);
}
运行后
3 红线绘制
创建红线类
public class Line {
// 起点坐标
int x=380;
int y=180;
// 终点坐标
int endx=500;
int endy=500;
// 线长
double length = 100;
double n = 0;
// 添加方向参数
int dir = 1;
// Graphics对象代表画笔
void paintSelf(Graphics g){
if (n<0.1){
dir=1;
}else if (n>0.9){
dir=-1;
}
n=n+0.005*dir;
endx = (int) (x + length*Math.cos(n*Math.PI));
endy = (int) (y+ length*Math.sin(n*Math.PI));
g.setColor(Color.RED);
g.drawLine(x,y,endx,endy);
}
}
方向参数初始值为1,当在0-2PI之间运动时,方向为正,当超出2PI时,红线转向dir为负
在主类中,创建Line对象,调用paintSelf方法绘制红线,注意,在绘制方法中,加入死循环,可以让红线一直运动
Line line = new Line();
// 窗口绘制方法
void launch(){
this.setVisible(true);
this.setSize(768,1000);
// 设置窗口位置,居中
this.setLocationRelativeTo(null);
this.setTitle("黄金矿工");
// 关闭窗口
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
//*******************************************************************************
while (true){
repaint();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//*******************************************************************************
}
4 线的延长和收回
在红线类中的定义线的状态 Line类
红线状态 0:摇摆 1:抓取 2:收回
int state;
将绘制红线的代码提出,写成一个方法
void lines(Graphics g){
endx = (int) (x + length*Math.cos(n*Math.PI));
endy = (int) (y+ length*Math.sin(n*Math.PI));
g.setColor(Color.RED);
g.drawLine(x,y,endx,endy);
}
在paintself中
void paintSelf(Graphics g){
switch (state){
//case0 线摇摆
case 0:
if (n<0.1){
dir=1;
}else if (n>0.9){
dir=-1;
}
n=n+0.005*dir;
lines(g);
break;
case 1: //抓取
if (length<500){
length = length+10;
lines(g);
}else {state=0;}
break;
case 2:
if (length>100){
length=length-10;
lines(g);
}else {state=0;}
}
}
在主类中,创建Line对象,添加鼠标监听事件,这样点击鼠标,就能控制线的移动
Line line = new Line();
// 窗口绘制方法
void launch(){
this.setVisible(true);
this.setSize(768,1000);
// 设置窗口位置,居中
this.setLocationRelativeTo(null);
this.setTitle("黄金矿工");
// 关闭窗口
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
//*******************************************************************************
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
// 点击鼠标左键(e.getButton()==1),线的状态为抓取
if (e.getButton()==1){
line.state=1;
}else if (e.getButton()==3){
// 点击鼠标右键(e.getButton()==3),线的状态为收回
line.state=2;
}
}
});
//*******************************************************************************
while (true){
repaint();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
6 金块绘制
金块和石头是我们后续要创建的对象,他们有很多共同的属性,创建Object类,让石头类和金块类继承Object类
- Object类
public class Object {
//坐标
int x;
int y;
//宽高
int width;
int height;
// 图片
Image img;
// 绘制方法
void paintSelf(Graphics g){
g.drawImage(img,x,y,null);
}
}
public class Gold extends Object{
Gold(){
this.x=300;
this.y=500;
this.width=52;
this.height=52;
this.img= Toolkit.getDefaultToolkit().getImage("imgs/gold1.gif");
}
}
7 闪动问题解决
每次都会创建新的画布,都会刷新一次
创建一个一摸一样的画布,放在最下层,每次重新绘制时,就不会出现闪动现象
public void paint(Graphics g){
// 创建两个画布,一个放在最下面,第一步先把金块背景放进去
offScreenImage = this.createImage(768,1000);
Graphics gImage = offScreenImage.getGraphics();
bg.paintSelf(gImage);
line.paintSelf(gImage);
gold.paintSelf(gImage);
// 第二步把画布画到背景中
g.drawImage(offScreenImage,0,0,null);
}
8 抓取判定与返回
当绳子碰到金块时,就把金块从屏幕移除,绳子返回
- 定义绳子新状态 state3 抓取返回
- 定义方法,当绳子碰到金块的所在区域时,判定绳子碰到了金块,此时绳子就是新状态
GameWin frame;
Line(GameWin frame){
this.frame = frame;
}
//当绳子在金块的四边形区域内时,绳子的状态为3
void logic(){
if (endx>this.frame.gold.x && endx<this.frame.gold.x+this.frame.gold.width &&
endy>this.frame.gold.y && endy<this.frame.gold.y+this.frame.gold.height){
state=3;
}
}
// Graphics对象代表画笔
void paintSelf(Graphics g){
// 调用判断,当绳子碰到金块时,绳子的状态时3,此时在switch中写状态3情况
logic();
switch (state){
//case0 线摇摆
case 0:
if (n<0.1){
dir=1;
}else if (n>0.9){
dir=-1;
}
n=n+0.005*dir;
lines(g);
break;
// case1 抓取
case 1:
if (length<500){
length = length+10;
lines(g);
}else {state=0;}
break;
case 2:
if (length>100){
length=length-10;
lines(g);
}else {state=0;}
//*******************************************************************************
case 3:
if (length>100){
length=length-10;
lines(g);
// 减去偏移量 金块长度的一半
this.frame.gold.x=endx-26;
this.frame.gold.y=endy;
}else {
// 抓取成功,直接将金块移除 -150就是放在屏幕外面
this.frame.gold.x= -150;
this.frame.gold.y= -150;
state = 0;
}
//*******************************************************************************
break;
}
}
9 添加多个金块
- 在主类中(GameWin),创建集合,用来存放金块,后续放石头
// 可以用来放金块,石块
List<Object> objectList = new ArrayList<>();
在方法体中写循环,来遍历上述集合
{
for (int i=0;i<3;i++){
objectList.add(new Gold());
}
}
在绘制方法中绘制出来
public void paint(Graphics g){
//创建一个一摸一样的画布,放在最下层,每次重新绘制时,就不会出现闪动现象
// 创建两个画布,一个放在最下面,第一步先把金块背景放进去
offScreenImage = this.createImage(768,1000);
Graphics gImage = offScreenImage.getGraphics();
bg.paintSelf(gImage);
line.paintSelf(gImage);
//*******************************************************************************
// 绘制金块,采用增强for循环
for (Object obj:objectList){
obj.paintSelf(gImage);
}
//*******************************************************************************
// 第二步把画布画到背景中
g.drawImage(offScreenImage,0,0,null);
}
金块类改变了,原来创建金块的类被用集合代替了,所以在 Line类 中,需要修改代码
原logic()方法:
void logic(){
if (endx>this.frame.gold.x && endx<this.frame.gold.x+this.frame.gold.width &&
endy>this.frame.gold.y && endy<this.frame.gold.y+this.frame.gold.height){
state=3;
}
}
修改后的:
void logic(){
// 从主窗口中拿objectList集合来遍历
for (Object obj: frame.objectList){
if (endx>obj.x && endx<obj.x+obj.width &&
endy>obj.y && endy<obj.y+obj.height){
state=3;
}
}
}
case3中也按照同样的方法来改
case 3:
if (length>100){
length=length-10;
lines(g);
// 减去偏移量 金块长度的一半
for (Object obj: frame.objectList){
obj.x=endx-26;
obj.y=endy;
if (length<=100){
// 抓取成功,直接将金块移除 -150就是放在屏幕外面
obj.x= -150;
obj.y= -150;
state = 0;
}
}
}
- 修改 Gold类
public class Gold extends Object{
Gold(){
// 把金块的位置变成随机数
this.x=(int) (Math.random()*700);
this.y=(int) (Math.random()*550+300);
this.width=52;
this.height=52;
this.img= Toolkit.getDefaultToolkit().getImage("imgs/gold1.gif");
// flag 的初始值为false,代表没被抓到,不移动
this.flag = false;
}
}
10 解决金块消失的bug
经过上面的操作我们会发现,每次抓一个金块,其余的都会被带走是因为case3中这段代码,抓到之后,所有的金块都会被移走
for (Object obj: frame.objectList){
obj.x=endx-26;
obj.y=endy;
if (length<=100){
// 抓取成功,直接将金块移除 -150就是放在屏幕外面
obj.x= -150;
obj.y= -150;
state = 0;
}
}
因为后续我们不仅抓金块,还会涉及到抓石块,所以在 Object类 中加入布尔类型的判断,判断位置的改变(其余代码省略)
public class Object {
// 标记,是否能移动
boolean flag;
}
Gold类 中(其余代码省略)
public class Gold extends Object{
Gold(){
// flag 的初始值为false,代表没被抓到,不移动
this.flag = false;
}
}
于是在 Line类 中,对flag 进行判断
// Graphics对象代表画笔
void paintSelf(Graphics g){
// 调用判断,当绳子碰到金块时,绳子的状态时3,此时在switch中写状态3情况
logic();
switch (state){
case 3:
if (length>100){
ength=length-10;
lines(g);
// 减去偏移量 金块长度的一半
for (Object obj: frame.objectList){
if (obj.flag==true){
obj.x=endx-26;
obj.y=endy;
if (length<=100){
//抓取成功,直接将金块移除 -150就是放在屏幕外面
obj.x= -150;
obj.y= -150;
obj.flag=false;
state = 0;
}
}
11 创建石块类
Gold类
public class Rock extends Object{
Rock(){
this.x=(int) (Math.random()*700);
this.y=(int) (Math.random()*550+300);
this.width=71;
this.height=71;
this.img= Toolkit.getDefaultToolkit().getImage("imgs/rock1.png");
// flag 的初始值为false,代表没被抓到,不移动
this.flag = false;
}
}
在GameWin类中,遍历创建金块石块对象,遍历
//创建金块,石块
{
for (int i=0;i<3;i++){
objectList.add(new Gold());
}
for (int i=0;i<3;i++){
objectList.add(new Rock());
}
}
第一部分小结
- 创建主类
GameWin类为主类,主要进行窗口绘制,绘制图片,添加金块、石块、背景类
public class GameWin extends JFrame {
// 可以用来放金块,石块
List<Object> objectList = new ArrayList<>();
//背景对象
Bg bg = new Bg();
// 线对象
Line line = new Line(this);
//定义画布
Image offScreenImage;
//创建金块,石块,遍历
{
for (int i=0;i<3;i++){
objectList.add(new Gold());
}
for (int i=0;i<3;i++){
objectList.add(new Rock());
}
}
// 窗口绘制方法
void launch(){
this.setVisible(true);
this.setSize(768,1000);
// 设置窗口位置,居中
this.setLocationRelativeTo(null);
this.setTitle("黄金矿工");
// 关闭窗口
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
// 鼠标监听事件
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
// 点击鼠标左键(e.getButton()==1),线的状态为抓取
if (e.getButton()==1){
line.state=1;
}else if (e.getButton()==3){
// 点击鼠标右键(e.getButton()==3),线的状态为收回
line.state=2;
}
}
});
while (true){
repaint();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 绘制图片
// Graphics 代表画笔
public void paint(Graphics g){
//创建一个一摸一样的画布,放在最下层,每次重新绘制时,就不会出现闪动现象
// 创建两个画布,一个放在最下面,第一步先把金块背景放进去
offScreenImage = this.createImage(768,1000);
Graphics gImage = offScreenImage.getGraphics();
bg.paintSelf(gImage);
line.paintSelf(gImage);
// 绘制金块,采用增强for循环来遍历金块和石块
for (Object obj:objectList){
obj.paintSelf(gImage);
}
// 第二步把画布画到背景中
g.drawImage(offScreenImage,0,0,null);
}
public static void main(String[] args) {
GameWin gameWin = new GameWin();
gameWin.launch();
}
}
- 创建背景类
背景类主要添加三个图片:人物、天空、矿
public class Bg {
Image bg = Toolkit.getDefaultToolkit().getImage("imgs/bg.jpg");
Image bg1 = Toolkit.getDefaultToolkit().getImage("imgs/bg1.jpg");
Image peo = Toolkit.getDefaultToolkit().getImage("imgs/peo.png");
void paintSelf(Graphics g){
g.drawImage(bg,0,200,null);
g.drawImage(bg1,0,0,null);
g.drawImage(peo,310,50,null);
}
}
- 创建Object类
Object类是金块类石头类的父类,写他们的公共部分
public class Object {
// 标记,是否能移动
boolean flag;
//坐标
int x;
int y;
//宽高
int width;
int height;
// 图片
Image img;
// 绘制方法
void paintSelf(Graphics g){
g.drawImage(img,x,y,null);
}
// Line方法中,需要获取金块石块的宽度的一般,来计算偏移量,这里定义getWidth就能在Line类的paintSelf方法中得到了
public int getWidth() {
return width;
}
}
- 金块类
写入金块的大小,图片状态
public class Gold extends Object{
Gold(){
this.x=(int) (Math.random()*700);
this.y=(int) (Math.random()*550+300);
this.width=52;
this.height=52;
this.img= Toolkit.getDefaultToolkit().getImage("imgs/gold1.gif");
// flag 的初始值为false,代表没被抓到,不移动
this.flag = false;
}
}
- 石块类
public class Rock extends Object{
Rock(){
this.x=(int) (Math.random()*700);
this.y=(int) (Math.random()*550+300);
this.width=71;
this.height=71;
this.img= Toolkit.getDefaultToolkit().getImage("imgs/rock1.png");
// flag 的初始值为false,代表没被抓到,不移动
this.flag = false;
}
}
- 创建线类
logic() 方法表示当线碰到金块/石块的范围后,线的状态发生改变,
lines() 方法表示绘制红线
public class Line {
// 起点坐标
int x=380;
int y=180;
// 终点坐标
int endx=500;
int endy=500;
// 线长
double length = 100;
double n = 0;
// 添加方向参数
int dir = 1;
// 红线状态 0:摇摆 1:抓取 2:收回 3:抓取返回
int state;
GameWin frame;
Line(GameWin frame){
this.frame = frame;
}
void logic(){
// 从主窗口中拿objectList集合来遍历
for (Object obj: frame.objectList){
if (endx>obj.x && endx<obj.x+obj.width &&
endy>obj.y && endy<obj.y+obj.height){
state=3;
// 当抓取到了,金块的值变为真
obj.flag = true;
}
}
}
void lines(Graphics g){
endx = (int) (x + length*Math.cos(n*Math.PI));
endy = (int) (y+ length*Math.sin(n*Math.PI));
g.setColor(Color.RED);
g.drawLine(x,y,endx,endy);
}
// Graphics对象代表画笔
void paintSelf(Graphics g){
// 调用判断,当绳子碰到金块时,绳子的状态时3,此时在switch中写状态3情况
logic();
switch (state){
//case0 线摇摆
case 0:
if (n<0.1){
dir=1;
}else if (n>0.9){
dir=-1;
}
n=n+0.005*dir;
lines(g);
break;
// case1 抓取
case 1:
if (length<500){
length = length+10;
lines(g);
}else {state=0;}
break;
case 2:
if (length>100){
length=length-10;
lines(g);
}else {state=0;}
case 3:
if (length>100){
length=length-10;
lines(g);
// 减去偏移量 金块长度的一半
for (Object obj: frame.objectList){
if (obj.flag==true){
obj.x=endx-obj.getWidth()/2;
obj.y=endy;
if (length<=100){
//抓取成功,直接将金块移除 -150就是放在屏幕外面
obj.x= -150;
obj.y= -150;
obj.flag=false;
state = 0;
}
}
}
}
break;
}
}
}