Java-Gaming.org    
Featured games (81)
games approved by the League of Dukes
Games in Showcase (498)
Games in Android Showcase (117)
games submitted by our members
Games in WIP (563)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Beginner Design Questions  (Read 1439 times)
0 Members and 1 Guest are viewing this topic.
Offline synLB

Junior Member


Medals: 2



« Posted 2014-03-31 12:10:11 »

Hello guys,

I have a few questions that I couldn't really answer with google and I would like to ask you. I've learned the basics of java a few years ago but did never really get into programming. I recently had new interest and tried again, first relearning the things I had forgotten. I seem to understand written code quite well but have problems in writing code myself and thought I would learn best if I try it myself.

I began with a simple pong tutorial and wanted to write something similar without copying the code and I have now a tiny little program where I can move a dot with the arrow keys. I know this is nothing big and a lot of people have done this before but I would be grateful if you could look at the code and point out mistakes I have done especially concerning oo-design and what I should change to make the code better.

Additionally I have following questions concerning the code:
  • I have two classes until now, a game class with an inner class that represents the panel on which is drawn and a player class. I have seen a pong tutorial where the panel is a separate class extending JPanel. I thought that the JPanel belonged to the Game class because it will use possibly variables from it in the future and it would be more of a hassle creating a separate class for it but I'm not really sure if this is true
  • @SuppressWarnings("serial") was added above my inner class, why this?
  • Should I make all member variables of the Player class private and use these with setters and getters? If there are maybe 20-30 variables that would be a huge amount of code or not?
  • The arrow keys work good but sometimes if you release just one key the dot stops for a short time, due to the fact that xa or xy is set to 0, what would be an approach to solve this?

I know this is not a finished product but an exercice for me to improve my skills. I probably have made a lot of mistakes and I am happy if you can point out some general mistakes, then I will try to add more things and play around with it.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Game implements KeyListener {

   static JFrame frame;
   static Player player;
   static DrawingPanel mapPanel;
   int mapPanelSize = 300;
   static boolean loopCondition;
   static int frameSize = 500;

   public Game() {
   }

   public static void main(String[] args) {

      Game game = new Game();

      game.runGame();

   }

   public void runGame() {

      player = new Player();

      frame = new JFrame();
      frame.setSize(500, 500);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setVisible(true);

      DrawingPanel mapPanel = new DrawingPanel();
      mapPanel.setSize(300, 300);

      frame.add(BorderLayout.CENTER, mapPanel);
      frame.addKeyListener(this);
      frame.requestFocus();

      loopCondition = true;

      while (loopCondition) {

         mapPanel.repaint();

         try {
            Thread.sleep(80);
         } catch (InterruptedException e) {
            // TODO Auto-generated catch block
           e.printStackTrace();
         }

      }

   }

   @Override
   public void keyPressed(KeyEvent event) {
      System.out.println("KeyPressed");
      player.keyPressed(event);

   }

   @Override
   public void keyReleased(KeyEvent event) {
      System.out.println("KeyReleased");
      player.keyReleased(event);

   }

   @Override
   public void keyTyped(KeyEvent e) {
      // TODO Auto-generated method stub

   }

   @SuppressWarnings("serial")
   class DrawingPanel extends JPanel {

      final int DIAMETER = 10;

      @Override
      protected void paintComponent(Graphics g) {

         Graphics2D g2d = (Graphics2D) g;

         g2d.setColor(Color.white);
         g2d.fillRect(0, 0, mapPanelSize, mapPanelSize);

         g2d.setColor(Color.red);
         g2d.fillOval(player.getMapPositionX(), player.getMapPositionY(),
               DIAMETER, DIAMETER);

      }

   }

}


1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
import java.awt.event.KeyEvent;

public class Player {
   private int mapPositionX;
   private int mapPositionY;

   private int xa;
   private int ya;

   public int getMapPositionX() {
      return mapPositionX;
   }

   public void setMapPositionX(int mapPositionX) {
      this.mapPositionX = mapPositionX;
   }

   public int getMapPositionY() {
      return mapPositionY;
   }

   public void setMapPositionY(int mapPositionY) {
      this.mapPositionY = mapPositionY;
   }

   public void movePlayer() {

      if (mapPositionX + xa > 0) {
         mapPositionX += xa;

      }
      if (mapPositionY + ya > 0) {
         mapPositionY += ya;

      }

   }

   public void keyPressed(KeyEvent event) {
      /*
       * switch(event.getKeyCode()){
       * case(KeyEvent.VK_LEFT):
       * System.out.println("KEY-LEFT"); xa= -1; movePlayer();
       *
       * case(KeyEvent.VK_RIGHT): xa= 1; movePlayer();
       *
       * case(KeyEvent.VK_DOWN): ya = 1; movePlayer();
       *
       * case(KeyEvent.VK_UP): ya = -1; movePlayer(); }
       */


      if (event.getKeyCode() == KeyEvent.VK_LEFT) {
         System.out.println("KEY-LEFT");

         xa = -1;
         movePlayer();
      }

      if (event.getKeyCode() == KeyEvent.VK_RIGHT) {
         System.out.println("KEY-Right");

         xa = 1;
         movePlayer();
      }

      if (event.getKeyCode() == KeyEvent.VK_UP) {
         System.out.println("KEY-Up");

         ya = -1;

         movePlayer();
      }

      if (event.getKeyCode() == KeyEvent.VK_DOWN) {
         System.out.println("KEY-Down");

         ya = 1;

         movePlayer();
      }

   }

   public void keyReleased(KeyEvent event) {
      xa = 0;
      ya = 0;

   }

}
Offline trollwarrior1
« Reply #1 - Posted 2014-03-31 15:57:05 »

For a simple game like pong with no effects, you don't need to do anything about class and code design. What I want to say is that simple game doesn't require good code design, because everything is easy to change. When the complexity of the game increases, you need better design, because with bad design it would be really hard to change high complexity code.

About the dot stopping, check which key has been released. If A|D was released, do xa=0; and the same for vertical direction.
You might want to create keyboard handling class, in which you do something like:
* Make a Key class which holds value such as "boolean down". Now in your keyboardhandler class in keypressed method, do something like "if (keyevent.getKeyCode()==KeyEvent.VK_W) this.KEY_W.down=true"
and in keyreleased method do the same thing except set down=false;

Here is a very simple higher level design keyboard handling class for you to start with Tongue

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
public class KeyboardHandler implements KeyListener {

   public KeyboardHandler() {
   }
   
   
   public Key KEY_W = new Key();
   public Key KEY_A = new Key();
   public Key KEY_S = new Key();
   public Key KEY_D = new Key();
   
   public class Key {
      public boolean down=false;
   }
   
   @override
   public void keyPressed(KeyEvent event) {
      int kc = event.getKeyCode();
     
      if(kc==KeyEvent.VK_W) KEY_W.down=true;
      if(kc==KeyEvent.VK_A) KEY_A.down=true;
      if(kc==KeyEvent.VK_S) KEY_S.down=true;
      if(kc==KeyEvent.VK_D) KEY_D.down=true;
   }
   
   @override
   public void keyReleased(KeyEvent event) {
      int kc = event.getKeyCode();
     
      if(kc==KeyEvent.VK_W) KEY_W.down=false;
      if(kc==KeyEvent.VK_A) KEY_A.down=false;
      if(kc==KeyEvent.VK_S) KEY_S.down=false;
      if(kc==KeyEvent.VK_D) KEY_D.down=false;
   }
   
   public void keyPressed(KeyEvent event) {
   }
   
}
Offline Longarmx
« Reply #2 - Posted 2014-03-31 16:07:18 »

No. Start with good code design. You don't want to fall into bad habits.

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Rayvolution

JGO Kernel


Medals: 207
Projects: 2
Exp: 1 year


Resident Crazyman


« Reply #3 - Posted 2014-03-31 16:11:01 »

No. Start with good code design. You don't want to fall into bad habits.

I agree, even if the game is ultra-simple, you want to use the best code design you can. Reason being even though the game is simple, it's good practice to learn good code design for more advanced projects later.

That and why would you want to half-ass anything?

- Raymond "Rayvolution" Doerr.
Retro-Pixel Castles - Survival Sim/Builder/Roguelike!
LIVE-STREAMING DEVELOPMENT: http://www.twitch.tv/SG_Rayvolution
Offline trollwarrior1
« Reply #4 - Posted 2014-03-31 16:21:58 »

I don't want to look like a stupid idiot, and I haven't sold any reasonble amount of products, but consumers don't want good code design, consumers want products.

Good code design requires a lot of time to lay out. The more complex the game, the better code design has to be in order for you to be productive.

Takes this for example: try drawing 10x10 cm paper with small brush. You would have no problems really.

Now try painting 10x10m wall with that same brush. You can't do that. Well you can, but it will take huge amount of time.
You will need to change your brush, which you don't have, so you go to store, and store doesn't have, you go back home, and order one online, and realize that you don't have credit card. Now you even have to get credit card to paint that wall.

Code design should be proportional to game complexity.
Offline Rayvolution

JGO Kernel


Medals: 207
Projects: 2
Exp: 1 year


Resident Crazyman


« Reply #5 - Posted 2014-03-31 16:30:25 »

I don't want to look like a stupid idiot, and I haven't sold any reasonble amount of products, but consumers don't want good code design, consumers want products.

Good code design requires a lot of time to lay out. The more complex the game, the better code design has to be in order for you to be productive.

Takes this for example: try drawing 10x10 cm paper with small brush. You would have no problems really.

Now try painting 10x10m wall with that same brush. You can't do that. Well you can, but it will take huge amount of time.
You will need to change your brush, which you don't have, so you go to store, and store doesn't have, you go back home, and order one online, and realize that you don't have credit card. Now you even have to get credit card to paint that wall.

Code design should be proportional to game complexity.


oddly enough I agree with you too! Cheesy

I guess it's an argument of complexity vs correctness. I mean, you shouldn't over-complicate code for the sake of complicating it. That's just silly. For example, in his "pong" game I think it's perfectly acceptable it's all really 1 or 2 big classes, because even the "big" class is kinda small in relation to a larger project. The code is still easy to read and there's not much redundancies, so who cares right? Be silly to break it into micro-classes of input controllers, or subclass out the paddles and balls, have another class to handle scoring. That's just overkill.

I totally agree you shouldn't over-complicate things, my only argument is to not do it wrong because it's just a small project. For example, even with 2 classes you should still have proper interface methods to communicate between the two even if it's kinda silly to since directly changing a variable/setting in one class from another probably wont hurt anything in a small project, it's just good practice to use getters and setters anyway.

But yeah, I totally agree, don't make things complicated and focus on finer details of a project that quite frankly, doesn't need it.

- Raymond "Rayvolution" Doerr.
Retro-Pixel Castles - Survival Sim/Builder/Roguelike!
LIVE-STREAMING DEVELOPMENT: http://www.twitch.tv/SG_Rayvolution
Offline 65K
« Reply #6 - Posted 2014-03-31 16:34:58 »

Good code design requires a lot of time to lay out.
No, not necessarily.
Good code design does not imply complexity.
Bad code design often requires a lot of time to get by with - after a certain delay.

Offline 65K
« Reply #7 - Posted 2014-03-31 16:36:57 »

  • Should I make all member variables of the Player class private and use these with setters and getters? If there are maybe 20-30 variables that would be a huge amount of code or not?
No, only ever expose as much as needed and keep your classes as closed as possible.

Offline trollwarrior1
« Reply #8 - Posted 2014-03-31 17:02:56 »

Here is a better design and more complex code. It requires a lot more brain power to understand.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
public class KeyboardHandler implements KeyListener {

   
   public KeyboardHandler() {
   }

   public void tick() {
      for(int i=0;i<keys.size();i++){
         keys.get(i).tick();
      }
   }
   
   private List<Key> keys = new ArrayList<Key>();
   
   public Key KEY_W = new Key(KeyEvent.VK_W);
   public Key KEY_A = new Key(KeyEvent.VK_A);
   public Key KEY_S = new Key(KeyEvent.VK_S);
   public Key KEY_D = new Key(KeyEvent.VK_D);
   
   public class Key {

      public int keycode;
      public boolean down=false,pressed=false;
      private boolean wasdown=false;
     
      public Key(int kc) {
         keys.add(this);
         this.keycode=kc;
      }
     
      public void tick(){
         pressed=false;
         
         pressed=false;
         if(!wasDown&&down) pressed=true;
         
         wasDown=down;
      }
     
   }
   
   @override
   public void keyPressed(KeyEvent event) {
      int kc = event.getKeyCode();

      for(int i=0;i<keys.size();i++) {
         Key key = keys.get(i);
         if(key.keycode==kc) key.down=true;
      }
   }
   
   @override
   public void keyReleased(KeyEvent event) {
      int kc = event.getKeyCode();

      for(int i=0;i<keys.size();i++) {
         Key key = keys.get(i);
         if(key.keycode==kc) key.down=false;
      }
   }
   
   public void keyPressed(KeyEvent event) {
   }
   
}
Offline kpars

JGO Wizard


Medals: 79
Projects: 4
Exp: 3 years


Extreme Typist.


« Reply #9 - Posted 2014-04-01 00:24:26 »

B-B-But, that formatting  Cry

- Jev

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Longarmx
« Reply #10 - Posted 2014-04-01 00:51:44 »

@OP That switch statement looks good except that you need breaks at the end of every case statement so that it doesn't execute the ones after it.

@trolllwarrior1 I would use a hasmap instead. It eliminates the need to loop through your list on every key press and release. Pointing Sorry if you feel like I am bashing you. I am just rambling on at this point.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
public class KeyboardHandler implements KeyListener {

     private HashMap<Integer, Key> keys = new HashMap<Integer, Key>();

     Key KEY_W = new Key(KeyEvent.VK_W);
     Key KEY_A = new Key(KeyEvent.VK_A);
     Key KEY_S = new Key(KeyEvent.VK_S);
     Key KEY_D = new Key(KeyEvent.VK_D);

     public KeyboardHandler(){}

     public void update()
     {
          for(Map.Entry entry : keys.entrySet())
               keys.get(entry.getKey()).update();
     }


     public class Key
     {
          public boolean down = false, pressed = false;
          private boolean wasDown = false;

          public Key(int keyCode)
          {
               keys.put(keyCode, this)
          }
     
          public void update()
          {
               pressed = !wasDown && down;
               wasDown = down;
          }
     }

     @Override
     public void keyPressed(KeyEvent event)
     {
          keys.get(event.getKeyCode()).down = true;
     }

     @Override
     public void keyReleased(KeyEvent event)
     {
          keys.get(event.getKeyCode()).down = false;
     }
}


Offline synLB

Junior Member


Medals: 2



« Reply #11 - Posted 2014-04-01 09:15:00 »

thanks trollwarrior1,

I will try to implement that or maybe the more complicated version(I didn't understand it right now but I will look into it). A will also look into the version of Longarmx, but I will need to refresh hash-maps again Wink

Is it common practice to use a KeyboardHandler?

And for a bigger project would it be better to make the JPanel class an own class instead of an inner class?
Offline trollwarrior1
« Reply #12 - Posted 2014-04-01 09:32:12 »

I doesn't really matter where you put the panel... It doesn't make any difference, as long as you know how it all works. It should be as simple as possible to understand it though.

One more things I would improve in your code, don't draw something in panel method using player coordinates. Better make a method in player class something like '.render(Graphics2D graphics)' and call that method in your panel's paintComponent method.
Offline Longarmx
« Reply #13 - Posted 2014-04-01 18:19:36 »

I prefer classes like this keyboard handler because it provides me with an easier way to get which keys are down or not. Also, "pressed" is nice to have because then you can easily execute something only once.

Offline synLB

Junior Member


Medals: 2



« Reply #14 - Posted 2014-04-02 08:21:11 »

@Longarmx

I just tried to try your KeyBoardHandler to understand how it works and I am getting an error in the update() function:

1  
2  
3  
4  
5  
 public void update()
     {
          for(Map.Entry entry : keys.entrySet())
               keys.get(entry.getKey()).update();
     }


Map.Entry cannot resolved to be a type. I am still looking through it and trying to figure it out. Help is really appreciated.
Offline trollwarrior1
« Reply #15 - Posted 2014-04-02 08:33:24 »

It is because longarmx probably just copied code from his game, and it includes a class called Map.entry, which you don't have, because only he has it.

His code does the same thing as mine, its just he uses hashmap, which one could argue is an easier thing to read than a list in this situation.
Offline Grunnt

JGO Wizard


Medals: 69
Projects: 8
Exp: 5 years


Complex != complicated


« Reply #16 - Posted 2014-04-02 09:15:03 »

I have two classes until now, a game class with an inner class that represents the panel on which is drawn and a player class. I have seen a pong tutorial where the panel is a separate class extending JPanel. I thought that the JPanel belonged to the Game class because it will use possibly variables from it in the future and it would be more of a hassle creating a separate class for it but I'm not really sure if this is true

Both are fine. I think once your game gets more complex putting the panel code in a seperate class may help you keep things manageable. However, that is something best learned through experience, which is precisely what you are doing Grin Just expand things and try to see when you get a hard time keeping a clear overview of what is where in your code. If things get hard to understand and to keep an overview, try refactoring / restructuring your code and move some things to a seperate class (the panel would be a good starting point for example).

@SuppressWarnings("serial") was added above my inner class, why this?

JPanel (or probably one of its parents) implements the Serializable interface, which means you could potentially write an object of that type to disk or send it via the network ("serialize" it to a bunch of bytes). A Serializable object is expected to have a unique class ID so if you deserialize it you can easily find out precisely what of class the object is. Anyway, the short story: this annotation stops Eclipse from giving warnings about this.

Should I make all member variables of the Player class private and use these with setters and getters? If there are maybe 20-30 variables that would be a huge amount of code or not?

Generally yes: make the member variables private. In general you want to minimize "contact points" between classes, so have as few as possible methods or variables accessible to the outside world. This makes it much easier to change the behaviour of the player without having to change everything else (amongst other reasons).
In the case of your Player class there are a couple of things you can do to improve it:
- the Player does not need to know about keyboard or mouse, just about game commands. For example you could use moveLeft(), moveRight(), moveUp() etc. That would make it easier when you want to introduce a different control scheme for example (e.g. using a controller or just different keys). Alternatively you could make a function called setVelocity(float xa, float ya) to set the player movement from the Game class.
- the movePlayer method could be called update(). This method then is called every frame to update the player state in general (of which movement of course is an important part). A big improvement would be to use float values for the player position and movement, and to pass along a "delta time" to the update function giving the time that has passed since the last call to update. E.g.
1  
2  
3  
4  
5  
6  
   public void update(float delta) {
     
      mapPositionX = mapPositionX + xa * delta;
      mapPositionY = mapPositionY + ya * delta;
      // check if the player does not go out of the screen
  }


This makes player movement much more smooth, because you do not have to move a single pixel every frame but can e.g. take multiple frames over one pixel. Read more about it here. One step more advanced would be to use a good game timing loop, e.g. as described here).

The arrow keys work good but sometimes if you release just one key the dot stops for a short time, due to the fact that xa or xy is set to 0, what would be an approach to solve this?

Instead of basing your game controls on KeyPressed and KeyReleased events, you may want to maintain an array of boolean values representing the current key state (e.g. true = key is currently down) and update this array each frame based on the keyboard events. Something like this. This will allow you to for example set the player's velocity if a certain key is down and set the player verlocity to zero whenever the key is up.

These are just some thoughts, I hope it helps a bit.

Offline Grunnt

JGO Wizard


Medals: 69
Projects: 8
Exp: 5 years


Complex != complicated


« Reply #17 - Posted 2014-04-02 09:16:11 »

No. Start with good code design. You don't want to fall into bad habits.

Thats a bit of a paradox though. Nobody starts with good code design as it requires experience to become good at it.

Offline Drenius
« Reply #18 - Posted 2014-04-02 16:31:18 »

Start trying to use a good code design.
Offline Longarmx
« Reply #19 - Posted 2014-04-02 17:04:51 »

Map.Entry cannot resolved to be a type. I am still looking through it and trying to figure it out. Help is really appreciated.
It is because longarmx probably just copied code from his game, and it includes a class called Map.entry, which you don't have, because only he has it.

1  
2  
3  
4  
import java.util.Map.Entry;

for(Entry<Integer, Key> entry : keys.entrySet())
     keys.get(entry.getKey()).update();


I found that code somewhere online when trying to figure out how to iterate through a hashmap.

Start trying to use a good code design.

Yep. Make an effort to use a good code design. At least try to use standard coding conventions and object-oriented practices.

Offline Grunnt

JGO Wizard


Medals: 69
Projects: 8
Exp: 5 years


Complex != complicated


« Reply #20 - Posted 2014-04-03 11:02:23 »

I changed my movePlayer() method to update() and wanted to also implement moveUp/Right/Left/Down methods but if I have a update() method this would not be necessary?

There's many approaches to do this, but the generally used one (and the one I prefer) is to do it the following way:
- the update() method updates the players state, i.e. the player does in fact change position, orientation etcetera. Similarly pretty much every thing in your game will be update()d every frame. After the game state has changed (i.e. the player has moved) you can render the new game state. Every game loop generally follows the same pattern: update the game state in one go, then render the new game state.
- other methods, like moveUp(), moveDown() should not directly move the player but instead set how the next update will play out. E.g. if you call moveUp() that would mean that the next update your player will move up a bit (depending on how much time has passed since the last frame, and the movement speed).

Doing all the update in one method makes it much easier to do more advanced stuff like more complex movement, collision detection etctera. Instead of having the moveUp() method set velocity, for example, you could make it set accelleration (change in velocity per second) and then change velocity and position in the update.

About the keyboard handling: the idea here is similar to what I described above. You should call the keyHandler.poll() method every frame (ideally before updating the player and other things to make it as responsive as possible), e.g.
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
        final long OPTIMAL_TIME = 1000000000 / TARGET_FPS;

        long lastFpsTime = 0;
        int fps = 0;
        while (loopCondition) {

            keyHandler.poll();                  // poll keyboard here
           
            long now = System.nanoTime();
            long updateLength = now - lastLoopTime;
            lastLoopTime = now;


You should remove anything referring to the player from the KeyboardHandler class and leave it as you copied it. Then give movement commands (like what direction to move) to the player from the Game's main update loop (you can think of this as the place where you link inputs to game commands). After that update the game state (in this case player 1 and 2 positions): (I move the poll a bit down here)

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
            if (lastFpsTime >= 1000000000) {
                System.out.println("(FPS: " + fps + ")");
                lastFpsTime = 0;
                fps = 0;
            }

            // do game updates
           keyHandler.poll(); // poll keyboard here
           
            if (keyHandler.keyDown(KeyEvent.VK_UP)) {
                player1.setya(-1);
            }
            if (keyHandler.keyDown(KeyEvent.VK_DOWN)) {
                player1.setya(1);
            }
            // and so on ...
       
            // updates (moves) the player according to the delta calculated and velocity set
           // above
           player1.update(delta);
            player2.update(delta);


Also the setMapPositionXBefore() calls in the Game class can be moved to the Player's update class, which is much nicer. E.g.

1  
2  
3  
4  
5  
6  
7  
8  
9  
    public void update(double delta) {
        // mapPositionX&Y are floats, therefore another cast

        setMapPositionXBefore(getMapPositionX());
        setMapPositionYBefore(getMapPositionY());

        if (this.onTrack == true) {
            mapPositionX = (float) (mapPositionX + xa * delta);
            mapPositionY = (float) (mapPositionY + ya * delta);


You could do the same with the location checking.

Some more advice:
- avoid using full paths (like the "C:/Users/racingtrack.png" etc) as this makes your game unusable on other computers and makes it hard to keep track of everything. Always use a path relative to your application, e.g. in a "data" subdirectory, and then use e.g. "data\racingtrack.png" as the path.
- you take an interesting approach towards checking whether the player is on the track. Its a creative solution and it could work. Collision detection is quite difficult to do right, though, but it is a great thing to learn.

Offline synLB

Junior Member


Medals: 2



« Reply #21 - Posted 2014-04-03 14:05:36 »


About the keyboard handling: the idea here is similar to what I described above. You should call the keyHandler.poll() method every frame (ideally before updating the player and other things to make it as responsive as possible), e.g.
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
        final long OPTIMAL_TIME = 1000000000 / TARGET_FPS;

        long lastFpsTime = 0;
        int fps = 0;
        while (loopCondition) {

            keyHandler.poll();                  // poll keyboard here
           
            long now = System.nanoTime();
            long updateLength = now - lastLoopTime;
            lastLoopTime = now;


You should remove anything referring to the player from the KeyboardHandler class and leave it as you copied it. Then give movement commands (like what direction to move) to the player from the Game's main update loop (you can think of this as the place where you link inputs to game commands). After that update the game state (in this case player 1 and 2 positions): (I move the poll a bit down here)

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
            if (lastFpsTime >= 1000000000) {
                System.out.println("(FPS: " + fps + ")");
                lastFpsTime = 0;
                fps = 0;
            }

            // do game updates
           keyHandler.poll(); // poll keyboard here
           
            if (keyHandler.keyDown(KeyEvent.VK_UP)) {
                player1.setya(-1);
            }
            if (keyHandler.keyDown(KeyEvent.VK_DOWN)) {
                player1.setya(1);
            }
            // and so on ...
       
            // updates (moves) the player according to the delta calculated and velocity set
           // above
           player1.update(delta);
            player2.update(delta);


I changed it in my code according to your sugestion. Every key should then have an if statement like that:
1  
2  
3  
 if (keyHandler.keyDown(KeyEvent.VK_UP)) {
                player1.setya(-1);
            }

should I move this code to a method or even another class? With several keys this would get unhandy. And could I even change the key values for that (clicking a button and the next key will be up/down/left/right)?


Also the setMapPositionXBefore() calls in the Game class can be moved to the Player's update class, which is much nicer. E.g.

1  
2  
3  
4  
5  
6  
7  
8  
9  
    public void update(double delta) {
        // mapPositionX&Y are floats, therefore another cast

        setMapPositionXBefore(getMapPositionX());
        setMapPositionYBefore(getMapPositionY());

        if (this.onTrack == true) {
            mapPositionX = (float) (mapPositionX + xa * delta);
            mapPositionY = (float) (mapPositionY + ya * delta);


You could do the same with the location checking.

Some more advice:
- avoid using full paths (like the "C:/Users/racingtrack.png" etc) as this makes your game unusable on other computers and makes it hard to keep track of everything. Always use a path relative to your application, e.g. in a "data" subdirectory, and then use e.g. "data\racingtrack.png" as the path.
- you take an interesting approach towards checking whether the player is on the track. Its a creative solution and it could work. Collision detection is quite difficult to do right, though, but it is a great thing to learn.

These changes seem to work fine, I have just one question about collision detection:
I tried to detect when the player is out of the racing track and move his position to the point where he was still in the track but somehow the point just gets stuck and can't move anymore.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
   public void update(double delta, GameHelper gameHelper) {
      // mapPositionX&Y are floats, therefore another cast
     gameHelper.checkLocation(this);

      if (onTrack) {
         mapPositionX = (float) (mapPositionX + xa * delta);
         mapPositionY = (float) (mapPositionY + ya * delta);
      } else {
         System.out.println("not on track");
         mapPositionX = mapPositionXBefore;
         mapPositionY = mapPositionYBefore;
      }

   }


and I set the mapPositionXBefore( I know, bad name Wink) here in the game loop:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
player1.setMapPositionXBefore(player1.getMapPositionX());
         player1.setMapPositionYBefore(player1.getMapPositionY());
         player2.setMapPositionXBefore(player2.getMapPositionX());
         player2.setMapPositionYBefore(player2.getMapPositionY());

         // Poll the KeyboardHandler!!!
        keyHandler.poll();

         // player 1
        if (keyHandler.keyDown(KeyEvent.VK_UP)) {
            player1.setya(-1);

         }
Offline Grunnt

JGO Wizard


Medals: 69
Projects: 8
Exp: 5 years


Complex != complicated


« Reply #22 - Posted 2014-04-04 06:21:48 »

should I move this code to a method or even another class? With several keys this would get unhandy. And could I even change the key values for that (clicking a button and the next key will be up/down/left/right)?

You could move it to a seperate method (e.g. handleInput). I dont really understand what you mean by changing the key values?

I tried to detect when the player is out of the racing track and move his position to the point where he was still in the track but somehow the point just gets stuck and can't move anymore.

Yeah thats where things get "interesting" Grin Collission detection and physics is often quite hard to get right. Things get stuck to walls, drop through or just generally explode in interesting directions, without any clear reason.

Do you use an IDE like Eclipse? For problems like these debugging is a great help. You can go through your program line by line and see what happens with the variables. From your problem description I expect the issue is with the checkLocation method, but I dont have time to figure out precisely what the issue is. Good luck!

Offline synLB

Junior Member


Medals: 2



« Reply #23 - Posted 2014-04-04 07:58:11 »

Say if I wanted to change the key that moves the player forward/backwards/left/right could I assign this to another key by clicking a button that registers the next key that is pressed?

Yeah I use Eclipse and know the basics of debugging, creating breakpoints and moving through the code, I will try to do this.

Thanks for the help Smiley
Offline princec

JGO Kernel


Medals: 380
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #24 - Posted 2014-04-04 08:09:58 »

Hey chaps please consider using the JGO pastebin for large bits of code.

Cas Smiley

Offline Grunnt

JGO Wizard


Medals: 69
Projects: 8
Exp: 5 years


Complex != complicated


« Reply #25 - Posted 2014-04-04 09:04:25 »

Good one, I have to get used to pastebin a bit more.

Offline Grunnt

JGO Wizard


Medals: 69
Projects: 8
Exp: 5 years


Complex != complicated


« Reply #26 - Posted 2014-04-04 09:05:32 »

Say if I wanted to change the key that moves the player forward/backwards/left/right could I assign this to another key by clicking a button that registers the next key that is pressed?

Sure, you could store this in an InputConfiguration class or something, which links each game command to an input (e.g. keyboard key or mouse press).

Pages: [1]
  ignore  |  Print  
 
 

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

radar3301 (12 views)
2014-09-21 23:33:17

BurntPizza (30 views)
2014-09-21 02:42:18

BurntPizza (22 views)
2014-09-21 01:30:30

moogie (20 views)
2014-09-21 00:26:15

UprightPath (28 views)
2014-09-20 20:14:06

BurntPizza (32 views)
2014-09-19 03:14:18

Dwinin (48 views)
2014-09-12 09:08:26

Norakomi (74 views)
2014-09-10 13:57:51

TehJavaDev (103 views)
2014-09-10 06:39:09

Tekkerue (50 views)
2014-09-09 02:24:56
List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50

List of Learning Resources
by SilverTiger
2014-07-31 16:26:06

List of Learning Resources
by SilverTiger
2014-07-31 11:54:12

HotSpot Options
by dleskov
2014-07-08 01:59:08
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!