I am making a game, and right now I am just tinkering around with image drawing, key input, etc. Right now all I have is a Pikachu moving around with the arrow keys (The game has nothing to do with Pokemon, Pikachu is just the image I chose) and I would like to make sure that the game does not run faster on other computers because of software capabilities (As in Pikachu would go from point A to point B faster than another computer, rather than it just lagging a little bit like other games)
I believe I'm doing it right, as I heard not to tie the engine to the frame, and the Only thing the frame is doing is displaying its X, Y, etc. everything else is done outside of the frame.
Here is my code:
package net.trenterprises;
import java.awt.MouseInfo;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
public class Main implements KeyListener {
static JLabel LabelImage = null;
static ArrayList<String> KeysPressed = new ArrayList<String>();
static int UP = 38;
static int DOWN = 40;
static int LEFT = 37;
static int RIGHT = 39;
public static void main(String[] args) {
Window TestWindow = new Window("Mouse Location", null);
KeyListener listener = new Main();
TestWindow.addKeyListener(listener);
TestWindow.setLayout(null);
int MouseX = 0;
int MouseY = 0;
JLabel MouseLabel = new JLabel("???");
MouseLabel.setSize(MouseLabel.getPreferredSize());
TestWindow.add(MouseLabel);
try {
BufferedImage CUPCAKES = (ImageIO.read(new URL("http://img3.wikia.nocookie.net/__cb20140328192412/pokemon/images/thumb/0/0d/025Pikachu.png/200px-025Pikachu.png")));
LabelImage = new JLabel(new ImageIcon(CUPCAKES));
TestWindow.add(LabelImage);
LabelImage.setSize(LabelImage.getPreferredSize());
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
LabelImage.setBounds(0, 0, LabelImage.getSize().width, LabelImage.getSize().height);
new PikaMoveUp().start();
new PikaMoveDown().start();
new PikaMoveLeft().start();
new PikaMoveRight().start();
while(true) {
MouseX = (MouseInfo.getPointerInfo().getLocation().x);
MouseY = (MouseInfo.getPointerInfo().getLocation().y);
MouseLabel.setText("Mouse X: " + MouseX + "\n Mouse Y: " + MouseY);
MouseLabel.setSize(MouseLabel.getPreferredSize());
MouseLabel.setBounds(0, 0, MouseLabel.getSize().width, MouseLabel.getSize().height);
}
}
public static int getPikaX() {
return LabelImage.getX();
}
public static int getPikaY() {
return LabelImage.getY();
}
public static void setPikachuPosition(int x, int y) {
LabelImage.setBounds(x, y, LabelImage.getSize().width, LabelImage.getSize().height);
}
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyPressed(KeyEvent e) {
int Char = (int) e.getKeyChar();
int Keycode = (int) e.getKeyCode();
if(Char == 65535) {
if(Keycode == UP) {
if(!KeysPressed.contains(Keycode + "")) {
KeysPressed.add(Keycode + "");
}
}
if(Keycode == DOWN) {
if(!KeysPressed.contains(Keycode + "")) {
KeysPressed.add(Keycode + "");
}
}
if(Keycode == LEFT) {
if(!KeysPressed.contains(Keycode + "")) {
KeysPressed.add(Keycode + "");
}
}
if(Keycode == RIGHT) {
if(!KeysPressed.contains(Keycode + "")) {
KeysPressed.add(Keycode + "");
}
}
}
}
@Override
public void keyReleased(KeyEvent e) {
int Char = (int) e.getKeyChar();
int Keycode = (int) e.getKeyCode();
if(Char == 65535) {
if(Keycode == UP) {
while(KeysPressed.contains(Keycode + "")) {
KeysPressed.remove(Keycode + "");
}
}
if(Keycode == DOWN) {
while(KeysPressed.contains(Keycode + "")) {
KeysPressed.remove(Keycode + "");
}
}
if(Keycode == LEFT) {
while(KeysPressed.contains(Keycode + "")) {
KeysPressed.remove(Keycode + "");
}
}
if(Keycode == RIGHT) {
while(KeysPressed.contains(Keycode + "")) {
KeysPressed.remove(Keycode + "");
}
}
}
}
class PikaMoveUp extends Thread implements Runnable {
public void run() {
while(true) {
try {
while(Main.KeysPressed.contains(Main.UP + "")) {
Thread.sleep(5);
Main.setPikachuPosition(Main.getPikaX(), Main.getPikaY() - 1);
}
}
catch(Exception E) {
E.printStackTrace();
}
}
}
}
class PikaMoveDown extends Thread implements Runnable {
public void run() {
while(true) {
try {
while(Main.KeysPressed.contains(Main.DOWN + "")) {
Thread.sleep(5);
Main.setPikachuPosition(Main.getPikaX(), Main.getPikaY() + 1);
}
}
catch(Exception E) {
E.printStackTrace();
}
}
}
}
class PikaMoveLeft extends Thread implements Runnable {
public void run() {
while(true) {
try {
while(Main.KeysPressed.contains(Main.LEFT + "")) {
Thread.sleep(5);
Main.setPikachuPosition(Main.getPikaX() - 1, Main.getPikaY());
}
}
catch(Exception E) {
E.printStackTrace();
}
}
}
}
class PikaMoveRight extends Thread implements Runnable {
public void run() {
while(true) {
try {
while(Main.KeysPressed.contains(Main.RIGHT + "")) {
Thread.sleep(5);
Main.setPikachuPosition(Main.getPikaX() + 1, Main.getPikaY());
}
}
catch(Exception E) {
E.printStackTrace();
}
}
}
You would commonly see either delta-timing or fixed-timestep implemented into the game loop.
Delta timing uses a delta
variable based on measuring the time between each frame. You would multiply all transformations by delta
to make the game run at the same speed on multiple computers. This is smooth and easy to implement, but you have to remember to multiply all transformations by delta
.
Fixed time-stepping, on the other hand, somehow controls the time between the frames, eliminating the need for a delta variable. It is known to be slightly choppy / difficult to implement, but you no longer need to apply delta
everywhere.
While you could implement it yourself, it would be easier switching over to an actual game library like LibGDX or Slick2D, where they handle it (and much more) for you.
Also, unstable performance may also be caused by using so many threads. The movement could be done in a much cleaner way while updating in a basic game loop;
if(Main.KeysPressed.contains(Main.LEFT + ""))
Main.setPikachuPosition(Main.getPikaX() - 1, Main.getPikaY());