Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
3D Java Game Programming – Episode 14 Building a Java First-Person Shooter Episode 14– Basic Mouse Movement In this episode CHERNO replaces the left-arrow and right-arrow for turning left and right respectively with mouse movements. Events Dispatched by Using the Mouse When you use the mouse by either moving it around or clicking a button or rolling the mouse wheel different events are generated and sent to the Event Dispatcher. Like most events if you are interested in processing them you establish an event listener and set up the corresponding method in your code to perform actions when the event takes place. In the InputHandler class we have already set it up by having it implement the MouseListener and MouseMotionListener. The MouseListener This is the listener to use when you want to obtain events associated with pressing and releasing a mouse button and entering and exiting a component. In order to use we need to have a class (in our case InputHandler) implement this interface and all the methods associated with it. public class InputHandler implements MouseListener, KeyListener, MouseMotionListener, FocusListener { . . . } We don’t actually modify or handle any of the methods associated with this listener in this episode. For completeness sake I will list the methods that got added for this listener: @Override public void mouseClicked(MouseEvent e) { // Invoked when the mouse button has been clicked (pressed or released) // on a component } @Override public void mousePressed(MouseEvent e) { // Invoked when a mouse button has been pressed on a component } @Override public void mouseReleased(MouseEvent e) { // Invoked when a mouse button has been released on a component } @Override public void mouseEntered(MouseEvent e) { 1 3D Java Game Programming – Episode 14 // Invoked when the mouse enters a component } @Override public void mouseExited(MouseEvent e) { // Invoked when the mouse exits a component Processing one of these events requires that we peek into the MouseEvent object provided when one of our methods gets called. The MouseEvent will contain information such as the button involved, the number of clicks, the absolute screen location (using a Point object) where the event took place, the location relative to the component as a Point object, etc. You can find out more information at: http://docs.oracle.com/javase/6/docs/api/java/awt/event/MouseEvent.html. The MouseMotionListener The MouseMotionListener interface is used to receive mouse motion/movement events on a component. The two key method that would be implemented with this interface are the following: @Override public void mouseDragged(MouseEvent e) { // Invoked when a mouse button is pressed on a component and then // dragged } @Override public void mouseMoved(MouseEvent e) { // Invoked when the mouse cursor had been moved onto a component // but no button have been pushed. } As in the MouseListener the MouseEvent object will be used by our application to gather and obtain information about the event specifics. Changes in InputHandler The changes to the InputHandler for this episode are minor. We introduce two new static variables: public static int MouseX; public static int MouseY; The variables MouseX and MouseY are made public static in order to make them easily accessible from other classes in our project. The variables will get updated whenever the mouseMoved() 2 3D Java Game Programming – Episode 14 method is triggered. The method will record form the MouseEvent object e the current location of the mouse in our application window. @Override public void mouseMoved(MouseEvent e) { MouseX = e.getX(); MouseY = e.getY(); } That’s it we are done with the InputHandler class. Changes to Controller.java There were several changes to Controller.java. The two parameters turnLeft and turnRight were removed from the parameter list from the tick() method. In order to provide direct access to them they were converted into public static variables. Table 1 - Controller.java package com.mime.minefront.input; public class Controller { public public public public public public double double double double double double x = 0; z = 0; rotation = 0; xa = 0; za = 0; rotationa = 0;; public static boolean turnLeft = false; public static boolean turnRight = false; public void tick(boolean forward, boolean back, boolean left, boolean right) { double rotationSpeed = 0.02; double walkSpeed = 1; double xMove = 0; double zMove = 0; if (forward) { zMove++; } if (back) { zMove--; } if (left) { xMove--; } 3 3D Java Game Programming – Episode 14 if (right) { xMove++; } if (turnLeft) { rotationa -= rotationSpeed; } if (turnRight) { rotationa += rotationSpeed; } xa += (xMove * Math.cos(rotation) + zMove * Math.sin(rotation)) * walkSpeed; za += (zMove * Math.cos(rotation) - xMove * Math.sin(rotation)) * walkSpeed; x += xa; z += za; xa *= 0.1; za *= 0.1; rotation += rotationa; rotationa *= 0.2; } } Everything else was left alone. Note: The use of all public static variables is considered a bad programming practice/style. It would not be hard to peruse the internet and find many articles that will explain how static variable are considered evil. They go against OO principles since they represent global state and create what is usually an unnecessary dependency between classes. Changes to Game.java This class was changed since it invokes the tick() method in Controller from its own tick() method. Table 2 - Game.java package com.mime.minefront; import java.awt.event.KeyEvent; import com.mime.minefront.input.Controller; public class Game { public int time; 4 3D Java Game Programming – Episode 14 public Controller controls; public Game() { controls = new Controller(); } public void tick(boolean[] key) { time++; boolean forward = key[KeyEvent.VK_W]; boolean back = key[KeyEvent.VK_S]; boolean left = key[KeyEvent.VK_A]; boolean right = key[KeyEvent.VK_D]; //boolean turnLeft = key[KeyEvent.VK_LEFT]; OLD WAY WHEN USING ARROWS TO TURN/ROTATE //boolean turnRight = key[KeyEvent.VK_RIGHT]; OLD WAY WHEN USING ARROWS TO TURN/ROTATE //controls.tick(forward, back, left, right, turnLeft, turnRight); OLD WAY WHEN USING ARROWS TO TURN/ROTATE controls.tick(forward, back, left, right); } } Changes to Display.java The last class to get updated is Display.java. It will track the current (newX) and previous (oldX) location of the mouse to determine if the user is moving left or right which will map on to turnLeft or turnRight, that is update the public static variables in Controller. The Event Dispatcher (remember it runs in its own thread) updates the values for MouseX and MouseY which will be the current location of the mouse (will be updated when the use is moving the mouse). We will update the class by adding variables and updating in the run method. Table 3 - Display.java package com.mime.minefront; import import import import import import import import import java.awt.Canvas; java.awt.Cursor; java.awt.Graphics; java.awt.Insets; java.awt.Point; java.awt.Toolkit; java.awt.image.BufferStrategy; java.awt.image.BufferedImage; java.awt.image.DataBufferInt; import javax.swing.JFrame; import javax.swing.SwingUtilities; import com.mime.minefront.graphics.Screen; import com.mime.minefront.input.Controller; 5 3D Java Game Programming – Episode 14 import com.mime.minefront.input.InputHandler; public class Display extends Canvas implements Runnable{ private static final long serialVersionUID = 1L; public static final int WIDTH = 800; public static final int HEIGHT = 600; public static final String TITLE = "Minefront Pre-Alpha 0.02"; public static final int FRAMES_PER_SECOND = 60; private Thread thread; private boolean running = false; // indicates if the game is running or not private private private private Screen screen; Game game; BufferedImage img; int[] pixels; private InputHandler input; private int newX = 0; private int oldX = 0; public Display() { screen = new Screen(WIDTH, HEIGHT); img = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB); pixels = ((DataBufferInt)img.getRaster().getDataBuffer()).getData(); game = new Game(); input = new InputHandler(); addKeyListener(input); addMouseListener(input); addMouseMotionListener(input); addFocusListener(input); } private void start() { if (running) { return; } running = true; thread = new Thread(this); thread.start(); } private void stop() { if (!running) { return; } running = false; try { 6 3D Java Game Programming – Episode 14 thread.join(); } catch (Exception e) { e.printStackTrace(); System.exit(0); } } private void tick() { game.tick(input.key); } private void render() { BufferStrategy bs = this.getBufferStrategy(); if (bs == null) { createBufferStrategy(3); return; } screen.render(game); for (int i = 0; i < WIDTH * HEIGHT; i++) { pixels[i] = screen.pixels[i]; } Graphics g = bs.getDrawGraphics(); g.drawImage(img, 0, 0, WIDTH, HEIGHT, null); g.dispose(); bs.show(); } @Override public void run() { int frames = 0; // holds the number of frames drawn in one second double unprocessedSeconds = 0; // accumulates time long previousTime = System.nanoTime(); // start counting double secondsPerTick = 1 / (double)FRAMES_PER_SECOND; defining 60 ticks in a second or in other words // We are // declaring that we want 60 frames / second int tickCount = 0; assume 60 ticks signal that we // If 60 ticks (or 1 second) we // should render a frame boolean ticked = false; frame should be rendered // our signal indicator that a while(running) { long currentTime = System.nanoTime(); // holds the time now long passedTime = currentTime - previousTime; // holds the time between now and when stop // watch started 7 3D Java Game Programming – Episode 14 previousTime = currentTime; // remember for the next time check unprocessedSeconds = unprocessedSeconds + (passedTime / 1000000000.0); // time elapsed while (unprocessedSeconds > secondsPerTick) { // Has 1/60 sec elapsed, i.e. a tick tick(); // do it! unprocessedSeconds -= secondsPerTick; // remember the time that went over a tick ticked = true; // signal to render a frame tickCount++; if (tickCount % FRAMES_PER_SECOND == 0) { // approximately a second has passed, output System.out.println(frames + "fps"); // the number of frames //previousTime += 1000; // fudge factor ??? tickCount = 0; frames = 0; } } if (ticked) { render(); // handles rendering things to the screen frames++; ticked = false; } newX = InputHandler.MouseX; if (newX > oldX) { Controller.turnRight = true; } else if (newX < oldX) { Controller.turnLeft = true; } else { Controller.turnLeft = false; Controller.turnRight = false; } oldX = newX; } } public void resizeToInternalSize(JFrame frame, int internalWidth, int internalHeight) { Insets insets = frame.getInsets(); final int newWidth = internalWidth + insets.left + insets.right; final int newHeight = internalHeight + insets.top + insets.bottom; Runnable resize = new Runnable() { public void run() { setSize(newWidth, newHeight); } }; if (SwingUtilities.isEventDispatchThread()) { try { 8 3D Java Game Programming – Episode 14 SwingUtilities.invokeAndWait(resize); } catch (Exception e) { // ignore ...but will be no no if using Sonar! } } else { resize.run(); } validate(); } public static void main(String[] args) { BufferedImage cursor = new BufferedImage(16,16, BufferedImage.TYPE_INT_ARGB); Cursor blank = Toolkit.getDefaultToolkit().createCustomCursor(cursor, new Point(0,0), "blank"); Display game = new Display(); JFrame frame = new JFrame(); frame.add(game); frame.getContentPane().setCursor(blank); frame.setTitle(TITLE); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(WIDTH, HEIGHT); frame.setLocationRelativeTo(null); frame.setResizable(false); frame.setVisible(true); // ensure the display canvas is the right size! game.resizeToInternalSize(frame, WIDTH, HEIGHT); game.start(); } } The determination of whether the user is moving left, right, or not moving the mouse is pretty straightforward. The program looks the same as in the last episode the only difference is how it now responds to mouse movements. 9