I've been working my way through Developing Games in Java by David Brackeen and have come across a problem. I am modifying the game and pulling it apart to better understand how it works. I have no idea however how to add a menu like the one that was demonstrated in chapter Chapter 3. Any guidance is appreciated.
I have been looking in the ScreenManager and the GameManager for an idea on how to do this. I know how the game gets the screen size, I just need a push on how to get a menu to display.
Here is the code:
ScreenManager.java
package com.brackeen.javagamebook.graphics;
import java.awt.*;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.lang.reflect.InvocationTargetException;
import javax.swing.JFrame;
import javax.swing.JLabel;
/**
The ScreenManager class manages initializing and displaying
full screen graphics modes.
*/
public class ScreenManager {
private GraphicsDevice device;
/**
Creates a new ScreenManager object.
*/
public ScreenManager() {
GraphicsEnvironment environment =
GraphicsEnvironment.getLocalGraphicsEnvironment();
device = environment.getDefaultScreenDevice();
}
/**
Returns a list of compatible display modes for the
default device on the system.
*/
public DisplayMode[] getCompatibleDisplayModes() {
return device.getDisplayModes();
}
/**
Returns the first compatible mode in a list of modes.
Returns null if no modes are compatible.
*/
public DisplayMode findFirstCompatibleMode(
DisplayMode modes[])
{
DisplayMode goodModes[] = device.getDisplayModes();
for (int i = 0; i < modes.length; i++) {
for (int j = 0; j < goodModes.length; j++) {
if (displayModesMatch(modes[i], goodModes[j])) {
return modes[i];
}
}
}
return null;
}
/**
Returns the current display mode.
*/
public DisplayMode getCurrentDisplayMode() {
return device.getDisplayMode();
}
/**
Determines if two display modes "match". Two display
modes match if they have the same resolution, bit depth,
and refresh rate. The bit depth is ignored if one of the
modes has a bit depth of DisplayMode.BIT_DEPTH_MULTI.
Likewise, the refresh rate is ignored if one of the
modes has a refresh rate of
DisplayMode.REFRESH_RATE_UNKNOWN.
*/
public boolean displayModesMatch(DisplayMode mode1,
DisplayMode mode2)
{
if (mode1.getWidth() != mode2.getWidth() ||
mode1.getHeight() != mode2.getHeight())
{
return false;
}
if (mode1.getBitDepth() != DisplayMode.BIT_DEPTH_MULTI &&
mode2.getBitDepth() != DisplayMode.BIT_DEPTH_MULTI &&
mode1.getBitDepth() != mode2.getBitDepth())
{
return false;
}
if (mode1.getRefreshRate() !=
DisplayMode.REFRESH_RATE_UNKNOWN &&
mode2.getRefreshRate() !=
DisplayMode.REFRESH_RATE_UNKNOWN &&
mode1.getRefreshRate() != mode2.getRefreshRate())
{
return false;
}
return true;
}
/**
Enters full screen mode and changes the display mode.
If the specified display mode is null or not compatible
with this device, or if the display mode cannot be
changed on this system, the current display mode is used.
<p>
The display uses a BufferStrategy with 2 buffers.
*/
public void setFullScreen(DisplayMode displayMode) {
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setUndecorated(true);
frame.setIgnoreRepaint(true);
frame.setResizable(false);
device.setFullScreenWindow(frame);
if (displayMode != null &&
device.isDisplayChangeSupported())
{
try {
device.setDisplayMode(displayMode);
}
catch (IllegalArgumentException ex) { }
// fix for mac os x
frame.setSize(displayMode.getWidth(),
displayMode.getHeight());
}
// avoid potential deadlock in 1.4.1_02
try {
EventQueue.invokeAndWait(new Runnable() {
public void run() {
frame.createBufferStrategy(2);
}
});
}
catch (InterruptedException ex) {
// ignore
}
catch (InvocationTargetException ex) {
// ignore
}
}
/**
Gets the graphics context for the display. The
ScreenManager uses double buffering, so applications must
call update() to show any graphics drawn.
<p>
The application must dispose of the graphics object.
*/
public Graphics2D getGraphics() {
Window window = device.getFullScreenWindow();
if (window != null) {
BufferStrategy strategy = window.getBufferStrategy();
return (Graphics2D)strategy.getDrawGraphics();
}
else {
return null;
}
}
/**
Updates the display.
*/
public void update() {
Window window = device.getFullScreenWindow();
if (window != null) {
BufferStrategy strategy = window.getBufferStrategy();
if (!strategy.contentsLost()) {
strategy.show();
}
}
// Sync the display on some systems.
// (on Linux, this fixes event queue problems)
Toolkit.getDefaultToolkit().sync();
}
/**
Returns the window currently used in full screen mode.
Returns null if the device is not in full screen mode.
*/
public JFrame getFullScreenWindow() {
return (JFrame)device.getFullScreenWindow();
}
/**
Returns the width of the window currently used in full
screen mode. Returns 0 if the device is not in full
screen mode.
*/
public int getWidth() {
Window window = device.getFullScreenWindow();
if (window != null) {
return window.getWidth();
}
else {
return 0;
}
}
/**
Returns the height of the window currently used in full
screen mode. Returns 0 if the device is not in full
screen mode.
*/
public int getHeight() {
Window window = device.getFullScreenWindow();
if (window != null) {
return window.getHeight();
}
else {
return 0;
}
}
/**
Restores the screen's display mode.
*/
public void restoreScreen() {
Window window = device.getFullScreenWindow();
if (window != null) {
window.dispose();
}
device.setFullScreenWindow(null);
}
/**
Creates an image compatible with the current display.
*/
public BufferedImage createCompatibleImage(int w, int h,
int transparancy)
{
Window window = device.getFullScreenWindow();
if (window != null) {
GraphicsConfiguration gc =
window.getGraphicsConfiguration();
return gc.createCompatibleImage(w, h, transparancy);
}
return null;
}
}
GameManager.java
package com.brackeen.javagamebook.tilegame;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.util.Iterator;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
import javax.sound.sampled.AudioFormat;
import com.brackeen.javagamebook.graphics.*;
import com.brackeen.javagamebook.sound.*;
import com.brackeen.javagamebook.input.*;
import com.brackeen.javagamebook.test.GameCore;
import com.brackeen.javagamebook.tilegame.sprites.*;
/**
GameManager manages all parts of the game.
*/
public class GameManager extends GameCore {
public static void main(String[] args) {
new GameManager().run();
}
// uncompressed, 44100Hz, 16-bit, mono, signed, little-endian
private static final AudioFormat PLAYBACK_FORMAT =
new AudioFormat(44100, 16, 1, true, false);
private static final int DRUM_TRACK = 1;
public static final float GRAVITY = 0.002f;
private Point pointCache = new Point();
private TileMap map;
private MidiPlayer midiPlayer;
private SoundManager soundManager;
private ResourceManager resourceManager;
private Sound prizeSound;
private Sound boopSound;
private Sound jacksound;
private InputManager inputManager;
private TileMapRenderer renderer;
public Boolean panimation = false;
public Boolean playing = false;
private int points;
private GameAction moveLeft;
private GameAction moveRight;
private GameAction jump;
private GameAction exit;
private GameAction pause;
public void init() {
super.init();
// set up input manager
initInput();
// start resource manager
resourceManager = new ResourceManager(
screen.getFullScreenWindow().getGraphicsConfiguration());
// load resources
renderer = new TileMapRenderer();
renderer.setBackground(
resourceManager.loadImage("background.png"));
// load first map
map = resourceManager.loadNextMap();
// load sounds
soundManager = new SoundManager(PLAYBACK_FORMAT);
prizeSound = soundManager.getSound("sounds/prizes.wav");
boopSound = soundManager.getSound("sounds/boop2.wav");
jacksound = soundManager.getSound("sounds/jack.wav");
// start music
midiPlayer = new MidiPlayer();
Sequence sequence =
midiPlayer.getSequence("sounds/music.midi");
midiPlayer.play(sequence, true);
toggleDrumPlayback();
}
/**
Closes any resurces used by the GameManager.
*/
public void stop() {
super.stop();
midiPlayer.close();
soundManager.close();
}
public void pause() {
if(!panimation){
panimation = true;
soundManager.setPaused(true);
midiPlayer.setPaused(true);
}
else{
panimation = false;
soundManager.setPaused(false);
midiPlayer.setPaused(false);
}
}
private void initInput() {
moveLeft = new GameAction("moveLeft");
moveRight = new GameAction("moveRight");
jump = new GameAction("jump",
GameAction.DETECT_INITAL_PRESS_ONLY);
exit = new GameAction("exit",
GameAction.DETECT_INITAL_PRESS_ONLY);
pause = new GameAction("pause",
GameAction.DETECT_INITAL_PRESS_ONLY);
inputManager = new InputManager(
screen.getFullScreenWindow());
inputManager.setCursor(InputManager.INVISIBLE_CURSOR);
inputManager.mapToKey(moveLeft, KeyEvent.VK_LEFT);
inputManager.mapToKey(moveRight, KeyEvent.VK_RIGHT);
inputManager.mapToKey(jump, KeyEvent.VK_SPACE);
inputManager.mapToKey(exit, KeyEvent.VK_ESCAPE);
inputManager.mapToKey(pause, KeyEvent.VK_F1);
}
private void checkInput(long elapsedTime) {
if (exit.isPressed()) {
stop();
String spoints = Integer.toString(points);
System.out.println("Points: " + spoints);
}
if (pause.isPressed()){
pause();
}
Player player = (Player)map.getPlayer();
if (player.isAlive()) {
float velocityX = 0;
if (moveLeft.isPressed()) {
velocityX-=player.getMaxSpeed();
}
if (moveRight.isPressed()) {
velocityX+=player.getMaxSpeed();
}
if (jump.isPressed()) {
player.jump(false);
}
player.setVelocityX(velocityX);
}
}
public void draw(Graphics2D g) {
renderer.draw(g, map,
screen.getWidth(), screen.getHeight());
}
/**
Gets the current map.
*/
public TileMap getMap() {
return map;
}
/**
Turns on/off drum playback in the midi music (track 1).
*/
public void toggleDrumPlayback() {
Sequencer sequencer = midiPlayer.getSequencer();
if (sequencer != null) {
sequencer.setTrackMute(DRUM_TRACK,
!sequencer.getTrackMute(DRUM_TRACK));
}
}
/**
Gets the tile that a Sprites collides with. Only the
Sprite's X or Y should be changed, not both. Returns null
if no collision is detected.
*/
public Point getTileCollision(Sprite sprite,
float newX, float newY)
{
float fromX = Math.min(sprite.getX(), newX);
float fromY = Math.min(sprite.getY(), newY);
float toX = Math.max(sprite.getX(), newX);
float toY = Math.max(sprite.getY(), newY);
// get the tile locations
int fromTileX = TileMapRenderer.pixelsToTiles(fromX);
int fromTileY = TileMapRenderer.pixelsToTiles(fromY);
int toTileX = TileMapRenderer.pixelsToTiles(
toX + sprite.getWidth() - 1);
int toTileY = TileMapRenderer.pixelsToTiles(
toY + sprite.getHeight() - 1);
// check each tile for a collision
for (int x=fromTileX; x<=toTileX; x++) {
for (int y=fromTileY; y<=toTileY; y++) {
if (x < 0 || x >= map.getWidth() ||
map.getTile(x, y) != null)
{
// collision found, return the tile
pointCache.setLocation(x, y);
return pointCache;
}
}
}
// no collision found
return null;
}
/**
Checks if two Sprites collide with one another. Returns
false if the two Sprites are the same. Returns false if
one of the Sprites is a Creature that is not alive.
*/
public boolean isCollision(Sprite s1, Sprite s2) {
// if the Sprites are the same, return false
if (s1 == s2) {
return false;
}
// if one of the Sprites is a dead Creature, return false
if (s1 instanceof Creature && !((Creature)s1).isAlive()) {
return false;
}
if (s2 instanceof Creature && !((Creature)s2).isAlive()) {
return false;
}
// get the pixel location of the Sprites
int s1x = Math.round(s1.getX());
int s1y = Math.round(s1.getY());
int s2x = Math.round(s2.getX());
int s2y = Math.round(s2.getY());
// check if the two sprites' boundaries intersect
return (s1x < s2x + s2.getWidth() &&
s2x < s1x + s1.getWidth() &&
s1y < s2y + s2.getHeight() &&
s2y < s1y + s1.getHeight());
}
/**
Gets the Sprite that collides with the specified Sprite,
or null if no Sprite collides with the specified Sprite.
*/
public Sprite getSpriteCollision(Sprite sprite) {
// run through the list of Sprites
Iterator i = map.getSprites();
while (i.hasNext()) {
Sprite otherSprite = (Sprite)i.next();
if (isCollision(sprite, otherSprite)) {
// collision found, return the Sprite
return otherSprite;
}
}
// no collision found
return null;
}
/**
Updates Animation, position, and velocity of all Sprites
in the current map.
*/
public void update(long elapsedTime) {
Creature player = (Creature)map.getPlayer();
// player is dead! start map over
if (player.getState() == Creature.STATE_DEAD) {
map = resourceManager.reloadMap();
return;
}
// get keyboard/mouse input
checkInput(elapsedTime);
if (!panimation) { //If game is paused, it stops the updating of the animation.
// update player
updateCreature(player, elapsedTime);
player.update(elapsedTime);
// update other sprites
Iterator i = map.getSprites();
while (i.hasNext()) {
Sprite sprite = (Sprite)i.next();
if (sprite instanceof Creature) {
Creature creature = (Creature)sprite;
if (creature.getState() == Creature.STATE_DEAD) {
i.remove();
}
else {
updateCreature(creature, elapsedTime);
}
}
// normal update
sprite.update(elapsedTime);
}
}
}
/**
Updates the creature, applying gravity for creatures that
aren't flying, and checks collisions.
*/
private void updateCreature(Creature creature,
long elapsedTime)
{
// apply gravity
if (!creature.isFlying()) {
creature.setVelocityY(creature.getVelocityY() +
GRAVITY * elapsedTime);
}
// change x
float dx = creature.getVelocityX();
float oldX = creature.getX();
float newX = oldX + dx * elapsedTime;
Point tile =
getTileCollision(creature, newX, creature.getY());
if (tile == null) {
creature.setX(newX);
}
else {
// line up with the tile boundary
if (dx > 0) {
creature.setX(
TileMapRenderer.tilesToPixels(tile.x) -
creature.getWidth());
}
else if (dx < 0) {
creature.setX(
TileMapRenderer.tilesToPixels(tile.x + 1));
}
creature.collideHorizontal();
}
if (creature instanceof Player) {
checkPlayerCollision((Player)creature, false);
}
// change y
float dy = creature.getVelocityY();
float oldY = creature.getY();
float newY = oldY + dy * elapsedTime;
tile = getTileCollision(creature, creature.getX(), newY);
if (tile == null) {
creature.setY(newY);
}
else {
// line up with the tile boundary
if (dy > 0) {
creature.setY(
TileMapRenderer.tilesToPixels(tile.y) -
creature.getHeight());
}
else if (dy < 0) {
creature.setY(
TileMapRenderer.tilesToPixels(tile.y + 1));
}
creature.collideVertical();
}
if (creature instanceof Player) {
boolean canKill = (oldY < creature.getY());
checkPlayerCollision((Player)creature, canKill);
}
}
/**
Checks for Player collision with other Sprites. If
canKill is true, collisions with Creatures will kill
them.
*/
public void checkPlayerCollision(Player player,
boolean canKill)
{
if (!player.isAlive()) {
return;
}
// check for player collision with other sprites
Sprite collisionSprite = getSpriteCollision(player);
if (collisionSprite instanceof PowerUp) {
acquirePowerUp((PowerUp)collisionSprite);
}
else if (collisionSprite instanceof Creature) {
Creature badguy = (Creature)collisionSprite;
if (canKill) {
// kill the badguy and make player bounce
soundManager.play(boopSound);
badguy.setState(Creature.STATE_DYING);
player.setY(badguy.getY() - player.getHeight());
player.jump(true);
}
else {
// player dies!
player.setState(Creature.STATE_DYING);
points = 0;
}
}
}
/**
Gives the player the speicifed power up and removes it
from the map.
*/
public void acquirePowerUp(PowerUp powerUp) {
// remove it from the map
map.removeSprite(powerUp);
Player player = (Player)map.getPlayer();
if (powerUp instanceof PowerUp.Star) {
// do something here, like give the player points
soundManager.play(prizeSound);
points = points + 5;
}
else if (powerUp instanceof PowerUp.Music) {
// change the music
player.setMaxSpeed();
if (! playing){
soundManager.play(jacksound);
playing = true;
}
else
{
//Do nothing
}
toggleDrumPlayback();
}
else if (powerUp instanceof PowerUp.Goal) {
// advance to next map
soundManager.play(prizeSound,
new EchoFilter(2000, .7f), false);
map = resourceManager.loadNextMap();
}
}
}
The book has an example menu in the chapter.
MenuTest.java
package com.brackeen.javagamebook.tilegame;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import com.brackeen.javagamebook.graphics.*;
import com.brackeen.javagamebook.sound.*;
import com.brackeen.javagamebook.input.*;
import com.brackeen.javagamebook.test.GameCore;
import com.brackeen.javagamebook.tilegame.sprites.*;
/**
Extends the InputManagerTest demo and adds Swing buttons
for pause, config and quit.
*/
public class Menu extends InputManager
implements ActionListener
{
public static void main(String[] args) {
new Menu().run();
}
protected GameAction configAction;
private JButton playButton;
private JButton configButton;
private JButton quitButton;
private JButton pauseButton;
private JPanel playButtonSpace;
public void init() {
super.init();
// make sure Swing components don't paint themselves
NullRepaintManager.install();
// create an addtional GameAction for "config"
configAction = new GameAction("config");
// create buttons
quitButton = createButton("quit", "Quit");
playButton = createButton("play", "Continue");
pauseButton = createButton("pause", "Pause");
configButton = createButton("config", "Change Settings");
// create the space where the play/pause buttons go.
playButtonSpace = new JPanel();
playButtonSpace.setOpaque(false);
playButtonSpace.add(pauseButton);
JFrame frame = super.screen.getFullScreenWindow();
Container contentPane = frame.getContentPane();
// make sure the content pane is transparent
if (contentPane instanceof JComponent) {
((JComponent)contentPane).setOpaque(false);
}
// add components to the screen's content pane
contentPane.setLayout(new FlowLayout(FlowLayout.LEFT));
contentPane.add(playButtonSpace);
contentPane.add(configButton);
contentPane.add(quitButton);
// explicitly layout components (needed on some systems)
frame.validate();
}
/**
Extends InputManagerTest's functionality to draw all
Swing components.
*/
public void draw(Graphics2D g) {
super.draw(g);
JFrame frame = super.screen.getFullScreenWindow();
// the layered pane contains things like popups (tooltips,
// popup menus) and the content pane.
frame.getLayeredPane().paintComponents(g);
}
/**
Changes the pause/play button whenever the pause state
changes.
*/
public void setPaused(boolean p) {
super.setPaused(p);
playButtonSpace.removeAll();
if (isPaused()) {
playButtonSpace.add(playButton);
}
else {
playButtonSpace.add(pauseButton);
}
}
/**
Called by the AWT event dispatch thread when a button is
pressed.
*/
public void actionPerformed(ActionEvent e) {
Object src = e.getSource();
if (src == quitButton) {
// fire the "exit" gameAction
super.exit.tap();
}
else if (src == configButton) {
// doesn't do anything (for now)
configAction.tap();
}
else if (src == playButton || src == pauseButton) {
// fire the "pause" gameAction
super.pause.tap();
}
}
/**
Creates a Swing JButton. The image used for the button is
located at "../images/menu/" + name + ".png". The image is
modified to create a "default" look (translucent) and a
"pressed" look (moved down and to the right).
<p>The button doesn't use Swing's look-and-feel and
instead just uses the image.
*/
public JButton createButton(String name, String toolTip) {
// load the image
String imagePath = "../images/menu/" + name + ".png";
ImageIcon iconRollover = new ImageIcon(imagePath);
int w = iconRollover.getIconWidth();
int h = iconRollover.getIconHeight();
// get the cursor for this button
Cursor cursor =
Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
// make translucent default image
Image image = screen.createCompatibleImage(w, h,
Transparency.TRANSLUCENT);
Graphics2D g = (Graphics2D)image.getGraphics();
Composite alpha = AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, .5f);
g.setComposite(alpha);
g.drawImage(iconRollover.getImage(), 0, 0, null);
g.dispose();
ImageIcon iconDefault = new ImageIcon(image);
// make a pressed iamge
image = screen.createCompatibleImage(w, h,
Transparency.TRANSLUCENT);
g = (Graphics2D)image.getGraphics();
g.drawImage(iconRollover.getImage(), 2, 2, null);
g.dispose();
ImageIcon iconPressed = new ImageIcon(image);
// create the button
JButton button = new JButton();
button.addActionListener(this);
button.setIgnoreRepaint(true);
button.setFocusable(false);
button.setToolTipText(toolTip);
button.setBorder(null);
button.setContentAreaFilled(false);
button.setCursor(cursor);
button.setIcon(iconDefault);
button.setRolloverIcon(iconRollover);
button.setPressedIcon(iconPressed);
return button;
}
}
Related
I am making a game where a badGuy AI is trying to chase me while I use arrow keys to move away. In my main I have a try catch block for if the player attempts to exit the screen border. For some reason however when I click on start I get the exception which I have a system print out saying "GAME OVER, you exited the map". Without my reCalcPath method in the badguy class this does not happen so it must be an issue within this method.
For this method I have a map array. This array is a 40x40 boolean array of 20x20 pixels/cells/squares which states true if I have clicked on that cell position if previously false, painting a white square and visa versa. Now I thought, check the cell position of the badGuy, then check all of his neighbouring cells, if the state of that cell is false i.e. no cell is painted (which means there is no wall blocking him in this sense), then check for the distance between him and my player. I use a Euclidean distance approach by treating xPlayer-xbadGuy, yPLayer-xBadGuy as the opposite and adjacent sides of a triangle. Using pythagoras I get the hypotenuse. Do this for each neighbouring cell, the one with the smallest hypotenuse means shortest distance. Now this is not working at all as when its called the game crashes. Ignore the move method as that is irrelevant if recalcpath won't work
Main
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.image.*;
import java.io.*;
public class AStarMaze extends JFrame implements Runnable, MouseListener, MouseMotionListener, KeyListener {
// member data
private boolean isInitialised = false;
private BufferStrategy strategy;
private Graphics offscreenBuffer;
public boolean map[][] = new boolean[40][40];
private boolean isGameRunning = false;
private BadGuy badguy;
private Player player;
private int startI, startJ;
private int endI, endJ;
private String pFilePath, bgFilePath;
// constructor
public AStarMaze () {
//Display the window, centred on the screen
Dimension screensize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
int x = screensize.width/2 - 400;
int y = screensize.height/2 - 400;
setBounds(x, y, 800, 800);
setVisible(true);
this.setTitle("A* Pathfinding Demo");
bgFilePath = "C:\\Users\\brads\\IdeaProjects\\PathfindingAssignment\\src\\badguy.png";
pFilePath = "C:\\Users\\brads\\IdeaProjects\\PathfindingAssignment\\src\\player.png";
// load raster graphics and instantiate game objects
ImageIcon icon = new ImageIcon(bgFilePath);
Image img = icon.getImage();
badguy = new BadGuy(img);
icon = new ImageIcon(pFilePath);
img = icon.getImage();
player = new Player(img);
// create and start our animation thread
Thread t = new Thread(this);
t.start();
// initialise double-buffering
createBufferStrategy(2);
strategy = getBufferStrategy();
offscreenBuffer = strategy.getDrawGraphics();
// register the Jframe itself to receive mouse and keyboard events
addMouseListener(this);
addMouseMotionListener(this);
addKeyListener(this);
// initialise the map state
for (x=0;x<40;x++) {
for (y=0;y<40;y++) {
map[x][y]=false;
}
}
isInitialised = true;
}
public boolean[][] getMap(){
return map;
}
// thread's entry point
public void run() {
long loops=0;
while ( 1==1 ) {
// 1: sleep for 1/5 sec
try {
Thread.sleep(100);
} catch (InterruptedException e) { }
try {
// 2: animate game objects
if (isGameRunning) {
loops++;
player.move(map); // player moves every frame
if (loops % 3 == 0) // badguy moves once every 3 frames
badguy.reCalcPath(map,player.x,player.y);
// badguy.move(map, player.x, player.y);
}
// 3: force an application repaint
this.repaint();
}
catch(IndexOutOfBoundsException e){
System.out.println("GAME OVER, you exited the map");
System.exit(-1);
}
}
}
private void loadMaze() {
String filename = "C:\\Users\\brads\\IdeaProjects\\PathfindingAssignment\\maze.txt";
String textinput = null;
try {
BufferedReader reader = new BufferedReader(new FileReader(filename));
textinput = reader.readLine();
reader.close();
}
catch (IOException e) { }
if (textinput!=null) {
for (int x=0;x<40;x++) {
for (int y=0;y<40;y++) {
map[x][y] = (textinput.charAt(x*40+y)=='1');
}
}
}
}
private void saveMaze() {
// pack maze into a string
String outputtext="";
for (int x=0;x<40;x++) {
for (int y=0;y<40;y++) {
if (map[x][y])
outputtext+="1";
else
outputtext+="0";
}
}
try {
String filename = "C:\\Users\\brads\\IdeaProjects\\PathfindingAssignment\\maze.txt";
BufferedWriter writer = new BufferedWriter(new FileWriter(filename));
writer.write(outputtext);
writer.close();
}
catch (IOException e) { }
}
// mouse events which must be implemented for MouseListener
public void mousePressed(MouseEvent e) {
if (!isGameRunning) {
// was the click on the 'start button'?
int x = e.getX();
int y = e.getY();
if (x>=15 && x<=85 && y>=40 && y<=70) {
isGameRunning=true;
return;
}
// or the 'load' button?
if (x>=315 && x<=385 && y>=40 && y<=70) {
loadMaze();
return;
}
// or the 'save' button?
if (x>=415 && x<=485 && y>=40 && y<=70) {
saveMaze();
return;
}
}
// determine which cell of the gameState array was clicked on
int x = e.getX()/20;
int y = e.getY()/20;
// toggle the state of the cell
map[x][y] = !map[x][y];
// throw an extra repaint, to get immediate visual feedback
this.repaint();
// store mouse position so that each tiny drag doesn't toggle the cell
// (see mouseDragged method below)
prevx=x;
prevy=y;
}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mouseClicked(MouseEvent e) {}
//
// mouse events which must be implemented for MouseMotionListener
public void mouseMoved(MouseEvent e) {}
// mouse position on previous mouseDragged event
// must be member variables for lifetime reasons
int prevx=-1, prevy=-1;
public void mouseDragged(MouseEvent e) {
// determine which cell of the gameState array was clicked on
// and make sure it has changed since the last mouseDragged event
int x = e.getX()/20;
int y = e.getY()/20;
if (x!=prevx || y!=prevy) {
// toggle the state of the cell
map[x][y] = !map[x][y];
// throw an extra repaint, to get immediate visual feedback
this.repaint();
// store mouse position so that each tiny drag doesn't toggle the cell
prevx=x;
prevy=y;
}
}
//
// Keyboard events
public void keyPressed(KeyEvent e) {
if (e.getKeyCode()==KeyEvent.VK_LEFT)
player.setXSpeed(-1);
else if (e.getKeyCode()==KeyEvent.VK_RIGHT)
player.setXSpeed(1);
else if (e.getKeyCode()==KeyEvent.VK_UP)
player.setYSpeed(-1);
else if (e.getKeyCode()==KeyEvent.VK_DOWN)
player.setYSpeed(1);
}
public void keyReleased(KeyEvent e) {
if (e.getKeyCode()==KeyEvent.VK_LEFT || e.getKeyCode()==KeyEvent.VK_RIGHT)
player.setXSpeed(0);
else if (e.getKeyCode()==KeyEvent.VK_UP || e.getKeyCode()==KeyEvent.VK_DOWN)
player.setYSpeed(0);
}
public void keyTyped(KeyEvent e) { }
//
// application's paint method
public void paint(Graphics g) {
if (!isInitialised)
return;
g = offscreenBuffer; // draw to offscreen buffer
// clear the canvas with a big black rectangle
g.setColor(Color.BLACK);
g.fillRect(0, 0, 800, 800);
// redraw the map
g.setColor(Color.WHITE);
for (int x=0;x<40;x++) {
for (int y=0;y<40;y++) {
if (map[x][y]) {
g.fillRect(x*20, y*20, 20, 20);
}
}
}
// redraw the player and badguy
// paint the game objects
player.paint(g);
badguy.paint(g);
if (!isGameRunning) {
// game is not running..
// draw a 'start button' as a rectangle with text on top
// also draw 'load' and 'save' buttons
g.setColor(Color.GREEN);
g.fillRect(15, 40, 70, 30);
g.fillRect(315, 40, 70, 30);
g.fillRect(415, 40, 70, 30);
g.setFont(new Font("Times", Font.PLAIN, 24));
g.setColor(Color.BLACK);
g.drawString("Start", 22, 62);
g.drawString("Load", 322, 62);
g.drawString("Save", 422, 62);
}
// flip the buffers
strategy.show();
}
// application entry point
public static void main(String[] args) {
AStarMaze w = new AStarMaze();
}
}
Player Class
import java.awt.Graphics;
import java.awt.Image;
public class Player {
Image myImage;
int x=0,y=0;
int xSpeed=0, ySpeed=0;
public Player( Image i ) {
myImage=i;
x=10;
y=35;
}
public void setXSpeed( int x ) {
xSpeed=x;
}
public void setYSpeed( int y ) {
ySpeed=y;
}
public void move(boolean map[][]) {
int newx=x+xSpeed;
int newy=y+ySpeed;
if (!map[newx][newy]) {
x=newx;
y=newy;
}
}
public void paint(Graphics g) {
g.drawImage(myImage, x*20, y*20, null);
}
}
Bad Guy
import java.awt.Graphics;
import java.awt.Image;
public class BadGuy {
Image myImage;
int x=0,y=0;
int distanceX = 0, distanceY = 0;
boolean hasPath=false;
public BadGuy( Image i ) {
myImage=i;
x = 30;
y = 10;
}
public void reCalcPath(boolean map[][],int targx, int targy) {
// TO DO: calculate A* path to targx,targy, taking account of walls defined in map[][]
int totalDistance = 0;
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
if (!map[(x / 20) + i][(y / 20) + j]) {
if ((((targx - x) ^ 2) + ((targy - y) ^ 2)) <= totalDistance) {
totalDistance = (((targx - x) ^ 2) + ((targy - y) ^ 2));
x = ((x / 20) + i);
y = ((y / 20) + j);
return;
}
}
}
}
System.out.println("Not working");
}
// public void move(boolean map[][],int targx, int targy) {
// if (hasPath) {
// // TO DO: follow A* path, if we have one defined
// }
// else if(map[x / 20][y / 20]) {
// hasPath = false;
// }
// }
public void paint(Graphics g) {
g.drawImage(myImage, x*20, y*20, null);
}
}
Your catch block is triggered by an IndexOutOfBoundsException. One easy way to find more information about your error is to log or output the stack trace. You can do that in your code by adding e.printStackTrace() to your catch block:
catch(IndexOutOfBoundsException e){
System.out.println("GAME OVER, you exited the map");
e.printStackTrace()
System.exit(-1);
}
This should show you the line number that's causing your problem.
Looking at the code in your reCalcPath method, my educated guess is the line causing the problem is this: if (!map[(x / 20) + i][(y / 20) + j]) {. If either (x / 20) + i or (y / 20) + j are less than 0 or greater than your map dimensions, it will throw the IndexOutOfBoundsException that you are seeing. In the case where you start and x=30, y=10, i=-1, and j=-1, that evaluates to map[0][-1].
This is what's known as an edge case. You need to check for the case where your x and y are on the edges of the grid. In that situation the neighboring grid cells are outside of the map and will throw an exception.
I am creating a program in Java, and would like to make my own button class as opposed to using a JButton. I've got all the aesthetics sorted out but I'm not sure how to get the mouse pressed event in Java.
This is my code:
// Button.java
package cella;
import java.awt.Color;
import java.awt.Point;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
public class Button extends MouseAdapter {
int x, y, w, h;
String ph, val;
boolean mouseDown;
Color LIGHTGRAY = new Color(200, 200, 200);
public Button(int xt, int yt, int wt, int ht, String pht, String valt) {
x = xt;
y = yt;
w = wt;
h = ht;
ph = pht;
val = valt;
mouseDown = false;
}
public void draw(Graphics g, Point mouse) {
if (contains(mouse)) {
g.setColor(Color.GRAY);
} else {
g.setColor(LIGHTGRAY);
}
g.fillRect(x, y, w, h);
g.setColor(Color.BLACK);
g.drawRect(x, y, w, h);
g.drawString(ph, x + 5, y + h - 5);
}
private boolean contains(Point pos) {
if (pos.x > x && pos.x < x + w && pos.y > y && pos.y < y + h) {
return true;
} else {
return false;
}
}
public boolean pressed(Point pos) {
if (contains(pos) && mouseDown) {
System.out.println("Pressed");
return true;
}
else return false;
}
}
The boolean mouseDown will be set to true when the mouse is pressed and then false when released however i can't find a way to catch these events, mouseListener gives errors about needing abstract classes when i try to implement it. Thanks for any help you can give me.
Full code
Try this.
JButton button = new JButton("Click!");
button.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.NOBUTTON) {
textArea.setText("No button clicked...");
} else if (e.getButton() == MouseEvent.BUTTON1) {
textArea.setText("Button 1 clicked...");
}
}
});
See available methods
Hope this help!
You can add a listener to your button that handles the event.
JButton button = new JButton("Click for Stuff");
button.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
switch(e.getButton())
{
case MouseEvent.NOBUTTON : // do stuff on button release
break;
case MouseEvent.BUTTON1 : // do stuff on click
break;
}
}
});
I know this question is a few years old, but I thought my input might be useful to someone down the road trying to accomplish the same task, considering I have some experience in custom UIs.
If you want a fully non-JComponent button, then you're going to need to also program your mouselistener and a UI Object Registry and a render/update function all operating on their required threads. I've done that, complete with NumberInputFields, Buttons, PasswordFields, TextFields, TextAreas, Graphics, and a variety of other UI objects. You don't want to unless you're going for a radically different look and feel than what is already supplied with very different functionality (for instance, my TextArea and TextField took in BitLines made up of TextBits rather than Strings, which allowed for each character to individually be formatted or colored or sized, complete with customized hover and click events). If such a different result is desired, you would do well to look into everything that goes into making a full UI within a Canvas object. If not, you have a couple other options.
Option 1: Extend the JButton class like has already been suggested. This is the easiest and likely the best option for you. It's fairly simple, and you can make that button be whatever you want it to be (within reason, of course).
Option 2: Create your own custom look and feel by extending the BasicLookAndFeel class. This is more complex and may take a bit of time and research, but in the end you can format all of your program to have a consistent L&F throughout, giving a satisfying and unique look to your software.
Here's an example of what it can take to make a button with basic functionality, completely separate from the JComponent class. Please note that this does NOT include the many background classes required to make this operate, such as the registry, the renderer, the animation and image loaders, the listeners, etc.
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
import dev.blue.neuron.storage.Animation;
public class Button extends UIObject {
private Animation whileDown;
private Animation whileUp;
private int tooltipTimer = 0;
private boolean showTooltip = false;
private boolean useTooltip = false;
protected boolean showingClicked = false;
protected boolean isSelected;
private boolean showID;
private int fontSize;
private Color color = Color.BLACK;
public Button(String id, boolean showID, boolean useTooltip, int fontSize, int x, int y, int width, int height,
int animationSpeed, Animation whileDown, Animation whileUp) {
this.id = id;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.whileDown = whileDown;
this.whileUp = whileUp;
this.bounds = new Rectangle(x, y, width, height);
this.showID = showID;
this.fontSize = fontSize;
this.useTooltip = useTooltip;
}
public void render(Graphics g) {
animate();
this.whileUp.render(g);
this.whileDown.render(g);
if (this.showID) {
g.setFont(new Font("Helvetica", 1, this.fontSize));
g.setColor(this.color);
g.drawString(this.id, this.x + this.width / 2 - g.getFontMetrics().stringWidth(this.id) / 2,
(int) ((this.y + this.height / 2) + g.getFontMetrics().getHeight() / 3.5D));
}
if (this.showTooltip && this.useTooltip) {
g.setFont(new Font("Helvetica", 0, 12));
g.setColor(Color.GRAY);
g.drawString(this.id, this.x, (int) (this.y + g.getFontMetrics().getHeight() * 1.5D));
}
}
public void setColor(Color color) {
this.color = color;
}
public void update() {
if (this.hovering) {
this.tooltipTimer++;
} else if (this.showingClicked) {
this.showingClicked = false;
}
if (this.tooltipTimer >= 50)
this.showTooltip = true;
runOnUpdate();
}
public void runOnUpdate() {
}
public void onMouseMove(Point p) {
if (this.bounds.contains(p)) {
if (!this.hovering) {
this.hovering = true;
runOnHover();
}
} else if (this.hovering) {
this.hovering = false;
this.showTooltip = false;
this.tooltipTimer = 0;
runOnStopHover();
}
}
private void animate() {
if (this.showingClicked) {
if (!this.whileDown.isRunning()) {
this.whileUp.end();
this.whileDown.run();
}
} else if (!this.whileUp.isRunning()) {
this.whileDown.end();
this.whileUp.run();
}
}
public boolean onClick(int button, Point p) {
if (this.bounds.contains(p)) {
runClick();
return true;
}
return false;
}
public void runOnMissedClick() {
}
public boolean onMouseDown(int button, Point p) {
if (this.bounds.contains(p)) {
(App.getInstance().getMouseManager()).clickedObject = this;
runMouseDown();
this.showingClicked = true;
return true;
}
return false;
}
public boolean onMouseUp(int button, Point p) {
if ((App.getInstance().getMouseManager()).clickedObject == this)
(App.getInstance().getMouseManager()).clickedObject = null;
if (!this.bounds.contains(p))
runOnMissedClick();
if (this.showingClicked) {
this.showingClicked = false;
if (this.bounds.contains(p)) {
runMouseUp();
onClick(button, p);
return true;
}
return false;
}
return false;
}
public void onType(KeyEvent e) {
}
public void onKeyPressed(KeyEvent e) {
}
public Animation getWhileUpAnim() {
return this.whileUp;
}
public Animation getWhileDownAnim() {
return this.whileDown;
}
public void setWhileUpAnim(Animation whileUp) {
this.whileUp = whileUp;
}
public void setWhileDownAnim(Animation whileDown) {
this.whileDown = whileDown;
}
public String getId() {
return this.id;
}
public void setId(String id) {
this.id = id;
}
}
So in this game i'm testing out, whenever the ball hits the paddle, I want the score to increase by 1. But instead it increases by 9 instead. How can I fix this? Here is the code. The 2nd method which is the update() method contains the score increase.
public class PlayState extends State {
private Catcher catcher;
private Ball ball;
boolean visible ;
private int playerScore = 0;
private Font scoreFont;
#Override
public void init() {
catcher = new Catcher(400,425,80,25);
ball= new Ball(200,0,40,40);
}
#Override
public void update() {
visible=true;
catcher.update();
ball.update();
if(ballCollides(catcher)){
playerScore++;
visible = false;
}
}
#Override
public void render(Graphics g) {
//Draw Background
g.setColor(Color.blue);
g.fillRect(0, 0, GameMain.GAME_WIDTH, GameMain.GAME_HEIGHT);
//Draw Ball(s)
if(visible == true){
g.setColor(Color.green);
g.fillOval(ball.getX(),ball.getY(),ball.getWidth(),ball.getHeight());
}
// Draw catcher
g.setColor(Color.white);
g.fillRect(catcher.getX(), catcher.getY(), catcher.getWidth(),
catcher.getHeight());
// Draw Score
g.setFont(scoreFont); // Sets scoreFont as current font
g.setColor(Color.white);
g.drawString("" + playerScore, 350, 40); // Draws String using current font
}
#Override
public void onClick(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void onKeyPress(KeyEvent e) {
// TODO Auto-generated method stub
if (e.getKeyCode() == KeyEvent.VK_RIGHT){
catcher.moveRight();
}
if (e.getKeyCode()==KeyEvent.VK_LEFT){
catcher.moveLeft();
}
}
private boolean ballCollides(Catcher c) {
return ball.getRect().intersects(c.getRect());
}
#Override
public void onKeyRelease(KeyEvent e) {
// TODO Auto-generated method stub
if (e.getKeyCode() == KeyEvent.VK_LEFT
|| e.getKeyCode() == KeyEvent.VK_RIGHT) {
catcher.stop();
}
}
}
2nd blck of code where the update method is being called
package com.jamescho.game.main;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.JPanel;
import com.jamescho.framework.util.InputHandler;
import com.jamescho.game.state.LoadState;
import com.jamescho.game.state.State;
#SuppressWarnings("serial")
public class Game extends JPanel implements Runnable {
private int gameWidth;
private int gameHeight;
private Image gameImage;
private Thread gameThread;
private volatile boolean running;
private volatile State currentState;
private InputHandler inputHandler;
public Game(int gameWidth, int gameHeight) {
this.gameWidth = gameWidth;
this.gameHeight = gameHeight;
setPreferredSize(new Dimension(gameWidth, gameHeight));
setBackground(Color.BLACK);
setFocusable(true);
requestFocus();
}
public void setCurrentState(State newState) {
System.gc();
newState.init();
currentState = newState;
inputHandler.setCurrentState(currentState);
}
#Override
public void addNotify() {
super.addNotify();
initInput();
setCurrentState(new LoadState());
initGame();
}
private void initGame() {
running = true;
gameThread = new Thread(this, "Game Thread");
gameThread.start();
}
#Override
public void run() {
// These variables should sum up to 17 on every iteration
long updateDurationMillis = 0; // Measures both update AND render
long sleepDurationMillis = 0; // Measures sleep
while (running) {
long beforeUpdateRender = System.nanoTime();
long deltaMillis = updateDurationMillis + sleepDurationMillis;
updateAndRender(deltaMillis);
updateDurationMillis = (System.nanoTime() - beforeUpdateRender) / 1000000L;
sleepDurationMillis = Math.max(2, 17 - updateDurationMillis);
try {
Thread.sleep(sleepDurationMillis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// End game immediately when running becomes false.
System.exit(0);
}
private void updateAndRender(long deltaMillis) {
currentState.update(deltaMillis / 1000f);
prepareGameImage();
currentState.render(gameImage.getGraphics());
renderGameImage(getGraphics());
}
private void prepareGameImage() {
if (gameImage == null) {
gameImage = createImage(gameWidth, gameHeight);
}
Graphics g = gameImage.getGraphics();
g.clearRect(0, 0, gameWidth, gameHeight);
}
public void exit() {
running = false;
}
private void renderGameImage(Graphics g) {
if (gameImage != null) {
g.drawImage(gameImage, 0, 0, null);
}
g.dispose();
}
private void initInput() {
inputHandler = new InputHandler();
addKeyListener(inputHandler);
addMouseListener(inputHandler);
}
}
It appears as if ballCollides may return true across multiple updates for a single collision. I am not sure of this without looking at the code for the entire program, but based on what has been posted this would seem to be the most likely case.
One fix would be to keep track of the previous collision state for each catcher and ball combination (if there can be more than a single ball and catcher), and only update the score when the current state is colliding and the previous state was non-colliding.
E.g.
// Example using a single previousCollides state variable...
// Not suitable for a multi catcher / multi-ball game.
// The modifications break the contract this methods name seems to
// imply so the previousCollides logic should probably exist elsewhere
// and this code is only to illustrate the idea.
private boolean ballCollides(Catcher c) {
boolean collides = ball.getRect().intersects(c.getRect());
boolean result = collides && !previousCollides;
previousCollides = collides;
return result;
}
The above is just an example, where to actually keep track of the previous collision data is up to you. Maybe it should be kept within the catcher object itself as that would seem to make the most sense.
Im using java to create a space invaders game but i seem to be having trouble detecting collisions, here is my game panel, we were given some sample code which i have added to. I have created different classes for each enemy which are extended from the enemy class.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Random;
import javax.swing.JPanel;
import javax.swing.Timer;
#SuppressWarnings("serial")
public class GamePanel extends JPanel implements ActionListener, KeyListener
{
private Player player;
private Enemy enemy;
private Enemy mothership;
private Enemy destroyer;
private Enemy meteor;
private int enemyNumber = 0;
private int totalscore = 0;
private boolean destroyerspawn = false;
private boolean meteorspawn = false;
private boolean mothershipstop = false;
private boolean meteorstop = false;
private boolean destroyerstop = false;
private Random random = new Random();
private ArrayList<Enemy> enemyList;
Timer redrawTimer;
public GamePanel()
{
// As above commented out until enemy classes have been made
enemyList = new ArrayList<Enemy>();
// This starts the time that controls how often the screen is redrawn
redrawTimer = new Timer(10, this);
}
// Method to start the game
public void startGame(int width, int height)
{
// Create player
player = new Player(width, height);
// Create mothership
mothership = new Mothership(width, height, 0, 0);
// Create destroyer
destroyer = new Destroyers(width, height, 0, 0);
// Create meteor
meteor = new Meteor(width, height, 0, 0);
// Loop to create multiple enemies
// Goes through y axis changing the enemy type at each increment
for (int y = 0; y < 3; y++)
{
// Goes through x axis making 6 of each enemy type
for (int x = 0; x < 6; x++)
{
if(y == 0 || y == 3)
{
enemy = new Martians(width, height, (x * 70), (y * 70));
enemyList.add(enemy);
enemyNumber++;
}
else if(y == 1 || y == 4)
{
enemy = new Mercurians(width, height, (x * 70), (y * 70));
enemyList.add(enemy);
enemyNumber++;
}
else if(y == 2 || y == 5)
{
enemy = new Venusians(width, height, (x * 70), (y * 70));
enemyList.add(enemy);
enemyNumber++;
}
}
}
// Starts draw timer
redrawTimer.start();
}
// Method to pause the game
public void pauseGame()
{
redrawTimer.restart();
}
// Handles the timer event, so this method repeats based on the interval set in your timer
#Override
public void actionPerformed(ActionEvent e)
{
this.revalidate();
// Causes the screen to be redrawn
this.repaint();
}
// Our paint component method that draws every thing we need to the screen
public void paintComponent(Graphics g)
{
// This line ensures that every that would usually be drawn by a panel is
super.paintComponent(g);
// Clear the screen
g.setColor(Color.black);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
if(player!= null && player.isActive())
{
// Draw the player
player.draw(g);
}
// If statement that stops this code running once it's deactivated once
if (mothershipstop == false)
{
// If there are no enemies left
if (enemyNumber == 0)
{
if(mothership!= null && mothership.isActive())
{
// Draw and move the mothership
mothership.draw(g);
mothership.Move();
}
// Detects whether there's been a collision
for (int b = 0; b < player.getBulletCount(); b++)
{
// Collision detection for bullet
player.getBullet(b).detectCollision(mothership);
}
if (player.isActive() == true && mothership.isActive())
{
// Collision detection for player
player.detectCollision(mothership);
}
}
}
// If statement that stops this code running once it's deactivated once
if (destroyerstop == false)
{
// Each tick will have a 1 in 1000 chance of spawning a destroyer
if (random.nextInt(1000) == 1)
{
destroyerspawn = true;
}
// If the random integer got activated, destroyer spawns
if (destroyerspawn == true && destroyer!= null && destroyer.isActive())
{
// Draw and move the destroyer
destroyer.draw(g);
destroyer.Move();
// Detects whether there's been a collision
for (int b = 0; b < player.getBulletCount(); b++)
{
// Collision detection for bullet
player.getBullet(b).detectCollision(destroyer);
}
}
else if (destroyer.isActive() == false)
{
destroyerstop = true;
totalscore = totalscore + destroyer.GetScore();
}
}
// If statement that stops this code running once it's deactivated once
if (meteorstop == false)
{
// Each tick will have a 1 in 700 chance of spawning an asteroid
if (random.nextInt(700) == 1)
{
meteorspawn = true;
}
// If the random integer got activated, asteroid spawns
if (meteorspawn == true && meteor!= null && meteor.isActive())
{
// Draw and move the asteroid
meteor.draw(g);
meteor.Move();
// Detects whether there's been a collision
for (int b = 0; b < player.getBulletCount(); b++)
{
// Collision detection for bullet
player.getBullet(b).detectCollision(meteor);
}
if (player.isActive() == true && meteor.isActive())
{
// Collision detection for player
player.detectCollision(meteor);
}
}
else if (meteor.isActive() == false)
{
meteorstop = true;
totalscore = totalscore + meteor.GetScore();
}
}
// Code to draw the enemy
Iterator<Enemy> iterator = enemyList.iterator();
// Loops to check each enemy
while (iterator.hasNext())
{
// Checks the next enemy
enemy = iterator.next();
// If there's an enemy or active enemy
if(enemy != null && enemy.isActive())
{
// Draw and move the enemy
enemy.draw(g);
enemy.Move();
} // If non-existent enemy or is inactive
else
{
// Get score
totalscore = totalscore + enemy.GetScore();
// Remove the enemy
iterator.remove();
}
// If an enemy has been removed
if (enemy.lives == 0)
{
// Decrement number of enemies
enemyNumber--;
}
// Detects whether there's been a collision
for (int b = 0; b < player.getBulletCount(); b++)
{
// Collision detection for bullet
player.getBullet(b).detectCollision(enemy);
}
if (player.isActive() == true && enemy.isActive())
{
// Collision detection for player
}
}
// Checks if game ends
endGame();
}
public void endGame()
{
if (player.isActive() == false)
{
// You lose
System.out.println("Game over! You lose.");
System.out.println("Your score is: " + totalscore);
// Close game
System.exit(ABORT);
}else if (mothership.isActive() == false)
{
// You win
totalscore = totalscore + mothership.GetScore();
System.out.println("Congratulations! You win.");
System.out.println("Your score is: " + totalscore);
// Close game
System.exit(ABORT);
}
}
// Code here controls the key events
#Override
public void keyPressed(KeyEvent event)
{
switch(event.getKeyCode())
{
case KeyEvent.VK_RIGHT: player.move(1); break;
case KeyEvent.VK_LEFT: player.move(-1); break;
case KeyEvent.VK_SPACE: player.fire(); break;
}
}
#Override
public void keyReleased(KeyEvent event)
{
}
#Override
public void keyTyped(KeyEvent event)
{
}
}
n my bullet class there is a detect collision method that we have to fill in, i however have no clue how to do this. here is the code for the bullet class.
import java.awt.Graphics;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Bullet {
private Point position;
private int height, width;
private BufferedImage img;
private int speed = 20;
private boolean active;
public Bullet(Point playerPosition){
//same as the player class this simply loads the image for the bullet.
try {
img = ImageIO.read(getClass().getResource("/playerbullet.jpg"));
//System.out.println("***************OK*******************");
} catch (IOException e) {
//System.out.println("***************CAN'T READ FILE*******************");
e.printStackTrace();
}
height = img.getHeight();
width = img.getWidth();
//sets the bullet position we have to do a little offset wit the bullet image so our bullet appears in the (horizontal) centre of our player
position = new Point(playerPosition.x - (width/2), playerPosition.y);
active = true;
}
//draws the bullet
public void draw(Graphics g) {
g.drawImage(img, position.x, position.y, width, height, null);
}
//moves the bullet unless it has gone off the top of the screen in which case the bullet is destroyed
public void move(){
if(position.y < 0){
destroy();
}
else{
position.y-=speed;
}
}
//sets the bullet as inactive
public void destroy(){
active = false;
}
//returns whether the bullet is active or not
public boolean isActive(){
return active;
}
//returns the bullets current position
public Point getPosition() {
// TODO Auto-generated method stub
return position;
}
// this method will flag as an error until you create your enemy classes
public void detectCollision(Enemy e)
{
}
}
You can try Rectangle class in Java
There is a method called "createIntersection" which returns the Rectangle where 2 rectangles overlap. If that area is zero, there's no collision.
Inside your Bullet class, you stored the position, width and height. But in fact this tells that you are actually storing the "Rectangle". So, try to refactor your implementation and use Rectangle instead.
In the detectCollision() method, the parameter should be the Rectangle of the checking target (i.e. player). Then you can use the createIntersection() to check the overlapping area
What is the best way drawing a hero and moving it? I just need the best code for doing that. Before writing this i found a way, but when i made the surface holder transparent, i realised that the code is drawing new bitmap in the front of the old one every milisecond. That way looks kind of laggy to me, but maymie i'm not right. Please help me. Actualy i'm kind of confused...
Anyway, here's the code that i think is laggy:
MainThread.java
/**
*
*/
package com.workspace.pockethero;
import android.graphics.Canvas;
import android.util.Log;
import android.view.SurfaceHolder;
/**
* #author impaler
*
* The Main thread which contains the game loop. The thread must have access to
* the surface view and holder to trigger events every game tick.
*/
public class MainThread extends Thread {
private static final String TAG = MainThread.class.getSimpleName();
// Surface holder that can access the physical surface
private SurfaceHolder surfaceHolder;
// The actual view that handles inputs
// and draws to the surface
private MainGamePanel gamePanel;
// flag to hold game state
private boolean running;
public void setRunning(boolean running) {
this.running = running;
}
public MainThread(SurfaceHolder surfaceHolder, MainGamePanel gamePanel) {
super();
this.surfaceHolder = surfaceHolder;
this.gamePanel = gamePanel;
}
#Override
public void run() {
Canvas canvas;
canvas = this.surfaceHolder.lockCanvas();
Log.d(TAG, "Starting game loop");
while (running) {
canvas = null;
// try locking the canvas for exclusive pixel editing
// in the surface
try {
synchronized (surfaceHolder) {
// update game state
this.gamePanel.update();
// render state to the screen
// draws the canvas on the panel
this.gamePanel.render(canvas);
}
} finally {
// in case of an exception the surface is not left in
// an inconsistent state
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
} // end finally
}
}
}
MainGamePanel.java
/**
*
*/
package com.workspace.pockethero;
import com.workspace.pockethero.model.Droid;
import com.workspace.pockethero.buttons.*;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
/**
* #author impaler
* This is the main surface that handles the ontouch events and draws
* the image to the screen.
*/
public class MainGamePanel extends SurfaceView implements
SurfaceHolder.Callback {
private static final String TAG = MainGamePanel.class.getSimpleName();
private MainThread thread;
public Droid droid;
public Butt butt;
public Butt1 butt1;
public Butt2 butt2;
public Butt3 butt3;
public Buttz buttz;
public Buttz1 buttz1;
public Buttz2 buttz2;
public Buttz3 buttz3;
public Buttx buttx;
public Build build;
public int decentreX;
public int decentreY;
public int debottomA;
public boolean moved;
public boolean moved1;
public boolean moved2;
public boolean moved3;
public boolean moved4;
public boolean moved5;
public boolean moved6;
public boolean moved7;
private Drawable myImage;
public boolean mapPainted;
public MainGamePanel(Context context) {
super(context);
// adding the callback (this) to the surface holder to intercept events
getHolder().addCallback(this);
// create droid and load bitmap
decentreX = PocketHero.centreX;
decentreY = PocketHero.centreY;
debottomA = PocketHero.bottomA;
droid = new Droid(BitmapFactory.decodeResource(getResources(), R.drawable.herod), decentreX, decentreY);
butt = new Butt(BitmapFactory.decodeResource(getResources(), R.drawable.button), 110, debottomA - 70);
butt1 = new Butt1(BitmapFactory.decodeResource(getResources(), R.drawable.button1), 70, debottomA - 110);
butt2 = new Butt2(BitmapFactory.decodeResource(getResources(), R.drawable.button2), 30, debottomA - 70);
butt3 = new Butt3(BitmapFactory.decodeResource(getResources(), R.drawable.button3), 70, debottomA - 30);
buttz = new Buttz(BitmapFactory.decodeResource(getResources(), R.drawable.zbutton), 110, debottomA - 110);
buttz1 = new Buttz1(BitmapFactory.decodeResource(getResources(), R.drawable.zbutton1), 30, debottomA - 110);
buttz2 = new Buttz2(BitmapFactory.decodeResource(getResources(), R.drawable.zbutton2), 30, debottomA - 30);
buttz3 = new Buttz3(BitmapFactory.decodeResource(getResources(), R.drawable.zbutton3), 110, debottomA - 30);
buttx = new Buttx(BitmapFactory.decodeResource(getResources(), R.drawable.xbutton), 70, debottomA - 70);
build = new Build(BitmapFactory.decodeResource(getResources(), R.drawable.building), 500, 200);
// create the game loop thread
//300 indicates start position of bitmapfield on screen
thread = new MainThread(getHolder(), this);
// make the GamePanel focusable so it can handle events
setFocusable(true);
this.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View V, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// delegating event handling to the droid
handleActionDown((int)event.getX(), (int)event.getY());
} if (event.getAction() == MotionEvent.ACTION_MOVE) {
handleActionDown((int)event.getX(), (int)event.getY());
// the gestures
} if (event.getAction() == MotionEvent.ACTION_UP) {
// touch was released
if (droid.touched) {
droid.setTouched(false);
}
if (droid.touched1) {
droid.setTouched1(false);
}
if (droid.touched2) {
droid.setTouched2(false);
}
if (droid.touched3) {
droid.setTouched3(false);
}
}
return true;
}
});
}
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
public void surfaceCreated(SurfaceHolder holder) {
// at this point the surface is created and
// we can safely start the game loop
thread.setRunning(true);
thread.start();
}
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "Surface is being destroyed");
// tell the thread to shut down and wait for it to finish
// this is a clean shutdown
boolean retry = true;
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
// try again shutting down the thread
}
}
Log.d(TAG, "Thread was shut down cleanly");
}
public void render(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
droid.draw(canvas);
butt.draw(canvas);
butt1.draw(canvas);
butt2.draw(canvas);
butt3.draw(canvas);
buttz.draw(canvas);
buttz1.draw(canvas);
buttz2.draw(canvas);
buttz3.draw(canvas);
buttx.draw(canvas);
}
/**
* This is the game update method. It iterates through all the objects
* and calls their update method if they have one or calls specific
* engine's update method.
*/
public void update() {
droid.update();
}
public void handleActionDown(int eventX, int eventY) {
if (eventX >= (butt.x - butt.bitmap.getWidth() / 2) && (eventX <= (butt.x + butt.bitmap.getWidth()/2))) {
if (eventY >= (buttz.y - buttz.bitmap.getHeight() / 2) && (eventY <= (buttz3.y + buttz3.bitmap.getHeight() / 2))) {
// droid touched
droid.setTouched(true);
} else {
droid.setTouched(false);
}
} else {
droid.setTouched(false);
}
if (eventX >= (buttz1.x - buttz1.bitmap.getWidth() / 2) && (eventX <= (buttz.x + buttz.bitmap.getWidth()/2))) {
if (eventY >= (butt1.y - butt1.bitmap.getHeight() / 2) && (eventY <= (butt1.y + butt1.bitmap.getHeight() / 2))) {
// droid touched
droid.setTouched1(true);
} else {
droid.setTouched1(false);
}
}else {
droid.setTouched1(false);
}
if (eventX >= (butt2.x - butt2.bitmap.getWidth() / 2) && (eventX <= (butt2.x + butt2.bitmap.getWidth()/2))) {
if (eventY >= (buttz1.y - buttz1.bitmap.getHeight() / 2) && (eventY <= (buttz2.y + buttz2.bitmap.getHeight() / 2))) {
// droid touched
droid.setTouched2(true);
} else {
droid.setTouched2(false);
}
}else {
droid.setTouched2(false);
}
if (eventX >= (buttz2.x - buttz2.bitmap.getWidth() / 2) && (eventX <= (buttz3.x + buttz3.bitmap.getWidth()/2))) {
if (eventY >= (butt3.y - butt3.bitmap.getHeight() / 2) && (eventY <= (butt3.y + butt3.bitmap.getHeight() / 2))) {
// droid touched
droid.setTouched3(true);
} else {
droid.setTouched3(false);
}
}else {
droid.setTouched3(false);
}
if (droid.touched & !droid.touched1 & !droid.touched3) {
if (!moved) {
droid.bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.heror);
moved = true;
}
}else {
moved = false;
}if (droid.touched1 & !droid.touched & !droid.touched2){
if (!moved1) {
droid.bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.herou);
moved1 = true;
}
}else {
moved1 = false;
} if (droid.touched2 & !droid.touched1 & !droid.touched3){
if (!moved2) {
droid.bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.herol);
moved2 = true;
}
}else {
moved2 = false;
} if (droid.touched3 & !droid.touched2 & !droid.touched){
if (!moved7) {
droid.bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.herod);
moved7 = true;
}
}else {
moved7 = false;
} if (droid.touched & droid.touched1){
if (!moved3) {
droid.bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.herour);
moved3 = true;
}
}else {
moved3 = false;
} if (droid.touched1 & droid.touched2){
if (!moved4) {
droid.bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.heroul);
moved4 = true;
}
}else {
moved4 = false;
} if (droid.touched2 & droid.touched3){
if (!moved5) {
droid.bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.herodl);
moved5 = true;
}
}else {
moved5 = false;
} if (droid.touched3 & droid.touched){
if (!moved6) {
droid.bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.herodr);
moved6 = true;
}
}else {
moved6 = false;
}
}
}
and the Droid.java
/**
*
*/
package com.workspace.pockethero.model;
import android.graphics.Bitmap;
import android.graphics.Canvas;
/**
* This is a test droid that is dragged, dropped, moved, smashed against
* the wall and done other terrible things with.
* Wait till it gets a weapon!
*
* #author impaler
*
*/
public class Droid {
public Bitmap bitmap; // the actual bitmap
public int x; // the X coordinate
public int y; // the Y coordinate
public boolean touched; // if droid is touched/picked up
public boolean touched1; // if droid is touched/picked up
public boolean touched2; // if droid is touched/picked up
public boolean touched3; // if droid is touched/picked up
public Droid(Bitmap bitmap, int x, int y) {
this.bitmap = bitmap;
this.x = x;
this.y = y;
}
public Bitmap getBitmap() {
return bitmap;
}
public void setBitmap(Bitmap bitmap) {
this.bitmap = bitmap;
}
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 void draw(Canvas canvas) {
canvas.drawBitmap(bitmap, x - (bitmap.getWidth() / 2), y - (bitmap.getHeight() / 2), null);
}
/**
* Method which updates the droid's internal state every tick
*/
public void setTouched(boolean touched) {
this.touched = touched;
}
public boolean isTouched() {
return touched;
}
public void setTouched1(boolean touched) {
this.touched1 = touched;
}
public boolean isTouched1() {
return touched1;
}
public void setTouched2(boolean touched) {
this.touched2 = touched;
}
public boolean isTouched2() {
return touched2;
}
public void setTouched3(boolean touched) {
this.touched3 = touched;
}
public boolean isTouched3() {
return touched3;
}
public void update() {
if (touched & !touched1 & !touched3) {
x += 1;
}else if (touched1 & !touched & !touched2){
y -= 1;
}else if (touched2 & !touched1 & !touched3){
x -= 1;
}else if (touched3 & !touched2 & !touched){
y += 1;
}else if (touched & touched1){
x += 1;
y -= 1;
}else if (touched1 & touched2){
x -= 1;
y -= 1;
}else if (touched2 & touched3){
x -= 1;
y += 1;
}else if (touched3 & touched){
x += 1;
y += 1;
}
}
/**
* Handles the {#link MotionEvent.ACTION_DOWN} event. If the event happens on the
* bitmap surface then the touched state is set to <code>true</code> otherwise to <code>false</code>
* #param eventX - the event's X coordinate
* #param eventY - the event's Y coordinate
*/
}
there are also other classes created for each button, but i didnt pasted them here, because the are practicly the same as droid.java
Re-drawing the whole frame each loop is the correct way to draw/move sprites and perform anything with canvas.
The buffer is cleared each frame and you need to re-draw the background and all objects at their specified position.
In your code render() and update() will not be called more frequently than approximately every 16milliseconds~ (60 frames per second), so all you have to think about is drawing the onscreen scene.
I'm not sure what you mean by 'laggy', maybe you have a performance issue related to the size of your Bitmaps or phone performance, but I hope this is close to what you were looking for.