Java - best way to add controls to a game - java

I'm making a 2D game in Java, I used the KeyListener and some Booleans to detect key press. But the problem is, whe I hold down a key the player wouldnt move for half a second, and then starts moving. Does anyone know how to fix this?
public void keyPressed(...)
{
PlayerX += 3;
}
Any answers would be appreciated thanks.

There are multiple ways to handle controls for games in java, but my prefereble way would include a class called.. lets say "Key.class"
Inside Key.class we can have:
public class Key{
// Creating the keys as simply variables
public static Key up = new Key();
public static Key down = new Key();
public static Key left = new Key();
public static Key special = new Key();
/* toggles the keys current state*/
public void toggle(){
isDown = !isDown;
}
public boolean isDown;
}
Now we have a class where we can access if certain keys are pressed, but first we need make sure that the keys .isDown function will be toggled properly. We do this in our class which implements KeyListener.
Let's say we have "Controller.class"
package game;
// Importing the needed packages
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.HashMap;
public class Controller implements KeyListener{
//Assigning the variable keys to actual letters
public Controller(Main main){
bind(KeyEvent.VK_W, Key.up);
bind(KeyEvent.VK_A, Key.left);
bind(KeyEvent.VK_S, Key.down);
bind(KeyEvent.VK_D, Key.right);
bind(KeyEvent.VK_SPACE, Key.special);
mainClass = main;
}
#Override
public void keyPressed(KeyEvent e) {
other[e.getExtendedKeyCode()] = true;
keyBindings.get(e.getKeyCode()).isDown = true;
}
#Override
public void keyReleased(KeyEvent e) {
other[e.getExtendedKeyCode()] = false;
keyBindings.get(e.getKeyCode()).isDown = false;
}
public boolean isKeyBinded(int extendedKey){
return keyBindings.containsKey(extendedKey);
}
#Override
public void keyTyped(KeyEvent e) {
}
public void bind(Integer keyCode, Key key){
keyBindings.put(keyCode, key);
}
public void releaseAll(){
for(Key key : keyBindings.values()){
key.isDown = false;
}
}
public HashMap<Integer, Key> keyBindings = new HashMap<Integer, Key>();
public static boolean other[] = new boolean[256];
}
Now this class will handle all of our keyBindings for us, and assuming you addKeyListener for the Canvas or whatever your game is running on it will function and change the Key.up/down/left/right/special accordingly.
Now the final step is to implement all of this into moving our character efficiently and easily.
Assuming your entities in game have update() methods which run every tick or something similar.. We can now simple add into it
if(Key.up.isDown) y+=3;
or in your case we could put it into the main class and do it the same way as long as its in the game tick loop.
if(Key.right.isDown) PlayerX += 3;

This sounds like a normal behavior of the repeat (auto-repeat) for key pressed in your OS. Just try holding a key down in any text editor and you will notice that there is a short amount of time between the first character being displayed and the next ones. On Windows this is 500ms, not sure on other platforms.

Related

Is there a way to change a Java property without firing a value changed event to it's listeners?

What I'm trying to do
I'm looking for a way to change a property, without a call to the listeners's changed method.
More specifically I'm trying to implement an undo/redo functionality. The way I've implemented it is as following, in an example with a BooleanProperty and a JavaFX CheckBox.
The selectedProperty of the CheckBox is changed by a mouse click.
A BooleanProperty (actually a JavaFX SimpleBooleanProperty) is changed because it is bound bidirectionally to the selectedProperty
The ChangeListener of the BooleanProperty registers this and adds a Command on the application's undoStack. The Command stores the property, the old and the new value.
The user clicks the undo button
Via the button the application takes that last Command from the stack and calls it's undo() method.
The undo() method changes the BooleanProperty back.
The ChangeListener registers this change again and creates a new Command
An endless cycle is created
My Hacky Solution
The way I did it is by passing the ChangeListener to the Command object. Then the undo() method first removes the ChangeListener, changes the BooleanProperty and then adds the ChangeListener again.
It feels wrong and hacky to pass the ChangeListener to the Command (in my actual implementation in the 3. step there are actually a few more classes between the ChangeListener and the Command which now all need to know about the ChangeListener)
My Question
Is this really the way to do it? Isn't there a way to change the property in step 6 and just tell it to not inform it's listeners? Or at least to get it's listeners?
There's no supported way of bypassing listeners, as you describe. You just need to build this logic into your undo/redo mechanism. The idea is basically to set a flag if you are performing an undo/redo, and not add the change to your stack if so.
Here's a very simple example: note this is not production quality - for example typing in a text control will add to the stack for every character change (keeping copies of the current text at each change). In real code, you should coalesce these changes together.
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import javafx.beans.property.Property;
import javafx.beans.value.ChangeListener;
public class UndoManager {
private boolean performingUndoRedo = false ;
private Deque<Command<?>> undoStack = new LinkedList<>();
private Deque<Command<?>> redoStack = new LinkedList<>();
private Map<Property<?>, ChangeListener<?>> listeners = new HashMap<>();
public <T> void register(Property<T> property) {
// don't register properties multiple times:
if (listeners.containsKey(property)) {
return ;
}
// FIXME: should coalesce (some) changes on the same property, so, e.g. typing in a text
// control does not result in a separate command for each character
ChangeListener<? super T> listener = (obs, oldValue, newValue) -> {
if (! performingUndoRedo) {
Command<T> cmd = new Command<>(property, oldValue, newValue) ;
undoStack.addFirst(cmd);
}
};
property.addListener(listener);
listeners.put(property, listener);
}
public <T> void unregister(Property<T> property) {
listeners.remove(property);
}
public void undo() {
if (undoStack.isEmpty()) {
return ;
}
Command<?> command = undoStack.pop();
performingUndoRedo = true ;
command.undo();
redoStack.addFirst(command);
performingUndoRedo = false ;
}
public void redo() {
if (redoStack.isEmpty()) {
return ;
}
Command<?> command = redoStack.pop();
performingUndoRedo = true ;
command.redo();
undoStack.addFirst(command);
performingUndoRedo = false ;
}
private static class Command<T> {
private final Property<T> property ;
private final T oldValue ;
private final T newValue ;
public Command(Property<T> property, T oldValue, T newValue) {
super();
this.property = property;
this.oldValue = oldValue;
this.newValue = newValue;
}
private void undo() {
property.setValue(oldValue);
}
private void redo() {
property.setValue(newValue);
}
#Override
public String toString() {
return "property: "+property+", from: "+oldValue+", to: "+newValue ;
}
}
}
And here's a quick test harness:
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;
import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class UndoExample extends Application {
#Override
public void start(Stage stage) throws Exception {
ComboBox<Color> textColor = new ComboBox<Color>();
textColor.getItems().addAll(Color.BLACK, Color.RED, Color.DARKGREEN, Color.BLUE);
textColor.setValue(Color.BLACK);
textColor.setCellFactory(lv -> new ColorCell());
textColor.setButtonCell(new ColorCell());
CheckBox italic = new CheckBox("Italic");
TextArea text = new TextArea();
updateStyle(text, textColor.getValue(), italic.isSelected());
ChangeListener<Object> listener = (obs, oldValue, newValue) ->
updateStyle(text, textColor.getValue(), italic.isSelected());
textColor.valueProperty().addListener(listener);
italic.selectedProperty().addListener(listener);
UndoManager undoMgr = new UndoManager();
undoMgr.register(textColor.valueProperty());
undoMgr.register(italic.selectedProperty());
undoMgr.register(text.textProperty());
Button undo = new Button("Undo");
Button redo = new Button("Redo");
undo.setOnAction(e -> undoMgr.undo());
redo.setOnAction(e -> undoMgr.redo());
HBox controls = new HBox(textColor, italic, undo, redo);
controls.setSpacing(5);
BorderPane root = new BorderPane(text);
root.setTop(controls);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
private void updateStyle(TextArea text, Color textColor, boolean italic) {
StringBuilder style = new StringBuilder()
.append("-fx-text-fill: ")
.append(hexString(textColor))
.append(";")
.append("-fx-font: ");
if (italic) {
style.append("italic ");
}
style.append("13pt sans-serif ;");
text.setStyle(style.toString());
}
private String hexString(Color color) {
int r = (int) (color.getRed() * 255) ;
int g = (int) (color.getGreen() * 255) ;
int b = (int) (color.getBlue() * 255) ;
return String.format("#%02x%02x%02x", r, g, b);
}
private static class ColorCell extends ListCell<Color> {
private Rectangle rect = new Rectangle(25, 25);
#Override
protected void updateItem(Color color, boolean empty) {
super.updateItem(color, empty);
if (empty || color==null) {
setGraphic(null);
} else {
rect.setFill(color);
setGraphic(rect);
}
}
}
public static void main(String[] args) {
Application.launch(args);
}
}
There is pretty much not a possibility to do this without "hacks"!
However, there is also a shorter solution, via using reflection:
/**
* Set the value of property without firing any change event.
* The value of property will be set via reflection.
* This property must be "Base" property such as {#link DoublePropertyBase}.
*
* #param property | Property to set!
* #param newValue | New value of property.
*/
public static <T> void setPropertyWithoutFiringEvent(Property<T> property, T newValue)
{
Class<?> cls = property.getClass();
while (cls != null) //While until helper variable is found
{
try
{
Field fieldH = cls.getDeclaredField("helper"), fieldV = cls.getDeclaredField("valid");
fieldH.setAccessible(true);
fieldV.setAccessible(true);
Object helper = fieldH.get(property), valid = fieldV.getBoolean(property); //Temporary values
fieldH.set(property, null); //Disabling ExpressionHelper by setting it on null;
property.setValue(newValue);
fieldH.set(property, helper); //Setting helper back!
fieldV.set(property, valid); //Important
return;
}
catch (Exception e)
{
cls = cls.getSuperclass(); //If not found go to super class of property next time!
}
}
System.err.println("Property " + property + " cant be set because variable \"helper\" was not found!");
}
This function temporarily disables ExpressionHelper what is an object responsible for firing change events, and then it will change the value of property and enable ExpressionHelper back! This will cause that one change will not be notified!
If the reflection is not friendly solution for you, then just use the solution above however this one is far shorter and simpler.

How do I use key name in key bindings?

This code works well for me to make key bindings more pleasant, via calls such as those that follow:
import java.awt.event.ActionEvent;
import javax.swing.*;
import static javax.swing.KeyStroke.getKeyStroke;
public abstract class KeyBoundButton extends JButton{
public abstract void action(ActionEvent e);
public KeyBoundButton(String actionMapKey, int key, int mask)
{
Action myAction = new AbstractAction()
{
#Override public void actionPerformed(ActionEvent e)
{
action(e);
}
};
setAction(myAction);
getInputMap(WHEN_IN_FOCUSED_WINDOW)
.put(getKeyStroke(key, mask),actionMapKey);
getActionMap().put( actionMapKey, myAction);
}
}
Calls:
button = new KeyBoundButton("WHATEVER", VK_X, CTRL_DOWN_MASK)
{
#Override
public void action(ActionEvent e)
{
JOptionPane.showMessageDialog(null,"Ctrl-X was pressed");
}
};
But I don't have a clue how to use the key name, WHATEVER, either intelligently or otherwise, elsewhere in a program.
I wondered about button.getActionCommand() but it returns null, even if I insert this line after action(e) in the class definition:
setActionCommand(actionMapKey);
What is the purpose of the key name? Am I supposed to use it somewhere in a program other than in defining the key binding?
The key name is used if you have only one listener to the events.
Usually:
setOnKeyListener(new OnKeyListener(){
void onKeyPressed(KeyEvent k){
if(k.getKey() == KeyEvent.VK_ENTER)
//Handle ENTER key
if(k.getKey() == KeyEvent.VK_ESCAPE)
//Handle ESC key
}
);
This code was wrote from my memory, probably is not the actual Object names from the Java API.

Is it possible to write your own objects that give out ActionEvents?

I've looked at the java tutorials online and they all seem concerned with catching ActionEvents given out by other components that are already written. Is it possible to write your own objects that have there own set of criteria that trigger actionEvents that can then be caught by other classes that have registered as listeners?
So for example: If I wanted an object that was counting sheep to send out an actionEvent when 100 sheep had been counted to all the sleeper objects that had registered as listeners.
Is there a way to do this are there any tutorials online?
Any help is greatly appreciated.
Yes, it's pretty straightforward, once someone shows you how to create your own listeners.
First, you create your own EventObject. Here's an example from one of my projects.
import gov.bop.rabid.datahandler.bean.InmateDataBean;
import java.util.EventObject;
public class InmatePhotoEventObject extends EventObject {
private static final long serialVersionUID = 1L;
protected InmateDataBean inmate;
public InmatePhotoEventObject(Object source) {
super(source);
}
public InmateDataBean getInmate() {
return inmate;
}
public void setInmate(InmateDataBean inmate) {
this.inmate = inmate;
}
}
There's nothing special about this class, other than it extends EventObject. Your constructor is defined by EventObject, but you can create any methods you want.
Second, you define an EventListener interface.
public interface EventListener {
public void handleEvent(InmatePhotoEventObject eo);
}
You would use the EventObject you created. You can use any method name or names that you want. This is the interface for the code that will be written as a response to the listener.
Third, you write a ListenerHandler. Here's mine from the same project.
import gov.bop.rabid.datahandler.bean.InmateDataBean;
import gov.bop.rabid.datahandler.main.EventListener;
import gov.bop.rabid.datahandler.main.InmatePhotoEventListener;
import gov.bop.rabid.datahandler.main.InmatePhotoEventObject;
import java.util.ArrayList;
import java.util.List;
public class InmatePhotoListenerHandler {
protected List<EventListener> listeners;
public InmatePhotoListenerHandler() {
listeners = new ArrayList<EventListener>();
}
public void addListener(EventListener listener) {
listeners.add(listener);
}
public void removeListener(EventListener listener) {
for (int i = listeners.size() - 1; i >= 0; i--) {
EventListener instance = listeners.get(i);
if (instance.equals(listener)) {
listeners.remove(i);
}
}
}
public void fireEvent(final InmatePhotoEventObject eo,
final InmateDataBean inmate) {
for (int i = 0; i < listeners.size(); i++) {
final EventListener instance = listeners.get(i);
Runnable runnable = new Runnable() {
public void run() {
eo.setInmate(inmate);
instance.handleEvent(eo);
}
};
new Thread(runnable).start();
}
}
public static void main(String[] args) {
System.out.println("This line goes in your DataHandlerMain class "
+ "constructor.");
InmatePhotoListenerHandler handler = new InmatePhotoListenerHandler();
System.out.println("I need you to put the commented method in "
+ "DataHandlerMain so I can use the handler instance.");
// public InmatePhotoListenerHandler getInmatePhotoListenerHandler() {
// return handler;
// }
System.out.println("This line goes in the GUI code.");
handler.addListener(new InmatePhotoEventListener());
System.out.println("Later, when you've received the response from "
+ "the web service...");
InmateDataBean inmate = new InmateDataBean();
inmate.setIntKey(23);
handler.fireEvent(new InmatePhotoEventObject(handler), inmate);
}
}
The main method in this class shows you how you use a ListenerHandler. The rest of the methods in the class are standard. You would use your own EventObject and EventListener.
Yes.
I suggest you look at the java API documentation for ActionEvent and EventListenerList.
I also suggest that you read about the Listener (also called Observer) pattern.

J2ME key event handling

I have created a textfield that takes all characters from the user.. but i want to disable the space so that user cant enter space ...help??
pin = new TextField("Pin#","",4,TextField.PASSWORD);
If it's a PIN number then maybe you should replace the constraints parameter with TextField.NUMERIC | TextField.PASSWORD.
Implement the ItemStateListener interface. Then call this.setItemStateListener(this) in the Form constructor.
Implement the itemStateChanged method so that if the Item is the one you are interested in then get its content and test it if it contains spaces.
In my case I create a MIDlet and a Form which contains a TextField. And it works. I do not know why did you say that the solution I gave you did not work to you ! Here is a very simple example I give ( I created it and tested it ! ) :
package hello;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class HelloMIDlet extends MIDlet {
public Display display;
public HelloMIDlet() {
display = Display.getDisplay(this);
}
public void startApp() {
Form f = new F(display);
display.setCurrent(f);
}
public void pauseApp() {
}
public void destroyApp(boolean unconditional) {
}
}
package hello;
import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Item;
import javax.microedition.lcdui.ItemStateListener;
import javax.microedition.lcdui.TextField;
public class F extends Form implements ItemStateListener {
private TextField pin = new TextField("PIN :","",4,TextField.PASSWORD);
private Alert alert;
private Display disp;
public F(Display d)
{
super("");
disp = d;
this.setItemStateListener(this);
this.append(pin);
}
public void itemStateChanged(Item item) {
if (item == pin)
{
for (int i=0; i<pin.getString().length(); i++)
{
if (String.valueOf(pin.getString().charAt(i)).equals(new String(" ")))
displayAlert();
}
}
}
private void displayAlert()
{
alert = new Alert("Info","No space please !",null, AlertType.ERROR);
disp.setCurrent(alert, this);
}
}

trying to create a switch using if in java

This is a HW problem. I keep getting the following error on screen related to my if(i==3) statement...
"Exception in thread "AWT-EventQueue-1" java.lang.NullPointerException
at ui.panels.ChoicePanel$1.itemStateChanged(ChoicePanel.java:31)"
...Can someone point me in the right direction? Here is my code. Thanks for your time.
package ui.panels;
import java.awt.Choice;
import java.awt.Panel;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import model.Model;
import interfaces.Resettable;
public class ChoicePanel extends Panel implements Resettable{
public int i = 0;
/**
*
*/
private static final long serialVersionUID = 1L;
Model model;
Choice selection;
public ChoicePanel(Model mdl) {
selection = new Choice();
for (String msg : Model.selections) {
selection.add(msg);
}
selection.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
if(i==3) {//drop down clicked three times)
System.out.println("PING");
}else{
model.setMessage(selection.getSelectedItem());
//this line is what sends a value to shape that is drawn on screen
//NOT line 36 of GUIDemo.java
//
model.setCurrentShapeType(selection.getSelectedItem());
model.repaint();
++i;
}
}
});
this.add(selection);
}
public void resetComponents() {
//this resets the drop down list selection array to the first choice on the list
selection.select(0);
//this sets selected item in the selection array set in the above line
model.setMessage(selection.getSelectedItem());
//model.repaint();
}
}
I believe you are not initializing model, which would give a NullPointerException when it was first dereferenced.
You did not initialize model. If i is not 3 the else-block will get executed, which contains model.setMessage(...) - but model does not yet "exist".

Categories

Resources