I am trying to make a small 2D java game by following this YouTube Guide.
I am not following it 100% but sort of along those lines. When I try to run my program, it opens infinitely and does not stop opening. I have turned it into a thread(??) and added starts and stops but it does not seem to work. Any ideas?
public class game implements Runnable {
private Thread thread;
private boolean running = false;
private BufferStrategy bs;
private Graphics g;
public game(){
}
private void init(){
new frame();
}
private void update(){
}
private void render(){
frame frame = new frame();
bs = frame.getCanvas().getBufferStrategy();
if (bs == null){
frame.getCanvas().createBufferStrategy(3);
return;
}
g = bs.getDrawGraphics();
}
public void run() {
init();
while(running){
update();
render();
}
stop();
}
public synchronized void start(){
if(running)
return;
running = true;
thread = new Thread(this);
thread.start();
}
public synchronized void stop(){
if(!running)
return;
running = false;
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
It runs off a Launcher class like so.
public static void main(String[] args) {
game game = new game();
game.start();
}
I can ive the full code if need be. I have tried asking the creator and looking at his source code but everything seems to be identical (apart from the display deviations I have made to alter my game from his.)
TL:DR Game opens infinite frames and eventually crashes.
Had to make getCanvas() method static and it worked. Simple.
Related
I'm working on a snake clone project and am having trouble implementing a feature where the background music stops only during my game over screen.
My music plays during the first round of gameplay but won't start up again after I press the reset key for the game. I've debugged as far as to see that the thread I created to loop the music dies at game-over, and I'm not sure how to fix this.
SoundFX backgroundSongLoop = new SoundFX(backgroundSong, true);
public Gameplay() {
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
//the speed of the snake
timer = new Timer(clockSpeed, this);
backgroundSongLoop.start();
timer.start();
}
public void paint(Graphics g){
//region Snake-Snake Collision
for (int b=1; b<lengthOfSnake; b++){
if(snakeXLength[b]==snakeXLength[0] && snakeYLength[b]==snakeYLength[0]){
backgroundSongLoop.setOn(false);
failedSoundFX.playSound(failSound);
failed=true;
timer.stop(); }
//region restart function
if (failed){
if (e.getKeyCode()==KeyEvent.VK_SPACE) {
backgroundSongLoop.setOn(true);
score = 0;
lengthOfSnake = 3;
moves = 0;
repaint();
timer.start();
}
}
//endregion
public class SoundFX extends Thread{
private final String soundName;
public boolean isOn;
public SoundFX(String soundName) {
this.soundName = soundName;
}
public SoundFX(String soundName, boolean isOn) {
this.soundName = soundName;
this.isOn = isOn;
}
public void setOn(boolean on) {
isOn = on;
}
public void playSound(String soundName){
File soundFile = new File(soundName);
try{
Clip clip = AudioSystem.getClip();
clip.open(AudioSystem.getAudioInputStream(soundFile));
clip.start();
} catch (Exception e){
e.printStackTrace();
}
}
#Override
public void run() {
//public void loopSong(String soundName, boolean playing){
File soundFile = new File(soundName);
try{
Clip clip = AudioSystem.getClip();
clip.open(AudioSystem.getAudioInputStream(soundFile));
while (isOn){
clip.loop(Clip.LOOP_CONTINUOUSLY);
}
while (!isOn){
clip.stop();
}
} catch (Exception e){
e.printStackTrace();
}
}
}
Two things:
Use clip.setFramePosition(0) to position the clip for restarting at the beginning.
Put the clip.open() method in a constructor and make clip an instance variable. That way, when you wish to restart it, only the following is needed:
public void play() {
clip.setFramePosition(0);
clip.loop();
}
By doing this, you avoid reloading from the file location every time you wish to play the cue. The cue won't even start until all the file operations are done, so this method gives you a much quicker start.
I think if you do this, you can also write a method that calls clip.stop() and just call all of these methods directly on your SoundFX class without needing to extend Thread.
I am developing a simple game that is running in its own thread. When I launch the app, everything works great. The thread is initialized, and the update() and render() functions are executed from the thread, which is great.
If I exit the app, the thread is paused. When I launch the app again, the thread resumes fine. (The update and render functions are executed). Again, this is great!
THE PROBLEM IS, I hit the power button to sleep the device. When I resume the app from this state, the render() function appears to work fine; it is drawing all of the elements (background, sprites, etc) exactly where they were before I hit the button. BUT: The sprites are no longer animated! After a bit of tracing and troubleshooting, I found out that although the "running" flag is set to TRUE, it seems like the "RUN" function is no longer running! (On an another note, I am confused why the RENDER function works, but the UPDATE function does not, unless the app is rendering the objects from some other method? Irregardless...)
I am working this problem from two possible angles; I am either missing something in the way Android manipulates my thread, OR would this have something to do with the fact that the member variables inside the objects have been reset...
Can someone please review the basic code I have posted below, and advise any suggestions on how to improve thread manipulation? I am an intelligent person, and I have read so many articles from different sources on the subject, and am more confused now more than ever! Am I on the right track here, or is there a more intelligent way of doing this? Do I really need a separate game thread, or can I run it using the device's main thread instead?
Thanks for your feedback!
public class GameThread extends Thread {
private boolean running=false;
private SurfaceHolder surfaceHolder;
private GameView gameView;
public GameThread(SurfaceHolder surfaceHolder, GameView gameView){
super();
this.surfaceHolder = surfaceHolder;
this.gameView = gameView;
}
#Override
public void run(){
Canvas canvas;
// THIS ONLY RUNS ONCE!
// WHEN I TURN THE POWER OFF AND BACK ON, THE RUN() METHOD DOES NOT RUN!
// ODDLY ENOUGH, IF I WERE TO EXIT THE APP, AND GO BACK IN, IT WORKS FINE!
// THE running FLAG SEEMS TO CHANGE PROPERLY WHEN PAUSING OR RESUMING (SEE gameView, BELOW).
while (running){
canvas = null;
try {
canvas = this.surfaceHolder.lockCanvas();
synchronized (surfaceHolder){
gameView.update();
gameView.render(canvas);
}
} finally {
if (canvas!=null)
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
public void setRunning(boolean running){
this.running = running;
}
}
public class GameView extends SurfaceView implements SurfaceHolder.Callback {
private GameThread thread;
private GameScreen gameScreen;
public GameView(Context context) {
super(context);
gameScreen = new GameScreen(context);
setFocusable(true);
thread = new GameThread(getHolder(), this);
getHolder().addCallback(this);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
thread = new GameThread(getHolder(), this);
thread.setRunning(true);
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
while (retry){
try {
thread.join();
retry = false;
} catch (InterruptedException e){
// Do nothing; continue trying
}
}
}
public void render(Canvas canvas){
gameScreen.draw(canvas);
}
public void update(){
gameScreen.update();
}
#Override
public boolean onTouchEvent(MotionEvent event){
gameScreen.touch(event);
}
public void resume(){
// TODO: Resume events go here
thread.setRunning(true);
}
public void pause(){
// TODO: Pause events go here
thread.setRunning(false);
}
}
public class GameScreen {
public GameScreen(Context context){
// Initialize private variables here
}
public void draw(Canvas canvas){
// Draw events go here
}
public void update(){
// Update events go here
}
public void touch(MotionEvent event){
// Touch events go here
}
}
The update() method needs to be executed BEFORE trying to post the canvas. Still unsure why this is, but it works as intended now.
Canvas canvas = null;
while (running){
gameView.update();
try {
canvas = this.surfaceHolder.lockCanvas();
synchronized (surfaceHolder){
gameView.render(canvas);
}
} finally {
if (canvas!=null)
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
I want to make a simple space shooter game but it is not working well at the start. How it will be its end? Something is going wrong with my code. The overridden run() method of the Runnable interface is not working. Why the run method is not working as it should? Moreover any information about how to shoot more independent bullets. Here is my code. Thanks in advance.
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JFrame;
public class GameWindow extends JFrame implements Runnable {
static final int WIDTH = 500;
static final int HEIGHT = 500;
static final String title = "East Game Development";
private boolean running = false;
private Thread thread;
GameWindow() {
setSize(new Dimension(WIDTH, HEIGHT));
getContentPane().setBackground(Color.BLACK);
setMaximumSize(new Dimension(WIDTH, HEIGHT));
setMinimumSize(new Dimension(WIDTH, HEIGHT));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle(title);
setResizable(false);
setVisible(true);
}
private synchronized void start() {
System.out.println("Debugging start method");
if (running) {
return;
}
running = true;
thread = new Thread();
thread.start();
}
private synchronized void stop() {
if (!running) {
return;
}
running = false;
try {
thread.join();
} catch (Exception ex) {
}
System.exit(1);
}
#Override
public void run() {
System.out.println("Debugging");
while (running) {
System.out.println("Working well...");
}
stop();
}
public static void main(String[] args) {
GameWindow gw = new GameWindow();
gw.start();
}
}
thread = new Thread();
thread.start();
You're creating a thread without passing it anything to run. The Thread constructor expects a Runnable as argument, and this runnable is what will be executed in the thread. You thus want
thread = new Thread(this);
thread.start();
Note, though, that your code is not thread-safe: the run() method reads the value of the running variable without synchonization. And it might thus see it as true aven if the stop() method has been called in another thread. You should use the stanard interruption mechanism to ask your thread to stop, and to check if the thread must continue to run:
public void stop() {
thread.interrupt();
}
public void run() {
while (!Thread.currentThread.isInterrupted()) {
...
}
}
When you create a thread, you should specify which runnable instance it will call run. In your case:
thread = new Thread(this);
thread.start();
thread = new Thread();
You are creating normal Thread and starting it. Why do you expect your run() method to run?
private synchronized void start() {
System.out.println("Debugging start method");
if (running) {
return;
}
running = true;
thread = new Thread(this);
thread.start();
}
if u have only JFrame no really need to make it a runnable. just use these in main method:
gw.setVisible(true);
gw.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
I am new to programming (I'm 11 and hoping for java coding to be my career, but its just a hobby right now :)) and I just made a countdown program, here is the class:
package me.NoahCagle.JAVA;
import javax.swing.JFrame;
public class Main extends JFrame implements Runnable {
private static final long serialVersionUID = 1L;
public static int width = 600;
public static int height = 500;
public static String title = "Countdown!";
public static boolean running = false;
public int number = 11;
public Thread thread;
Dimension size = new Dimension(width, height);
public Main() {
super(title);
setSize(size);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
Main m = new Main();
m.start();
}
public void start() {
if (running) {
return;
}
running = true;
Thread thread = new Thread(this);
thread.start();
}
#SuppressWarnings("static-access")
public void run() {
while (running) {
number--;
if (number == -1) {
System.out.println("Done!");
System.exit(0);
}
try {
thread.sleep(1000);
}catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
System.out.println("" + number);
}
}
public void stop() {
if (!running) {
return;
}
running = false;
try {
thread.join();
}catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
}
}
That may not have been necessary, but whatever. Well like I was saying, if you read the code, you will notice that it prints the value to the console. Well, if I could get that to display on a JLabel, while updating at the same time. I have tried just doing setText("" + number) thinking that because I have a thread going, it would repaint. But that didn't happen. It was just stuck at 11. Can someone please help me? Thanks
First, you may want to take a read through Concurrency in Swing. There are some very important constraints when it comes to dealing with multiple threads and Swing.
For your problem, you really should be using a javax.swing.Timer, and with examples...
Java Label Timer and Saving
Adding a timer and displaying label text
How could I add a simple delay in a Java Swing application?
As 11yrs old you have done good job here. But where did you add any panel to the frame on which u want to show the number? Once you do it and put some label to add the number you will need to call the repaint method. Also to use threads with swings, there are many libraries you can use like Timer.
Happy Coding!
I want to cause the "main thread" (the thread started which runs main()) to do some work from the actionPerformed() method of a button's ActionListener, but I do not know how to achieve this.
A little more context:
I am currently programming a 2D game using Swing (a flavour of Tetris).
When the application starts, a window opens which displays the main menu of the game.
The user is presented several possibilities, one of them is to start the game by pushing a "Start" button, which causes the game panel to be displayed and triggers the main loop of the game.
To be able to switch between the two panels (that of the main menu and that of the game), I am using a CardLayout manager, then I can display one panel by calling show().
The idea is that I would like my start button to have a listener that looks like this:
public class StartListener implements ActionListener {
StartListener() {}
public void actionPerformed(ActionEvent e) {
displayGamePanel();
startGame();
}
}
but this does not work because actionPerformed() is called from the event-dispatch thread, so the call to startGame() (which triggers the main loop: game logic update + repaint() call at each frame) blocks the whole thread.
The way I am handling this right now is that actionPerformed() just changes a boolean flag value: public void actionPerformed(ActionEvent e) {
startPushed = true;
}
which is then eventually checked by the main thread:
while (true) {
while (!g.startPushed) {
try {
Thread.sleep(100);
} catch (Exception e) {}
}
g.startPushed = false;
g.startGame();
}
But I find this solution to be very inelegant.
I have read the Concurrency in Swing lesson but I am still confused (should I implement a Worker Thread – isn't that a little overkill?). I haven't done any actual multithreading work yet so I am a little lost.
Isn't there a way to tell the main thread (which would be sleeping indefinitely, waiting for a user action) "ok, wake up now and do this (display the game panel and start the game)"?.
Thanks for your help.
EDIT:
Just to be clear, this is what my game loop looks like:
long lastLoopTime = System.currentTimeMillis();
long dTime;
int delay = 10;
while (running) {
// compute the time that has gone since the last frame
dTime = System.currentTimeMillis() - lastLoopTime;
lastLoopTime = System.currentTimeMillis();
// UPDATE STATE
updateState(dTime);
//...
// UPDATE GRAPHICS
// thread-safe: repaint() will run on the EDT
frame.repaint()
// Pause for a bit
try {
Thread.sleep(delay);
} catch (Exception e) {}
}
This doesn't make sense:
but this does not work because actionPerformed() is called from the event-dispatch thread, so the call to startGame() (which triggers the main loop: game logic update + repaint() call at each frame) blocks the whole thread.
Since your game loop should not block the EDT. Are you using a Swing Timer or a background thread for your game loop? If not, do so.
Regarding:
while (true) {
while (!g.startPushed) {
try {
Thread.sleep(100);
} catch (Exception e) {}
}
g.startPushed = false;
g.startGame();
}
Don't do this either, but instead use listeners for this sort of thing.
e.g.,
import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class GameState extends JPanel {
private CardLayout cardlayout = new CardLayout();
private GamePanel gamePanel = new GamePanel();
private StartPanel startpanel = new StartPanel(this, gamePanel);
public GameState() {
setLayout(cardlayout);
add(startpanel, StartPanel.DISPLAY_STRING);
add(gamePanel, GamePanel.DISPLAY_STRING);
}
public void showComponent(String displayString) {
cardlayout.show(this, displayString);
}
private static void createAndShowGui() {
GameState mainPanel = new GameState();
JFrame frame = new JFrame("GameState");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class StartPanel extends JPanel {
public static final String DISPLAY_STRING = "Start Panel";
public StartPanel(final GameState gameState, final GamePanel gamePanel) {
add(new JButton(new AbstractAction("Start") {
#Override
public void actionPerformed(ActionEvent e) {
gameState.showComponent(GamePanel.DISPLAY_STRING);
gamePanel.startAnimation();
}
}));
}
}
class GamePanel extends JPanel {
public static final String DISPLAY_STRING = "Game Panel";
private static final int PREF_W = 500;
private static final int PREF_H = 400;
private static final int RECT_WIDTH = 10;
private int x;
private int y;
public void startAnimation() {
x = 0;
y = 0;
int timerDelay = 10;
new Timer(timerDelay , new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
x++;
y++;
repaint();
}
}).start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillRect(x, y, RECT_WIDTH, RECT_WIDTH);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
}
you should be using a SwingWorker this will execute the code in doInBackground() in a background thread and the code in done() in the EDT after doInBackground() stops
The easiest way: use a CountDownLatch. You set it to 1, make it available in the Swing code by any means appropriate, and in the main thread you await it.
You can consider showing a modal dialog with the game panel using SwingUtilities.invokeAndWait() so that when the dialog is closed the control returns back to main thread.
You can make all code except the EDT run on single thread execution service and then just post runnables whenever you need some code executed.