Okay, so i've been trying to make smooth jumps in a 2D pure android with no game engine for a week now. I am using ScheduledThreadPoolExecutor right now.
So i have a variable called FPS. This is how many time the character has to go up before reaching 180 Y value;
Note: FPS is not really Frames per Second in the whole game.
When FPS is set to 45, the app never freezes (Unless I repeatly spam the jump button)
But on 90, AT RANDOM OCCASIONS WHEN I PLAY THE GAME the game FREEZES and logcat indicates:
E/WindowManager: Performed 6 layouts in a row. Skipping
It dosent matter what I use (EX: Handler Runnable and Thread), app
Here is the jump code:
public void jump() {
final ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
if(!isRunning) {
executor.scheduleWithFixedDelay(new Runnable() {
boolean moveUpBln = true;
int timesLooped = 0;
float FPS = 90; //Never freezes with value of 45 (or under)
float charMovementFPS = 180 / FPS;
#Override
public void run() {
if(moveUpBln) {
isRunning = true;
characterModel.setY(characterModel.getY() - charMovementFPS);
if (timesLooped == FPS || timesLooped > FPS) {
moveUpBln = false;
timesLooped = -1;
}
timesLooped++;
}else{
isRunning = true;
characterModel.setY(characterModel.getY() + charMovementFPS);
if (timesLooped == FPS || timesLooped > FPS) {
executor.shutdownNow();
isRunning = false;
}
timesLooped++;
}
}
}, 0, 3, TimeUnit.MILLISECONDS);
}
}
NOTE: the -1 isn't the problem, this is intentional
What are these layouts the logcat is talking about? Am I putting too much pressure on my Non-Engine game?
Related
I currently have a game loop that looks like this
new AnimationTimer() {
public void handle(long currentNanoTime) {
if (isPaused == false) {
double t = (currentNanoTime - startNanoTime) / 1000000000.0;
timerCorner = Math.round(t * 100.0) / 100.0;
} else {
...
}
}
I would like to pause my timer without stopping the AnimationTimer loop. I thought it simplest to just accumulate an offset during the else statement, but you can't overwrite variables within the AnimationTimer loop. I think I can overcome this by updating a value within a class object, but don't know how to do so accurately? Or is there a better method.
My game is a time challenge so it's important the clock stops when the pause menu is active. The pause looks like this
else if (event.getCode() == KeyCode.ESCAPE) {
// System.out.println("Escape key is pressed");
isPaused = true;
...
}
Basically, I want to calculate a value X such that
double t = currentNanoTime - startNanoTime - X
counts up from when the game was paused, rather then jumping ahead to where CurrentNanoTime is now.
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?
I am making a java 2d side scroller and im having problems with multiple keys at the same time. Such as right + up. Whenever you release the up you stop moving right as you slowly go back to the ground even though right is still being pressed. Here are the keyboard listeners I have setup. dx is my horizontal movement speed and dy is the vertical height. Below is from the character class
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_LEFT) {
dx = -5;
mainCharacterImageFacing = l.getImage();
}
if (key == KeyEvent.VK_RIGHT) {
dx = 5;
mainCharacterImageFacing = r.getImage();
}
if (key == KeyEvent.VK_UP) {
dy = 1;
mainCharacterImageFacing = r.getImage();
}
}
public void keyReleased(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_LEFT);
dx = 0;
if (key == KeyEvent.VK_RIGHT)
dx = 0;
if (key == KeyEvent.VK_UP)
{dy = 0;
mainCharacterImageFacing = r.getImage();
}
This is some code from the main game window that deals with calling the key press/release methods as well as dealing with the jump.
private class AL extends KeyAdapter{
public void keyReleased(KeyEvent e) {
p.keyReleased(e);
}
public void keyPressed(KeyEvent e) {
p.keyPressed(e);
}
}
#Override
public void run()
{
long beforeTime;
long timeDiff;
long sleep;
beforeTime = System.currentTimeMillis();
while(done == false)
{
cycle();
timeDiff = System.currentTimeMillis() - beforeTime;
sleep = 10 - timeDiff;
if (sleep < 0 )
sleep = 2;
try {
Thread.sleep(sleep);
} catch (Exception e)
{}
beforeTime = System.currentTimeMillis();
}
done = false;
h = false;
k = false;
}
boolean h = false;
boolean done = false;
public void cycle() {
if (h == false)
v = v - 2; //jump speed falling
if (v == 350) //max y value of jump. Lower numbers = higher jumps
h = true;
if (h == true && v <= 470) //starting y value
{
v = v + 2; //jump speed rising
if (v == 470)
done = true;
}
}
Do not handle your movement directly in the AL class. Instead, use booleans and set them to true/false accordingly:
private class AL extends KeyAdapter{
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_LEFT) {
left = true;
}
if (key == KeyEvent.VK_RIGHT) {
right = true
}
if (key == KeyEvent.VK_UP) {
//...
}
}
public void keyReleased(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_LEFT)
left = false
if (key == KeyEvent.VK_RIGHT)
right = false
if (key == KeyEvent.VK_UP)
//...
}
}
The reason for this is because keyPressed and keyReleased are called when the keys are acted upon, though that means it will not be in sync with your main loop.
To move your sprite call a method in the loop which, if your conditions are true, moves your sprite accordingly, else does nothing.
EDIT: After reviewing your code some more I have a few suggestions.
First of all, look up sub-pixel rendering. Sub-pixel rendering is used to make the movement of sprites look smoother.
Another thing to look up is variable timesteps (sometimes referred to as delta timesteps). Delta time can be used to make sure that actions are performed in sync with a players fps. If one player has 60fps and another has 30fps, the player with 30fps will see actions performed at half the speed. With a delta timestep however, actions are always performed at the same speed. Here's a good article.
The third thing is your timing. Currently you're using System.getCurrentTimeMills. This is bad, and not precise. Instead, use System.nanoTime.
A fourth thing I've spotted is your jumping algorithm. A better way is to include acceleration. First you need to declare a constant for acceleration (0.1 or 0.2 are good) and tally a vertical speed. You can then fall when the vertical speed gets to a certain value. Here's a small snippet, though if you have gravity you may want to edit it. First of all set vSpeed to say -8 by default.
public void jump() {
vSpeed += ACCELERATION;
if(vSpeed < 8)
dy += vSpeed;
else
vSpeed = -8;
falling = false;
}
I just wrote this off the top of my head, but basically, since moving upwards is taking away a number, you appear to jump when it adds negative numbers, then you'll start falling when it reaches 0, and land when reaches 8 again.
The problem is the way the OS handles key events and passes them to your application. When you press a key, it fires an event for down. Then based on your settings for repeating, it will start pressing the key again after some amount of time, at some amount of frequency. (you can see this when typing. Press a key, and hold it).
When you press another key, it stops the repeated triggers for the other key. You can see this while typing as well. Hold a key, then start holding another. The original key stops, as you would expect.
This won't even be consistent with every os, and people could cheat in your game as it is, by making the repeat timer on their keyboard faster.
For these reasons, it's a very bad idea to do animations inside of key listeners. You should be settings flags to true/false and reading them in a game loop. (As demonstrated in #Troubleshoot's answer).
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
Hey guys on my 2D game the movement speed varies.. I was making my game on my Desktop and it ran fine but then I went on my laptop and the Player moved slower then the Desktop.
Here is my current game loop:
public void gameLoop() throws IOException {
isRunning = true;
while(isRunning == true) {
Graphics2D g2d = (Graphics2D) strategy.getDrawGraphics();
//main game loop
//updates everything and draws
//main componenets
//e.g. Player
// clear the screen
g2d.setColor(Color.lightGray);
g2d.fillRect(0,0,640,496);
//drawing
player.paint(g2d);//paint player
map.paint(g2d);//paint each wall in wall list
g2d.dispose();
strategy.show();
//update
try { player.updateMovement(); } catch (Exception e) {};
try { Thread.sleep(4); } catch (Exception e) {};
}
}
Here is my player.updateMovement() method:
public void updateMovement() {
if(!down || !up) {
ny = 0;
}
if(!left || !right) {
nx = 0;
}
if(left) {
nx = -1;
}
if(right) {
nx = 1;
}
if(up) {
ny = -1;
}
if(down) {
ny = 1;
}
if ((nx != 0) || (ny != 0)) {
x += nx;
y += ny;
}
}
How can I fix this issue?
You could have a fixed-rate drawing loop: every iteration should last the same, and if there is some time left, sleep that amount of time. For instance, if we fix a period of 41.6 ms (which is 24 fps), and a certain iteration lasts 20 ms, then you should sleep 41.6 - 20 = 21.6 ms that pass.
If your code is too heavy to run in that time on a low end PC, then you can increase the period so that every machine can cope with it.
By the way, you could also optimize your code.
You can find more information on gaming.stackexange.com