I have a maze where robots can move around and explore. I'm trying to use the timer to repaint as the robot move but timer's not kicking in for some reason. It's not delaying the program so I can't see the repainting process. Here's my code:
public void updateDrawing(Maze maze) {
// 1000 millisecond delay
Timer t = new Timer(1000, new TimerListener(maze));
t.start();
}
private class TimerListener implements ActionListener {
private Maze maze;
public TimerListener(Maze maze) {
super();
this.maze = maze;
}
public void actionPerformed(ActionEvent e) {
maze.repaint();
}
}
public void explore (int id, Maze maze) {
visited.add(maze.getCell(row, col));
//Loop until we find the cavern
outerloop: //Label the outerloop for breaking purposes
while(!foundCavern){
//Move to a Cell
Cell validCell = chooseValidCell(maze);
//If validCell is null then we have to backtrack till it's not
if(validCell == null){
while(chooseValidCell(maze) == null){
//Go back in route till we find a valid cell
Cell lastCell = route.pollLast();
if(lastCell == null){ //Implies we didn't find cavern, leave route empty
break outerloop;
}
this.row = lastCell.getRow();
this.col = lastCell.getCol();
updateDrawing(maze); // <- this calls repaint using timer
}
//Add back the current location to the route
route.add(maze.getCell(row, col));
validCell = chooseValidCell(maze);
}
this.row = validCell.getRow();
this.col = validCell.getCol();
updateDrawing(maze); // <- this calls repaint using timer
//Add to the route
route.add(validCell);
//Add to visited
visited.add(validCell);
//Check if we're at the cavern
if(row == targetCavern.getRow() && col == targetCavern.getCol()){
foundCavern = true;
}
}
}
Can anyone tell me why? Thank you!
try use not ** updateDrawing(maze)** but this method:
void updateMaze() {
EventQueue.invokeLater(()->updateDrawing(maze));
}
Here's how to make a basic timer.
All you need to do to calculate the time to display, is to record the time that the timer started:
long startTime = System.currentTimeMillis();
Later, when you want to display the amount of time, you just subtract this from the current time.
long elapsedTime = System.currentTimeMillis() - startTime;
long elapsedSeconds = elapsedTime / 1000;
long secondsDisplay = elapsedSeconds % 60;
long elapsedMinutes = elapsedSeconds / 60;
//put here code to format and display the values
You can make your program wait until elapsedSeconds == whatever value you want.
From Make a simple timer in Java
Related
I'm playing around with graphics in Java. At the moment I have a rectangle that moves from left to right. I want it to start moving left once it hits the right side of the Canvas and left when it hits the right side, i have included a game loop as this will eventually turn into my first very basic game. Thanks.
P.S - I followed some tutorials for different parts of this code hence why it might be a bit messy, I'm working on it :)
Main Class:
public class Game extends JFrame implements Runnable {
private Canvas canvas = new Canvas();
private RenderHandler renderer;
private boolean running = true;
public static int WIDTH = 1200, HEIGHT = WIDTH / 12*9;
public static int moveX =WIDTH/2;
public Game() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(WIDTH, HEIGHT);
setLocationRelativeTo(null);
setLocationRelativeTo(null);
add(canvas);
setVisible(true);
canvas.createBufferStrategy(3);
renderer = new RenderHandler(getWidth(), getHeight());
}
public void update() {
}
public void render() {
BufferStrategy bufferStrategy = canvas.getBufferStrategy();
Graphics graphics = bufferStrategy.getDrawGraphics();
super.paint(graphics);
renderer.render(graphics);
graphics.dispose();
bufferStrategy.show();
}
public void run() {
BufferStrategy bufferStrategy = canvas.getBufferStrategy();
int FRAMES = 0;
int TICKS = 0;
long lastTime = System.nanoTime();
double unprocessed = 0;
double nsPerSecs = 1000000000 /60.0;
long Timer = System.currentTimeMillis();
while(running) {
long now = System.nanoTime();
unprocessed += (now - lastTime) / nsPerSecs;
lastTime = now;
if(unprocessed >= 1) {
TICKS ++;
update();
unprocessed -= 1;
}
try
{
Thread.sleep(3);
}catch (InterruptedException e) {
e.printStackTrace();
}
FRAMES++;
render();
if(System.currentTimeMillis() - Timer > 1000) {
System.out.println("Ticks: " + TICKS + " FPS: " + FRAMES);
TICKS = 0;
FRAMES = 0;
Timer += 1000;
}
}
}
public static void main(String[] args) {
Game game = new Game();
Thread gameThread = new Thread(game);
gameThread.start();
}
}
Class drawing the graphics:
public class RenderHandler {
public RenderHandler(int width, int height) {
}
public void render(Graphics g) {
g.setColor(Color.BLACK);
g.fillRect(0, 0, Game.WIDTH, Game.HEIGHT);
g.setColor(Color.RED);
g.fillRect(Game.moveX, Game.HEIGHT/2, 50, 50);
if (Game.moveX >= Game.WIDTH) {
Game.moveX ++;
} else if (Game.moveX <= 0) {
Game.moveX --;
}else { Game.moveX++;
}
}
}
If you know how to draw on the screen and how stuffs work, I would think that this is more about getting the logic down.
This code slice I so brutally tore from your question is right next to where the rendering takes place (a problem because I view it as rather unorganized; I would recommend game logic and rendering to take place in two different functions). It basically says that it will move right if it is beyond the right of the screen, if not, it will move left if it is beyond the left of the screen, and finally, if not, it will just move left.
if (Game.moveX >= Game.WIDTH) {
Game.moveX ++;
} else if (Game.moveX <= 0) {
Game.moveX --;
}else { Game.moveX++;
}
If you want it to bounce, you will have to use a boolean to keep track of its moving state, or, if you want more versatility, use a pair of floats or doubles (floats are typically used in Java game design) to keep track of its position, and another for its velocity. I'm in a tight squeeze right now, I will return.
Add this to render handler instead of the current if statement in render
bool rol = true; // initialize this outside the method
If(Game.movex + 50 >= Game.width)
rol = false;
Else if(Game.movex <= 0)
rol = true;
If(rol)
Game.movex++;
Else
Game.movex--;
You need to store current moving direction somewhere, so add this to Game class:
public static int deltaX = 1;
And replace condition in render() with
if (Game.moveX >= Game.WIDTH-50) {
Game.deltaX =-1;
} else if (Game.moveX <= 0) {
Game.deltaX =1;
}
Game.moveX += Game.deltaX;
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I'm fairly new to game programming in Java and I was wondering if someone could help me out with my game loop. I'm trying to avoid using premade engine libraries (I want to see if I can do it myself). Currently, my game loop is giving me a really low FPS, sometimes even in the single digits. Would this be a case where I have to use multithreading? Or is there a better fix to this problem? Any help is much appreciated!
Here is the code which runs the game loop. I don't include all of the code (because it would be a lot of files), but I think this might be enough, tell me if you need more. I also include a screenshot of the game that I'm trying to create. Thanks in advance!
package com.Farthorn.game;
import java.awt.Canvas;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferStrategy;
import javax.swing.ImageIcon;
public class Game extends Canvas implements Runnable
{
private static final long serialVersionUID = -4431078110090264106L;
public final static int WIDTH = 640;
public final static int HEIGHT = WIDTH / 12 * 9;
private Thread thread;
private boolean running = false;
private CharacterHandler cHandler;
private ObjectHandler oHandler;
public int change = 0;
private String mapBackgroundTop = "/Users/Ahhblala/Desktop/FarthornPics/background_top.png";
private String mapBackgroundBottom = "/Users/Ahhblala/Desktop/FarthornPics/background_bottom.png";
private Image backgroundTop;
private Image backgroundBottom;
public Game()
{
ImageIcon i = new ImageIcon(mapBackgroundTop);
backgroundTop = i.getImage();
i = new ImageIcon(mapBackgroundBottom);
backgroundBottom = i.getImage();
cHandler = new CharacterHandler();
cHandler.addCharacter(new Player(ID.Player, WIDTH/2-32, HEIGHT/2+64));
cHandler.addCharacter(new PlantEnemy(ID.PlantEnemy, WIDTH+180, HEIGHT/2+104, (Player) cHandler.characters.getLast()));
cHandler.addCharacter(new PlantEnemy(ID.PlantEnemy, WIDTH+195, HEIGHT/2+104, (Player) cHandler.characters.get(0)));
cHandler.addCharacter(new PlantEnemy(ID.PlantEnemy, WIDTH+300, HEIGHT/2+104, (Player) cHandler.characters.get(0)));
cHandler.addCharacter(new PlantEnemy(ID.PlantEnemy, WIDTH+315, HEIGHT/2+104, (Player) cHandler.characters.get(0)));
oHandler = new ObjectHandler();
oHandler.addObject(new HUD(10, 10, cHandler));
this.addKeyListener(new KeyInput(cHandler));
this.addMouseListener(new MouseInput(((Player) cHandler.characters.get(0))));
FarthornWindow window = new FarthornWindow(WIDTH, HEIGHT, this);
}
public synchronized void start()
{
running = true;
thread = new Thread(this);
thread.start();
}
public synchronized void stop()
{
try
{
thread.join();
running = false;
}
catch(Exception e)
{
e.printStackTrace();
}
}
public void run()
{
this.requestFocus();
long lastTime = System.nanoTime();
double amountOfTicks = 60.0;
double ns = 1000000000 / amountOfTicks;
double delta = 0;
long timer = System.currentTimeMillis();
int frames = 0;
while (running)
{
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
while (delta >= 1)
{
tick();
delta--;
}
if (running)
{
render();
}
frames++;
if(System.currentTimeMillis() - timer > 1000)
{
timer += 1000;
System.out.println("FPS " + frames);
frames = 0;
}
}
stop();
}
private void tick()
{
int deltaX = 0;
int afterX = 0;
deltaX = cHandler.characters.get(0).getX();
oHandler.tick();
cHandler.tick();
deltaX = cHandler.characters.get(0).getX();
if (deltaX != afterX)
change = (afterX-deltaX);
else
change = 0;
render();
}
private void render()
{
BufferStrategy bs = this.getBufferStrategy();
if (bs == null)
{
this.createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
g.drawImage(backgroundTop, 0+change, 0, null);
g.drawImage(backgroundBottom, 0+change, 364, null);
oHandler.render(g);
cHandler.render(g);
g.dispose();
bs.show();
}
public static int clamp(int var, int min, int max)
{
if (var >= max)
{
return var = max;
}
else if (var <= min){
return var = min;
}
else
return var;
}
}
As you might be able to tell, I am rendering a lot of outside images.
There's probably something inefficient in your rendering causing it to drop below your target of 60hz. That aside, there's definitely a problem in your "catch up" loop:
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
while (delta >= 1)
{
tick();
delta--;
}
Since your tick() routine also calls render(), tick() is effectively a frame in itself, but you are not incrementing your frame counter. Your tick() should be causing all the parts to update, but not render the image, render should only be called once per frame. You probably want to remove the render() line from your tick() routine.
At the moment I'm trying to make a simple videogame in Java, just for fun. But there seems to be lag, and I'm not sure why it's happening. I'll give the lowdown:
The way it draws is using JFrame, and the actual drawing happens in the ImagePanel class. In ImagePanel, this is how I draw. It includes some things about debugging to show FPS and a timer to show length of run, but I'm not sure if that's important. It goes through multiple ArrayLists to show all the objects on the JFrame.
//Painting
public void paintComponent(Graphics g)
{
//Paint the background with its upper left corner at the upper left corner of the panel
g.drawImage(background, 0, 0, null);
//Paint each image in the foreground where it should go
for(MovingImage img : backgrounds)
{
g.drawImage(img.getImage(), (int)(img.getX()), (int)(img.getY()), null);
}
for(MovingImage img : foreground)
{
g.drawImage(img.getImage(), (int)(img.getX()), (int)(img.getY()), null);
}
if(g instanceof Graphics2D)
{
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.scale(2, 2);
g2.setColor(Color.WHITE);
String milSpace;
if(timer%100 < 10)
milSpace = "0";
else
milSpace = "";
String secSpace;
if(timer/100 < 10)
secSpace = "0";
else
secSpace = "";
g2.drawString(secSpace + timer/100 + ":" + milSpace + timer%100, 10, 20);
//Debug
if(debug)
{
long currentTime = System.currentTimeMillis();
if (currentTime > nextSecond)
{
nextSecond += 1000;
frameInLastSecond = framesInCurrentSecond;
framesInCurrentSecond = 0;
}
framesInCurrentSecond++;
//g2.drawString("LagMS:" + (-frameRate - 10) + " FPS:" + frameInLastSecond, 20, 40); <-includes "lag"
g2.drawString("FPS:" + frameInLastSecond, 20, 40);
}
}
}
//Replaces the list of foreground images with the one given, and repaints the panel
public void updateImages(ArrayList<MovingImage> newForeground, ArrayList<MovingImage> newBackgrounds)
{
foreground = newForeground;
backgrounds = newBackgrounds;
//time checking
long time = System.currentTimeMillis();
lastTime = time;
repaint(); //This repaints stuff... you don't need to know how it works
}
Inside the primary class I made that includes a tick system, which causes it to be painted in the first place.
public void tick()
{
long lastTime = System.currentTimeMillis();
int place = 0;
boolean go = true;
while(go)
{
long time = System.currentTimeMillis(); //The current time
if(time - 10 > lastTime) //Update every .01 seconds, or 1 TICK (if time - 10 > lastTime)
{
lastTime = time;
//Reset the last time
place++;
imagePanel.incTime();
for(MovingImage object : movingObjects)
{
if(object instanceof Building)
{
object.incrementPosition(); //Augment position by velocity
if(place%500 == 0)//If 5 seconds have passed...
{
((Building) object).speedUp();//make it go a little faster!
}
}
if(object instanceof Player)
{
if(jumpKeyOn)
((Player) object).jump();//Initiate jump class assuming key is pressed
object.incrementPosition();
((Player) object).constrainPlayerToObjects(movingObjects, yMax);
if(object.getY()>yMax + 1000)
{
go = false;
}
}
}
//Repaint all the things in their new positions, possibly faster
for(MovingImage bg : backgrounds)
{
bg.incrementPosition();
if(place%500 == 0)
bg.setVelocity(bg.getXvel() - 0.1, 0);
}
/*
* Acceleration
*/
//Removes buildings once left screen
int i = 0;
while(i < movingObjects.size())
{
if(movingObjects.get(i) instanceof Building)
{
if(movingObjects.get(i).getX() + movingObjects.get(i).getImage().getWidth(null) < 0)
movingObjects.remove(i);
else
i++;
}
else
i++;
}
imagePanel.updateImages(movingObjects, backgrounds);
}
}
gameOver();
}
It's an endless loop that essentially runs the program. I used multiple ArrayLists in order to put different layers down. What am I doing that's causing it to lag? I'm still fairly new, but I'll answer any questions about the code or provide more details. I couldn't find any other questions that helped.
EDIT: There are some odd things I should mention. Occasionally it runs at nearly the full FPS, but most of the time not. I also noticed that when I ran another java program at the same time, it ran at nearly full speed.
EDIT 2: Should I include the entire primary class code and ImagePanel?
So I made a java game with jumping some time ago and I used this method for all the moving:
double height = 0, speed = 4;
public static final double gravity = 9.81;
double x = 25;
int a;
int y = (int) (500-(height*100));
boolean left = false, right = false, up = false;
public void the_jump() {
long previous = 0, start = 0;
while(true){
start = System.nanoTime();
if(previous != 0 && up){
double delta = start - previous;
height = (height + (delta/1000000000) * speed);
speed -= (delta/1000000000) * gravity;
y = (int) (500-(height * 100));
}
if(left){
x-= 3;
}
if(right){
x+= 3;
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(height < 0){
height = 0;
speed = 4;
up = false;
}
previous = start;
}
}
Now It was okay when I did it all with just JComponents and such, but now when I want to implement it in a Slick enviroment, it fails.
The problem is in the while(true){} loop. If I change it against for(int i = 0; i < 1; i++) loop, then moving left and right will work. But this will not work for the jumping. I could increase the i < 1 to i < 5 and then the jump will work, but at the cost of a lot of performance.
So how would people implement this in slick? Right now I am calling the the_jump(); out in my public void update(GameContainer gc, int t) throws SlickException method, and if I use the while loop, the game will crash.
Slick already loop on update(GameContainer gc, int delta), you have to put all the code located in your while loop into the update method.
Moreover, you get the delta time between two update as parameter, and so not have to calculate it.
Feel free to ask me more question ;)
Off Topic, do you know if Slick2d is still maintain ? I switch to libGDX a few month ago, and I really advice you to test it, it's soooo fun :)
I'm making a game in Java and I want to create a character that moves randomly. The one I made is very spastic. I basically want to add a delay between random numbers generated. I'm a beginner so don't judge my code lol
public class Monster extends Entity{
private World world;
Image monster;
public Monster(int x, int y, World world) {
super(x, y, world);
w = 32;
h = 32;
this.world = world;
}
public void render(GameContainer gc, Graphics g) throws SlickException{
super.render(gc, g);
monster = new Image("gfx/world/monster.png");
g.drawImage(monster, x, y);
}
public void update(GameContainer gc, int delta) throws SlickException{
super.update(gc, delta);
Random move = new Random();
int number;
for(int counter=1; counter<=1;counter++){
number = move.nextInt(4);
System.out.println(number);
if(number == 0){
setDy(-1);
}else if(number == 1){
setDx(-1);
}else if(number == 2){
setDy(5);
}else if(number == 3){
setDx(5);
}else{
setDx(0);
setDy(0);
}
}
}
}
This is a common technique used on games to have a different update and render rate.
What you have to do is (examples in pseudo code):
1 - Initialize a time variable - DateTime lastUpdate = new DateTime();
Every time you enter in the loop:
2 - Check if a certain time has passed - lastUpdate.hasPassed(X_TIME, new DateTime());
3 - if the time has passed (last line was true) lastUpdate = new DateTime();
4 - Else return
You'll want to encapsulate the NPC movement in a separate thread, and within that thread call thread.sleep to pause the movement.
Here is a good tutorial on how to define threads, and the oracle docs show an example of a thread that sleeps.
Try to add
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
e.printStackTrace();
}
where ms - how many miliseconds, e.g. 1000