Site Search:

PublishingVehicleTrackerDemo.java

<Back


PublishingVehicleTrackerDemo.java
This version just replaced the game engine DelegatingVehicleTracker with PublishingVehicleTracker, since both classes are thread safe, the program continues to work.


/*press arrow key to move, press space key to shoot*/
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.Rectangle2D;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.Timer;

/******* game graphics ********/
public class PublishingVehicleTrackerDemo extends JFrame{
    
    public PublishingVehicleTrackerDemo() {
        initGUI();
    }
    
    private void initGUI() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(10, 10, 400, 400);
        setLayout(new BorderLayout());
        
        Map<String, SafePoint> locations = new HashMap<>();
        for(int i=0; i<5; i++) {
            SafePoint p = new SafePoint(i* GameBoard.TANKWIDTH * 2, i* GameBoard.TANKHEIGHT * 2, 0);
            locations.put("tank"+i, p);
        }
        
        PublishingVehicleTracker mt = new PublishingVehicleTracker(locations);
        
        final GameBoard paintPanel = new GameBoard(mt);
        add(paintPanel, BorderLayout.CENTER);
        paintPanel.startTimer();
        setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new PublishingVehicleTrackerDemo();
            }});
    }
}

class GameBoard extends JPanel implements KeyListener, ActionListener {
    public static final int HEIGHT = 300;
    public static final int WIDTH = 400;
    public static final int TANKWIDTH = 30;
    public static final int TANKHEIGHT = 10;
    public static final int MOVESPEED = 2;
    public static final int TARGETX = 100;
    public static final int TARGETY = 200;
    public static final int DELAY = 70;
    private static int dx = WIDTH / 2;
    private static int dy = HEIGHT;
    private static final Rectangle2D yourtank = new Rectangle2D.Double(dx, dy, TANKWIDTH,
            TANKHEIGHT); //reference to Rectangle2D is final, but the object's states can change
    //{System.out.println(javax.swing.SwingUtilities.isEventDispatchThread());}
    private static final Rectangle2D laserBeam = new Rectangle2D.Double(-10, -10, 0,
            0);
    private volatile PublishingVehicleTracker mt//thread safe
    private static final JButton startButton = new JButton("Start");

    public GameBoard(PublishingVehicleTracker mt) {
        this.addKeyListener(this);
        this.setBackground(Color.white);
        this.setFocusable(true);
        this.mt = mt;
        setDoubleBuffered(true);
        startButton.setText("Start");
        startButton.setActionCommand("Start");
        startButton.addActionListener(this);
        this.add(startButton);
    }
    
    //avoid starting timer in constructor (partially constructed this object).
    public void startTimer() {
        new Timer(DELAY/2, this).start();
    }
    
    private class Animator extends SwingWorker<Void, Void> {
        @Override
        protected Void doInBackground() throws Exception {
            while(!isCancelled()) {
                Thread.sleep(DELAY);
                //System.out.println(javax.swing.SwingUtilities.isEventDispatchThread());
                for(String id: mt.getLocations().keySet()) {
                    SafePoint p = mt.getLocation(id);
                    if(p.get()[2] == 0) {
                        p.set(p.get()[0] + GameBoard.MOVESPEED, p.get()[1] + GameBoard.MOVESPEED * 2, 0);
                    }
                }
            }
            return null;
        }
    }
    
    @Override
    public void actionPerformed(ActionEvent e) {
        if(e.getActionCommand() == "Start") {
            new Animator().execute();
            startButton.setEnabled(false);
            startButton.setVisible(false);
        }
        //System.out.println(javax.swing.SwingUtilities.isEventDispatchThread());
        repaint();
    }
    
    @Override
    protected void paintComponent(Graphics grphcs) {
        super.paintComponent(grphcs);
        cleanDead();
        Graphics2D gr = (Graphics2D) grphcs;
        gr.draw(yourtank);
        gr.draw(laserBeam);
        for(SafePoint p: mt.getLocations().values()) {
            if (p.get()[2] == 0)
                drawTank(grphcs, p.get()[0], p.get()[1]);
        }
        repaint();
        Toolkit.getDefaultToolkit().sync(); //smooth on Linux systems that buffer graphics events.
    }

    private void cleanDead() {
        for(String id: mt.getLocations().keySet()) {
            SafePoint p = mt.getLocation(id);
            if (overlaps(p.get()[0], p.get()[1], TANKWIDTH, TANKHEIGHT, laserBeam)) {
                mt.setLocation(id, -10, -10, 1);
            }
            if (overlaps(p.get()[0], p.get()[1], TANKWIDTH, TANKHEIGHT, yourtank)) {
                this.removeKeyListener(this);
            }
        }
    }

    private boolean overlaps(int x, int y, int width, int height, Rectangle2D r) {
        return x < r.getX() + r.getWidth() && x + width > r.getX()
                && y < r.getY() + r.getHeight() && y + height > r.getY();
    }
    
    private void drawTank(Graphics g, int x, int y) {
        g.setColor(Color.yellow);
        g.draw3DRect(x, y, TANKWIDTH, TANKHEIGHT, true);
    }

    @Override
    public void keyTyped(KeyEvent e) {
        System.out.println(e.getKeyCode());
        shoot();
        repaint();
    }

    @Override
    public void keyPressed(KeyEvent e) {
        moveRec(e);
        repaint();
    }

    @Override
    public void keyReleased(KeyEvent e) {
        laserBeam.setRect(dx + TANKWIDTH/2, -10, 0, 0);  //hide it
        repaint();
    }

    public void shoot() {
        laserBeam.setRect(dx + TANKWIDTH/2, 0, 2, dy);
    }

    public void moveRec(KeyEvent evt) {
        switch (evt.getKeyCode()) {
        case KeyEvent.VK_LEFT:
            dx -= MOVESPEED;
            yourtank.setRect(dx, dy, TANKWIDTH, TANKHEIGHT);
            break;
        case KeyEvent.VK_RIGHT:
            dx += MOVESPEED;
            yourtank.setRect(dx, dy, TANKWIDTH, TANKHEIGHT);
            break;
        case KeyEvent.VK_UP:
            dy -= MOVESPEED;
            yourtank.setRect(dx, dy, TANKWIDTH, TANKHEIGHT);
            break;
        case KeyEvent.VK_DOWN:
            if (dy < HEIGHT)
                dy += MOVESPEED;
            yourtank.setRect(dx, dy, TANKWIDTH, TANKHEIGHT);
            break;
        }
    }
}

/*****Game Engine********/

class PublishingVehicleTracker {
    private final Map<String, SafePoint> locations;
    private final Map<String, SafePoint> unmodifiableMap;

    public PublishingVehicleTracker(Map<String, SafePoint> locations) {
        this.locations = new ConcurrentHashMap<String, SafePoint>(locations);
        this.unmodifiableMap = Collections.unmodifiableMap(this.locations);
    }

    public Map<String, SafePoint> getLocations() {
        return unmodifiableMap;
    }

    public SafePoint getLocation(String id) {
        return locations.get(id);
    }

    public void setLocation(String id, int x, int y, int alive) {
        if (!locations.containsKey(id))
            throw new IllegalArgumentException("invalid vehicle name: " + id);
        locations.get(id).set(x, y, alive);
    }
}

class SafePoint {
    private int x, y, alive;

    private SafePoint(int[] a) {
        this(a[0], a[1], a[2]);
    }

    public SafePoint(SafePoint p) {
        this(p.get());
    }

    public SafePoint(int x, int y, int alive) {
        this.set(x, y, alive);
    }

    public synchronized int[] get() {
        return new int[]{x, y, alive};
    }

    public synchronized void set(int x, int y, int alive) {
        this.x = x;
        this.y = y;
        this.alive = alive;
    }

}