游戏规则:抓取石块右键可以爆破、抓取金块右键快速抓取。
完成任务:窗体绘制、钩爪往返旋转、物体批量绘制、双缓存解决闪动问题、点和矩形碰撞检测、多种类物体随机生成、物体堆叠检测、多种类抓取速度判定、积分设置、关卡设置、重新开始、单关卡倒计时、金块的快速拉取、石块爆破、商店购物。
游戏大致界面:
一、窗口绘制 创建GameWin类import javax.swing.*;
public class GameWin extends JFrame {
void launch(){
this.setVisible(true);
this.setSize(500,500);
this.setLocationRelativeTo(null);//屏幕中央出现窗口
this.setTitle("黄金矿工");
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public static void main(String[] args) {
GameWin gameWin = new GameWin();
gameWin.launch();
}
}
二、绘制图片
需要几个图片素材,私信。
新建imgs文件夹存放图片。
创建Bg类背景由多个图片组成,所以创建一个背景类。
获取图片,用绘图技术将图片画在界面上;
注意根据图片大小计算好xy位置。
import java.awt.*;
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(bg1,0,0,null);
g.drawImage(bg,0,282,null);
g.drawImage(peo,350,133,null);
}
}
GameWin类增加
Bg bg = new Bg();
public void paint(Graphics g){
bg.paintSelf(g);
}
查看图片大小,修改界面大小
this.setSize(814,1000);三、红线绘制 创建Line类
import java.awt.*;
public class Line {
int x = 400;//起点在人物坐标处
int y = 282;//起点坐标
int endx = 500;
int endy = 500;//重点坐标
void paintSelf(Graphics g){
g.setColor(Color.red);
g.drawLine(x,y,endx,endy);
}
}
GameWin类增加
Line line = new Line();
paint方法增加
line.paintSelf(g);四、红线摇摆 实现原理:
如何求出endx和endy,找到endx、endy、x、y之间的关系。
计算机中,x轴向右为正方向,y轴向下为正方向;
角度起点为x轴正方向,顺时针旋转90°到达y轴,也就是旋转Π/2即1.57;
可利用此实现摇摆效果,如1小于Π/2,则在y轴右方,2大于Π/2,则在y轴左方。
如果要想实现在0-Π之间摇摆,可以设n∈(0,1)代替cosα中的α,则操作n可实现角度的变化,n*Π∈(0,Π)。
Line类增加/修改double length = 100;//线长
double n = 0;//线摇摆角度
void paintSelf(Graphics g){
n+=0.005;
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);
}
GameWin类增加
需要每隔0.01s绘制一次画面,达到摇摆效果。
while (true){
repaint();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
目前,红线是摇摆一整圈,原因是n一直是+0.005,需要设置摇摆的范围,在0-Π之间摇摆。
五、红线摇摆范围设置一个dir表示方向即正负,通过判断,改变符号。
Line类增加int dir = 1;//方向
paintSelf方法增加/修改
if(n<0){
dir = 1;
}
else if (n>1){
dir = -1;
}
n+=0.005*dir;
六、红线抓取
Line类增加
state表示状态,通过鼠标点击修改state数值,下面根据state数值实现红线的不同状态。
int state;//状态0为摇摆,1为抓取,2为收回
由于后面多次使用方法的四行代码,所以定义一个方法,后面直接调用方法。
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方法,通过switch判断state数值,使红线做出不同动作。
void paintSelf(Graphics g){
switch (state){
case 0:
if(n<0){
dir = 1;
}
else if (n>1){
dir = -1;
}
n+=0.005*dir;
lines(g);
break;
case 1:
if(length<500){//限制抓取长度
length+=10;
lines(g);
}else{state = 2;}//如果超出长度则收回
break;
case 2:
if(length>100){//限制收回长度
length-=10;
lines(g);
}else{state = 0;}
break;
default:
}
}
GameWin类增加
鼠标点击事件,通过getButton()获取点击的键值修改line类的state。
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
if(e.getButton() == 1){//鼠标左键1,滚轮2,右键3
line.state = 1;
}
}
});
七、创建金块
创建Object类
金块和岩石都有坐标和宽高以及绘制,所以需要定义一个类用来继承。
import java.awt.*;
public class Object {
int x;
int y;
int width;
int height;
Image img;
void paintSelf(Graphics g){
g.drawImage(img,x,y,null);
}
}
创建金块类Gold
import java.awt.*;
public class Gold extends Object{
Gold(){
this.x = 300;
this.y = 500;
this.width = 78;
this.height = 88;
this.img = Toolkit.getDefaultToolkit().getImage("imgs/gold0.png");
}
}
GameWin类增加
Gold gold = new Gold();
paint方法绘制
gold.paintSelf(g);八、双缓存技术解决闪动
目前,窗口图片存在闪动问题,原因是像金块这样的图片在岩土这样的背景图片上层,先绘制背景图片再绘制金块图片,再绘制背景图片,再绘制金块......所以产生了图片闪动问题。
可以先将背景图片和金块在绘制之前组织好再一起放置在窗口之上。
GameWin类增加Image offScreenImage;
paint方法增加
offScreenImage = this.createImage(814,1000);//创建画布 Graphics gImage = offScreenImage.getGraphics();//创建画笔 bg.paintSelf(gImage); line.paintSelf(gImage); gold.paintSelf(gImage); g.drawImage(offScreenImage,0,0,null);//将图片一起绘制九、抓取判定 Line类增加
GameWin frame;
Line(GameWin frame){
this.frame = frame;
}
void logic(){//碰撞检测,检测红线是否在金块范围内
if(endx > this.frame.gold.x && endx this.frame.gold.y&&endy
paintSelf方法增加
logic();
GameWin修改
需要传入主类,才能调用主类的方法
Line line = new Line(this);
十、抓取返回
Line类增加/修改
如果红线在金块范围内则改变state状态为3,表示抓取返回。
void logic(){//碰撞检测,检测红线是否在金块范围内
if(endx > this.frame.gold.x && endx this.frame.gold.y&&endy
switch增加判断state为3的情况;
金块的x坐标于红线的x坐标的关系为endx-金块长度/2;
如果抓取金块到达位置则将金块移出屏幕外。
case 3:
if(length>100){
length-=10;
lines(g);
this.frame.gold.x = endx - 39;
this.frame.gold.y = endy;
}else{
this.frame.gold.x = -150;//移除到屏幕外
this.frame.gold.y = -150;
state = 0;
}
break;
十一、多个金块
使用集合生成多个金块
GameWin类增加
定义集合存储金块
List
循环往集合添加金块
{
for (int i = 0; i < 3; i++) {
objectList.add(new Gold());
}
}
paint方法需要循环绘制多个金块
for(Object obj:objectList){
obj.paintSelf(gImage);
}
Line类修改
修改前面使用一个金块的方法
for(Object obj:this.frame.objectList){
if(endx > obj.x && endx obj.y&&endy
for(Object obj:this.frame.objectList){
obj.x = endx - 39;
obj.y = endy;
if(length<=100){
obj.x = -150;//移除到屏幕外
obj.y = -150;
state = 0;
}
}
Gold类修改
修改金块生成位置随机
this.x = (int)(Math.random()*750);//防止生成在窗体
this.y = (int)(Math.random()*550+300);//防止生成在天上
十二、抓取金块BUG
BUG原因:抓取金块后,修改了所有金块的位置。
BUG解决:设置变量flag标记金块是否能移动
Object类增加
boolean flag;//标记是否移动
Glod类增加
this.flag = false;
Line类增加
logic方法if增加
obj.flag = true;
switch case 3增加
for(Object obj:this.frame.objectList){
if(obj.flag){//判断是否能够移动
obj.x = endx - 39;
obj.y = endy;
if(length<=100){
obj.x = -150;//移除到屏幕外
obj.y = -150;
obj.flag = false;
state = 0;
}
}
}
至此已完成一半内容,下篇文章继续。



