I've created a static Input class, that basicly have a method that I can call, which is this:
public static boolean GetKeyDown(int keyCode) {
while(Keyboard.next()) {
Keyboard.enableRepeatEvents(false);
if (Keyboard.getEventKeyState()) {
if (Keyboard.getEventKey() == keyCode) {
return true;
} else {
return false;
}
}
}
return false;
}
And in my game update loop, I've wanted to use this, instead of having to make a single while-loop:
if(Input.GetKeyDown(KeyCode.S)) {
//Something happens
}
if(Input.GetKeyDown(KeyCode.R)) {
//Something happens
}
//etc..
But it seems that only the first one loaded, will work. In this case 'S'. Is there a way for me to do be able to use the others too?
That is because in your GetKeyDown() method, you call Keyboard.next(), when you call that method it removes the Event of the current key from Keyboard, the only gets refilled with Events, when you call Display.update();
NOTE: This method does not query the operating system for new events. To do that, Display.processMessages() (or Display.update()) must be called first.
Source: LWJGL Docs
You Could
Instead you can use the Keyboard.isKeyDown(int key) method, to achieve what you're trying to do.
Though it returns true/false depending on the following.
Returns: true if the key is down according to the last poll()
But that still doesn't quite fix the problem because it relies on the poll() method.
Fixing The Problem
You can fix the problem by creating some custom methods to use with the Keyboard class, as you already did, though as said the Keyboard Events only gets updated when you call the Display.update(); method.
You already got the right idea about which function to create, though you need to split them into, two different methods. You need a secondary method which you call once each time you want to update your keyboard.
public class MyKeyboard {
/*
* Remember that the index where we store the value,
* is the index of the key. Thereby one key might have
* an index of 400, then your array need to have at least
* the same size, to be able to store it.
*/
public static boolean[] keys = new boolean[100]; // 100 is the amount of keys to remember!
public static void update() {
while(Keyboard.next()) {
if (Keyboard.getEventKey() < keys.length) {
keys[Keyboard.getEventKey()] = Keyboard.getEventKeyState();
}
}
}
public static boolean isKeyDown(int key) {
if ((key > 0) && (key < keys.length)) {
return keys[key];
}
return false;
}
}
Remember to only call the MyKeyboard.update() method once per Display.update() I also renamed your GetKeyDown() method to isKeyDown(), because I think that sounds and describes it better, but you can rename it again in your project if you want to.
The above code was made within this answer, without the use of an IDE, etc. So if there's anything wrong with it I apologize, but just comment and I will fix it.
One problem that arises with this method is the lack of rechecking. Since Keyboard.next() only checks the inputs that have occurred in the current frame. A button which was once pressed will remain "pressed" until it is pressed again. I ran into this problem while trying to implement this solution. The answer to this new problem is here:
public static void update() {
for(int i = 0; i < keys.length; i++) {
keys[i] = false;
}
while(Keyboard.next()) {
keys[Keyboard.getEventKey()] = Keyboard.getEventKeyState();
}
}
You must clear the keypresses of the previous frame by setting everything to false.
Related
Method FXCollections.shuffle() fires only wasRemoved change notification. As we may know, shuffling isn't only about removing, but removing and adding.
In the documentation we can see:
Shuffles all elements in the observable list. Fires only one change notification on the list.
If I'm not mistaken, a single change can contain both wasAdded and wasRemoved. What a shame wasPermutated isn't being fired with the default ObservableList FX api (or is it?).
Code to test it out:
public class SimpleMain {
public static void main(String[] args) {
ObservableList<Integer> integers = FXCollections.observableArrayList(1, 2, 3, 4);
integers.addListener(initListener());
FXCollections.shuffle(integers);
}
private static ListChangeListener<Integer> initListener() {
return change -> {
while (change.next()) {
if (change.wasPermutated()) {
System.out.println("wasPermutated");
} else if (change.wasRemoved()) {
System.out.println("wasRemoved");
} else if (change.wasAdded()) {
System.out.println("wasAdded");
}
}
};
}
}
Problem
Your code assumes that wasAdded() and wasRemoved() are mutually exclusive, whether you intended that or not. That assumption is wrong. If one or more contiguous elements are replaced then both those methods will return true.
Keep in mind there's a difference between a Change object and a "change". A single Change instance can carry multiple changes. When the documentation says:
Fires only one change notification on the list.
It is not saying only one Change object will be sent to the ListChangeListener. What it's saying is that the Change object will only carry one change. In other words, the Change#next() method will only return true for the first invocation and thus your while loop will only loop once.
Solution
You need to rewrite your code with the knowledge that both wasAdded() and wasRemoved() can both be true. For instance, here's a listener that checks for all types of changes:
private static ListChangeListener<Integer> initListener() {
return change -> {
while (change.next()) {
if (change.wasPermutated()) {
System.out.println("wasPermutated");
} else if (change.wasUpdated()) {
System.out.println("wasUpdated");
} else if (change.wasReplaced()) {
System.out.println("wasReplaced");
} else if (change.wasRemoved()) {
System.out.println("wasRemoved");
} else { // only other change type is "added"
System.out.println("wasAdded");
}
}
};
}
The above uses wasReplaced() which is the same as wasAdded() && wasRemoved(). Note that a check for wasReplaced() must happen before either wasRemoved() or wasAdded() if you use an if-else-if structure. Otherwise the above will suffer the same problem your code has.
If you insert the above into your code and run it you'll see the following output:
wasReplaced
The documentation of ListChangeListener.Change gives a more general explanation (and example) of how to implement a ListChangeListener.
Note the example in the documentation does not check for wasReplaced() specifically. Instead, it processes both removed and added elements in a final else block (after wasPermutated() and wasUpdated() return false). This is possible because getRemoved() and getAddedSubList() will return empty lists if no element was removed or added, respectively. And it's typically the same effect if you process any removed elements and then any added elements as it is if you specially handle replacements. Depending on your use case, however, it may be beneficial to specially handle replacements.
Why not Permutation?
The way they've implemented the shuffle method is:
public static void shuffle(ObservableList list, Random rnd) {
Object newContent[] = list.toArray();
for (int i = list.size(); i > 1; i--) {
swap(newContent, i - 1, rnd.nextInt(i));
}
list.setAll(newContent);
}
Source: javafx.collections.FXCollections, JavaFX 15.
As you can see, the elements are extracted out into an array, the array is shuffled, and then the elements in the list are replaced with the array. That results in a single "replacement change" being fired.
I added one line to your ListChangeListener in the while loop:
System.out.println("change: "+change);
The output I get is:
change: { [1, 2, 3, 4] replaced by [4, 2, 3, 1] at 0 }
wasRemoved
That hints at your mistake. The verb used in the string representation is "replaced".
Getting rid of the 'else's in your listener and adding a check for wasReplaced() you will see that is exactly what you get.
I'm trying to do a swing application which adds names to an ArrayList and then displays it in Jcombobox.I already did the window and everything but I can't seem to get the hang off detecting duplicate names.
I tried
btnnext.addActionListener(new ActionListener() {
Override
public void actionPerformed(ActionEvent e) {
if(checkDuplicate(names)==true)
{
names.add(txtname.getText());
txtname.setText("");
}
else {
JOptionPane.showMessageDialog(null,"DUPLICATE! do not add");
}
}
});
public static boolean checkDuplicate(ArrayList<String> list) {
HashSet set = new HashSet();
for (int i = 0; i < list.size(); i++) {
boolean val = set.add(list.get(i));
if (val == false) {
return val;
}
}
return true;
}
It only says that I have duplicate when I already add it to the ArrayList and when I get the message I can't add anything else.
input example:
test
test
and then it stops accepting new Strings and only displays the message DUPLICATE! do not add
As I said in my comment:
This happens because you are basically creating a Set view of your ArrayList every time you call checkDuplicate rather than comparing the item you're trying to add with the existing list. In other words your checkDuplicate is written such that it only returns true when a duplicate already exists within the list. You need to compare the new item with the list instead. Step through your logic carefully either with a debugger or by manually writing down the values of your variables and you will see the problem.
You could simply change this line:
if(checkDuplicate(names)==true)
to this:
if(!names.contains(txtname.getText()))
You don't really need checkDuplicate since it's "reinventing the wheel" so to speak. ArrayList already provides a contains method that you can use to your advantage.
I am trying to make a hangman game as follows:
public void guessLetter(String letter) {
HashSet<String> guessedLettersA = new HashSet<>();
guessedLettersA.add(letter);
for (String guessedLetterA : guessedLettersA) {
this.guessedLetters += guessedLetterA;
}
if (!this.word.contains(letter)) {
this.numberOfFaults++;
}
}
public boolean letterCheck() {
if ( = false ) {
System.out.println("You have already guessed this letter!");
}
I am currently working in the letterCheck method and want to see if one of the inputs is a repeat and let the user know that their guess doesn't count. I assume it wont make up their failures or count as another guess because it is never added to the hashset. So where I am struggling with, is how do I do as I want, I was thinking of using the built-in way a hashset returns false to detect this, but I have no idea how to implement this since it needs to refer to another method and I don't know how to make a string hashset return booleans. I would greatly appreciate any help at all, thanks.
The add API would return false for an existing value(that it cannot add to the Set), so your condition can be dealt with
boolean letterCheck = guessedLettersA.add(letter);
if(!letterCheck) {
System.out.println("You have already guessed this letter!");
}
Note: The invocation of this block is solely dependent on the design of your application.
I have some suspicions about the way I'm handling the heldKeys variable (adding and removing keycodes from it).
Everything works fine when only a single key is pressed at once (and this is why I believe the problem, but be in the this class, and not the ones that take care of drawing or updating). However, everything completely breaks when I press more than one key at the same time.
By "breaks", it no longer works properly: pressing up moves all subscribers up sometimes, other times moves the one that should move up up, and the others down, etc. Other times a certain action stops doing anything until I restart the program.
I've tried debugging and seeing the evolution of the heldKeys array, but I really have no clue where the problem is.
Code:
<code>
public class MainInputProcessor implements InputProcessor {
// REFERENCE:
// <Listener, <ACTION, KEYCODE>>
private Array<ObjectMap<RacketMovementListener, ObjectMap<Racket.ACTIONS, Integer>>> racketMovementListeners;
// Stores the keys that are held down
private IntArray heldKeys;
// Initialisation
public MainInputProcessor() {
heldKeys = new IntArray();
racketMovementListeners = new Array<ObjectMap<RacketMovementListener, ObjectMap<Racket.ACTIONS, Integer>>>();
}
// Adds a RacketMovementListener to the respective subscriber list
public void subscribeToRacketMovement(RacketMovementListener newSubscriber, ObjectMap<Racket.ACTIONS, Integer> movementKeys) {
ObjectMap<RacketMovementListener, ObjectMap<Racket.ACTIONS, Integer>> map = new ObjectMap<RacketMovementListener, ObjectMap<Racket.ACTIONS, Integer>>();
map.put(newSubscriber, movementKeys);
racketMovementListeners.add(map);
}
// Update and execute event action
public void updateEvents() {
// Fire events for racket movement subscribers if the keys match
if (!heldKeys.isEmpty()) {
for (ObjectMap<RacketMovementListener, ObjectMap<Racket.ACTIONS, Integer>> map : racketMovementListeners) {
for (RacketMovementListener listener : map.keys()) {
for (Integer keycode : heldKeys.items) {
if (map.get(listener).get(Racket.ACTIONS.MOVE_DOWN).equals(keycode)) {
listener.moveDownPressed(listener);
} if (map.get(listener).get(Racket.ACTIONS.MOVE_UP).equals(keycode)) {
listener.moveUpPressed(listener);
}
}
}
}
}
}
#Override
public boolean keyDown(int keycode) {
// Add keycode to heldKeys if it's not already there
if (!heldKeys.contains(keycode)) {
heldKeys.add(keycode);
return true;
}
return false;
}
#Override
public boolean keyUp(int keycode) {
// Remove keycode from heldKeys since the key is no longer held down
for (Integer code : heldKeys.items) {
if (code == keycode) {
heldKeys.removeValue(code);
}
}
return true;
}
</code>
I'm not really looking for solutions or fixed code, just some starting point or somewhere to look, because I don't even know where to start. I've thought about rewriting this but I think I would do the same mistakes.
For context, this is code for a Pong clone, in LibGDX.
UPDATE
I rewrote the whole input manager to do the exact same but using switch statements and overall in a simpler way, but the exact same problem remains.
I'm now sure the problem is in the way I'm handling the heldKeys array.
However, I still can't figure out the problem with it...
public void run() {
if(spleggEnabled == 3){
if(player.isBlocking()){
player.sendMessage(ChatColor.GREEN + "Projected Splegg!");
WitherSkull head = (WitherSkull) ((ProjectileSource) player).launchProjectile(WitherSkull.class);
BlockIterator iterator = new BlockIterator(event.getEntity().getWorld(), event.getEntity().getLocation().toVector(), event.getEntity().getVelocity().normalize(), 0.0D, 4);
}
if(player.isDead()){
spleggEnabled = 0;
}
The error is on each event.getEntity(), and the error is event cannot be resolved.
Any ideas?
What it looks like is that event is undefined.
If you are unsure of what event.getEntity() is, try replacing it with head or player in your code.
If you know what it is and what it does, make the event from your listener accessible (define it in the class as public static Event event and set it in your listener.)
You obviously stole some code from online without a single clue as to what it does. Next time, read the whole forum/blog/wiki post instead of blindly copying.