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.
Related
just to preface, I am very new to java. So expect dumb mistakes.
I am trying to do a project with java's drawing panel, within BlueJ, and I cannot figure out how to make a program that has a moving object. This is a project, so the code is provided. We are having to modify it in whatever way we want. We are not able to add any other packages.
I know it has to do with some sort of loop, but I am making some type of mistake where it is just printing a ton of circles, instead of a new type every time I press refresh. Here is the code.
import java.awt.*;
import javax.swing.*;
public class DrawingPanel extends JPanel {
public void paintComponent(Graphics g)
{
// clear screen
g.setColor(Color.white);
g.clearRect(0,0,500,500);
{
System.out.printf("Spring Design Barker Spring 2018%n");
int x = 125;
int y = 125;
int w = 50;
int h =80;
int b = 50;
int rd = 255 ;
int gn = 255 ;
int bl = 0 ;
Circle c1,c2;
Rectangle r1,r2;
Triangle t1,t2;
Color clr1,c;
clr1 = new Color(rd,gn,bl);
r1 = new Rectangle(x,y,w,h,clr1);
clr1 = new Color(106,96,200);
t1=new Triangle(x,y,w,h,clr1);
clr1 = new Color(220,15,15);
c1=new Circle(x,25,25,clr1);
r1.draw(g); /*display the rectangle */
t1.draw(g); /*display the triangle */
c1.draw(g); /*display the circle */
t1.setH(-h); /*display the triangle */
t1.setColor(new Color(15,220,15)); /*display the triangle */
t1.draw(g); /*display the triangle */
x=200;
y=200;
for(int k=0;k<9;k++)
{
c=new Color(255-k*20,0+k*15,0+k*25); // vary color
c1=new Circle(200,10 * k,50,c);
c1.draw(g); /*display the new circle */
}
//c=new Color(0,255,0); // change paint in can to green
//c2=new Circle(300,50,10,c);
//c2.draw(g); /*display the new circle */
}
}
}
The mistake is that you are drawing the circle again and again. Every time the code in the loop runs a new circle is drawn. You have to understand that when you draw the circle you are not actually redrawing the same circle but drawing a new circle. What I understand you want to do is to make a circle moving. You can do that by running this whole method again and again. The way I prefer to do this is by using the Swing Timer. It is a way to run a loop calling the paintComponent() method in simple words.
I am actually working on something and I am using this library to display graphics. The only thing I don't like about that is that it uses a lot of CPU. Maybe there is better way to do this.
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 ;)
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.
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!!!
I have a field which extends BitmapField (called AnimatedGIFField) and an AnimatorThread (extending Thread) which does the work of looping through the GIF frames, incrementing the current frame and invalidating the field, which calls the paint() method to draw the next frame (resulting in animation). The animation code works fine, but my issue is in the paint() method of the AnimatedGIFField class. I'm calling 'graphics.drawImage()' and I'm having trouble getting proper positions for x and y (the first two args to drawImage()). Positioning the AnimatedGIFField is working and is accomplished by overriding 'getPreferredWidth()' and 'getPreferredHeight()'. The relevant code is here:
public class AnimatedGIFField extends BitmapField {
/**
The current frame in the animation sequence; the AnimatorThread
increments this so paint() knows which frame to draw.
*/
private int currentFrame;
public AnimatedGIFField(GIFEncodedImage image, long style) {
super(image.getBitmap(), style);
this.image = image;
this.preferredWidth = this.image.getWidth();
this.preferredHeight = -(this.image.getHeight() * 4);
}
protected void paint(Graphics graphics) {
// Calling super draws the first background frame.
super.paint(graphics);
// Don't redraw if this is the first frame.
if (this.currentFrame != 0) {
// Draw the animation frame.
/* getFrameLeft() and getFrameTop() both return the top left-most position (0, 0). */
/* graphics.drawImage(this.image.getFrameLeft(this.currentFrame), */
/* this.image.getFrameTop(this.currentFrame), */
/* this.image.getFrameWidth(this.currentFrame), */
/* this.image.getFrameHeight(this.currentFrame), */
/* this.image, this.currentFrame, 0, 0); */
/*
Currently trying some hackish nonsense like this to position the frame, although
it probably won't scale properly across different devices/screen sizes.
*/
int x = (this.getManager().getWidth() / 2) - 45;
int y = (this.getManager().getHeight() / 2) + 83;
graphics.drawImage(x, y,
this.image.getFrameWidth(this.currentFrame),
this.image.getFrameHeight(this.currentFrame),
this.image, this.currentFrame, 0, 0);
}
}
}
What about something like this?
graphics.drawImage(this.image.getFrameLeft(this.currentFrame,
this.image.getFrameTop(this.currentFrame),
this.image.getFrameWidth(this.currentFrame),
this.image.getFrameHeight(this.currentFrame),
this.image, this.currentFrame, 0, 0);
Fixed the problem by stripping out all getPreferredWidth/Height stuff, removed the sublayout() method on my custom field and just overwrote layout() inside of my custom manager to position all fields (just the one) properly. That caused image.getFrameLeft() and image.getFrameTop() return proper values, which is where I had my positioning hacks before.
Thanks for the responses. I was making it way more complicated than it needed to be.