I'm currently in the process of making a 2D turn based strategy game in Java. At this stage in development I am testing out different varieties of AIs.
The AIs interact with the game by sending a x and y variable to a function called gameLoop, which would would normally be called by a mouseClicked function. This allows them to directly emulate a human user which has made having them interact with the game much easier. Below is an overview for some of my code:
paintComponent() {
drawTiles();
drawUnits();
if(unitSelected && gameState == DISP_MOVE)
drawMoveSpots();
}
gameLoop(int x, int y) {
// various logic based
// on the values of x and y
// logic may set unitSelected to true or false
// may also change gameState bteween normal and displayMoveSpots
repaint();
}
My issue right now stems from the fact that the AI is executing code faster than repaint happens. When this happens, I start getting errors such as NullPointerException's and ConcurrentModificationException's in my various drawing functions because the logic inside of gameLoop is changing variables faster than repaint can happen. If I add in a manual delay to how often the AI calls gameLoop or only have 2 humans playing, the errors disappear.
From my research and reading of the Java docs, I understand that repaint returns immediately and all the graphics calls are put onto the EventQueue until Java decides that there are enough calls to warrant an actual repaint. The only solution I have been able to find so far is putting the logic code that happens in gameLoop into a anonymous runnable class and call the code using SwingUtilities.invokeAndWait, but this is not a valid solution for me, because this causes actual human interaction from clicking to bug out. I have considered using a lock to provide mutual exclusion to gameLoop's logic section/repaint (so only one can execute at a time), but I'm not sure if this is a good practice when dealing with Java graphics.
At this point I'm open to any suggestions on what I can do to alleviate my problem.
Thanks.
My advise is that you should use a swing timer
import javax.swing.Timer;
public static void main(String[] args) {
/*this is 1 sec/ 1000ms*/
Timer aiTimer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
//move AI
//repaint
}
});
aiTimer.start();
}
By doing that you will not repaint so often. Hope that helps!
Related
I need a code, more specifically a game, in which meteorites fall down and the user has to dodge them with the arrow keys.
Moving the player is not my problem. I will do this at the end.
I figured out how to create an animated object falling down, from top to bottom.
How can I create a loop which creates one new falling element every 2 seconds? The position of the element should be randomized.
This is my code at the moment:
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class Game extends JPanel {
int x = 135;
int y = 0;
private void moveBall() {
y = y + 1;
}
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.fillOval(x, y, 30, 30);
}
public static void main(String[] args) throws InterruptedException {
JFrame frame = new JFrame("Meteorites");
Game game = new Game();
frame.add(game);
frame.setSize(300, 400);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
while (true) {
game.moveBall();
game.repaint();
Thread.sleep(10);
}
}
}
You will need a game loop (gl) first of all. The gl updates the state and position of every object on the screen and other state related objects not seen on the screen.
It should be easy for objects to register themselves to the gl. The gl is not responsible for updating the state of every object, but it does call back every registered object to update themselves (or update other objects that it is responsible for, for example the collision detection object will update any objects on the screen that have collided).
The gl passes the current time in nano-seconds (typically) to all the objects that have registered with the gl. For example check out the AnimationTimer in Javafx. In JavaFX, by creating an AnimationTimer, and calling the start method on it, JavaFX will call your handle method on every frame... essentially registering it to JavaFX's main thread (or game loop).
So you will need to create a gl. Register objects to it, maybe create an interface or abstract class similar to JavaFX's AnimationTimer so that objects can register themselves to your gl. The gl should not have any Thread.sleeps in it, but if it does it should be very short and I imagine you would add it in only if your gl is going to fast, because maybe there is not a lot of stuff being calculated (hence no use crippling the CPU), but you can adjust this. I know that working on JavaFX games I can tell you that we work very hard to keep any delays in he gl down to a minimum, hence no Thread sleeps, as this will halt the smooth flow of your animation.
The objects that get updated will track the passage of time and can execute events based on how much time has passed by. For example you can have an object that registers itself to the gl and is responsible for putting new falling animated objects on the screen. It can have a field for how long to wait before dropping the next item. Since it is called every frame by the gl, it can see how much time is passing by, and when x seconds pass by, it can create and drop the next object, then reset its timer.
The animated objects falling would also be called by the gl so they can smoothly drop down. At every frame these falling objects will calculate their new position. Typically this is done using an interpolator (its a good idea to look into this). Interpolators make it easy to abstract out where the position of an animating object should be relative to a fraction of time passing by. For example if I am moving an item linearly over X distance and Y time, an interpolater will tell you how far along X distance the object should be after a percentage of Y time has passed. Interpolators are very powerful because you can create an interpolator that moves your object not just linearly but can slow it up or speed it up (like gravity effect), it can even add bouncing effects to your objects. So you'll want to get a firm understanding of this. JavaFX comes with interpolators. There are also online tools I have seen in the past that let you construct your own JavaFX interpolators based on the kind of animation effect you are trying to get.
So to put it all together, have a gl, have an object that registers to the gl that is responsible for putting new falling objects on the screen, lets call this object ObjectDropper. The ObjectDropper will implement something like AnimationTimer so it can get called every frame by your gl. It will decide when to drop objects and can also decide the random point from where that object will drop. You will have objects that will drop, they will be given information like where their starting point is, where their ending point is, and how long it will take to drop, and maybe even an interpolator. These objects will also be registered to the gl and be called every frame so that they can adjust their position according to their interpolator.
This is all a very broad generic outline that tends to be true for most animated games. There might be tools and libraries that make this all easily implemented in Swing. As you can see JavaFX was already designed to handle many of these concepts.
My KeyPressed is working but there is no change in avatarDX from my moveLeft method. Did I do something wrong with moveLeft? It seems like it should be simple but I'm not sure what I did wrong. I don't have any error messages.
I'm drawing an image on a canvas with
gc.drawImage(avatar, avatarSX, avatarSY, avatarSW, avatarSH, avatarDX, avatarDY, avatarDW, avatarDH);
For KeyPressed I have
canvas.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent ke) {
double speed = 2;
switch(ke.getCode()) {
case A:
System.out.println("pressed a");
moveLeft(avatarDX, speed);
System.out.println(avatarDX);
break;
}
}
});
And moveLeft is
private void moveLeft(double avatarDX, double speed) {
while (avatarDX > 0) {
avatarDX -= speed;
}
}
I would appreciate any help I am very new at this.
A reason (there may be others) the program doesn't act as you expect is that you are trying to process via a while loop to control your avatar.
This will just pause the JavaFX application until the loop is completed and then update the screen to the final state at the end of the loop. JavaFX is not an immediate drawing system, it is a retained drawing system that only renders a scene when you cede control of the JavaFX application thread back to the JavaFX system.
Instead, you need to use a callback to update the scene on each pulse.
I suggest you use the following approach:
As you are new at this, try writing the application using the JavaFX scene graph rather than a Canvas. Programming the scene graph is simply easier for many things. If, at a later stage, you find that Canvas is a better fit, then you can always convert to Canvas at that time when you better understand the JavaFX programming model.
Review this sample of moving a character around the scene graph using key presses.
Don't try to loop to move your character. Instead, either use the in-built high level JavaFX animation facilities such as transitions, or (when those aren't a good fit, as is likely the case here), use the low level AnimationTimer interface.
Read up on game loops and JavaFX and apply the knowledge you learn there to your program.
I am trying to program a Battleship game with different states depending on user input. I am implementing the State Design Pattern for the different types of game types, the instructions panel and other features it is going to have. My question is, when I make the JFrame for my game, should the logic be invoked during construction, or should it be during the update of the window?
For example:
public class BattleshipGui extends JFrame
{
public BattleshipGui()
{
initizalizeFrame();
executeLogic();
}
}
The execute logic contains the game loop that changes according to the game events.
The GUI should only be responsible for rendering. All actual game logic should be handled on your main thread, while the EDT is painting for you (repaint posts events to the EDT). As for "invokingGameLogic", this should first never be done in the constructor (considering that method contains the loop thats updating your game). You should initFrame then start your loop dedicated to updating/rendering your game. Create an interface between updating and rendering; have variables that both rendering code and updating code can access. That way, your render code renders whatever the current state is, while update code changes it.
class Game extends Canvas {
//the items used between render and update
Player player;
Enemy player;
//render values
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.BLUE);
g.fillRect(player.x, player.y, 50, 50);
g.setColor(Color.RED);
g.fillRect(enemy.x, enemy.y, 50, 50);
}
//affect values
public void update() {
player.x++;
}
}
Keep your game logic away from your rendering. It makes programming a game easier, since you'll be able to easily find code that handles graphics, and what code handles moving players and what not.
If you are using Component#paint(Graphics), then you REALLY want to seperate the game logic from rendering. Code within the paint method executes om the Event Dispatch Thread, meaning issues with updating will cause very apparent issues in GUI.
-Hi all! I'm making a Java applet that simulates wave interference, which I have almost finished (will license under GPL). However, I have two questions regarding the AWT paint cycle that I am having difficulty finding answers to.
I want to make an 'about' overlay that appears when I press a button. The way I want to do this is to draw over the entire applet window with my static message and legend objects. The problem is stopping the AWT components from drawing themselves in the foreground without using remove(). Is there a way I can stop AWT from drawing itself temporarily?
For my standing waves mode I want to have node and anti-node markers calculated and drawn to a secondary graphics every time the standing wave reaches a maximum amplitude (all of which I can do myself), but drawn to the primary graphics (and thus displayed) every paint cycle. Could someone explain the steps to do so? I imagine it would involve creating a second graphics object, drawing to it once, then drawing it to the primary graphics every cycle.
If you are able to answer either of my questions I would be very grateful!
Cheers, Jack Allison
Responding to your first question:
You can't disable the paint()/paintComponent() method if you've inlcluded it in your code. If its there, it runs. However, you can create a flag so that only if the flag is true, the stuff gets drawn. Let me show you what I mean:
boolean flag;
...
public void paintComponent(Graphics comp) {
if (flag) {
Graphics2D comp2D = (Graphics2D) comp;
//drawing statements
}
}
public void actionPerformed(ActionEvent event) {
flag = true;
repaint();
}
I've been working on a java game recently, and I have a lot of it figured out. One thing still plagues me, however. The way it's set up, a player moves across a background (the game board). Currently, every time the player moves, it repaints the whole frame, including the background. This causes a brief, yet annoying screen flicker whenever the player moves.
I've separated out my code to draw the background separately from the things that need to be repainted:
public void drawMap(Graphics pane) {...}
public void drawPlayer(Graphics pane) {...}
The problem is that I can't find a way to make the board stay on the screen when I use repaint(); , a necessity for the player to move. Any suggestions?
You should look into double buffering, basically you paint an image to the buffer, then paint the buffer. It should remove the flickering effect you are talking about. Below are a few helpful links:
http://docs.oracle.com/javase/tutorial/extra/fullscreen/doublebuf.html
http://content.gpwiki.org/index.php/Java:Tutorials:Double_Buffering
http://www.ecst.csuchico.edu/~amk/classes/csciOOP/double-buffering.html
Just comment if your having trouble understanding it.
UPDATE: I would also suggest you look in 'Killer game programming in java'. You can get a free ebook of one of the older versions. Some of it is a bit out dated, but the first few chapters about setting up a game loop and drawing to the screen etc are still very much relevant.
UPDATE 2: From the second link, try something like this:
private void drawStuff() {
BufferStrategy bf = this.getBufferStrategy();
Graphics g = null;
try {
g = bf.getDrawGraphics();
drawMap(g);
drawPlayer(g);
} finally {
// It is best to dispose() a Graphics object when done with it.
g.dispose();
}
// Shows the contents of the backbuffer on the screen.
bf.show();
//Tell the System to do the Drawing now, otherwise it can take a few extra ms until
//Drawing is done which looks very jerky
Toolkit.getDefaultToolkit().sync();
}
UPDATE 3: This post here gives a nice code sample that you can play with and adapt, that should give you the best idea on how to do double buffering
I suggest to avoid redrawing everything with every change. Instead draw the whole frame at a fixed interval, e.g. every 50ms. Just keep the status of every element in a class and if something changes just change the data value. Due to the fixed redrawing interval the display will pick up any changes at the next redraw.