一、功能点
本项目分为15个功能点:1.窗口绘制2.勾爪往返旋转3.物体批量绘制4.双缓存解决闪动问题5.点和矩形碰撞检测6.多种类物体随机生成7.物体堆叠检测8.多种类抓取速度判定9.积分设置10.关卡设置11.重新开始12.单关卡倒计时13.金块的快速拉取14.石块爆破15.商店购物。
二、项目结构
代码结构:
结构体:
功能实现:
在Game类中继承JFrame,对屏幕上window的对象,能够最大化、最小化、关闭。
设置了5种游戏状态(参数为state)
0:游戏未开始
1:游戏运行中
2:进入商店
3:游戏失败
4:游戏胜利
创建石块,金块的列表集合,随机生成石块,金块,同时判断是否发生了重叠,如重叠则不可生成。
初始化窗口信息,大小、位置、标题等,根据游戏的状态应用鼠标点击,具体为左右摇摆,点击左键,抓取返回,点击右键。
当满足本关卡获胜条件,通过重复绘制图片游戏进入下一关。
调用画笔类绘制金块,石块。
三:类
主类:Game
背景类:BackGround
绳索类:Rope
父类:Object
子类:Rock,Gold
四:代码解读:
主类Game类:
public class Game extends JFrame {
//0未开始,1表示运行中,2表示商店,3表示失败,4表示胜利
static int state;
//存储金块,石块
List<Object> objectList = new ArrayList<>();
//将图片载入到程序当中
BackGround bg = new BackGround();
Rope line = new Rope(this);
{
//是否可以放置
boolean isPlace = true;
for (int i=0;i<11;i++){
double random = Math.random();
Gold gold;//存放当前生成的金块
if(random<0.3){
gold = new SmallGold();
}else if(random<0.7){
gold = new Gold();
}else {
gold = new BigGold();
}
for (Object obj:objectList){
if(gold.getRec().intersects(obj.getRec())){
//不可放置,需重新生成
isPlace = false;
i--;
}
}
if (isPlace){
objectList.add(gold);
}else {
isPlace = true;
i--;
}
}
for (int i=0;i<3;i++){
Rock rock = new Rock();
for (Object obj:objectList){
if (rock.getRec().intersects(obj.getRec())){
isPlace = false;
}
}
if (isPlace){
objectList.add(rock);
}else {
isPlace = true;
i--;
}
}
}
Image offScreenImage;
void launch(){ //初始化窗口信息
this.setVisible(true); //设置窗口是否可见
this.setSize(768,1000);//设置窗口大小
this.setLocationRelativeTo(null);//设置窗口位置,使窗口居中
this.setTitle("黄金矿工");//设置窗口标题
setDefaultCloseOperation(EXIT_ON_CLOSE);//创建关闭窗口的方法
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
switch (state){
case 0:
if (e.getButton()==3){
state = 1;
bg.startTime = System.currentTimeMillis();
}
break;
case 1:
//左右摇摆,点击左键
if (e.getButton()==1 && line.state==0){
line.state=1;
}
//抓取返回,点击右键
if (e.getButton()==3 && line.state==3 && BackGround.waterNum>0){
BackGround.waterFlag = true;
BackGround.waterNum--;
}
break;
case 2:
if (e.getButton()==1){
bg.shop = true;
}
if (e.getButton()==3){
state = 1;
bg.startTime = System.currentTimeMillis();
}
break;
case 3:
case 4:
if (e.getButton()==1){
state = 0;
bg.reGame();
line.reGame();
}
break;
default:
}
}
});
//重复绘制图片
while (true){
repaint();
nextLevel();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//下一关
public void nextLevel(){
if (bg.gameTime() && state == 1){
if (BackGround.count>=bg.goal){
if (BackGround.level == 5){
state = 4;
}else {
state=2;
BackGround.level++;
}
}else {
state = 3;
}
dispose();
Game gameWin = new Game();
gameWin.launch();
}
}
//绘制方法
public void paint(Graphics g){
offScreenImage = this.createImage(768,1000);
Graphics gImage = offScreenImage.getGraphics();
bg.paintSelf(gImage);
if (state == 1){
//绘制物体
for (Object obj:objectList){
obj.paintSelf(gImage);
}
line.paintSelf(gImage);
}
g.drawImage(offScreenImage,0,0,null);
}
//程序入口
public static void main(String[] args) {
Game gameWin = new Game();
gameWin.launch();
}
}
背景BackGround类:
定义关卡数初始化为1,目标得分,总分,药水数量,定义药水状态,默认状态时false,true表示正在使用,开始时间,结束时间,利用函数随机生成药水价格,是否进入商店;
载入背景图片,背景图片1,任务图片,药水图片;
利用paintSelf绘制图片,在游戏状态为0时,绘制准备开始字样;
在游戏状态为1时,绘制药水组件,关卡数,目标积分,时间组件;
在游戏状态为2时,绘制是否购买药水界面;
在游戏状态为3时,绘制游戏失败界面;
在游戏状态为4时,绘制游戏成功界面。
设计游戏计时功能gameTime,返回true,倒计时完成,返回false则表示正在倒计时。
设计游戏重置元素reGame,对关卡数,目标得分,总分,药水数量,药水状态重置;
绘制字符串,设置游戏中字体的颜色,类别。
public class BackGround {
//关卡数
static int level = 1;
//目标得分
int goal = level*15;
//总分
static int count = 0;
//药水数量
static int waterNum = 3;
//药水状态,默认是false,true表示正在使用
static boolean waterFlag = false;
//开始时间
long startTime;
//结束时间
long endTime;
//药水价格
int price = (int) (Math.random()*10);
//是否进入商店
boolean shop = false;
//载入图片
Image bg = Toolkit.getDefaultToolkit().getImage("D:\\JavaLearn\\ProjectOverview\\GoldenMineral\\imgs\\bg.jpg");
Image bg1 = Toolkit.getDefaultToolkit().getImage("D:\\JavaLearn\\ProjectOverview\\GoldenMineral\\imgs\\bg1.jpg");
Image peo = Toolkit.getDefaultToolkit().getImage("D:\\JavaLearn\\ProjectOverview\\GoldenMineral\\imgs\\peo.png");
Image water = Toolkit.getDefaultToolkit().getImage("D:\\JavaLearn\\ProjectOverview\\GoldenMineral\\imgs\\water.png");
//绘制
void paintSelf(Graphics g){
g.drawImage(bg1,0,0,null);
g.drawImage(bg,0,200,null);
switch (Game.state){
case 0:
drawWord(g,80,Color.green,"准备开始",200,400);
break;
case 1:
g.drawImage(peo,310,50,null);
drawWord(g,30,Color.BLACK,"积分:"+count,30,150);
//药水组件
g.drawImage(water,450,40,null);
drawWord(g,30,Color.BLACK,"*"+waterNum,510,70);
//关卡数
drawWord(g,20,Color.BLACK,"第"+level+"关",30,60);
//目标积分
drawWord(g,30,Color.BLACK,"目标"+goal,30,110);
//时间组件
endTime = System.currentTimeMillis();
long tim = 20-(endTime-startTime)/1000;
drawWord(g,30,Color.BLACK,"时间"+(tim>0?tim:0),520,150);
break;
case 2:
g.drawImage(water,300,400,null);
drawWord(g,30,Color.BLACK,"药水价格"+waterNum,300,500);
drawWord(g,30,Color.BLACK,"是否购买"+waterNum,300,550);
if(shop){
count = count-price;
waterNum++;
shop = false;
Game.state = 1;
startTime = System.currentTimeMillis();
}
break;
case 3:
drawWord(g,80,Color.cyan,"失败",250,350);
drawWord(g,80,Color.cyan,"积分"+count,200,450);
break;
case 4:
drawWord(g,80,Color.red,"成功",250,350);
drawWord(g,80,Color.red,"积分"+count,200,450);
break;
default:
}
}
//true倒计时完成,false正在倒计时
boolean gameTime(){
long tim = (endTime-startTime)/1000;
if(tim>20){
return true;
}
return false;
}
//重置元素
void reGame(){
//关卡数
int level = 1;
//目标得分
int goal = level*15;
//总分
int count = 0;
//药水数量
int waterNum = 3;
//药水状态,默认是false,true表示正在使用
boolean waterFlag = false;
}
//绘制字符串
public static void drawWord(Graphics g,int size,Color color,String str,int x,int y){
g.setColor(color);
g.setFont(new Font("仿宋",Font.BOLD,size));
g.drawString(str,x,y);
}
}
父类Object类:
定义坐标,宽高,图片,标记是否移动,质量,积分,类型。
public class Object {
//坐标
int x;
int y;
//宽高
int width;
int height;
//图片
Image img;
//标记是否能移动
boolean flag;
//质量
int m;
//积分
int count;
//类型1金块,2石块
int type;
void paintSelf(Graphics g){
g.drawImage(img,x,y,null);
}
public int getWidth() {
return width;
}
//获取矩形
public Rectangle getRec(){
return new Rectangle(x,y,width,height);
}
}
子类Rock类:
继承父类Object类;
利用函数使石块的位置随机生成 ;
定义石块的基本属性。
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.flag = false;
this.m = 50;
this.count = 1;
this.type = 2;
this.img = Toolkit.getDefaultToolkit().getImage("D:\\JavaLearn\\ProjectOverview\\GoldenMineral\\imgs\\rock1.png");
}
}
子类Gold类:
继承父类Object类;
Gold类又细分成Gold类,BigGold类,SmallGold类;
利用函数使金块的位置随机生成 ;
定义金块的基本属性。
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.flag = false;
this.m = 30;
this.count = 4;
this.type = 1;
this.img = Toolkit.getDefaultToolkit().getImage("D:\\JavaLearn\\ProjectOverview\\GoldenMineral\\imgs\\gold1.gif");
}
}
class SmallGold extends Gold{
SmallGold(){
this.width = 36;
this.height = 36;
this.m = 15;
this.count = 2;
this.img = Toolkit.getDefaultToolkit().getImage("D:\\JavaLearn\\ProjectOverview\\GoldenMineral\\imgs\\gold0.gif");
}
}
class BigGold extends Gold {
BigGold() {
this.x = (int) (Math.random()*650);
this.width = 105;
this.height = 105;
this.m = 60;
this.count = 8;
this.img = Toolkit.getDefaultToolkit().getImage("D:\\JavaLearn\\ProjectOverview\\GoldenMineral\\imgs\\gold2.gif");
}
}
绳索Rope类:
定义绳索的起点坐标,终点坐标,绳索长,绳索长最小值,最大值,方向参数,状态0摇摆,1抓取,2收回,3抓取收回,设置勾爪图片;
碰撞检测logic,检测物体是否被抓取;
通过调用绳索的绘制方法绘制绳索的图像;
传入画笔;
重置绳索。
public class Rope {
//绳索的起点坐标
int x = 380;
int y = 180;
//终点坐标
int endx = 500;
int endy = 500;
//绳索长
double length = 100;
//绳索长最小值
double MIN_length = 100;
//绳索长最大值
double MAX_length = 750;
double n = 0;
//方向参数
int dir = 1;
//状态0摇摆,1抓取,2收回,3抓取返回
int state;
//勾爪图片
Image hook = Toolkit.getDefaultToolkit().getImage("D:\\JavaLearn\\ProjectOverview\\GoldenMineral\\imgs\\hook.png");
Game frame;
Rope(Game frame){
this.frame = frame;
}
//碰撞检测,检测物体是否被抓取
void logic(){
for (Object obj:this.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-1,y,endx-1,endy);
g.drawLine(x, y, endx, endy);
g.drawLine(x+1,y,endx+1,endy);
g.drawImage(hook,endx-36,endy-2,null);
}
void paintSelf(Graphics g){ //传入画笔
logic();
switch (state){
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<MAX_length) {
length = length + 5;
lines(g);
}else {
state = 2;
}
break;
case 2:
if (length>MIN_length) {
length = length - 5;
lines(g);
}else {
state = 0;
}
break;
case 3:
int m = 1;
if (length>MIN_length) {
length = length - 5;
lines(g);
for (Object obj:this.frame.objectList) {
if (obj.flag){
m = obj.m;
obj.x = endx - obj.getWidth()/2;
obj.y = endy;
if (length <= MIN_length) {
obj.x = -150;
obj.y = -150;
obj.flag = false;
BackGround.waterFlag = false;
//加分
BackGround.count += obj.count;
state = 0;
}
if(BackGround.waterFlag){
if(obj.type==1){
m = 1;
}
if (obj.type==2){
obj.x = -150;
obj.y = -150;
obj.flag = false;
BackGround.waterFlag = false;
state = 2;
}
}
}
}
}
try {
Thread.sleep(m);
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
default:
}
}
//重置线
void reGame(){
n = 0;
length = 100;
}
}