I'm working on a Java 2D game which requires a max of six keys be held down at the same time.
The game is for two players on the same keyboard, playing simultaneously.
However, all three computers I ran the program on only allow a max of three keys held at a time. They all have trouble with reacting to more than three keys being held.
It seems that pressing a new key after three are already held, either cancels some other key-holding or is ignored.
I've been told that this is a hardware issue. Most keyboards can't handle more than three keys held at a time. But a lot of games do require this, and they do not require special gaming-keyboards to run on my computer without problems.
So there has to be a solution that will make the game playable on any standard keyboard.
If there is, could you please explain to me how to code it in my program?
(I use Key Bindings).
The game's controls:
Player 1
Rotate sprite and set angle of movement: LEFT arrow
Rotate sprite and set angle of movement: RIGHT arrow
Move forward: UP arrow
Shoot missile: ENTER key
Player 2
Rotate sprite and set angle of movement: 'A' key
Rotate sprite and set angle of movement: 'D' key
Move forward: 'W' key
Shoot missile: 'T' key
Relevant code:
The Key Bindings part:
// An action for every key-press.
// Each action sets a flag indicating the key is pressed.
leftAction = new AbstractAction(){
public void actionPerformed(ActionEvent e){
keysPressed1[0] = true;
}
};
rightAction = new AbstractAction(){
public void actionPerformed(ActionEvent e){
keysPressed1[1] = true;
}
};
// And so on...
// ....
// An action for every key-release.
// Each action sets a flag indicating the key was released.
// This is only necessary for some of the keys.
leftReleased = new AbstractAction(){
public void actionPerformed(ActionEvent e){
keysPressed1[0] = false;
}
};
rightReleased = new AbstractAction(){
public void actionPerformed(ActionEvent e){
keysPressed1[1] = false;
}
};
// And so on...
// ....
// Binding the keys to the actions.
inputMap.put(KeyStroke.getKeyStroke("UP"),"upAction");
inputMap.put(KeyStroke.getKeyStroke("LEFT"),"leftAction");
// etc...
actionMap.put("upAction",upAction);
actionMap.put("leftAction",leftAction);
// etc...
In the Board class. It has most of the game's code.
This part checks the flags and reacts to key presses and releases.
keysPressed1 = tank1.getKeys(); // get flags-array of tank1.
keysPressed2 = tank2.getKeys(); // get flags-array of tank2.
if(keysPressed1[0]==true) // if LEFT is pressed.
tank1.setAngle(tank1.getAngle()-3);
if(keysPressed1[1]==true) // if RIGHT is pressed.
tank1.setAngle(tank1.getAngle()+3);
if(keysPressed1[2]==true){ // if UP is pressed.
tank1.setDX(2 * Math.cos(Math.toRadians(tank1.getAngle())));
tank1.setDY(2 * Math.sin(Math.toRadians(tank1.getAngle())));
}
if(keysPressed1[2]==false){ // if UP is released.
tank1.setDX(0);
tank1.setDY(0);
}
// And the same for the second player's keys...
This is mostly how reacting to key-presses and key-releases works in my program. When a key is pressed or released, a flag is set. The Board class reades the flags every game-loop cycle and reacts accordingly.
As I said, the program doesn't react correctly to more than 3 keys held at a time, probably because of the keyboard. Is there a way to code a solution?
Help will be very appreciated.
Thanks a lot
Are you sure you're not experiencing ghosting? If it is ghosting, it's a hardware limitation.
Here is a tester -> http://www.microsoft.com/appliedsciences/content/projects/KeyboardGhostingDemo.aspx
And here is a description of ghosting -> http://www.microsoft.com/appliedsciences/antighostingexplained.mspx
I'm not very familiar with this topic, but doing some research I stumbled upon LWJGL. It's a Java game library and looks pretty promising, according to your problem. Take a look at this.
Related
I am creating a simple game in which a keypressed event moves a JLabel a certain number of pixels in a JPanel.
It works without any errors and when i hit the right arrow it moves right, and so on. However, as soon as i press a second key it interrupts the original movement.
For example if i was holding down the left key (and the object was moving left) and then i hit the up arrow the object stops moving left. This is (of course) because i am using only one key event for all my key.
This obviously is not an acceptable situation because as soon as i add a second object (for another player) each player will be constantly stopping the other players movement.
So my question is this: How best can i incorporate multiple simultaneous key actions? (preferably without writing dozens of KeyAdapter's and KeyEvent's)
INFO: currently i have a second class which extends a KeyAdapter. This second class includes a KeyPressed function which then uses .getKeyCode(); to find out which key is pressed and act.
EDIT: any suggestions are welcome!
The solution is actually quite simple. When a key pressed event is registered store a variable that records the movement. When a key released event is registered un-store that variable.
This then means that until you release you keys the movement will continue to be registered. The solution also requires a timer in order to loop repeatedly and check if your event is running or not. You could use a delay but a separate thread is the most effective for this type of task.
First you need a global variable that can store your action:
String action = "";
Then you need a key listener to record your input and when you stop inputting:
// create key listener
addKeyListener(new KeyListener()
{
#Override
public void keyPressed(KeyEvent e)
{
int keyCode = e.getKeyCode();
String event = KeyEvent.getKeyText(keyCode);
if (event.equals("A")) {
// DO YOUR EVENT IF THE KEY HAS BEEN PRESSED
action = "A";
}
}
#Override
public void keyTyped(KeyEvent e) {}
#Override
public void keyReleased(KeyEvent e) {
int keyCode = e.getKeyCode();
String event = KeyEvent.getKeyText(keyCode);
if (event.equals("A")) {
// STOP DOING YOUR EVENT
action = "";
}
}
});
Lastly you will need a timer thread to loop and do your action:
// CREATE A TIMER THREAD
Timer timer = new Timer();
timer.schedule(new TimerTask() {
// this is what happens every time the timer runs
#Override
public void run() {
// check if your action should be preformed
if (action.equals("A")) {
// ... do something
}
}
}, 0, 1000); // 1000 MILLESECONDS = 1 SECOND
// note: you will need to run 'timer.cancel();' at some point to end your timer
Also it is worth looking at these other topics, including this about keylistener is games. And about timer tasks.
I'm looking to make a boulderdash problem but first i'm just trying to get the player to move around, he's allowed to move to any space that is not a rock(1) or a wall(0).
My up and left motions work fine but right and down are screwed up, they're moving multiple spaces even if the key is only pressed once. Here's a visualization if it helps, i'm posting all the relevant code but i'm guessing the bug is in he keylistener section
Here's the code i uploaded on another website http://textuploader.com/13k1
I spent several minutes trying to copy paste the code here but the code function but not all the code was being classified as code and i wasn't allowed to submit it
Here are the links to other relevant classes and files
http://www.scs.ryerson.ca/~ikokkari/BDTile.java
http://www.scs.ryerson.ca/~ikokkari/BDLevelReader.java
http://www.scs.ryerson.ca/~ikokkari/levels.xml
First of all never use the numeric constants instead of the field constants,
public void keyPressed(KeyEvent k) {
int keyCode = k.getKeyCode();
if(keyCode == KeyEvent.VK_R) // not keyCode == 82
}
What would be a better approach is to use an InputMap and ActionMap, which I think is also called "Key Binding" (as suggested by MadProgrammer). The input map maps a key stroke to an action name, and the action map maps the action name to the action you want to take.
Replace your line (and the whole KeyListener extended class)
this.addKeyListener(new MyKeyListener());
with something like
this.getInputMap().put(KeyStroke.getKeyStroke("control L"), "link");
where you need to refer to the documentation of KeyStroke.getKeyStroke to modify the specified key stroke to your needs. In my example "link" is the name of the action to be taken when CTRL+L is pressed. Now we need to specify what "link does"
this.getActionMap().put("link", new LinkAction());
where LinkAction is my class extending AbstractAction which in your case should includes your methods such as levelReaderObject.setCurrentLevel(presentLevel);.
Note that you don't need to create an Action for every key. For movement (up, down, left, right) I would bind all of the movement buttons to different action names ("move up" etc.), but then map all the action names to the same action and let the methods inside that action do the work:
this.getActionMap().put("move up", new MoveAction(0));
this.getActionMap().put("move down", new MoveAction(1));
this.getActionMap().put("move right", new MoveAction(2));
this.getActionMap().put("move left", new MoveAction(3));
with
class MoveAction extends AbstractAction {
int direction;
public MoveAction (int direction) {
this.direction = direction;
}
#Override
public void actionPerformed(ActionEvent e) {
switch(direction) // perform the action according to the direction
}
}
Please note that my suggestion to grouping the movement actions together is a design decision and you should decide how to structure the bindings yourself (you could use one action for everything or one action for each).
I'm making a small game project in Java. In it you have a character that shoots in the direction of the mouse when "pressed" or "dragged" (you know, in Java's terms). The only problem is that if you stop dragging but you still hold the left mouse button down you stop shooting.
Is there a way to detect if the mouse button is down after a drag?
NOTE: the mouse is not sensed as "pressed" after the drag.
You will get the information when a mouse button is pressed and when it is released again. If you want to know the state in between, you need to use a boolean to store that information.
Example:
final boolean[] buttonStates = new boolean[3];
public void mousePressed(MouseEvent e) {
buttonStates[e.getButton()] = true;
}
public void mouseReleased(MouseEvent e) {
buttonStates[e.getButton()] = false;
}
You would do the same for keyboard input by the way.
I'm trying to make the game tetris in Java as a fun side project.
My game board is a grid of tiles:
grid = new Tile[height][width];
And within the grid, I create a new Tile object: activetile = new Tile(this,0, 0); //add new tile to "this" board
Currently:
I'm able to control a single tile- move it down, left and right
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if(keyCode == KeyEvent.VK_DOWN) {
checkBottomFull(0,4);
collisionCheck(activetile.getX(),activetile.getY());
checkEndGame(activetile.getX(), activetile.getY());
activetile.setLocation(activetile.getX(), activetile.getY()+1);
System.out.println("coordinates: " + activetile.getX() + ", " + activetile.getY());
repaint();
}
...right key and left key code omitted
As you can see from the keyPressed method, checkBottomFull() will clear bottom row if full, collisionCheck() will generate a new piece if block hits floor or another piece below, and checkEndGame() will end the game if block is stuck at the top.
I'm struggling with the following:
To create an actual tetris piece, I was thinking I should just generate 3 other instances of Tile, and based on what piece it is (L, O, Bar, Z, etc), set their locations at the appropriate places according to activetile (the single tile I have control over), like so:
if (piece == "Bar") {
block2 = new Tile(this, activetile.getX(), activetile.getY());
block3 = new Tile(this, activetile.getX()+2, activetile.getY());
block4 = new Tile(this, activetile.getX()+3, activetile.getY());
}
The problem with this is, my collision detection for activetile won't allow it to move appropriately because it will run into its other blocks. I tried to fix that in keyPressed() by setting the location of block2, block3, block4 AFTER the activetile's new location has been set, like so: (so once activetile moves down, all the others are allowed to move down so they don't overlap)
activetile.setLocation(activetile.getX(), activetile.getY()+1);
block2.setLocation(activetile.getX(), activetile.getY()+1);
block3.setLocation(activetile.getX(), activetile.getY()+1);
block4.setLocation(activetile.getX(), activetile.getY()+1);
This may work for going down, but it won't work for moving left or right because the tiles will overlap.
So, am I correctly creating a new instane of a Bar piece by generating new blocks like that? Is my thinking correct?
executable
https://www.dropbox.com/s/oyh26dfbmsvt5c8/my_tetris_test.jar
link to source code zip
https://www.dropbox.com/s/9kt3sl6qqo54amk/Tetris%20Two.rar
Thanks!
I would take a look at the Polygon class: http://docs.oracle.com/javase/6/docs/api/java/awt/Polygon.html
There are methods provided that can test for collision (insideness) with points on another object. You can also use translate(deltaX, deltaY) to greatly simplify the "motion" of your objects.
I had asked this in the comments section of another question (> How do I handle simultaneous key presses in Java?), and was asked to make a new question altogether.
My problem is that when I create an ArrayList of keypresses they are not removed fast enough via the keyReleased event if the user holds down the keys. I want movement to be with "asdf" and North, East, South, West, NorthEast... etc.
Here is my code for both events:
#Override
public void keyPressed(KeyEvent e) {
if(chatTextField.isFocusOwner() == true){
//do nothing - don't walk
} else {
logger.debug("Key Pressed: " + e.getKeyChar());
lastKey = keysPressed.get(keysPressed.size()-1);
for (String key : keysPressed){
if (!key.contains(String.valueOf(e.getKeyChar())) && !lastKey.contains(String.valueOf(e.getKeyChar()))){
keysPressed.add(String.valueOf(e.getKeyChar()));
System.out.println("ADDED: " + keysPressed);
}
}
String keysList = keysPressed.toString();
if (keysList.contains("w")){
if (keysList.contains("d")){
requestCharacterMove("NorthEast");
} else if(keysList.contains("a")){
requestCharacterMove("NorthWest");
} else{
requestCharacterMove("North");
}
} else if (keysList.contains("s")){
if (keysList.contains("d")){
requestCharacterMove("SouthEast");
} else if(keysList.contains("a")){
requestCharacterMove("SouthWest");
} else{
requestCharacterMove("South");
}
} else if (keysList.contains("d")){
requestCharacterMove("East");
} else if (keysList.contains("a")){
requestCharacterMove("West");
}
}
}
#Override
public void keyReleased(KeyEvent e) {
if(chatTextField.isFocusOwner() == true){
//do nothing - don't walk
} else {
logger.debug("Key Released: " + e.getKeyChar());
for (String key : keysPressed){
if (key.contains(String.valueOf(e.getKeyChar()))){
keysPressed.remove(String.valueOf(e.getKeyChar()));
System.out.println("REMOVED: " + keysPressed);
}
}
}
}
#Override
public void keyTyped(KeyEvent arg0) {
// TODO Auto-generated method stub
}
Until I added the second check in there via the lastKey(String) variable the pyramid created was enormous. Even with that second check the list grows and almost always has two-three duplicates. Any help on this would be great as my character is moving awkwardly. :(
Also any way to remove duplicate conversions to char, string, arrayList would be great as I'm nervous I used too many types for something "simple".
Your obseravtion that things are handled slowly most likely is caused solely be the many System.out.println() statements.
Your problem that you do not get diagonal movement stems from your somewhat faulty checking logic - instead of explicitly checking if (for example) keys A and B are pressed, just check them independently - key A moves the character in one direction, B in another. In total (e.g.), by moving WEST and NORTH you will have effectively moved NORTHWEST.
Instead of a list of pressed keys, you could use a java.util.BitSet and just set the bit for each key that is currently pressed. That should also drastically reduce the amount of code you need to write (keyPressed just sets the bit indicated by key code, keyReleased clears it). To check if a key is pressed you ask the BitSet then if the bit for the code is currently set.
EDIT: Example of using BitSet instead of a list
public class BitKeys implements KeyListener {
private BitSet keyBits = new BitSet(256);
#Override
public void keyPressed(final KeyEvent event) {
int keyCode = event.getKeyCode();
keyBits.set(keyCode);
}
#Override
public void keyReleased(final KeyEvent event) {
int keyCode = event.getKeyCode();
keyBits.clear(keyCode);
}
#Override
public void keyTyped(final KeyEvent event) {
// don't care
}
public boolean isKeyPressed(final int keyCode) {
return keyBits.get(keyCode);
}
}
I made the example implement KeyListener, so you could even use it as is. When you need to know if a key is pressed just use isKeyPressed(). You need to decide if you prefer with raw key code (like I did) or go with key character (like you currently do). In any case, you see how using the BitSet class the amount of code for recording the keys reduces to a few lines :)
As an alternative, this game uses the numeric keypad to implement each (semi-) cardinal direction with a single keystroke. The default arrangement is shown in the Design section. The keys may be individually reassigned to map a similar rosette anywhere on the keyboard.
Looks like you are not handling threading in Java right. There are three threads (minimum) to any Java program. They are the main program thread, the event dispatch thread, and one more that i can't remember right now.
Whenever you get an event it is delivered to you by a special thread (I believe it's the event dispatch thread, but that is besides the point). You are not allowed to do anything (that takes time) on this thread, that will freeze up your input and cause you to miss events, making Java look unresponsive. So what has happened is you have broke the event system in java. What you should do is store the result in some sort of buffer, which is the fasted thing you can be expected to do with the event, then it is handled later as I will describe.
[Aside:
A funny application is to make a simple gui, and on the press of the button call wait on the thread for like 5 seconds. Your entire gui will freeze until the delay has finished!]
You should have a different thread running on the side (probably your main thread). It will run some sort of loop, which controls the frames in your program, completing once per game cycle. Once each cycle this thread reads the results stored in the input buffer and processes them. The theory behind this is simple, but the execution can be a little messy, because you will need to make sure that no input events are dropped or read more then once. Either way, good luck with your game!