📄 game.java
字号:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.image.MemoryImageSource;
import java.util.Random;
import java.util.Vector;
/**
* 飞行游戏的主程序
* @author fantasyer
* @version 1.0
*/
public class Game extends JFrame
{
private LogoPanel logoPanel;
private GamePanel gamePanel;
//该类唯一实例
private static Game game;
Game()
{
super("Space Invader");
//全屏模式
setUndecorated(true);
setResizable(false);
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gd = ge.getDefaultScreenDevice();
if (gd.isFullScreenSupported())
{
gd.setFullScreenWindow(this);
gd.setDisplayMode(new DisplayMode(800, 600, 32, 75));
}
//隐藏鼠标指针
int[] pixels = new int[16*16];
Image image = Toolkit.getDefaultToolkit().createImage(new MemoryImageSource(16, 16, pixels, 0, 16));
Cursor transparentCursor = Toolkit.getDefaultToolkit().createCustomCursor
(image, new Point(0, 0), "Transparent");
this.setCursor(transparentCursor);
gamePanel = new GamePanel();
addKeyListener(gamePanel);
logoPanel = new LogoPanel();
getContentPane().add(logoPanel, "Center"); //播放开场动画的面板
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
/**
* 获取该类的唯一实例
*/
public static Game getInstance()
{
if (game == null)
{
game = new Game();
}
return game;
}
/**
* 播放完开场画面,进入游戏区域
*/
public void start()
{
try
{
synchronized(game)
{
//等待Logo动画结束
game.wait();
//这里暂缓一秒钟,否则界面显示有点问题(界面来不及刷新)
Thread.sleep(1000);
getContentPane().add(gamePanel, "Center");
validate();
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
public static void main(String[] args)
{
Game.getInstance().start();
}
}
/**
* 完成游戏绘制的面板
*/
class GamePanel extends JPanel implements Runnable, KeyListener, MouseListener
{
private int width, height; //面板的高度和宽度
private int y, totalY; //背景移动的速度、整个背景运行的高度
private int gamestate; //游戏状态
private int level; //关卡数
private int menuIndex; //菜单索引
private int fontWidth; //菜单文字的宽度
private Font menuFont, keyFont, textFont; //菜单的字体、按键的字体、显示得分的字体
private String[] menuIndexDes = {"开始游戏", "按键帮助", "退出游戏"}; //菜单文字
private Image[] bgImage; //背景图片
private Image heroImage, hpImage, overImage; //英雄飞机、血条图片、游戏结束图片
//敌机、Boss、子弹、爆炸图片、奖品图片
private Image[] enemyImages, bossImages, bulletImages, blastImages, awardImages;
private int bgHeight; //背景图片的高度
private Plane heroPlane;
private Vector heroBullets, enemyBullets; //英雄的子弹、敌人的子弹
private Vector enemyPlanes; //敌机
private Vector blast; //敌机的爆炸
private Vector planeBlast; //飞机的爆炸
private Vector award; //奖品
private int heroNumber; //玩家的飞机数目
private Random random; //用于随机产生敌机坐标等
private boolean[] keys; //记录用户的按键
private int fps; //游戏运行的FPS
private String fpsStr; //描述FPS的字符串
private long startTime; //游戏运行的起始时间(用于计算游戏的FPS)
private int unbeatTime, levelTime; //飞机刚出生处于无敌状态的时间、关卡之间间隔的时间
private int newPlaneTime; //新的飞机重生的间隔时间
private Thread bgThread; //控制背景移动的线程
/**
* 加载声音图片资源,初始化相关属性
*/
GamePanel()
{
//确定游戏画面的大小
width = Utility.GAME_WIDTH;
height = Utility.GAME_HEIGHT;
setPreferredSize(new Dimension(Utility.SCREEN_WIDTH,Utility.SCREEN_HEIGHT));
//确定菜单文字的字体以及宽度
menuFont = new Font("华文仿宋", Font.BOLD, 36);
keyFont = new Font("黑体", Font.PLAIN, 20);
textFont = new Font("黑体", Font.PLAIN, 14);
setFont(menuFont);
Font f = this.getFont();
FontMetrics fm = getFontMetrics(f);
fontWidth = fm.stringWidth(menuIndexDes[0]);
//初始化英雄飞机、敌机等相关属性
heroPlane = new Plane();
enemyPlanes = new Vector();
heroBullets = new Vector();
enemyBullets = new Vector();
blast = new Vector();
planeBlast = new Vector();
award = new Vector();
heroNumber = Utility.HERO_NUMBER;
//游戏初始状态设置
random = new Random(System.currentTimeMillis());
gamestate = Utility.GAME_START;
level = 1;
menuIndex = 0;
startTime = System.currentTimeMillis();
fpsStr = "";
unbeatTime = levelTime = 0;
//加载资源
loadResource();
//初始化keys数组
keys = new boolean[256];
for (int i = 0; i<keys.length; i++)
{
keys[i] = false;
}
//添加键盘事件
addKeyListener(this);
requestFocus();
loadLevelData(1);
//启动背景移动的线程
bgThread = new Thread(this);
PlayMidi.play(true);
}
/**
* 加载游戏中用到的声音和图片资源
*/
public void loadResource()
{
PlayMidi.loadMidi("Audio/bg.mid");
//等待图片加载进来
MediaTracker mt = new MediaTracker(this);
bgImage = new Image[Utility.BACKGROUND_IMAGE_NUMBER];
for (int i = 0; i<bgImage.length; i++)
{
bgImage[i] = Toolkit.getDefaultToolkit().getImage(GamePanel.class.getResource("Images/background"+i+".gif"));
mt.addImage(bgImage[i], i);
}
enemyImages = new Image[Utility.ENEMY_IMAGE_NUMBER];
for (int i = 0; i<enemyImages.length; i++)
{
enemyImages[i] = Toolkit.getDefaultToolkit().getImage(GamePanel.class.getResource("Images/enemy"+i+".gif"));
mt.addImage(enemyImages[i], bgImage.length+i);
}
bossImages = new Image[Utility.BOSS_IMAGE_NUMBER];
for (int i = 0; i<bossImages.length; i++)
{
bossImages[i] = Toolkit.getDefaultToolkit().getImage(GamePanel.class.getResource("Images/boss"+i+".gif"));
mt.addImage(bossImages[i], bgImage.length+enemyImages.length+i);
}
bulletImages = new Image[2];
for (int i = 0; i<bulletImages.length; i++)
{
bulletImages[i] = Toolkit.getDefaultToolkit().getImage(GamePanel.class.getResource("Images/bullet"+i+".gif"));
mt.addImage(bulletImages[i], 17+i);
}
blastImages = new Image[8];
for (int i = 0; i<blastImages.length; i++)
{
blastImages[i] = Toolkit.getDefaultToolkit().getImage(GamePanel.class.getResource("Images/bomb"+i+".gif"));
mt.addImage(blastImages[i], 19+i);
}
awardImages = new Image[3];
for (int i = 0; i<awardImages.length; i++)
{
awardImages[i] = Toolkit.getDefaultToolkit().getImage(GamePanel.class.getResource("Images/award"+i+".gif"));
mt.addImage(awardImages[i], 27+i);
}
heroImage = Toolkit.getDefaultToolkit().getImage(GamePanel.class.getResource("Images/hero.gif"));
overImage = Toolkit.getDefaultToolkit().getImage(GamePanel.class.getResource("Images/gameover.gif"));
hpImage = Toolkit.getDefaultToolkit().getImage(GamePanel.class.getResource("Images/hp.gif"));
mt.addImage(heroImage, 30);
mt.addImage(overImage, 31);
mt.addImage(hpImage, 32);
try
{
mt.waitForAll();
}
catch (Exception e)
{
e.printStackTrace();
}
//记录背景图片的高度
bgHeight = bgImage[0].getHeight(this);
}
/**
* 加载该关卡数据
* @param level 关卡编号
*/
public void loadLevelData(int level)
{
enemyPlanes.clear();
enemyPlanes = DataManager.loadData("level_"+level+".dat");
}
/**
* 绘制游戏区域
* @param g 用于绘图的Graphics对象
*/
public void paintComponent(Graphics g)
{
super.paintComponent(g);
//绘制两边不动的区域
g.drawImage(bgImage[0], 0, 0,(Utility.SCREEN_WIDTH-Utility.GAME_WIDTH)/2,Utility.SCREEN_HEIGHT, this);
g.drawImage(bgImage[0], (Utility.SCREEN_WIDTH+Utility.GAME_WIDTH)/2, 0,(Utility.SCREEN_WIDTH-Utility.GAME_WIDTH)/2,
Utility.SCREEN_HEIGHT, this);
//绘制游戏中心区域
g.translate((Utility.SCREEN_WIDTH-Utility.GAME_WIDTH)/2, 0);
//绘制背景
g.drawImage(bgImage[level], 0, y, width, bgHeight, this);
g.drawImage(bgImage[level], 0, y-bgHeight, width, bgHeight, this);
if (gamestate==Utility.GAME_START)
{
//绘制菜单项
g.setFont(menuFont);
for (int i = 0; i<menuIndexDes.length; i++)
{
if (i==menuIndex)
{
//g.setColor(new Color(200,200,200));
g.setColor(new Color(255, 0, 0));
}
else
{
g.setColor(new Color(100, 100, 100));
}
g.drawString(menuIndexDes[i], (width-fontWidth)/2,height/2-50+i*50);
}
}
else if (gamestate==Utility.KEY_HELP)
{
g.setFont(keyFont);
g.setColor(Color.red);
g.drawString("W 向上", width/2-60, height/2-140);
g.drawString("S 向下", width/2-60, height/2-120);
g.drawString("A 向左", width/2-60, height/2-100);
g.drawString("D 向右", width/2-60, height/2-80);
g.drawString("K 子弹", width/2-60, height/2-60);
g.drawString("P 暂停", width/2-60, height/2-40);
g.drawString("R 恢复", width/2-60, height/2-20);
g.drawString("ESC 退出", width/2-60, height/2);
g.drawString("按 BackSpace 返回主菜单", width/2-130, height/2+50);
}
else if (gamestate==Utility.GAME_RUNNING||gamestate==Utility.GAME_PAUSE)
{
g.setColor(Color.red);
setFont(textFont);
//绘制血条、分数、剩余飞机数目
g.drawImage(hpImage, 0, 10, heroPlane.life, 10, this);
g.drawString("分数:"+heroPlane.score, width/2-20, 20);
for (int i = 1; i<heroNumber; i++)
{
g.drawImage(heroImage, width-i*20, 10, 15, 15, this);
}
//g.drawString(fpsStr,300,20);
//绘制英雄飞机
if (heroPlane.getVisible())
{
g.drawImage(heroImage, heroPlane.x, heroPlane.y,heroPlane.width, heroPlane.height, this);
}
//绘制敌机
for (int i = 0; i<enemyPlanes.size(); i++)
{
Enemy emy = (Enemy)enemyPlanes.get(i);
if (emy.getVisible())
{
if (emy.kind<10)
{
g.drawImage(enemyImages[emy.kind-1], emy.x, emy.y,emy.width, emy.height, this);
}
else
{
g.drawImage(bossImages[emy.kind-10-1], emy.x, emy.y,emy.width, emy.height, this);
}
}
}
//绘制飞机爆炸效果
for (int i = 0; i<planeBlast.size(); i++)
{
Blast b1 = (Blast)planeBlast.elementAt(i);
g.drawImage(blastImages[b1.index], b1.x, b1.y, b1.width,b1.height, this);
}
//绘制敌机爆炸效果
for (int i = 0; i<blast.size(); i++)
{
Blast b1 = (Blast)blast.elementAt(i);
g.drawImage(blastImages[b1.index], b1.x, b1.y, b1.width,b1.height, this);
}
//绘制敌机的子弹
for (int i = 0; i<enemyBullets.size(); i++)
{
Bullet b1 = (Bullet)enemyBullets.get(i);
g.drawImage(bulletImages[0], b1.x, b1.y, b1.width, b1.height, this);
}
//绘制英雄的子弹
for (int i = 0; i<heroBullets.size(); i++)
{
Bullet b1 = (Bullet)heroBullets.get(i);
g.drawImage(bulletImages[1], b1.x, b1.y, b1.width, b1.height, this);
}
//绘制奖品
for (int i = 0; i<award.size(); i++)
{
Award a = (Award)award.get(i);
g.drawImage(awardImages[a.kind-Utility.AWARD_1], a.x, a.y,a.width, a.height, this);
}
}
else if (gamestate==Utility.GAME_OVER)
{
g.setColor(Color.black);
g.fillRect(0, 0, width, height);
int imgWidth = overImage.getWidth(this);
int imgHeight = overImage.getHeight(this);
g.drawImage(overImage, (Utility.GAME_WIDTH-imgWidth)/2,(Utility.GAME_HEIGHT-imgHeight)/2,imgWidth, imgHeight, this);
}
g.translate(-(Utility.SCREEN_WIDTH-Utility.GAME_WIDTH)/2, 0);
}
/**
* 控制背景移动,游戏中的逻辑计算
*/
public void run()
{
while (true)
{
if (gamestate==Utility.GAME_RUNNING)
{
//是否进入下一关
if (enemyPlanes.size()==0)
{
levelTime++;
if (levelTime>100)
{
levelTime = 0;
level++;
if (level>3) //目前最多只有三关
{
level = 1;
}
loadLevelData(level);
totalY = Utility.CURRENT_Y = 0;
heroPlane.setLife(Utility.HERO_HP);
heroPlane.setDefaultLocation();
}
}
long roundStartTime = System.currentTimeMillis();
doGameLogic(); //这是真个游戏的核心部分,处理相关的逻辑运算
moveBackground(); //移动背景地图
caculateFPS(); //计算FPS
//控制背景移动同时将游戏的速率稳定在40fps/s
//这种方法不是很精确,实际只有20多
long roundEndTime = System.currentTimeMillis();
try
{
if ( (roundEndTime-roundStartTime)<25 )
{
Thread.sleep(25-(roundEndTime-roundStartTime));
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -