Download Episode 14 - brainycode

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts
no text concepts found
Transcript
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