Java-Gaming.org    
Featured games (79)
games approved by the League of Dukes
Games in Showcase (477)
Games in Android Showcase (108)
games submitted by our members
Games in WIP (536)
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  
  [LibGDX] One Controller class, multiple controllers  (Read 2691 times)
0 Members and 1 Guest are viewing this topic.
Offline Gibbo3771
« Posted 2013-09-26 00:07:41 »

I first created a basic pong game some weeks ago, terribly down and learned alot.

I have started fresh and I am trying to figure out how to do this.

I have one controller class called PaddleControl, you can guess what it does.

In here we have the following:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
   enum Control {

      UP, DOWN
   }

   static Map<Control, Boolean> keys = new HashMap<PaddleControl.Control, Boolean>();
   static {

      keys.put(Control.UP, false);
      keys.put(Control.DOWN, false);

   };


I also have a bunch of methods that are self explanatory as such:

1  
2  
3  
   public void upPressed() {
      keys.get(keys.put(Control.UP, true));
   }


1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
   public boolean keyDown(int keycode) {
      switch (keycode) {
      case Keys.W:
      case Keys.UP:
         upPressed();
         break;
      case Keys.S:
      case Keys.DOWN:
         downPressed();
         break;
      default:
         assert false;
      }
      return false;
   }


Now the problem persists in this part here, I want to NOT do this as it makes the object useless since it would be constraint to only 1 paddle being controlled despite it being a 2 player game:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
   public void processInput(){
     
      if(keys.get(Control.UP)){
         GameScreen.p1Paddle.getBody().setLinearVelocity(0, 10f);
         System.out.print("Am I moving?");
      }else if(keys.get(Control.DOWN)){
         GameScreen.p1Paddle.getBody().setLinearVelocity(0, -10f);
      }else if(!keys.get(Control.UP) && !keys.get(Control.DOWN)){
         GameScreen.p1Paddle.getBody().setLinearVelocity(0, 0);
      }
   }


So this is in my update method and that update method is within the GameScreen, the code is pretty straight forward, you press up, booleans take it from there and the if statements decide what to do, however as you can see I am having to access the GameScreen.p1Paddle in order to get that specific body. The way I want it is body.getBody, since I have the field declared for it.

Whenever I do this with just a single controller controller the p1 paddle, I get a null exception on the following line in bold ():

1  
2  
3  
      }else if(!keys.get(Control.UP) && !keys.get(Control.DOWN)){
         GameScreen.p1Paddle.getBody().setLinearVelocity(0, 0);[/b]
      }


Ideally what I want to do is create 2 controllers, then use the multiplexer to handle both seperately. However I see some issues, how on earth can it tell which paddle should move to which key, as you can see I have it setup to be controlled with either W/S and Up/Down.

Am I going about this wrong?

EDIT: Fixed the null exception problem, now I just need a way for each controllers to control specific paddles without getting mixed up


"This code works flawlessly first time and exactly how I wanted it"
Said no programmer ever
Offline Jimmt
« League of Dukes »

JGO Kernel


Medals: 128
Projects: 4
Exp: 3 years



« Reply #1 - Posted 2013-09-26 06:17:45 »

Pardon me if I skipped something, but what's wrong with InputProcessor/Gdx.input.isKeyPressed()?
Offline Gibbo3771
« Reply #2 - Posted 2013-09-26 09:22:41 »

I done it like this last time is all.

Also that would have the same problem would it not? If I am using Paddle.getBody, how would it tell apart the paddles?

The idea behind it is to stop the controller being constraint to 2 controllers, later I will be added p1 vs cpu, p2 vs cpu and cpu vs cpu. Therfore i need seperate controllers for each and be able to create them without having to go into the PaddleControl class and add GameScreen.p1Paddle etc etc.

"This code works flawlessly first time and exactly how I wanted it"
Said no programmer ever
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline xsvenson
« Reply #3 - Posted 2013-09-26 10:28:44 »

You need 2 mappings:
  • input -> action
  • input -> body

This means that You need to be able to understand to which body and to which action the current input goes.
Your currently have only the input -> action part. (if Keys.W doPressUp() )

How You implement the mappings is largely up to You but You need to know which key to goes where. Here is small something to get an idea.
Lets say, P1 has the controls of W and S and P2 has I and K (2 players on 1 keyboard)
You can do something like that
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
switch(inputKey)
{
    case S:
    case W: this.body = P1.paddle.body;break;
    case I:
    case K: this.body = P2.paddle.body;break
}

switch ( inputKey)
{
    case W:
    case I: performUp(this.body);break;
    case S:
    case K: performDown(this.body);break;
}


Basically 2 selects. This is quick and dirty, apply abstraction and design according to own desire.

“The First Rule of Program Optimization: Don't do it. The Second Rule of Program Optimization (for experts only!): Don't do it yet.” - Michael A. Jackson
Offline opiop65

JGO Kernel


Medals: 153
Projects: 7
Exp: 3 years


JumpButton Studios


« Reply #4 - Posted 2013-09-26 12:23:14 »

I uh... I don't entirely understand why you would ever structure an input handler like that. For one, just use LibGDX's built in one, don't reinvent the wheel. Two, your structure is bad. Don't use hashmaps, use an array of booleans. Loop through, set them all the false each frame and then get a key code and change the corresponding boolean to true.

Offline Gibbo3771
« Reply #5 - Posted 2013-09-26 17:08:11 »

Well my Java skills are pretty weak and I am still learning proper design and use.

Can you post a rough idea of what you mean?

So basically create a boolean array that holds a key status, true or false. Loop through the array in my render method and adjust movement depending on the values.

I understand all that but how would it work if I am setting them to false every frame?, if I continue to hold the key down will setting a false boolean to override my key? as if I lifted my finger?

"This code works flawlessly first time and exactly how I wanted it"
Said no programmer ever
Offline SHC
« Reply #6 - Posted 2013-09-26 17:18:10 »

You are not going to loop through the array. As a rough idea, most of the keys that exist on a standard ps/2 keyboard with 106 keys have keycode values which are less than 603. So, creating an array of 603 elements will suffice. So

1  
private static boolean[] KEYS = new boolean[603];

Now in with the KeyListener methods, this array can be populated by

1  
2  
3  
4  
5  
6  
7  
8  
9  
public void keyPressed(KeyEvent e)
{
    KEYS[e.getKeyCode()] = true;
}

public void keyReleased(KeyEvent e)
{
    KEYS[e.getKeyCode()] = false;
}

And then you can easily query them every frame with

1  
2  
3  
4  
if (KEYS[KeyEvent.VK_LEFT))
{
    // Left arrow has been pressed
}

This basically works with all the keycodes are defined as integers. This is actually for Java2D but there should be an identical one for LibGdx.

Offline xsvenson
« Reply #7 - Posted 2013-09-26 17:20:19 »

For libgdx
1  
Gdx.input.isKeyPressed(Input.Key.W)

“The First Rule of Program Optimization: Don't do it. The Second Rule of Program Optimization (for experts only!): Don't do it yet.” - Michael A. Jackson
Offline Gibbo3771
« Reply #8 - Posted 2013-09-26 18:30:26 »

You are not going to loop through the array. As a rough idea, most of the keys that exist on a standard ps/2 keyboard with 106 keys have keycode values which are less than 603. So, creating an array of 603 elements will suffice. So

1  
private static boolean[] KEYS = new boolean[603];

Now in with the KeyListener methods, this array can be populated by

1  
2  
3  
4  
5  
6  
7  
8  
9  
public void keyPressed(KeyEvent e)
{
    KEYS[e.getKeyCode()] = true;
}

public void keyReleased(KeyEvent e)
{
    KEYS[e.getKeyCode()] = false;
}

And then you can easily query them every frame with

1  
2  
3  
4  
if (KEYS[KeyEvent.VK_LEFT))
{
    // Left arrow has been pressed
}

This basically works with all the keycodes are defined as integers. This is actually for Java2D but there should be an identical one for LibGdx.

Ah right this makes sense. I have tried to implement this and came up with this for my class:

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  
package com.gibbo.pongv2.controller;

import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.InputAdapter;
import com.gibbo.pongv2.screens.GameScreen;

public class PaddleControl extends InputAdapter {
   
   private boolean[] KEYS = new boolean[603];

   @Override
   public boolean keyDown(int keycode) {
      KEYS[keycode] = true;
      return true;
   }

   @Override
   public boolean keyUp(int keycode) {
      KEYS[keycode] = false;
      return true;
   }

   @Override
   public boolean scrolled(int amount) {
      // TODO Auto-generated method stub
     return false;
   }
   
   public void processInput(){
      if(KEYS[Keys.W]){  
         GameScreen.p1Paddle.getBody().setLinearVelocity(0, 10);
      }else{
         GameScreen.p1Paddle.getBody().setLinearVelocity(0, 0);
      }
     
   }
   
   public void update(float delta){
      processInput();
   }

}


I now have this, which is MUCH less code than before and seems to do the same thing.

Now I still have the problem of moving each paddle seperate.
Even if I use GameScreen.p1Paddle it still moves p2 paddle and vice versa, it can't tell the different between them.

So now all I want to do is have 2 paddles, 1 controller. I have to head out for dinner so will have a good bash at it later, thanks everyone!

"This code works flawlessly first time and exactly how I wanted it"
Said no programmer ever
Offline opiop65

JGO Kernel


Medals: 153
Projects: 7
Exp: 3 years


JumpButton Studios


« Reply #9 - Posted 2013-09-26 21:53:19 »

I dont understand the problem, just have seperate variables that you increase/decrease in the input handler and then call something like paddle1.move(x1, y1) and paddle2.move(x2, y2). I still would recommend using the libgdx way though. You're reinventing the circle here.

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline SHC
« Reply #10 - Posted 2013-09-27 05:14:41 »

As xsvenson said, LibGdx had this ability. It already manages what I've said underneath. So you can now change the class code to

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  
package com.gibbo.pongv2.controller;

import com.badlogic.gdx.Input.Keys;
import com.gibbo.pongv2.screens.GameScreen;

public class PaddleControl
{

    public void processInput()
    {
        if (Gdx.input.isKeyPressed(Input.Key.W))
        {
            GameScreen.p1Paddle.getBody().setLinearVelocity(0, 10);
        }
        else
        {
            GameScreen.p1Paddle.getBody().setLinearVelocity(0, 0);
        }
    }

    public void update(float delta)
    {
        processInput();
    }

}

This is much less code to than your previous ones.

Offline Gibbo3771
« Reply #11 - Posted 2013-09-27 14:10:36 »

Yeah I am really making this more complex than it needs to be haha.

OK I will use the gdx.input.isKeyPressed method, at least I know how to do it without that at least Smiley

"This code works flawlessly first time and exactly how I wanted it"
Said no programmer ever
Offline Gibbo3771
« Reply #12 - Posted 2013-09-30 17:11:22 »

I can not get this to work lol, it seems that only 1 paddle actually gets recognized at any given time, normally the most recently created.

I have this for my controller class:

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  
public class PaddleControl {
   
   
   
   public void processInput(){
      // Player 1 controls
     if(Gdx.input.isKeyPressed(Keys.W)){  
         GameScreen.p1Paddle.getBody().setLinearVelocity(0, 10);
         System.out.println("Test");
      }else if(Gdx.input.isKeyPressed(Keys.S)){
         GameScreen.p1Paddle.getBody().setLinearVelocity(0, -10);
         System.out.println("Test");
      }else{
         GameScreen.p1Paddle.getBody().setLinearVelocity(0, 0);
         
      }
     
      // Player 2 controls
     if(Gdx.input.isKeyPressed(Keys.UP)){
         GameScreen.p2Paddle.getBody().setLinearVelocity(0, 10);
      }else if(Gdx.input.isKeyPressed(Keys.DOWN)){
         GameScreen.p2Paddle.getBody().setLinearVelocity(0, -10);
      }else{
         GameScreen.p2Paddle.getBody().setLinearVelocity(0, 0);
      }
     
   }
   
   
   
   public void update(float delta){
      processInput();
   }

}


As suggested by everyone here, much cleaner code and does the same thing.

This is my gamescreen:

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  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
public class GameScreen extends InputAdapter implements Screen {
   
   private World world;
   private Box2DDebugRenderer debugRender;
   private OrthographicCamera cam;
   
   // Scene2D animation stuff
  TweenManager tweenManager;
   
   // For drawing images
  private SpriteBatch batch;
   private Sprite pongTableSprite;  
   private Array<Body> tmpBodies = new Array<Body>();  

   // Paddle stuff
  public static Paddle p1Paddle;
   public static Paddle p2Paddle;
   
   // Paddle control
  private PaddleControl paddleControl;
   
   private static final float STEP_TIME = 1f / 60f;

   @Override
   public void render(float delta) {
      // TODO Test this, I am not clearing the screen since the main menu has the same background
//      Gdx.gl.glClearColor(0, 0, 0, 1);
//      Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
     
      debugRender.render(world, cam.combined);
     
      // update our animations
     tweenManager.update(delta);
      cam.update();

      // Start drawing shit
     batch.setProjectionMatrix(cam.combined);
      cam.update();
      batch.begin();
      pongTableSprite.draw(batch);
     
     
      //Iterate through bodies, get sprite
     world.getBodies(tmpBodies);
      for(Body body : tmpBodies){
         if(body.getUserData() instanceof Sprite){
            Sprite sprite = (Sprite) body.getUserData();
            sprite.setPosition(body.getPosition().x - sprite.getWidth() / 2, body.getPosition().y - sprite.getHeight() / 2);
            sprite.setRotation(body.getAngle() * MathUtils.radiansToDegrees);
            sprite.draw(batch);
         }
      }
     
      batch.end();      
           
      // TODO Remove later
     if(Gdx.input.isKeyPressed(Keys.ESCAPE)){
         ((Game) Gdx.app.getApplicationListener()).setScreen(new MainMenuScreen());
      }
     
      paddleControl.update(delta);
     

     
      // Update all box2d related calculations
     world.step(STEP_TIME, 8, 5);

   }

   @Override
   public void resize(int width, int height) {


   }

   @Override
   public void show() {  
      // Setup tween manager
     tweenManager = new TweenManager();
      Tween.registerAccessor(Sprite.class, new SpriteAccessor());
     
      // Create world
     world = new World(new Vector2(0, 0), true);      
      // Create camera
     cam = new OrthographicCamera(30, 14);  
      cam.position.set(15, 7f, 0);
      // Create debug renderer
     debugRender = new Box2DDebugRenderer();      
     
      //Create sprites and textures
     batch = new SpriteBatch();
      pongTableSprite = new Sprite(new Texture("img/pongTable.png"));
      pongTableSprite.setSize(30, 14);
     
      // Create the paddles
     p1Paddle = new Paddle(world, 1f, 7f);
      p2Paddle = new Paddle(world, 28f, 7f);
     
      // Background animation
     // TODO test this, it is supposed to fade a the background in from nothing
     // instead it fades out the main menu text and writing by pasting a new image over
     Tween.set(pongTableSprite, SpriteAccessor.ALPHA).target(0).start(tweenManager);
      Tween.to(pongTableSprite, SpriteAccessor.ALPHA, 1.5f).target(1).start(tweenManager);
      // Fade in for paddles
     world.getBodies(tmpBodies);
      for(Body body : tmpBodies){
         if(body.getUserData() instanceof Sprite){
            Sprite sprite = (Sprite) body.getUserData();
            sprite.setPosition(body.getPosition().x - sprite.getWidth() / 2, body.getPosition().y - sprite.getHeight() / 2);
            sprite.setRotation(body.getAngle() * MathUtils.radiansToDegrees);
            Tween.set(sprite, SpriteAccessor.ALPHA).target(0).start(tweenManager);
            Tween.to(sprite, SpriteAccessor.ALPHA, 1.5f).target(1).start(tweenManager);
         }
      }
     
      // Create controller
     paddleControl = new PaddleControl();
     
   }

   @Override
   public void hide() {
      dispose();
   }

   @Override
   public void pause() {

   }

   @Override
   public void resume() {

   }

   @Override
   public void dispose() {
      batch.dispose();
      debugRender.dispose();
   }

}


I can't quite figure out what is wrong here, this is a list of things I have tried:

  • Remove each paddle and test separately, both work 100% fine on there own without the other
  • Created an update method in the Paddle class to check if they are moving on the Y axis, only the most recent created paddle moves
  • Removed sprites to make sure it was not a sprite update problem
  • Remove graphics completely to make sure it was not a problem caused by the background
Nothing seems to work, the input from the left paddle just gets completely ignored lol.

any ideas?

"This code works flawlessly first time and exactly how I wanted it"
Said no programmer ever
Offline Gibbo3771
« Reply #13 - Posted 2013-09-30 17:39:13 »

Heh, moral of the story....don't use static when you don't need to. Seems to have fixed it:

1  
2  
3  
4  
5  
6  
7  
8  
9  
-- omitted for clarity --
public class Paddle {
   
   private Body body;
   private Fixture fixture;
   private float sizeX = 0.1f, sizeY = 1.15f;
   
   Sprite paddleSprite;
-- omitted for clarity --


I had the Body set to private static for some weird reason. Fixed now.

"This code works flawlessly first time and exactly how I wanted it"
Said no programmer ever
Offline Jimmt
« League of Dukes »

JGO Kernel


Medals: 128
Projects: 4
Exp: 3 years



« Reply #14 - Posted 2013-09-30 18:19:28 »

The Controller class is kind of unnecessary - why not just do the polling/movement directly in the paddle class?
Offline Gibbo3771
« Reply #15 - Posted 2013-09-30 18:35:56 »

The Controller class is kind of unnecessary - why not just do the polling/movement directly in the paddle class?

I could yeah, I could even put it in the GameScreens render method but just have this thought that if the game was complex, with a lot of controls such as zooming, panning, keyboard shortcuts. May as well practice doing this stuff in a seperate class to keep it neat.

Or is that a bad idea? It's not as if it is causing a problem imo, seems to work just fine.

What you think?

Cheers

"This code works flawlessly first time and exactly how I wanted it"
Said no programmer ever
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

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

The first screenshot will be displayed as a thumbnail.

CogWheelz (16 views)
2014-07-30 21:08:39

Riven (22 views)
2014-07-29 18:09:19

Riven (15 views)
2014-07-29 18:08:52

Dwinin (12 views)
2014-07-29 10:59:34

E.R. Fleming (33 views)
2014-07-29 03:07:13

E.R. Fleming (12 views)
2014-07-29 03:06:25

pw (43 views)
2014-07-24 01:59:36

Riven (42 views)
2014-07-23 21:16:32

Riven (30 views)
2014-07-23 21:07:15

Riven (31 views)
2014-07-23 20:56:16
List of Learning Resources
by SilverTiger
2014-07-31 13:54:12

HotSpot Options
by dleskov
2014-07-08 03:59:08

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:58:24

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:47:22

How do I start Java Game Development?
by ra4king
2014-05-17 11:13:37

HotSpot Options
by Roquen
2014-05-15 09:59:54

HotSpot Options
by Roquen
2014-05-06 15:03:10

Escape Analysis
by Roquen
2014-04-29 22:16:43
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!