i'm using the keyboard class from the LWJGL and am currently using
if(Keyboard.isKeyDown(Keyboard.KEY_A))
{
//move left
}
else if(Keyboard.isKeyDown(Keyboard.KEY_D))
{
//move right
}
if i have the 'a' key down then the 'd' key it will move right but if i have the 'd' key down then the 'a' key it will still continue to move right due to the "else" part.
i've tried it without the else and just letting them both run but if both keys are press there is no movement
im looking for a piece of code that allows me to only take the input from the last key that was pressed.
i also need to be able to move up and down (using 'w' and 's') so the solution can't stop other keys from working, only 'a' or 'd'.
thanks. Alex.
Use Keyboard.getEventKeyState to determine the current event, followed by Keyboard.getEventKey to determine which key this is. Then, you need to make sure you disable repeat events via Keyboard.enableRepeatEvents. Maintain state for the current movement, changing based on these events, and every tick move accordingly. Something like the following, as quick sketch:
Keyboard.enableRepeatEvents(false);
...
/* in your game update routine */
final int key = Keyboard.getEventKey();
final boolean pressed = Keyboard.getEventKeyState();
final Direction dir = Direction.of(key);
if (pressed) {
movement = dir;
} else if (movement != Direction.NONE && movement == dir) {
movement = Direction.NONE;
}
...
/* later on, use movement to determine which direction to move */
In the above example, Direction.of returns the appropriate direction for the pressed key,
enum Direction {
NONE, LEFT, RIGHT, DOWN, UP;
static Direction of(final int key) {
switch (key) {
case Keyboard.KEY_A:
return Direction.LEFT;
case Keyboard.KEY_D:
return Direction.RIGHT;
case Keyboard.KEY_W:
return Direction.UP;
case Keyboard.KEY_S:
return Direction.DOWN;
default:
return Direction.NONE;
}
}
}
Idk if this is still going to help you, but you could lock the key that was first pressed until it is let up. I'd suggest putting this variable outside of the methods (if I remember correctly, it's called a field) so it can be accessed through any class and isn't reinstantiated every time update is called. That would look something like this:
boolean isHorizontalLocked = false;
String lockedKey = null;
After that, write something like this in the update method:
if (!isHorizontalLocked) {
if (input.isKeyDown("KEY_A")) {
isHorizontalLocked = true;
lockedKey = "A";
}
else if (input.isKeyDown("KEY_D")) {
isHorizontalLocked = true;
lockedKey = "D";
}
else {
isHorizontalLocked = false;
}
}
else if (isHorizontalLocked) {
if (lockedKey == "A") {
if (input.isKeyDown("KEY_A")) {
//move left
}
if (!input.isKeyDown("KEY_A")) {
lockedKey = null;
isHorizontalLocked = false;
}
}
else if (lockedKey == "D") {
if (input.isKeyDown("KEY_D")) {
//move right
}
if (!input.isKeyDown("KEY_D")) {
lockedKey = null;
isHorizontalLocked = false;
}
}
}
I think this should work, but if anyone finds an error, let me know and I'll fix it.
Related
New user here.
I've been working on a "framework" or draft for a text based adventure game in my object oriented programming class. I showed it to my TA and he said it looked good, but I should try putting the movement in its own private class. Any reason why I should do this? And how I should do it?
Thank you in advance for any help.
Here is my code for my main class:
public class Main {
public static void main(String args[]) {
// This is where we will build rooms
// Load inventory
ArrayList<String> inventory = new ArrayList<>();
// Start game
boolean playing = true;
while (playing) {
String input = Input.getInput();
// Movement commands
if (input.equals("north")) {
if (y > 0) {
y--;
Rooms.print(room, x, y);
} else {
System.out.println("You can't go that way.");
}
} else if (input.equals("south")) {
if (y < HEIGHT - 1) {
y++;
Rooms.print(room, x, y);
} else {
System.out.println("You can't go that way.");
}
} else if (input.equals("east")) {
if (x > 0) {
x--;
Rooms.print(room, x, y);
} else {
System.out.println("You can't go that way.");
}
} else if (input.equals("west")) {
if (x < WIDTH - 1) {
x++;
Rooms.print(room, x, y);
} else {
System.out.println("You can't go that way.");
}
}
// Look commands
else if (input.equals("look")) {
Rooms.print(room, x, y);
}
// Look at item commands
else if (input.equals("look thing")) {
//print item description
}
/* Take command
else if statement
Drop command
else if statement
*/
// Inventory commands
else if (input.equals("i") || input.equals("inv")
|| input.equals("inventory")) {
Inventory.print(inventory);
}
// Quit commands
else if (input.equals("quit")) {
System.out.println("Goodbye!");
playing = false;
// Catch-all for invalid input
} else {
System.out.println("Invalid input");
}
}
System.exit(0);
}
}
One reason that you would want to put the movement in its' own class is to enable what is known as Separation of Concerns. Following this principle, you want to keep your classes as distinct and unique as possible.
This approach makes debugging/developing much easier as your program grows in size and complexity.
As far as making the class private, I don't know that I necessarily agree with that. I would simply make a movement class that handles all the movement related functions and data. Put this in its' own file that is separate from your main file, but within your project.
You can follow the same approach for your game inventory, attacking (if this exists), settings, etc.!
As my comment mentions:
One tip I can give you is to refactor all of your System.out.println("You can't go that way."); into a function and call it. You are repeating this one line a few times and you may want to change it later, easier to do in 1 function, instead of finding every occurrence of that string.
public static void moveError() {
System.out.println("You can't go that way.");
}
A few people in the comments have reccommended transitioning into a switch statement. I agree with this approach; it's not only easier to look at. But easier to maintain as well. You can use specific 'commands' passed as strings/integers/characters that represent specific functions within your code:
switch (direction) {
case "up":
// code to move up and error handle
break;
case "down":
// code to move down and error handle
break;
}
You can also implement specific commands for things like picking up items, adjusting settings, or whatever else you envision.
Maybe the Room Movement functionality that your TA is asking you to enclose within a private class is completely related to your outer class and hence has no significance if existed independently.
I have been working on some code to guide a 'robot' through a maze with multiple dead ends and 1 correct path to the goal like this:
I have used a stack to record the direction the robot is facing the first time it reaches a square with 3 or 4 possible exits and if all adjacent squares have already been visited, used pop() to make the robot return from the direction it first came from (opposite to direction arrived). At the end of the run the stack contains the direction arrived at all squares on the route to the target. Following the opposite directions of the stack would take the robot from the goal back to the start point. I am struggling to find out how to use this stack so that on the next run the robot will take the optimal path to reach the goal.
Some of my code:
private int pollRun = 0; // Incremented after each pass
private int explorerMode; // 1 = explore, 0 = backtrack
public void exploreControl(IRobot robot) {
byte exits = nonwallExits(robot);
int direction;
switch (exits) { //passes control to respective method
case 1: direction = deadEnd(robot); break;
case 2: direction = corridor(robot); break;
case 3: direction = junction(robot); break;
default: direction = crossroads(robot); break;
}
if (exits == 1) {explorerMode = 0;}
robot.face(direction);
pollRun++;
}
public void backtrackControl(IRobot robot) {
byte exits = nonwallExits(robot);
int direction = IRobot.CENTRE;
switch (exits) { //passes control to respective method
case 1: direction = deadEnd(robot); break;
case 2: direction = corridor(robot); break;
default: direction = junction(robot); break; // do nothing
}
if (exits > 2) {
if (passageExits(robot) > 0){
exploreControl(robot);
explorerMode = 1;
pollRun++;
return;
} else {
robot.setHeading(st.pop());
robot.face(IRobot.BEHIND);
pollRun++;
return;
}
}
robot.face(direction);
pollRun++;
}
public void optimal(IRobot robot) {
byte exits = nonwallExits(robot);
int direction;
int heading;
for(int i = 0; i < st.size(); i++) {
stNew.push(st.pop());
}
if (exits < 3) {
switch (exits) { //passes control to respective method
case 1: direction = deadEnd(robot); break;
default: direction = corridor(robot); break;
}
robot.face(direction);
} else {
robot.setHeading(stNew.pop());
}
}
public void controlRobot(IRobot robot) {
if ((robot.getRuns() == 0) && (pollRun == 0)) {
robotData = new RobotData(); //reset the data store
explorerMode = 1;
}
if (robot.getRuns() = 1) {
optimal(robot);
return;
}
if (robot.getRuns() <= 0 && (nonwallExits(robot) >= 3)
&& (beenbeforeExits(robot) <= 0)) {
st.push(robot.getHeading());
}
if (explorerMode == 1) {
exploreControl(robot);
} else {backtrackControl(robot);}
}
The optimal method shows my attempt at solving it, however all it does is cause the robot to head straight at every junction
For example this maze,
Would leave me with the stack: EAST, EAST, SOUTH, SOUTH, EAST, SOUTH, SOUTH, EAST, EAST, SOUTH, SOUTH, EAST, EAST, EAST, SOUTH, EAST, SOUTH
It is true that this problem can be solved using a stack and an exhaustive search of the maze. There are more efficient methods but this one will work.
It's fairly difficult to know how your code is intended to function because you've only given a part of it. However in general these sorts of exhaustive searches make heavy use of recursion - which is a very common use case for stacks. I assume yours does the same though I can't see that code in the sample you've provided.
Here is some sample psuedo code for an exhaustive 'depth first' search. This code will end up with all possible solutions rather than just one. You should have a method that looks something like this in your code.
void findPath(Stack currentPath) {
if (currentPath.peek() == goal) {
solutions.add(currentPath);
} else {
for (Position next: currentPath.openPositions()) {
currentPath.push(next);
findPath(currentPath);
currentPath.pop();
}
}
}
The 'openPositions' method needs to explicitly stop any doubling back by looking at the current path - in other words it shouldn't return any positions that are already in the currentPath stack or you will end up with infinite recursion.
Because this finds all possible solutions you would then need to find the one with the shortest length as the optimal path. In your case it seems the mazes only have one path so you can exit as soon as you have found a path.
A final note: you've tried to mix the task of setting the direction the robot needs to turn with the task of finding a path through the maze. I recommend keeping these separate. Use the algorithm above to find a path (or a more efficient one such as a*) and then once you have paths walk through it to determine the list of directions for the robot.
I am learning about Recursions, and I haven't fully grasped it yet. So here I am trying to do an assignment on recursion but I'm stuck.
In this assignment, I am supposed to ask the user to input phrases, and the program determines whether it's a palindrome or not. We are supposed to accomplish this task using recursions.
This is the section with the recursion, and I can't quite figure out how to tackle it, as when I run it, I get no error, but it always comes up as false.
I'm using a ArrayList to keep all the user input.
Here's the code I've got right now
//instance variables
private boolean det;
private String input;
private String inputHelp;
//constructor
public RecursivePalindrome(String i)
{
det = false;
input = i;
inputHelp = "";
}
//determines if the method is a palindrome or not using recursions
public boolean palindrome(String b)
{
if(inputHelp.length() == 0)
{
det = true;
}
if(inputHelp.substring( 0 , 1 ).equals(inputHelp.substring( inputHelp.length() )))
{
inputHelp = inputHelp.substring( 1, inputHelp.length());
palindrome(inputHelp);
}
else
{
det = false;
}
return det;
}
There are three mistakes. First, note the substring documentation: second parameter is the end index "exlusive". Secondly, you need to use the result of the recursive call. And finally (as pointed out correctly by ajb in the comments), you should account for palindromes with odd letter count (first condition):
if (inputHelp.length() <= 1)
{
det = true;
}
else if (inputHelp.substring(0, 1)
.equals(inputHelp.substring(inputHelp.length() - 1)))
{
inputHelp = inputHelp.substring( 1, inputHelp.length() - 1);
det = palindrome(inputHelp);
}
else
{
det = false;
}
Also, you can make it a bit more readable:
public boolean palindrome(String b)
{
if (b.length() <= 1)
{
return true;
}
if (b.substring(0, 1)
.equals(b.substring(b.length() - 1)))
{
return palindrome(b.substring(1, b.length() - 1));
}
return false;
}
Further improvements can be made - lines still seem to long, especially the second condition (left as an exercise for the reader ;)).
As far as I can tell you are never setting inputHelp to anything other than an empty string, and the string b which is passed in to your method is not used anywhere.
So the method will never call back on to itself, and even if it did the value passed in would not be used for anything, rendering the recursion useless.
At the moment , when I hit F or f :
private static final char FILL_POLYGON_LOWERCASE = 'f';
private static final char FILL_POLYGON = 'F';
#Override
public void keyTyped(KeyEvent keyEvent)
{
PolygonFiller polyFiller = new PolygonFiller();
char key = keyEvent.getKeyChar();
switch(key)
{
/**
* Fill the polygons
*/
case FILL_POLYGON:
{
if (greenLightForFilling == true)
{
fillPolygon(polyFiller);
System.out.print("called");
}
break;
} // end FILL_POLYGON
case FILL_POLYGON_LOWERCASE:
{
if (greenLightForFilling == true)
{
fillPolygon(polyFiller);
}
break;
}
...
}
The program goes into fillPolygon(polyFiller); .
Meaning , when I hit for the first time f , I go into fillPolygon() .
How can I go into some other method , for example other() , when I hit f or F again ?
Thanks
So the thing is, if you click f/F you goto fill polygon, and pressing f/F again will call other().
This can be a classic case of Stateful Class.
Have an attribute in this at class level.
And on entering f/F check the value and increment it by one.
And on entering f/F again, check the value and increment it by one.
Before each increment you should check whether,
//Am assuming that there are more than two functions, else could use boolean
if (value == 1) {
fillpolygon();
}
else if (value == 2) {
other();
}
else if (value == 2) {
some_other();
}
Remember the entry point will be a single function, from there the flow is delegated based on checks similar to this.
Hope this helps.
Store the previously used button in a variable like 'currentCommand' and 'previousCommand'. Everytime you detect a new input you put current to previous and store the new one in the current member.
Or maybe if you want more than the last two key pressed use a stack.
How can I go into some other method, for example other(), when I hit f or F again ?
You need to introduce a boolean flag.
This is a very simple example of a state machine.
I am teaching myself, from online tutorials, how to write games in Java. I am using Java Applets to create a Pong game. each paddle is controlled from different keys for 1v1 competition. this works fine if both users are hitting the keys at different times. but when one key is being held down and then another key is held down(ex: holding down on the arrow key, then user 2 holds the 'S' key), the second key overrides the first and the first paddle will stop moving. i'm guessing that i need to use threads but i don't know much about them and i am having trouble understanding how to use/implement them. how would i go about handling the case when two (or more) keys are being held down?
Bonus: like i said i don't know much about threads - i'm assuming i also need one for the ball/puck to be moving around while all else is going on. is the right and if so how do i put a thread on something that takes no input?
Thanks for you help,
DJ
What you usually do is to remember the state of every keypress.
You keep an array of your actions(or an array of all the keys if you want too). A keyDown event results in e.g.
boolean actions[12];...
...
public boolean keyDown( Event e, int key ) {
if (key == 'a') {
actions[ACTION_LEFT] = true;
}
..
}
And you'll need to catch the keyup event and set the actions to false when the keys are released.
In the movement logic you can just check the states of the keypresses
if(actions[ACTION_LEFT] == true)
moveLeft();
if(actions[ACTION_RIGTH] == true)
moveRight();
Usually instead of threads, games use something called the game loop (google it).
http://en.wikipedia.org/wiki/Game_programming
The basic idea is
Loop
Get all inputs
Get game clock
Update state based on clock and inputs
Update Display
Limit frame rate if you need to
Anyway -- instead of getting keyboard events -- you check the keyboard's state at certain points. This code seems to do it for Java
http://www.gamedev.net/reference/articles/article2439.asp
It uses events to set variables you look at in your loop.
Just in case anyone wanted to see how I ended up answering this question. My keyDown() and keyUp() method are as follows:
public boolean keyDown(Event e, int key)
{
message = key + "pressed";
//right paddle
if(key == 1005)
{
rpaddle_up = true;
}
if(key == 1004)
{
rpaddle_down = true;
}
//left paddle
if(key == 115)
{
lpaddle_up= true;
}
if(key == 119)
{
lpaddle_down=true;
}
//x key = exit
if(key == 'x')
System.exit(0);
return true;
}
public boolean keyUp(Event e, int key)
{
//right paddle
if(key == 1005)
{
rpaddle_up = false;
}
if(key == 1004)
{
rpaddle_down = false;
}
//left paddle
if(key == 115)
{
lpaddle_up= false;
}
if(key == 119)
{
lpaddle_down=false;
}
return true;
}
Hopefully if someone has the same issue this will help them. And thanks everyone for your input and help.
If I'm not mistaken, you have the keyUp() and keyDown() methods at your disposal with Applet. Have you tried setting a flag on keyDown, then unsetting it on keyUp? For example:
public boolean keyDown( Event e, int key )
{
if (key == 'a') {
player1.go("left")
}
}
public boolean keyUp( Event e, int key )
{
if (key == 'a') {
player1.stop("left")
}
}
Just an idea. I'm sure there's a standard way to deal with this.
You could use Swing Timer to periodically fire movement events between a keydown/keyup event.