This question already has answers here:
How do i get a graphics object in Java?
(2 answers)
Closed 9 years ago.
Im making a basic space invaders game. I got all the resources from the LWJGL .zip file (Im not using LWJGL librarys to create my game, just got the pictures, etc. from it.) Anyway, whenever i press "space" on my keyboard, my KeyListener creates a new bullet that my ship fires. However, i dont know how to draw the bullets image, since my KeyListener doesnt pass a graphics object, and you need one to draw a image. The code thats causing the problem is the "drawImage" method in the "Shot" constructor. Heres my code:
public class KeyTyped{
public void keyESC(){
Screen.isRunning = false;
}
public void keyLEFT() {
Screen.shipPosX -= 10;
}
public void keyRIGHT() {
Screen.shipPosX += 10;
}
//This is automatically called from my KeyListener whenever i
//press the spacebar
public void keySPACE(){
if(!spacePressed){
ShotHandler.scheduleNewShot();
}else{
return;
}
}
}
public class ShotHandler {
public static int shotX = Screen.shipPosX;
public static int shotY = Screen.shipPosY + 25;
public static void scheduleNewShot() {
//All this does is set a boolean to 'false', not allowing you to fire any more shots until a second has passed.
new ShotScheduler(1);
new Shot(25);
}
}
public class Shot extends ShotHandler{
public Shot(int par1){
//This is my own method to draw a image. The first parameter is the graphics object that i need to draw.
GUI.drawImage(*????????*, "res/spaceinvaders/shot.gif", ShotHandler.shotX, ShotHandler.shotY);
}
//Dont worry about this, i was just testing something
for(int i = 0; i <= par1; i++){
ShotHandler.shotY++;
}
}
}
Thanks guys! Any help will be appreciated!
(This is a re-post because the last time i posted this, i didnt get a sufficient answer, at least for my skill level)
Start by taking a look at
Painting in AWT and Swing
Performing Custom Painting
You may also find Concurrency in Swing of some usefulness.
The basic gist is you should have some kind of model which describes the state of the game play at any moment in time. This model is updated outside the content of the Event Dispatching Thread, normally in some kind of background thread, where the time it takes to perform the updates doesn't effect the ability for Swing to continue running and remain responsive to the user.
You will need to override the JPanel's paintComponent method. In here, you would paint the model state to the screen.
There are a bunch of other techniques which could be discussed, but lets get this started
Examples...
Java Bouncing Ball
Drawing 2 Balls to move in different direction on Java but one disappeared
Multiple bouncing balls thread issue
I am trying to make ball gradually move
Java ball object doesn't bounce off of drawn rectangles like it's supposed to.
https://stackoverflow.com/questions/15858623/how-to-use-a-swing-timer-to-animate/15859932#15859932
the images are not loading
Swing animation running extremely slow
How to make line animation smoother?
Related
I'm creating a galaga clone in java. I am trying to understand the basics of a game loop and I have a couple problems/ questions. I have an update method that changes the positions of all the game objects and I have a draw method that draws all the updated positions of said objects. Should I ever call the paint() method? How do I repaint the objects (clear the screen then draw everything again)? Also, what is the best way to call my update() method at a fixed rate? Thanks for any input.
public Galaga() throws IOException
{
//set the background
setBackground(Color.BLACK);
//create a controller
c = new Controller();
addKeyListener(c);
setFocusable(true);
//create a player
player = new Ship(40,50);
running = true;
}
public void paint(Graphics g)
{
super.paint(g);
}
//game loop
public void run()
{
while(running)
{
draw(getGraphics());
if(c.right())
player.move(1, 0);
}
}
//update logic
public void update()
{
}
//renders all objects
public void draw(Graphics g)
{
player.draw(g);
}
I have an update method that changes the positions of all the game objects
Yep, good
and I have a draw method that draws all the updated positions of said objects
Ah...not so good
Should I ever call the paint() method?
No, you should never have to do this, in fact, you shouldn't be call getGraphics either, this is not how painting is done.
You would normally call repaint to trigger a new paint cycle.
Have a look at Painting in AWT and Swing and Performing Custom Painting for how painting works in Swing - I'm assuming you're using Swing based on the methods you're using
Also, beware, Swing is single thread - see Concurrency in Swing for more details and How to use Swing Timers for more a simple solution.
If you still want to use a Thread, beware, that painting can occur at any time for any number of reasons, which are beyond your control, so you need to be aware of the risk of dirty paints when update states
If you want more control over the paint/update cycle, then you should have a look at BufferStrategy and BufferCapabilities
There is really good code for a game-loop found here. Thought I would just share this link to save people time!
Use the loop with the fixed time-step. The code can be greatly simplified, as the code creates a bouncing ball example to show the interpolation of frames for smoothness and the benefits of that type of loop.
I have created a pastebin with my simplified version of the code from the website above. I separated the game loop into separate classes, and added more comments in the code to show what is happening. This makes it easier to create your own game without going through the whole game loop to see what's happening and how it works.
Let me know any suggestions to my pastebin version, and I will update it :)
Folder layout:
src
|
|--app
|
|--game
| |
| |--logic
| | .--Game.java
| |--loop
| .--GameLoop.java
| .--GamePanel.java
|
.--App.java
.--Window.java
So, I'm creating a isometric game in JAVA; to put it simply, it has a map made of tiles, and when the user drag the mouse on the screen, the map moves. To give you guys an idea, it currently looks like this:
Before the actual prototype version, I built a small java application that had no "Game Loop" per se; the only thing updating the tile-map position was the event listeners for the mouse dragging, and it called the repaint() method after the movement update. It worked fine, and I could select Tiles and move the map without a problem.
Since then, I rebuild the prototype thinking in developing something more like a real game engine, with a Game States Manager and a real Game Loop; the Game Loop code looks like this:
init();
long start;
long elapsed;
long wait;
while(running){
start = System.nanoTime();
update();
draw();
drawToScreen();
elapsed = System.nanoTime() - start;
wait = targetTime - elapsed / 1000000;
if(wait < 0)wait = 5;
try{
Thread.sleep(wait);
} catch(Exception e) {
e.printStackTrace();
}
}
A GameStateManager is acessed with the update() and draw() methods, so I can draw the map just fine, like the older prototype. The problem happened when I dragged the mouse on the screen so the map could move; the animation became VERY croppy, in the extent that I could actually see the black background between the tiles moving before the final position of all tiles.
At first I thought the problem was in a concurrency between the Game Loop thread and the event listeners of the main class, because the mouseDragged event could be called while the JPanel was trying to draw what the map was like a moment ago; then I tested this on my game loop code:
SwingUtilities.invokeLater(new Runnable(){
public void run(){
draw();
drawToScreen();
}
});
//draw();
//drawToScreen();
And now the prototype is working just fine.
So, my question is, is this performance heavy, or just a bad practice in Java? Also, was my "concurrency" assumption right? This is my first time dealing with threads in Java, so I don't really know if I dealed with the problem the right way.
obs: This is the entire class I made in which the game loop is in:
http://pastebin.com/RMRHYc5X
obs2: If someone is interested, the game loop logic I worked in was based on a youtube java tutorial made by pj644 named "2D Game Programming in Java".
obs3: Sorry if the question became too big, this is my first time posting a question on this site!
Swing isn't really a great technology for writing games, it updates slowly and is quite limited in a number of ways.
Having said that yes calling invokeLater or using a SwingWorker would be the correct way to update the screen when your game thread has done its work.
Before you go too much further though I really suggest looking at a 2d or 3d java graphics and game framework. You will be able to get much better results that way and a lot of the work in terms of setting up the game loop, updates, managing frame rates, etc will be done for you.
To preface, sorry for the semi-vague question. I have been searching for weeks to this question.
So I understand the necessities a game needs. (init, update, draw).
To be more specific, I'm using Java with Slick2D as a learning environment. Slick2D makes the set-up of the game pretty simple. I think I understand MVC structure, but my biggest problem right now is where do I write the player movement code?
I have the Player class which has all the fields of where it's at, what image/animation it needs to render, and right now, all of the controls. It doesn't feel right, though that could just be my own insecurities. I feel that the player movement controls should be in its own class, the collision detection in its own class, and the all the player would have is simply what defines it(coordinates, size, hit box, etc.).
I'm not entirely sure how to explain this, as every time I think about it, my mind races all over the place and I have a hard time "chunking" this information in my head.
If you need more information, please let me know. I'm just trying to find the "right" place for everything to make my code more manageable, readable, and possibly reusable.
(edited to show program flow[not runnable]) As it is, my flow is,
public class Game extends StateBasedGame{
//with main included, this sets up the window, fps, etc...
start "level 1" state...;
}
public class level1 extends BasicGameState{
public void init(){
initialize player, environment, etc...
}
public void update(){
player.update();
}
public void render(){
environment.render();
player.render();
}
}
public class Player{
All player variables
public Player(){
construct player with coords and starting image/animation
}
public void init(){
initializes all images/animations this player could potentially use
}
public void update(){
All environment based input controls go here, example;
if(this.isGrounded && input.keyIsDown(KEY_SPACE){
make character jump;
change animation;
}
}
public void render(){
draw image/animation;
}
}
Collision detection should be taken care of in the update method. If you mean movement from input, you should create a separate class for input. Store the position coordinates for a character in the character object. The Input class should then change these coordinates. If you are talking about an AI you should have a separate AI class that controls the movement.
Every game is different. The important part is that you understand the code and that it works. I recommend finding helpful online tutorials that show example code so you can begin to understand the workflow of creating a game.
As you can see from the code below, I am trying to import "someimage.png". This is so in the render loop, I have the right image. I put the second line of code. Is this possible? If not what is an alternative. Thanks in advance.
public void update(GameContainer container, int delta) {
//if statement
Image x = new Image("someimage.png");
}
public void render() {
//draw image x
}
There is an "unhanded exception type SlickException".
It looks like the variable "x" is local to the update() function. If your render() function tries to call "x", maybe it's a different variable?
Check out this Slick tutorial here, it shows a basic game setup with image loading. Basically, you want the Images to be member variables of your game class, their values set in the init method, and the render method to draw the pictures. Although this setup will work fine for very simple games, you are probably better off using objects to represent the different things in your game. I hope this helps.
I'm planning to write a simple spaceshooter. I have read that the repaint() method is only a request, and it doesn't execute every time it's called. I believe I'm noticing the effects of this, as my spaceship tends to lag ever so slightly when I'm moving it. Currently I'm simply drawing my ship in a a JPanel's paintComponent() method, and keep calling repaint() on regular intervals (my panel's also Runnable). Seeing as repaint() may potentially screw me over, I'm trying to find a way to work arround it, however I've ran out of ideas. The code I have so far:
private void renderGraphics() {
if (MyImage == null) {
MyImage = new BufferedImage(getPreferredSize().width,
getPreferredSize().height, BufferedImage.TYPE_INT_RGB);
}
MyGraphics = MyImage.getGraphics();
MyGraphics.setColor(Color.BLACK);
MyGraphics.fillRect(0, 0, getPreferredSize().width, getPreferredSize().height);
MyGraphics.drawImage(ship.getImage(), ship.getCurrentX(), ship.getCurrentY(), null);
}
The idea was to create my own graphics, and then make the JPanel draw it, and keep calling this instead of repaint() in my run() method, however I have no idea how to do that. I'd appriciate any input on the matter.
There are multiple ways to approach this.
The best is probably to use BufferStrategy and draw to that, of which I have included a code snippet that should work for you.
You can take this one step further and abandon Swing altogether, just using Frame/BufferStrategy. There is a fully working example (from which the code snippet was taken and adapted) in my question here:
AWT custom rendering - capture smooth resizes and eliminate resize flicker
Anyway, here is an implementation BufferStrategy that you should be able to just drop in:
// you should be extending JFrame
public void addNotify() {
super.addNotify();
createBufferStrategy(2);
}
private synchronized void render() {
BufferStrategy strategy = getBufferStrategy();
if (strategy==null) return;
sizeChanged = false;
// Render single frame
do {
// The following loop ensures that the contents of the drawing buffer
// are consistent in case the underlying surface was recreated
do {
MyGraphics draw = strategy.getDrawGraphics();
draw.setColor(Color.BLACK);
draw.fillRect(0, 0, getPreferredSize().width, getPreferredSize().height);
draw.drawImage(ship.getImage(), ship.getCurrentX(), ship.getCurrentY(), null);
draw.dispose();
// Repeat the rendering if the drawing buffer contents
// were restored
} while (strategy.contentsRestored());
// Display the buffer
strategy.show();
// Repeat the rendering if the drawing buffer was lost
} while (strategy.contentsLost());
}
any drawing will still be performed in the Swing Thread, so no matter what you try work around, it wont help.
Make sure you are not doing any lengthy calculations in the swing thread, this may be stopping repaint from being executed as soon as it needs to be executed
Separate all the logic into 2 parts. Static and Dynamic. (e.g. sea and moving ship. Ship changes shape/location on a static image of sea)
Draw static content in an image once and use the image in your paintComponent(). Call dynamic parts painting after the static image.
Use setClip() to restrict repainting areas.
Calling repaint without any arguments means that the whole panel is repainted.
If you need to repaint parts of the screen (the spaceship has moved to a different location) you should make shure that only those parts of the screen are repainted. The areas that stay the same should not be touched.
Repaint takes coordinates of a rectangle that should be repainted. When moving the ship you should know the old coordinates of the ship and the coordinates the ship should move to.
repaint( oldShipCoordinateX, oldShipCoordinateY, shipWidth, shipLength );
repaint( newShipCoordinateX, newShipCoordinateY, shipWidth, shipLength );
This is usually much faster than calling repaint() without arguments. However you have extra effort to remember the last position of the ship and must be able to calculate the new position of the ship.
See also: http://download.oracle.com/javase/tutorial/uiswing/painting/index.html - especially step 3
Just for code that you post here:
1/ if you want to display Image/ImageIcon, then the best and easiest way is to Use Labels
2/ as you mentioned Runnable{...}.start(); Swing is simple threaded and all output to GUI must be done on EDT; you have to look at Concurrency in Swing, result is that all output from BackGround Task(s) must be wrapped into invokeLater(), and if is there problem with perfomancie then into invokeAndWait()
3/ if you be switch (between JComponents)/add/delete/change Layout then you have to call revalidate() + repaint() as last lines in concrete code block
EDIT:
dirty hack would be paintImmediately()
I have read that the repaint() method is only a request, and it doesn't execute every time it's called
It consolidates multiple repaint() requests into one to be more efficient.
I believe I'm noticing the effects of this, as my spaceship tends to lag ever so slightly when I'm moving it.
Then post your SSCCE that demonstrates this problem. I suspect the problem is your code.
Regarding the solution you accepted, take a look at Charles last posting: Swing/JFrame vs AWT/Frame for rendering outside the EDT comparing Swing vs AWT solutions.