diff --git a/docs/坦克大战笔记.docx b/docs/坦克大战笔记.docx index e411e9b..e0753c6 100644 Binary files a/docs/坦克大战笔记.docx and b/docs/坦克大战笔记.docx differ diff --git a/src/com/demo/tank/course8/Tank.java b/src/com/demo/tank/course8/Tank.java index 9a4331b..7ecda1a 100644 --- a/src/com/demo/tank/course8/Tank.java +++ b/src/com/demo/tank/course8/Tank.java @@ -9,6 +9,7 @@ import java.util.Random; public class Tank extends GameObject{ private int x,y; + int oldX, oldY; private Direction dir; private static final int SPEED = 8; private boolean moving = true; @@ -70,6 +71,8 @@ public class Tank extends GameObject{ } public void move(){ + oldX = x; + oldY = y; //如果没有移动 return if(!moving) return; switch (dir){ @@ -100,6 +103,11 @@ public class Tank extends GameObject{ rect.y = this.y; } + public void back(){ + this.x = oldX; + this.y = oldY; + } + private void boundsCheck() { if(x < 0) x = 0; if(x > TankFrameV8.GAME_WIDTH - Tank.WIDTH) x = TankFrameV8.GAME_WIDTH - Tank.WIDTH; diff --git a/src/com/demo/tank/course8/TankTankCollider.java b/src/com/demo/tank/course8/TankTankCollider.java index 33682a8..64930ae 100644 --- a/src/com/demo/tank/course8/TankTankCollider.java +++ b/src/com/demo/tank/course8/TankTankCollider.java @@ -13,8 +13,8 @@ public class TankTankCollider implements Collider{ Tank t2 = (Tank) g2; if(t1.rect.intersects(t2.rect)){ // simple deal - t1.setDir(Direction.values()[random.nextInt(4)]); - t2.setDir(Direction.values()[random.nextInt(4)]); + t1.back(); + t2.back(); } }else{ return; diff --git a/src/com/demo/tank/course9/Bullet.java b/src/com/demo/tank/course9/Bullet.java new file mode 100644 index 0000000..902efe2 --- /dev/null +++ b/src/com/demo/tank/course9/Bullet.java @@ -0,0 +1,117 @@ +package com.demo.tank.course9; + +import com.demo.tank.enums.Direction; +import com.demo.tank.enums.Group; +import com.demo.tank.util.ResourceManager; + +import java.awt.*; + +public class Bullet extends GameObject { + private int x, y; + private Direction direction; + private static final int SPEED = 10; + public static final int WIDTH = ResourceManager.bulletD.getWidth(); + public static final int HEIGHT = ResourceManager.bulletD.getHeight(); + private boolean live = true; + private Group group = Group.BAD; + Rectangle rect = new Rectangle(); + + public Bullet(int x, int y, Direction direction, Group group) { + this.x = x; + this.y = y; + this.direction = direction; + this.group = group; + + rect.x = this.x; + rect.y = this.y; + rect.width = Bullet.WIDTH; + rect.height = Bullet.HEIGHT; + GameModel.getInstance().add(this); + } + + public void paint(Graphics g){ + if(!live){ + GameModel.getInstance().remove(this); + } + switch (direction){ + case UP: + g.drawImage(ResourceManager.bulletU, x, y, null); + break; + case DOWN: + g.drawImage(ResourceManager.bulletD, x, y, null); + break; + case LEFT: + g.drawImage(ResourceManager.bulletL, x, y, null); + break; + case RIGHT: + g.drawImage(ResourceManager.bulletR, x, y, null); + break; + } + move(); + } + + private void move() { + switch (direction){ + case UP: y -= SPEED; + break; + case DOWN: y += SPEED; + break; + case LEFT: x -= SPEED; + break; + case RIGHT: x += SPEED; + break; + default: + break; + } + if(x < 0 || y < 0 || x > TankFrameV9.GAME_WIDTH || y > TankFrameV9.GAME_HEIGHT){ + live = false; + } + rect.x = this.x; + rect.y = this.y; + } + + public void die() { + this.live = false; + } + + public int getX() { + return x; + } + + public void setX(int x) { + this.x = x; + } + + public int getY() { + return y; + } + + public void setY(int y) { + this.y = y; + } + + public Direction getDirection() { + return direction; + } + + public void setDirection(Direction direction) { + this.direction = direction; + } + + public boolean isLive() { + return live; + } + + public void setLive(boolean live) { + this.live = live; + } + + public Group getGroup() { + return group; + } + + public void setGroup(Group group) { + this.group = group; + } + +} diff --git a/src/com/demo/tank/course9/BulletTankCollider.java b/src/com/demo/tank/course9/BulletTankCollider.java new file mode 100644 index 0000000..d943f8f --- /dev/null +++ b/src/com/demo/tank/course9/BulletTankCollider.java @@ -0,0 +1,25 @@ +package com.demo.tank.course9; + +public class BulletTankCollider implements Collider { + @Override + public void collide(GameObject g1, GameObject g2) { + if(g1 instanceof Bullet && g2 instanceof Tank){ + Bullet b = (Bullet) g1; + Tank t = (Tank) g2; + //关闭队友伤害 + if(b.getGroup() == t.getGroup()) return; + if(b.rect.intersects(t.rect)){ + t.die(); + b.die(); + //爆炸 + int ex = t.getX() + Tank.WIDTH/2 - Explode.WIDTH/2; + int ey = t.getY() + Tank.HEIGHT/2 - Explode.HEIGHT/2; + GameModel.getInstance().add(new Explode(ex, ey)); + } + }else if(g1 instanceof Tank && g2 instanceof Bullet){ + collide(g2, g1); + }else{ + return; + } + } +} diff --git a/src/com/demo/tank/course9/BulletWallCollider.java b/src/com/demo/tank/course9/BulletWallCollider.java new file mode 100644 index 0000000..3071f20 --- /dev/null +++ b/src/com/demo/tank/course9/BulletWallCollider.java @@ -0,0 +1,18 @@ +package com.demo.tank.course9; + +public class BulletWallCollider implements Collider { + @Override + public void collide(GameObject g1, GameObject g2) { + if(g1 instanceof Bullet && g2 instanceof Wall){ + Bullet b = (Bullet) g1; + Wall w = (Wall) g2; + if(b.rect.intersects(w.rect)){ + b.die(); + } + }else if(g1 instanceof Wall && g2 instanceof Bullet){ + collide(g2, g1); + }else{ + return; + } + } +} diff --git a/src/com/demo/tank/course9/Collider.java b/src/com/demo/tank/course9/Collider.java new file mode 100644 index 0000000..7ab6d92 --- /dev/null +++ b/src/com/demo/tank/course9/Collider.java @@ -0,0 +1,5 @@ +package com.demo.tank.course9; + +public interface Collider { + void collide(GameObject g1, GameObject g2); +} diff --git a/src/com/demo/tank/course9/ColliderChain.java b/src/com/demo/tank/course9/ColliderChain.java new file mode 100644 index 0000000..3ed6a94 --- /dev/null +++ b/src/com/demo/tank/course9/ColliderChain.java @@ -0,0 +1,25 @@ +package com.demo.tank.course9; + +import java.util.LinkedList; +import java.util.List; + +public class ColliderChain { + private List colliders = new LinkedList<>(); + + public ColliderChain(){ + add(new BulletTankCollider()); + add(new TankTankCollider()); + add(new BulletWallCollider()); + add(new TankWallCollider()); + } + + public void add(Collider collider){ + colliders.add(collider); + } + + public void collide(GameObject g1, GameObject g2) { + for (Collider c : colliders){ + c.collide(g1, g2); + } + } +} diff --git a/src/com/demo/tank/course9/DefaultFireStrategy.java b/src/com/demo/tank/course9/DefaultFireStrategy.java new file mode 100644 index 0000000..ec2d86d --- /dev/null +++ b/src/com/demo/tank/course9/DefaultFireStrategy.java @@ -0,0 +1,14 @@ +package com.demo.tank.course9; + +import com.demo.tank.enums.Group; +import com.demo.tank.util.Audio; + +public class DefaultFireStrategy implements FireStrategy { + @Override + public void fire(Tank tank) { + int bx = tank.getX() + Tank.WIDTH/2 - Bullet.WIDTH/2; + int by = tank.getY() + Tank.HEIGHT/2 - Bullet.HEIGHT/2; + new Bullet(bx, by, tank.getDir(), tank.getGroup()); + if (tank.getGroup() == Group.GOOD) new Thread(() -> new Audio("audio/tank_fire.wav").play()).start(); + } +} diff --git a/src/com/demo/tank/course9/Explode.java b/src/com/demo/tank/course9/Explode.java new file mode 100644 index 0000000..3a8c16d --- /dev/null +++ b/src/com/demo/tank/course9/Explode.java @@ -0,0 +1,45 @@ +package com.demo.tank.course9; + +import com.demo.tank.util.Audio; +import com.demo.tank.util.ResourceManager; + +import java.awt.*; + +public class Explode extends GameObject { + private int x, y; + public static final int WIDTH = ResourceManager.explodes[0].getWidth(); + public static final int HEIGHT = ResourceManager.explodes[0].getHeight(); + + private int step = 0; + + public Explode(int x, int y) { + this.x = x; + this.y = y; + new Thread(() -> new Audio("audio/explode.wav").play()).start(); + } + + public void paint(Graphics g){ + g.drawImage(ResourceManager.explodes[step++], x, y, null); + if(step >= ResourceManager.explodes.length){ + //播放完爆炸效果图片, remove + GameModel.getInstance().remove(this); + } + } + + public int getX() { + return x; + } + + public void setX(int x) { + this.x = x; + } + + public int getY() { + return y; + } + + public void setY(int y) { + this.y = y; + } + +} diff --git a/src/com/demo/tank/course9/FireStrategy.java b/src/com/demo/tank/course9/FireStrategy.java new file mode 100644 index 0000000..e2c0e41 --- /dev/null +++ b/src/com/demo/tank/course9/FireStrategy.java @@ -0,0 +1,5 @@ +package com.demo.tank.course9; + +public interface FireStrategy { + void fire(Tank tank); +} diff --git a/src/com/demo/tank/course9/FourDirectionFireStrategy.java b/src/com/demo/tank/course9/FourDirectionFireStrategy.java new file mode 100644 index 0000000..0b508d1 --- /dev/null +++ b/src/com/demo/tank/course9/FourDirectionFireStrategy.java @@ -0,0 +1,17 @@ +package com.demo.tank.course9; + +import com.demo.tank.enums.Direction; +import com.demo.tank.enums.Group; +import com.demo.tank.util.Audio; + +public class FourDirectionFireStrategy implements FireStrategy { + @Override + public void fire(Tank tank) { + int bx = tank.getX() + Tank.WIDTH/2 - Bullet.WIDTH/2; + int by = tank.getY() + Tank.HEIGHT/2 - Bullet.HEIGHT/2; + for(Direction direction : Direction.values()){ + new Bullet(bx, by, direction, tank.getGroup()); + } + if (tank.getGroup() == Group.GOOD) new Thread(() -> new Audio("audio/tank_fire.wav").play()).start(); + } +} diff --git a/src/com/demo/tank/course9/GameModel.java b/src/com/demo/tank/course9/GameModel.java new file mode 100644 index 0000000..1e05609 --- /dev/null +++ b/src/com/demo/tank/course9/GameModel.java @@ -0,0 +1,67 @@ +package com.demo.tank.course9; + +import com.demo.tank.enums.Direction; +import com.demo.tank.enums.Group; +import com.demo.tank.util.PropertyManager; + +import java.awt.*; +import java.util.ArrayList; +import java.util.List; + +public class GameModel { + private static final GameModel GM = new GameModel(); + static { + GM.init(); + } + + private List gameObjects = new ArrayList<>(); + +// Collider collider = new BulletTankCollider(); +// Collider collider2 = new TankTankCollider(); + ColliderChain chain = new ColliderChain(); + Tank tank; + + private GameModel(){ + + } + private void init(){ + tank = new Tank(380, 660, Direction.UP, Group.GOOD); + int enemyTankNum = PropertyManager.getInt("enemy.tank.number"); + for(int i = 0; i < enemyTankNum; i++){ + add(new Tank(50 + i*80, 200, Direction.DOWN, Group.BAD)); + } + add(new Wall(150, 150, 200, 50)); + add(new Wall(550, 150, 200, 50)); + add(new Wall(300, 300, 50, 200)); + add(new Wall(550, 300, 50, 200)); + } + + public static GameModel getInstance(){ + return GM; + } + + public void add(GameObject go){ + gameObjects.add(go); + } + + public void remove(GameObject go){ + gameObjects.remove(go); + } + + public void paint(Graphics g){ + tank.paint(g); + + for(int i = 0; i< gameObjects.size(); i++){ + gameObjects.get(i).paint(g); + } + + //碰撞检测 + for(int i = 0; i< gameObjects.size(); i++){ + for(int j=i+1; j< gameObjects.size(); j++){ + GameObject g1 = gameObjects.get(i); + GameObject g2 = gameObjects.get(j); + chain.collide(g1, g2); + } + } + } +} diff --git a/src/com/demo/tank/course9/GameObject.java b/src/com/demo/tank/course9/GameObject.java new file mode 100644 index 0000000..21dce58 --- /dev/null +++ b/src/com/demo/tank/course9/GameObject.java @@ -0,0 +1,8 @@ +package com.demo.tank.course9; + +import java.awt.*; + +public abstract class GameObject { + int x, y; + public abstract void paint(Graphics g); +} diff --git a/src/com/demo/tank/course9/MainV9.java b/src/com/demo/tank/course9/MainV9.java new file mode 100644 index 0000000..38064b2 --- /dev/null +++ b/src/com/demo/tank/course9/MainV9.java @@ -0,0 +1,16 @@ +package com.demo.tank.course9; + +import java.io.IOException; + +public class MainV9 { + public static void main(String[] args) throws InterruptedException, IOException { + TankFrameV9 tf = new TankFrameV9(); + +// new Thread(() -> new Audio("audio/war1.wav").loop()).start(); + + while (true){ + Thread.sleep(50); + tf.repaint(); + } + } +} diff --git a/src/com/demo/tank/course9/Tank.java b/src/com/demo/tank/course9/Tank.java new file mode 100644 index 0000000..2e36d22 --- /dev/null +++ b/src/com/demo/tank/course9/Tank.java @@ -0,0 +1,184 @@ +package com.demo.tank.course9; + +import com.demo.tank.enums.Direction; +import com.demo.tank.enums.Group; +import com.demo.tank.util.ResourceManager; + +import java.awt.*; +import java.util.Random; + +public class Tank extends GameObject { + private int x,y; + int oldX, oldY; + private Direction dir; + private static final int SPEED = 8; + private boolean moving = true; + private boolean living = true; + private Group group = Group.BAD; + public static final int WIDTH = ResourceManager.tankD.getWidth(); + public static final int HEIGHT = ResourceManager.tankD.getHeight(); + private Random random = new Random(); + Rectangle rect = new Rectangle(); + FireStrategy fireStrategy; + + + public Tank(int x, int y, Direction dir, Group group) { + this.x = x; + this.y = y; + this.dir = dir; + this.group = group; + + rect.x = this.x; + rect.y = this.y; + rect.width = Tank.WIDTH; + rect.height = Tank.HEIGHT; + + if(this.group == Group.GOOD) { +// String className = PropertyManager.getString("good.tank.fire.strategy"); + try { + fireStrategy = (FireStrategy) Class.forName("com.demo.tank.course9.FourDirectionFireStrategy").newInstance(); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + }else if(this.group == Group.BAD) fireStrategy = new DefaultFireStrategy(); + } + + @Override + public void paint(Graphics g) { + if(!living) GameModel.getInstance().remove(this); + //根据方向绘制坦克 + switch (dir){ + case UP: + g.drawImage(this.group == Group.GOOD ? ResourceManager.tankU : ResourceManager.badTankU, x, y, null); + break; + case DOWN: + g.drawImage(this.group == Group.GOOD ? ResourceManager.tankD : ResourceManager.badTankD, x, y, null); + break; + case LEFT: + g.drawImage(this.group == Group.GOOD ? ResourceManager.tankL: ResourceManager.badTankL, x, y, null); + break; + case RIGHT: + g.drawImage(this.group == Group.GOOD ? ResourceManager.tankR : ResourceManager.badTankR, x, y, null); + break; + } + move(); + } + + public void move(){ + oldX = x; + oldY = y; + //如果没有移动 return + if(!moving) return; + switch (dir){ + case UP: y -= SPEED; + break; + case DOWN: y += SPEED; + break; + case LEFT: x -= SPEED; + break; + case RIGHT: x += SPEED; + break; + default: + break; + } + if(this.group == Group.BAD) { + if(random.nextInt(10) == 5){ + this.fire(); + } + if(random.nextInt(100) > 95){ + this.randomDirection(); + } + } + + //边界检测 + boundsCheck(); + + rect.x = this.x; + rect.y = this.y; + } + + public void back(){ + this.x = oldX; + this.y = oldY; + } + + private void boundsCheck() { + if(x < 0) x = 0; + if(x > TankFrameV9.GAME_WIDTH - Tank.WIDTH) x = TankFrameV9.GAME_WIDTH - Tank.WIDTH; + if(y < 30) y = 30; //算上菜单条 + if(y > TankFrameV9.GAME_HEIGHT - Tank.HEIGHT) y = TankFrameV9.GAME_HEIGHT - Tank.HEIGHT; + } + + private void randomDirection() { + this.dir = Direction.values()[random.nextInt(4)]; + } + + public void fire() { + fireStrategy.fire(this); + } + + public void die(){ + this.living = false; + } + + public int getX() { + return x; + } + + public void setX(int x) { + this.x = x; + } + + public int getY() { + return y; + } + + public void setY(int y) { + this.y = y; + } + + public Direction getDir() { + return dir; + } + + public void setDir(Direction dir) { + this.dir = dir; + } + + public boolean isMoving() { + return moving; + } + + public void setMoving(boolean moving) { + this.moving = moving; + } + + public boolean isLiving() { + return living; + } + + public void setLiving(boolean living) { + this.living = living; + } + + public Group getGroup() { + return group; + } + + public void setGroup(Group group) { + this.group = group; + } + + public Rectangle getRect() { + return rect; + } + + public void setRect(Rectangle rect) { + this.rect = rect; + } + +} diff --git a/src/com/demo/tank/course9/TankFrameV9.java b/src/com/demo/tank/course9/TankFrameV9.java new file mode 100644 index 0000000..29ec300 --- /dev/null +++ b/src/com/demo/tank/course9/TankFrameV9.java @@ -0,0 +1,113 @@ +package com.demo.tank.course9; + +import com.demo.tank.enums.Direction; + +import java.awt.*; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +public class TankFrameV9 extends Frame { + GameModel gm = GameModel.getInstance(); + public static final int GAME_WIDTH = 1080; + public static final int GAME_HEIGHT = 800; + Image image = null; + + public TankFrameV9(){ + setVisible(true); + setBounds(400, 100 , GAME_WIDTH, GAME_HEIGHT); + setResizable(false); + setTitle("tank war"); + this.addKeyListener(new MyKeyListener()); + this.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + System.exit(0); + } + }); + } + + @Override + public void update(Graphics g) { + if(image == null){ + image = this.createImage(GAME_WIDTH, GAME_HEIGHT); + } + Graphics imgGraphic = image.getGraphics(); + Color color = g.getColor(); + imgGraphic.setColor(Color.BLACK); + imgGraphic.fillRect(0,0, GAME_WIDTH, GAME_HEIGHT); + imgGraphic.setColor(color); + paint(imgGraphic); + g.drawImage(image, 0, 0, null); + } + + @Override + public void paint(Graphics g){ + gm.paint(g); + } + + class MyKeyListener extends KeyAdapter{ + boolean bL = false; + boolean bR = false; + boolean bU = false; + boolean bD = false; + + @Override + public void keyPressed(KeyEvent e) { + switch (e.getKeyCode()){ + case KeyEvent.VK_A: + bL = true; + break; + case KeyEvent.VK_D: + bR = true; + break; + case KeyEvent.VK_W: + bU = true; + break; + case KeyEvent.VK_S: + bD = true; + break; + default: + break; + } + setTankDirection(); + } + + @Override + public void keyReleased(KeyEvent e) { + switch (e.getKeyCode()){ + case KeyEvent.VK_A: + bL = false; + break; + case KeyEvent.VK_D: + bR = false; + break; + case KeyEvent.VK_W: + bU = false; + break; + case KeyEvent.VK_S: + bD = false; + break; + case KeyEvent.VK_SPACE: + gm.tank.fire(); + break; + default: + break; + } + setTankDirection(); + } + + public void setTankDirection(){ + if(!bL && !bR && !bU && !bD){ + gm.tank.setMoving(false); + }else{ + gm.tank.setMoving(true); + if(bL) gm.tank.setDir(Direction.LEFT); + if(bR) gm.tank.setDir(Direction.RIGHT); + if(bU) gm.tank.setDir(Direction.UP); + if(bD) gm.tank.setDir(Direction.DOWN); + } + } + } +} diff --git a/src/com/demo/tank/course9/TankTankCollider.java b/src/com/demo/tank/course9/TankTankCollider.java new file mode 100644 index 0000000..310016a --- /dev/null +++ b/src/com/demo/tank/course9/TankTankCollider.java @@ -0,0 +1,21 @@ +package com.demo.tank.course9; + +import java.util.Random; + +public class TankTankCollider implements Collider { + Random random = new Random(); + @Override + public void collide(GameObject g1, GameObject g2) { + if(g1 instanceof Tank && g2 instanceof Tank){ + Tank t1 = (Tank) g1; + Tank t2 = (Tank) g2; + if(t1.rect.intersects(t2.rect)){ + // simple deal + t1.back(); + t2.back(); + } + }else{ + return; + } + } +} diff --git a/src/com/demo/tank/course9/TankWallCollider.java b/src/com/demo/tank/course9/TankWallCollider.java new file mode 100644 index 0000000..773713d --- /dev/null +++ b/src/com/demo/tank/course9/TankWallCollider.java @@ -0,0 +1,18 @@ +package com.demo.tank.course9; + +public class TankWallCollider implements Collider{ + @Override + public void collide(GameObject g1, GameObject g2) { + if(g1 instanceof Tank && g2 instanceof Wall){ + Tank t = (Tank) g1; + Wall w = (Wall) g2; + if(t.rect.intersects(w.rect)){ + t.back(); + } + }else if(g1 instanceof Wall && g2 instanceof Bullet){ + collide(g2, g1); + }else{ + return; + } + } +} diff --git a/src/com/demo/tank/course9/Wall.java b/src/com/demo/tank/course9/Wall.java new file mode 100644 index 0000000..60910db --- /dev/null +++ b/src/com/demo/tank/course9/Wall.java @@ -0,0 +1,24 @@ +package com.demo.tank.course9; + +import java.awt.*; + +public class Wall extends GameObject{ + int width, height; + Rectangle rect; + + public Wall(int x, int y, int width, int height) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + this.rect = new Rectangle(x, y, width, height); + } + + @Override + public void paint(Graphics g) { + Color c = g.getColor(); + g.setColor(Color.DARK_GRAY); + g.fillRect(x, y, width, height); + g.setColor(c); + } +}