How to fix java keyPressed Lag/Late events - java

I am sort of a noob at this but have attempted to create my own mario game.
It's going well but I need a variable called distanceTraveled to be determined
It isn't reliable at all, when I hold the arrow key numbers go faster than if I was to tap them.
Here are my codes(Everything is commented on the right):
.
frame.java
package EvilMario; //Include this class in the EvilMario game package
import javax.swing.JFrame; //Import the JFrame
public class frame { //Run this class to run the game
public static void main(String[] args) { //The first method called by java
JFrame frame = new JFrame("EvilMario v.1.0.0 by Max Mastalerz"); //Create JFrame called frame
frame.getContentPane().add(new board()); //Go to board class
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //Make frame close on X click
frame.setSize(600,413); //Set the frame size to the size of the background
frame.setVisible(true); //Make the frame visible
frame.setResizable(false); //Make sure the user can't resize the frame
}
}
player.java
package EvilMario; //Include this class in the EvilMario game package
import java.awt.Image;
import java.awt.event.KeyEvent;
import javax.swing.ImageIcon;
public class player {
int x, dx, y, nx, nx2, distanceTraveled; //x coordinate,change in x coordinate,y coordinate,1st rep bg,2nd rep bg,dist traveled
Image player; //The player variable
ImageIcon playerFacingLeft = new ImageIcon("D:/ICS3U1/EvilMario/images/MarioLeft.png"); //Image for player while he is turning left
ImageIcon playerFacingRight = new ImageIcon("D:/ICS3U1/EvilMario/images/MarioRight.png");//Image for player while he is turning right
public player() {
player = playerFacingRight.getImage(); //Give the player the image
x = 75; //The original x position of the player
y = 265; //The original y position of the player
nx = -0; //Repeating background 1
nx2 = -575; //Repeating background 2
distanceTraveled = 0;
}
public void move() {
if(x>0 && x<300) { //If the player is within the moving area
x = x+dx; //The x position is updated to become itself+the amount you moved
nx = nx+dx; //Place the repeating background at regular speed
nx2 = nx2+dx; //Place the repeating background at regular speed
}
if(x==0) { //If the player has reached he very left side of the screen(0px)
x=1; //Move him up a pixel so he can move again
nx = nx+(dx*(int)0.5); //Place the background at a slower speed since Mario stops moving
nx2 = nx2+(dx*(int)0.5); //Place the background at a slower speed since Mario stops moving
}
if(x==300) { //If the player has reached the center of the screen(300px)
x=299; //Move him down a pixel so he can move again
nx = nx+(dx*(int)0.5); //Place the background at a slower speed since Mario stops moving
nx2 = nx2+(dx*(int)0.5); //Place the background at a slower speed since Mario stops moving
}
System.out.println(distanceTraveled);
}
public int getX() { return x; } //This method will return the x. Is used by other classes
public int getY() { return y; } //This method will return the y. Is used by other classes
public Image getImage() { return player; } //This method will return the player. Is used by other classes
public void keyPressed(KeyEvent e) { //Called from the board class, the argument is whatever key was pressed
int key = e.getKeyCode(); //The key originally sent from the board class
if(key == KeyEvent.VK_LEFT) { //If the key sent was LEFT
player = playerFacingLeft.getImage(); //Make the player face leftwards
dx = -1;
distanceTraveled--;
}
if(key == KeyEvent.VK_RIGHT) { //If the key sent was RIGHT
player = playerFacingRight.getImage(); //Make the player face rightwards
dx = 1; //Move right
distanceTraveled++;
}
}
public void keyReleased(KeyEvent e) { //Called from the board class, the argument is whatever key was released
int key = e.getKeyCode(); //The key originally sent from the board class
if(key == KeyEvent.VK_LEFT || key == KeyEvent.VK_RIGHT) //If the left or right key was released
dx = 0; //Stop moving
}
}
board.java
package EvilMario; //Include this class in the EvilMario game package
import java.awt.*; //Imported to allow use of Image
import java.awt.event.*; //Imported to allow use of ActionListener
import javax.swing.*;
public class board extends JPanel implements ActionListener {
player p; //Instance of player class
Image background; //The background variable
Timer time; //A timer
public board() {
p = new player(); //Start running player class
addKeyListener(new AL()); //Listen for keys
setFocusable(true); //Allows movement
ImageIcon i = new ImageIcon("D:/ICS3U1/EvilMario/images/EvilMario_Background.png"); //Image for background
background = i.getImage(); //Give the background the image
time = new Timer(7,this); //Timer set to update "this" class every 5 milliseconds
time.start(); //Actually start the timer
}
public void actionPerformed(ActionEvent e) {
p.move(); //Call the move method from the player class
repaint(); //Repaint
}
public void paint(Graphics g) { //Graphics method
super.paint(g);
Graphics2D g2d = (Graphics2D) g; //casts 2d graphics(or however you would explain it)
g2d.drawImage(background, -p.nx, 0, null); //Draw the background image
g2d.drawImage(background, -p.nx2, 0, null); //Draw the background image
if(-p.nx<-575) //If going forwards
p.nx=-575; //Start placing forwards every 575px in front on the last one
else if(-p.nx>575) //If going backwards
p.nx=575; //Start placing backwards every 575px behind the last one
if(-p.nx2<-575) //If going forwards
p.nx2=-575; //Start placing forwards every 575px in front on the last one
else if(-p.nx2>575) //If going backwards
p.nx2=575; //Start placing backgrounds every 575px behind the last one
g2d.drawImage(p.getImage(), p.getX(), p.getY(), null); //Draw the player at the position he is currently(Coordinate values taken from player class)
}
private class AL extends KeyAdapter { //Action Listener extends key adapter
public void keyPressed(KeyEvent e) { //On key press
p.keyPressed(e); //Send whatever key was pressed TO the keyPressed method in the player class
}
public void keyReleased(KeyEvent e) { //On key release
p.keyReleased(e); //Send whatever key was released TO the keyReleased method in the player class
}
}
}
If you wish to test this game yourself, you will need these three images
EvilMario_Background.png
MarioLeft.png
MarioRight.png
Also change the directories
I've been trying too many things with this and have not come up with anything, If you figure this out I will be forever grateful :)

Rather then trying to calculate the distance in the key event handlers, calculate it within the move method. This would be based on the value of dx been either greater or less then 0
You really should be using paintComponent by convention, rather the paint
You should consider using the key bindings API as it solves many of the problems associated with the KeyListener
I would rely more on the actual width and height properties of the JPanel rather then "magic" numbers. The problem here is different platforms and look and feels have different frame border sizes, which will effect these values. Consider overriding getPreferredSize in the board to return the preferred size of the gaming canvas. Then you can use pack on the frame instead.
Call setResizable before trying to size or show the window, this will change the size of the frame border and change the size of the viewable area...
A 7 millisecond delay is something like 143fps...I seriously doubt you really need that kind of refresh rate. 40 milliseconds is 25fps and I dare you do tell the difference, event 60fps is only 16 milliseconds. Don't change the speed of your refresh rate, change the speed of your delta values to alter the speed of the game...just saying ;)

Related

Moving a Pacman

This is a program I wrote for creating a pacman. I now want the Pacman to move in a straight line from a random start point to a random goal point.
Could you please suggest how to do it.
import javax.swing.JFrame;
/**
* Main class for pacman example. All it does is create a frame and put
* the pacman panel in it.
*/
public class PacmanGUI extends JFrame{
private Pacman pc;
public PacmanGUI(){
super("Pacman");
pc = new Pacman();
this.getContentPane().add(pc);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.pack();
this.setVisible(true);
}
public static void main(String[] args) {
new PacmanGUI();
}
}
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
/**
* Pacman class that extends JPanel and paints a pacman animation.
* It uses Timers and Actionlistener to do the Animation.
*
*/
public class Pacman extends JPanel implements ActionListener{
private int figureSize = 50;
private static final int DELAY = 200;
private double mouthOpenPercentages[] = {.1,.5};
private Timer animationTimer;
private int mouthState = 0;
private Point pacManPosition;
/**
* No args constructor that starts the animation.
*/
public Pacman(){
startAnimation();
}
/**
* Overriden paintComponent method that paints pacman.
*/
public void paintComponent(Graphics g) {
super.paintComponent(g);
pacManPosition = new Point(this.getWidth()/2 - figureSize/2,
this.getHeight()/2 - figureSize/2);
g.fillRect(0,0,this.getWidth(), this.getHeight());
drawPacMan(g);
mouthState = (++mouthState) % mouthOpenPercentages.length;
}
/**
* Stops the animation by stopping the animation timer.
*/
public void stopAnimation(){ animationTimer.stop(); }
/**
* Method do deal with actionevents that are triggered. In this
* case we only have actionevents being triggered from our timer
* and by the more usual case of a button click.
*/
public void actionPerformed(ActionEvent e){ repaint(); }
/**
* Gets the size that this component would like to be.
*/
public Dimension getPreferredSize(){ return new Dimension(400,400); }
/**
* Gets the minimum size for this component.
*/
public Dimension getMinimumSize(){ return new Dimension(200,200); }
/**
* Starts the animation by setting a timer. When this timer goes
* off the actionPerformed method will be triggered, which in
* turn triggers the painting.
*/
private void startAnimation(){
if (animationTimer == null){
mouthState = 0;
animationTimer = new Timer(DELAY, this);
animationTimer.start();
} else { //continue animating..
if (!animationTimer.isRunning())
animationTimer.restart();
}
}
/**
* Draws our little pacman on the given graphics canvas.
* #param g
*/
private void drawPacMan(Graphics g){
Color c = g.getColor();
g.setColor(Color.yellow);
g.fillOval(pacManPosition.x, pacManPosition.y, figureSize, figureSize);
//Change color back to original and draw pacman's mouth
g.setColor(c);
//calculate mouth offsets
int yOffset = (int)((figureSize/2)*mouthOpenPercentages[mouthState]);
//draw the mouth cutout.
int x[] = {pacManPosition.x + figureSize/2, pacManPosition.x + figureSize, pacManPosition.x + figureSize};
int y[] = {pacManPosition.y + figureSize/2,
pacManPosition.y + figureSize/2 + yOffset,
pacManPosition.y + figureSize/2 - yOffset};
g.fillPolygon(x, y, x.length);
}
}
Inside the Pacman class you would need to create 2 more values to store the start and end points. You already have private Point pacManPosition; declared so I would also declare these as Points. You'll want to set pacManPosition initially to the start point.
Point start = // random start point
Point end = // random end point
Point pacManPoint = new Point(start);
Now you'll want to determine the speed you want your Pacman to move at, let's say 2 pixels per frame.
int speed = 2;
To determine how much to move the Pacman each frame, we'll need to do some calculations. First, get the distance of the line -
double distance = Math.sqrt(Math.pow(end.x - start.x, 2) +
Math.pow(end.y - start.y, 2));
Then we calculate how many frames it will take to go that distance at the speed we want.
int totalFrames= (int)Math.round(distance / speed);
And add a frame counter -
int frame = 0;
Now, look inside your paintComponent method. Right now you're setting pacManPosition to the same point (the center of the panel) each time it paints. What you want to do here instead is to update pacManPosition each frame until it gets to the end position. You're doing something similar lower in paintComponent where you're updating mouthState each time to get the mouth to animate. For animating position it will look like -
if (frame < totalFrames) {
pacManPosition.x = start.x + frame * (end.x - start.x) / totalFrames;
pacManPosition.y = start.y + frame * (end.y - start.y) / totalFrames;
frame++;
}
This is only one way to do movement animation, and it assumes several things - constant speed, no need to avoid obstacles, no player control. The calculation in totalFrames isn't exact - it moves pacMan close to the end point, but there's no guarantee it will end exactly there. It is also tied to the frame rate, which has drawbacks. There are many, many other ways to do this depending on the situation.
Problem
You have to manage two animations at the same time.
The first, which you've already coded, opens and closes the Pacman's mouth.
The second animation is responsible for moving the Pacman from one location to another.
Solution - Sprite class
I suggest you create a Sprite class. The Sprite class would be responsible for holding the current position of the sprite, the next position of the sprite, and the speed at which the sprite moves.
You would extend Sprite to get one Pacman class, and a Chaser class with 4 instances.
Pacman class
The Pacman class would be responsible for the mouth animation.
Chaser class
The Chaser class would be responsible for determining whether to chase the Pacman, or run away from the Pacman.
Swing Tips
You should not extend Java Swing components, unless you are overriding one or more of the component classes. You should use Swing components.
You should always start your Swing GUI on the Event Dispatch Thread (EDT). You do this by executing the invokeLater method of SwingUtilities.
You should have a GUI model, separate from your GUI components. The three classes I defined would be part of your GUI model. You also need to lay out a maze.

How Do I Draw Two Overlapping JPanels such that the Graphics Drawn On Both Of Them Display On The Screen?

I have two JPanels. One panel has a 100x100 rectangle drawn at 0,0. And the other has a 100x100 rectangle drawn at 100, 100. My problem is that when both JPanels are drawn on the JFrame's content pane, one JPanel (the last one drawn) covers the other, hiding its graphics. Below is oversimplified code drawing two rectangles and the things I've tried.
package playground;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Playground{
public Playground(){
JFrame frame = new JFrame("My Frame");
frame.setSize(400, 400);
JPanel backPanel = new JPanel(){;
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
Rectangle2D rect = new Rectangle2D.Double(0, 0, 100, 100);
g2.draw(rect);
}
};
JPanel frontPanel = new JPanel(){
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
Rectangle2D rect = new Rectangle2D.Double(150, 150, 100, 100);
g2.draw(rect);
}
};
frontPanel.setOpaque(true); //Does nothing
frontPanel.setBackground(new Color(0, 0, 0, 0)); //Does nothing
frontPanel.setForeground(new Color(0, 0, 0, 0)); //Erases the rectangle drawn
frame.getContentPane().add(backPanel);
frame.getContentPane().add(frontPanel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public static void main(String[] args){
System.out.println("Hello World");
new Playground();
}
}
If anyone cares why I want to do this,
I'm creating the game breakout. I am a novice programmer and I have no knowledge of gaming theory. So I decided the smartest way to avoid a lot of rendering and buffering is to have four JPanels. A static JPanel at the very back with an image drawn on it (fun background image). A JPanel with the paddle drawn on it. A JPanel with bricks drawn on it. And a JPanel with a ball drawn on it. My rationale is that I won't have to redraw the paddle if it is not being moved, the background, and bricks that are not being hit. If a brick lets say is hit, I will update an arrayList of bricks and call repaint on the corresponding JPanel.
I am a novice programmer and I have no knowledge of gaming theory.
Ok, we can work with that.
So I decided the smartest way to avoid a lot of rendering and buffering is to have four JPanels.
You've just unnecessarily complicated your program.
Think of a JPanel as a canvas. You want to draw the entire Breakout game; bricks, paddle, and ball, on one JPanel canvas. Don't worry, you'll be able to redraw the entire canvas fast enough to get 60 frames per second if you want.
The way to do this is to create a Brick class, a Paddle class, and a Ball class. You create a Game Model class that contains one instance of the Paddle class, one instance pf the Ball class, and a List of instances of the Brick class.
The Brick class would have fields to determine its position in the wall, the number of points scored when the ball collides with the brick, the color of the brick, and a draw method that knows how to draw one brick.
The ball class would have fields to determine its x, y position, its direction, its velocity, and a draw method that knows how to draw the ball.
The Paddle class would have fields to determine its x, y position, its direction, its velocity, and a draw method that knows haw to draw the paddle.
The Game Model class would have methods to determine when the ball collides with a brick, determine when the ball collides with a brick, determine when the ball collides with a wall, and a draw method that calls the other model class draw methods to draw a ball, a paddle, and a wall of bricks.
This should be enough for now to get you started going in the right direction.
Edited to answer questions:
How would I implement a draw method in all these classes?
Here's an example Ball class. I haven't tested the moveBall method, so it might need some adjustment
import java.awt.Graphics;
import java.awt.geom.Point2D;
public class Ball {
private Point2D position;
/** velocity in pixels per second */
private double velocity;
/**
* direction in radians
* <ul>
* <li>0 - Heading east (+x)</li>
* <li>PI / 2 - Heading north (-y)</li>
* <li>PI - Heading west (-x)</li>
* <li>PI * 3 / 2 - Heading south (+y)</li>
* </ul>
* */
private double direction;
public Point2D getPosition() {
return position;
}
public void setPosition(Point2D position) {
this.position = position;
}
public double getVelocity() {
return velocity;
}
public void setVelocity(double velocity) {
this.velocity = velocity;
}
public double getDirection() {
return direction;
}
public void setDirection(double direction) {
this.direction = direction;
}
public void moveBall(long milliseconds) {
Point2D oldPosition = position;
// Calculate distance of ball motion
double distance = velocity / (1000.0D * milliseconds);
// Calculate new position
double newX = distance * Math.cos(direction);
double newY = distance * Math.sin(direction);
newX = oldPosition.getX() + newX;
newY = oldPosition.getY() - newY;
// Update position
position.setLocation(newX, newY);
}
public void draw(Graphics g) {
int radius = 3;
int x = (int) Math.round(position.getX());
int y = (int) Math.round(position.getY());
// Draw circle of radius and center point x, y
g.drawOval(x - radius, y - radius, radius + radius, radius + radius);
}
}
The draw method draws the ball wherever it actually is located. That's all the draw method does.
Actually moving the ball is the responsibility of the Game Model class. The method for moving the ball is included in this class because the information necessary to move the ball is stored in the Ball class.
I gave the ball a radius of 3, or a diameter of 6 pixels. You may want to make the ball bigger, and use the fillOval method instead of drawOval.
should I just call repaint() at a 30ms interval
Basically, yes.
In psudeocode, you create a game loop
while (running) {
update game model();
draw game();
wait;
}
First, you update the game model. I gave you a Ball class. You would have similar classes for the paddle and bricks. They all have draw methods.
Your Game model class calls all of these draw methods in the proper order. In Breakout, you would draw the boundaries first, then the bricks, then the paddle, and finally, the ball.
Your JPanel (canvas) calls the one draw method in the Game Model class.
I don't have an example game to show you, but if you read the article Sudoku Solver Swing GUI, you'll see how to put together a Swing GUI and you'll see how model classes implement draw methods.
I suggest that you stop working on Breakout for a while and go through the Oracle Swing Tutorial. Don't skip any sections in your haste to write a program. Go through the entire tutorial so you understand how Swing works before you try and use it.

SWT - Drawing a Moving Image Over Tiles

I've been experimenting with different ways of moving a image over a grid of tiles for a game, but I've been unable to get a working implementation.
First I tried using a grid layout to hold a bunch of Tiles that extended Canvas and drew themselves. This drew the tiles nicely, however it seems that I am unable to draw my Player object on top of them. Originally, the Player also extended Canvas and I intended to have the widget on top of the tiles. It seems like this is impossible.
I then tried to have the Tile simply extend nothing, and just hold the image. I then hold each Tile in a 2D array and draw each Tile by a nested for loop, using the int from the for loop, multiplied by the image size, to draw Tile's Image. I put this code in a PaintListener inside of my constructor for my Map class that extended Canvas and dropped my Map onto my Shell in a Fill layout, but the PaintListener never gets called (I tested with a print statement).
What implementation could I use to draw the Tiles at the start of the game, then allow me to control the movement of my Player image?
I did something similar.
Using a PaintListener I get the calls when the Widget needs to be repainted. In my paint function, I loop over a tile array (wrapped in a World class) and draw all tiles. Afterwards I use the same technique with a worldObjects array/class:
public class WorldWidget extends Canvas {
WorldWidget() {
addPaintListener(new PaintListener() {
#Override
public void paintControl(PaintEvent e) {
WorldWidget.this.paintControl(e);
}
});
}
protected void paintControl(PaintEvent e) {
GC gc = e.gc;
for (short y = 0; y < world.getHeight(); y++) {
for (short x = 0; x < world.getWidth(); x++) {
final ITile tile = world.getTile(x, y);
final Image image = ImageCache.getImage(tile);
gc.drawImage(image, x * tileSize, y * tileSize);
}
}
// Here is used a similar loop, to draw world objects
}
}
This is obviously a condensed code example, as the class is part of an editor and reacts on mouse clicks and movement amongst other things.
When I did a tile based simulation while ago I did it this way:
I had 2 layers of the tile map - one for the terrain and second for the units.
The map itself was represented by a JPanel.
So roughly you got this for the JPanel:
public void paintComponent(Graphics graphics) {
// create an offscreen buffer to render the map
if (buffer == null) {
buffer = new BufferedImage(SimulationMap.MAP_WIDTH, SimulationMap.MAP_HEIGHT, BufferedImage.TYPE_INT_ARGB);
}
Graphics g = buffer.getGraphics();
g.clearRect(0, 0, SimulationMap.MAP_WIDTH, SimulationMap.MAP_HEIGHT);
// cycle through the tiles in the map drawing the appropriate
// image for the terrain and units where appropriate
for (int x = 0; x < map.getWidthInTiles(); x++) {
for (int y = 0; y < map.getHeightInTiles(); y++) {
if (map.getTerrain(x, y) != null) {
g.drawImage(tiles[map.getTerrain(x, y).getType()], x * map.getTILE_WIDTH(), y * map.getTILE_HEIGHT(), null);
}
}
}
if (map.getSimulationUnits() != null) {
for (Unit unit : map.getSimulationUnits()) {
g.drawImage(tiles[unit.getUnitType()], (int) Math.round(unit.getActualXCor() * map.getTILE_WIDTH()), (int) Math.round(unit.getActualYCor() * map.getTILE_HEIGHT()),
null);
}
}
// ...
// draw the buffer
graphics.drawImage(buffer, 0, 0, null);
}
Logic:
private Terrain[][] terrain = new Terrain[WIDTH][HEIGHT];
/** The unit in each tile of the map */
private Unit[][] units = new Unit[WIDTH][HEIGHT];
Then you have your game loop where you update the position of the units and other things, basically render() and update() the game. Check the links I've provided below.
NOTE
Since you are making a simple game this post about making game loops will be definitely useful for you. This hopefully also answer your question about moving the object on the map.
This site will be also very helpful since you will probably need to detect collision at some point too.

Appropriate way to draw to a JPanel for 2D Game development?

I am wanting to make a 2D Game but I am having trouble finding the best and most efficient way to draw to a 2D Surface/Canvas using a BufferStrategy. I will be using a JFrame as the main window and want to draw to the surface of that. I would love to see some example code of how this is done (if this is a good way to do it) aswell as other examples. It would be great if someone could also explain some advantages and disadvantages of doing so.
I am currently adding my drawing class to my JFrame using 'frame.add(new Painter());' and then overriding the paintComponent method. The only problem I have found with this is it seems to only call this method once.
this is my Painter class:
public class Painter extends JPanel{
public static Player player;
public Painter() {
player = new Player(300, 300);
}
public void paint(Graphics g) {
player.x++;
g.setColor(Color.white);
g.fillRect(player.x, player.y, 32, 32);
}
}
In the simplest case, where your game needs to update the screen based on user actions only, you will need to call repaint() (on the JFrame for instance) when you update something.
In other cases, you need to construct a so called Game Loop, where you update the game state and render updated game state in a timely manner. A nice tutorial on a simple Game Loop with Java code can be found here: http://obviam.net/index.php/the-android-game-loop/
In case you develop a serious game, you should stick with a game engine for managing the game loop and other routine game development aspects. A good Java game engine can be found here: http://code.google.com/p/playn/
For a game loop and an efficient way to printing to the screen for game objects and others(images, explosions, etc), you need a timing mechanism and a JFrame to print to over and over again, while at the same time, getting updates from the player and the game state itself.
First, you need a main method, and im going to guess that you are pretty new to this type of field in java so I will break it down for you as fast and clearly as possible.
public static main(String[] args)
{
yourClassName game = new yourClassName();
game.run();
System.exit(0);
}
This main method is doing 2 things first, creating an object to your run method (because static references are not recommended for game loops) and then calling that method with your newly created object. When that run method is done, the system will exit the program.
Now im not going into full detail here but once you have the run method running you need an init method that will only run once. In this method, you are going to create your JFrame that you are rendering to. That is fairly simple to do so, so i will not go any farther in creating a JFrame class. However your Run method should look something like this...
void Run()
{
init();
while(isRunning)
{
update();
draw();
time = (1000 / fps) - (System.currentTimeMillis() - time);
if (time > 0)
{
try
{
Thread.sleep(time);
}
catch(Exception e){}
}
}
}
This method will slow the computing speed to a certain fps that you desire(in this case the fps variable is named fps for show).
Now in your update method, you will have your keyBinding and other stuff to look for changes with the keyboard or mouse(and in my case currently, a server). Most people dont know how to print or draw to the JFrame without taking in a paramitor that the Repaint method requires, so we are going to override that by adding double buffering.
BufferedImage backBuffer;
In the initialize method you will add
backBuffer = new BufferedImage(getWidth(),getHeight(),
BufferedImage.TYPE_INT_RGB)
In the draw method you will have...
Graphics g = getGraphics();
Graphics bbg = backBuffer.getGraphics();
And with those you have the ability to draw to the JFrame created by the init class(also, by doing this, you have the ability to draw from any class or thread to decrease the processing load).
To see if it works, go ahead and use the bbg.fillRect(updateX,updateY,5,5)
The updateX and updateY represent variables that you use to update the location of the rectangle. Save them as a global variable or a public variable saved in a character class.
And if all else fails, here is sample code that i have used for creating my own game engine...
import input.InputHandler;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
/**
* Main class for the game
*/
public class GameTutorial extends JFrame
{
boolean isRunning = true;
int fps = 30;
int windowWidth = 500;
int windowHeight = 500;
BufferedImage backBuffer;
Insets insets;
InputHandler input;
int x = 0;
public static void main(String[] args)
{
GameTutorial game = new GameTutorial();
game.run();
System.exit(0);
}
/**
* This method starts the game and runs it in a loop
*/
public void run()
{
initialize();
while(isRunning)
{
long time = System.currentTimeMillis();
update();
draw();
// delay for each frame - time it took for one frame
time = (1000 / fps) - (System.currentTimeMillis()- time);
if (time > 0)
{
try
{
Thread.sleep(time);
}
catch(Exception e){}
}
}
setVisible(false);
}
/**
* This method will set up everything need for the game to run
*/
void initialize()
{
setTitle("Game Tutorial");
setSize(windowWidth, windowHeight);
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
insets = getInsets();
setSize(insets.left + windowWidth + insets.right,
insets.top + windowHeight + insets.bottom);
backBuffer = new BufferedImage(windowWidth, windowHeight, BufferedImage.TYPE_INT_RGB);
input = new InputHandler(this);
}
/**
* This method will check for input, move things
* around and check for win conditions, etc
*/
void update()
{
if (input.isKeyDown(KeyEvent.VK_RIGHT))
{
x += 5;
}
if (input.isKeyDown(KeyEvent.VK_LEFT))
{
x -= 5;
}
}
/**
* This method will draw everything
*/
void draw()
{
Graphics g = getGraphics();
Graphics bbg = backBuffer.getGraphics();
bbg.setColor(Color.WHITE);
bbg.fillRect(0, 0, windowWidth, windowHeight);
bbg.setColor(Color.BLACK);
bbg.drawOval(x, 10, 20, 20);
g.drawImage(backBuffer, insets.left, insets.top, this);
}
}
I hope this helps and I know this may be a VERY late answer but i have yet to see one decent java game engine that was simple to understand and use and fun to work with. Hope you stay with it and happy programming!!!

Difficulty understanding Java MouseEvent

I'm doing these iTunes Stanford classes, and I've been learning beginning Java. Things are going great, but they recently introduced events-and specifically MouseEvents. I've been reading the chapters in the book, and pouring through the example code, and something is just not clicking right for me...it's always that asynchronous stuff that gives me trouble :-D
Earlier, some people mentioned it was important that I mention that the "addMouseListener" is a class in the Graphics import. As far as I can tell, that just adds a blanket mouse listener to the canvas.
I'm still real new to this, so I may not be describing things as well as I should.
This is a piece of code that I have been trying to simplify in order to better understand it. Currently, it will build a red rectangle, and I can click on it and drag it along the x axis. Great!!!
import java.awt.*;
import java.awt.event.*;
import acm.graphics.*;
import acm.program.*;
/** This class displays a mouse-draggable rectangle and oval */
public class DragObject extends GraphicsProgram {
/* Build a rectangle */
public void run() {
GRect rect = new GRect(100, 100, 150, 100);
rect.setFilled(true);
rect.setColor(Color.RED);
add(rect);
addMouseListeners();
}
/** Called on mouse press to record the coordinates of the click */
public void mousePressed(MouseEvent e) {
lastX = e.getX();
lastY = e.getY();
gobj = getElementAt(lastX, lastY);
}
/** Called on mouse drag to reposition the object */
public void mouseDragged(MouseEvent e) {
if((lastX) > 100){
gobj.move(e.getX() - lastX, 0);
lastX = e.getX();
lastY = e.getY();
}
}
/** Called on mouse click to move this object to the front */
public void mouseClicked(MouseEvent e) {
if (gobj != null) gobj.sendToFront();
}
/* Instance variables */
private GObject gobj; /* The object being dragged */
private double lastX; /* The last mouse X position */
private double lastY; /* The last mouse Y position */
}
If I drag the mouse off the canvas, I want the rectangle to stay within the canvas, and not move off it (the same behavior that a horizontal scroll bar would do if you moved beyond the scroll area with the mouse button still clicked). How can I do that?
I've been trying something along these lines, but it's not working right:
if ( ( lastX > (getWidth() - PADDLE_WIDTH) ) || ( lastX < PADDLE_WIDTH ) ) {
gobj.move(0, 0);
} else {
gobj.move(e.getX() - lastX, 0);
}
Your code is moving the rectangle relative to the last position of the mouse. This works fine when you are simply moving things, but for your needs when you want it to stop at the borders, you need to use absolute positioning.
// When the mouse is pressed, calculate the offset between the mouse and the rectangle
public void mousePressed(MouseEvent e) {
lastX = e.getX();
lastY = e.getY();
gobj = getElementAt(lastX, lastY);
}
public void mouseDragged(MouseEvent e) {
double newX;
// Assuming you can get the absolute X position of the object.
newX = gobj.getX() + e.getX() - lastX;
// Limit the range to fall within your canvas. Adjust for your paddle width as necessary.
newX = Math.max( 0, Math.min( newX, getWidth() ) );
// Set the new position of the paddle, assuming you can set the absolute position.
gobj.setX( newX );
lastX = e.getX();
lastY = e.getY();
}
}
This may not be quite what you want because as soon as you go off the edge, the object will stop moving, but then once you move back toward the canvas, your paddle will move immediately instead of waiting for the mouse to reach the same relative position to the paddle at which it started.
You can probably experiment to get it to do what you want.
In order to do this you will need to know the width of the Canvas object, i'm sure there will be a method that will provide this value. You can then check the current x location of the MouseEvent against the width of the canvas, and not increment the x coordinates of the shape object once you are past the width of the canvas. Depending on how much of the shape you want to remain in the canvas, you may need to take into account the width of the shape object as well.
One thing that helps me when dealing w/ animation and moving objects in a gui is drawing out a few scenarios on paper, and noting how the coordinates change.

Categories

Resources