Game.java
import javax.swing.JFrame;
public class Game {
public static void main(String [] args) {
JFrame window = new JFrame("飞碟大战");
window.setResizable(false);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.add(new GamePanel());
window.pack();
window.setLocationRelativeTo(null);
window.setVisible(true);
}
}
GamePanel.java
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import javax.swing.JPanel;
public class GamePanel extends JPanel implements Runnable, KeyListener {
// 640 480
public static int WIDTH = 320;
public static int HEIGHT = 240;
public static int SCALE = 2;
// 游戏线程 游戏循环
private Thread thread;
private boolean running;
// 实现双缓冲
private BufferedImage image;
private Graphics2D g;
private Player player;
public static ArrayList bullets;
public static ArrayList enemies;
// 设置窗口大小以及事件的捕捉
public GamePanel() {
setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
setFocusable(true);
requestFocus();
}
// 初始化游戏线程并启动游戏线程 增加键盘监听器
public void addNotify() {
super.addNotify();
if (thread == null) {
thread = new Thread(this);
thread.start();
}
addKeyListener(this);
}
private void init() {
running = true; //实现游戏循环
image = new BufferedImage(WIDTH*SCALE, HEIGHT*SCALE, BufferedImage.TYPE_INT_RGB); //创建一个后备缓冲
g = (Graphics2D) image.getGraphics();
player = new Player(WIDTH, HEIGHT);
bullets = new ArrayList();
enemies = new ArrayList();
for(int i = 0; i < 100; i++) {
enemies.add(new Enemy(1,1));
}
}
public void run() {
init();
int fps = 30;
long startTime;
long elapsedTime;
long waitTime;
long targetTime = 1000 / fps;
while (running) {
startTime = System.nanoTime();
update();
render();
draw();
elapsedTime = (System.nanoTime() - startTime) / 1000000;
waitTime = targetTime - elapsedTime;
try {
Thread.sleep(waitTime);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
private void update() {
player.update();
// 子弹更新
for (int i = 0; i < bullets.size(); i++) {
boolean remove = bullets.get(i).update();
if (remove) {
bullets.remove(i);
i--;
}
}
// 敌人更新
for (int i = 0; i < enemies.size(); i++) {
enemies.get(i).update();
}
// 子弹和敌人的碰撞检测
for (int i = 0; i < bullets.size(); i++) {
Bullet b = bullets.get(i);
double br = b.getr();
double bx = b.getx();
double by = b.gety();
for(int j = 0; j < enemies.size(); j++) {
Enemy e = enemies.get(j);
double er = e.getr();
double ex = e.getx();
double ey = e.gety();
double dx = bx - ex;
double dy = by - ey;
double dist = Math.sqrt(dx * dx + dy * dy);
if (dist < br + er) {
e.hit();
bullets.remove(i);
i--;
break;
}
}
}
// 敌人的更新
// 死亡则删除
for (int i = 0; i < enemies.size(); i++) {
if(enemies.get(i).isDead()) {
enemies.remove(i);
i--;
}
}
}
private void render() {
g.setColor(new Color(20, 15, 20));
g.fillRect(0, 0, GamePanel.WIDTH*GamePanel.SCALE, GamePanel.HEIGHT*GamePanel.SCALE);
player.draw(g);
// 绘制子弹
for (int i = 0; i < bullets.size(); i++)
bullets.get(i).draw(g);
// 绘制敌人
for (int i = 0; i < enemies.size(); i++) {
enemies.get(i).draw(g);
}
}
private void draw() {
// 主缓冲
Graphics g2 = getGraphics();
if (g2 == null) {
System.out.println("G2 is NULL");
System.exit(0);
}
// 把后备缓冲绘制到主缓冲 实现平滑的动态效果
g2.drawImage(image, 0, 0, null);
g2.dispose();
}
public void keyPressed(KeyEvent event) {
int keyCode = event.getKeyCode();
player.keyPressed(keyCode);
}
public void keyReleased(KeyEvent event) {
int keyCode = event.getKeyCode();
player.keyReleased(keyCode);
}
public void keyTyped(KeyEvent event) {
}
}
Player.java
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
public class Player {
// w: 宽度
// h: 高度
// x, y: 玩家坐标
// dx, dy: 要移动的坐标
// speed: 速度
private int w;
private int h;
private double x;
private double y;
private double dx;
private double dy;
private double speed;
private boolean up;
private boolean down;
private boolean left;
private boolean right;
private boolean firing;
private long firingTimer;
private long firingDelay;
public Player(double x, double y) {
this.x = x;
this.y = y;
this.dx = 0;
this.dy = 0;
this.w = 16;
this.h = 16;
this.speed = 5;
// 发射状态
// 以及子弹的刷新时间
this.firing = false;
this.firingTimer = System.nanoTime();
this.firingDelay = 150;
}
public void update() {
// 角色移动
if (left) {
dx = -speed;
}
if (right) {
dx = speed;
}
if (up) {
dy = -speed;
}
if (down) {
dy = speed;
}
x += dx;
y += dy;
// 碰撞检测
if (x < 0) { x = 0; }
if (y < 0) { y = 0; }
if (x > GamePanel.WIDTH * GamePanel.SCALE - w) {
x = GamePanel.WIDTH * GamePanel.SCALE - w;
}
if (y > GamePanel.HEIGHT * GamePanel.SCALE - h) {
y = GamePanel.HEIGHT * GamePanel.SCALE - h;
}
if (firing) {
long elapsed = (System.nanoTime() - firingTimer) / 1000000;
if (elapsed > firingDelay) {
GamePanel.bullets.add(new Bullet(270, x, y, this));
firingTimer = System.nanoTime();
}
}
// 重置目的坐标
dx = 0;
dy = 0;
}
public void draw(Graphics2D g) {
g.setColor(new Color(0, 120, 255));
g.fillRect((int)x, (int)y, w, h);
}
public int getw() { return w; }
public int geth() { return h; }
private void setUp(boolean v) { this.up = v; }
private void setDown(boolean v) { this.down = v; }
private void setLeft(boolean v) { this.left = v; }
private void setRight(boolean v) { this.right = v; }
private void setFiring(boolean v) { this.firing = v; }
public void keyPressed(int keyCode) {
if (keyCode == KeyEvent.VK_UP) {
setUp(true);
}
if (keyCode == KeyEvent.VK_DOWN) {
setDown(true);
}
if (keyCode == KeyEvent.VK_RIGHT) {
setRight(true);
}
if (keyCode == KeyEvent.VK_LEFT) {
setLeft(true);
}
if (keyCode == KeyEvent.VK_SPACE) {
setFiring(true);
}
}
public void keyReleased(int keyCode) {
if (keyCode == KeyEvent.VK_UP) {
setUp(false);
}
if (keyCode == KeyEvent.VK_DOWN) {
setDown(false);
}
if (keyCode == KeyEvent.VK_RIGHT) {
setRight(false);
}
if (keyCode == KeyEvent.VK_LEFT) {
setLeft(false);
}
if (keyCode == KeyEvent.VK_SPACE) {
setFiring(false);
}
}
}
Bullet.java
import java.awt.Color;
import java.awt.Graphics2D;
public class Bullet {
protected Player player;
private double x;
private double y;
private int w;
private int h;
private double r; // to bullet-enemies collision
private double dx;
private double dy;
private double rad;
private double speed;
private Color color;
public Bullet(double angle, double x, double y, Player player) {
this.player = player;
this.x = x;
this.y = y;
this.rad = Math.toRadians(angle);
this.speed = 15.44;
this.w = this.h = 3;
// 计算子弹的半径
// 用于实现子弹与敌人的碰撞检测
this.r = Math.sqrt(w*w + h*h)/2;
// 确保子弹在角色上方
this.dx = (int) (Math.cos(rad) * speed);
this.dy = (int) (Math.sin(rad) * speed);
color = new Color(255, 220, 0);
}
public double getx() { return x; }
public double gety() { return y; }
public int getw() { return w; }
public int geth() { return h; }
public double getr() { return r; }
public boolean update() {
x += dx;
y += dy;
if (x < -w || x > GamePanel.WIDTH*GamePanel.SCALE + w ||
y < -h || y > GamePanel.HEIGHT*GamePanel.SCALE + h) {
return true;
}
return false;
}
public void draw(Graphics2D g) {
g.setColor(color);
g.fillRect((int)(x+player.getw()/2-1), (int)(y+player.geth()/2), w, h);
}
}
Enemy.java
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
public class Enemy {
private double x;
private double y;
private double r;
private double dx;
private double dy;
private double rad;
private double speed;
private int health;
private int type;
private int rank;
private Color color1;
private boolean dead;
public Enemy(int type, int rank) {
this.type = type; // 类型
this.rank = rank; // 级别
if (type == 1) {
color1 = Color.YELLOW; // 颜色
r = 5; // 外观
if (rank == 1) { // 战斗属性
speed = 2;
health = 2;
}
}
// 生成坐标
x = (Math.random() * GamePanel.HEIGHT*GamePanel.SCALE / 2 + GamePanel.WIDTH*GamePanel.SCALE / 4);
y = -r;
// 坐标方向
double angle = Math.random() * 140 + 20;
rad = Math.toRadians(angle);
// 偏移坐标
dx = Math.cos(rad)*speed;
dy = Math.sin(rad)*speed;
dead = false;
}
public double getx() { return x; }
public double gety() { return y; }
public double getr() { return r; }
public boolean isDead() { return dead; }
public void hit() {
health--;
color1 = Color.RED;
if (health <= 0) {
dead = true;
}
}
public void update() {
x += dx;
y += dy;
// 屏幕的碰撞检测
// 碰撞屏幕后自动便宜
if (x < r && dx < 0) dx = -dx;
if (y < r && dy < 0) dy = -dy;
if (x > GamePanel.WIDTH*GamePanel.SCALE - r && dx > 0) dx = -dx;
if (y > GamePanel.HEIGHT*GamePanel.SCALE - r && dy > 0) dy = -dy;
}
public void draw(Graphics2D g) {
g.setColor(color1);
g.fillOval((int)(x - r), (int) (y - r), (int)(2 * r), (int)(2 * r));
g.setStroke(new BasicStroke(3));
g.setColor(color1.darker());
g.drawOval((int)(x-r), (int)(y-r), (int)(2 * r), (int)(2 * r));
g.setStroke(new BasicStroke(1));
}
}