So I've been trying to write a simple game where a character is moving using the statement
character1.x+=0.1f*delta
where delta is the time (in milliseconds) between frames. This works well when I want to move it every frame, but I want to be able to move it a set distance (50 pixels, or a single tile in my game) at that speed. Using for() loops makes it move the sprite at maximum speed, so I can't do that. It has to work on a single keypress, so they don't have to hold the key down.
Of course, I've barely done anything ever in Java, so there's probably a really stupidly simple solution that I'm missing. For now, I can just change it to instantaneous movement, but it looks really jerky. Can someone help?
In this case, you might want to add a keyListener yo your component.
yourComponent.addKeyListener(new KeyAdapter () { // Possibly your Frame
#Override // Overrides the keyPressed event
public void keyPressed(KeyEvent e) {
if ( e == YOUR_KEY_CODE ) {
for( int var = 0; var < yourDistance; var ++ ){
yourSpriteComponent.x += 1;
try{
Thread.sleep(yourTimeInMilliSec);
} catch( Exception e ){
// Your catch block here
}
}
}
}
});
Where YOUR_KEY_CODE is the key that the user needs to be pressed in order to execute the for() loop. Key Codes can be found in javadoc
Related
So I was making a simple game in Java + Processing where there were buttons and loops in draw(). Apparently the PApplet function mousePressed() doesn't work constantly if there is a loop, so I tried putting my own checkmouse() function to be checked during the loop. However, it still doesn't work. How do I make it so that I can run a game with while-loops and constantly check for mousePressed at the same time?
//draw() func
public void draw() {
for (int i = 0; i < 10000; i++) { //to simulate a while loop
//do something, like run some other functions that create the buttons
checkmouse();
}
}
//checkmouse function
public void checkmouse() {
if (mousePressed) {
System.out.println("x");
}
}
When I click the mouse in the processing window, it never shows "x" even though checkmouse() runs every time it loops, so theoretically it should be checking it pretty constantly while the loop runs.
Also could someone explain why this doesn't work?
boolean esc = false;
while (!esc) {
if (mousePressed) {
System.out.println("x");
esc = true;
}
}
The event variables (mousePressed, keyPressed, etc.) are updated between calls to the draw() function.
In other words: the mousePressed function will never change within a call to the draw() function. You need to let the draw() function complete and then be called again if you want the event variables to be updated.
Behind the scenes, this is because Processing is single-threaded. (This is by design, because multi-threaded UI programs are a nightmare.)
Taking a step back, you probably don't want to include a long loop inside your draw() function. Take advantage of the 60 FPS loop that's implemented by Processing instead.
I am trying to make a NXT Robot that has attached the Ultrasonic Sensor. It has to drive until the distance is 15, and then the engines have to stop. After it stops it has to turn, but it doesn't work.
import lejos.nxt.*;
public class test {
public static void main(String [] args) throws InterruptedException {
UltrasonicSensor ultra = new UltrasonicSensor(SensorPort.S1);
for (int i = 0; i < 5; i++) {
try {
Motor.B.rotate(-1500 , true);
Motor.C.rotate(-1500 , true);
} catch (Exception E){}
while ( ultra.getDistance() < 15 ) {
Motor.B.backward();
Motor.C.backward();
}
LCD.clear();
LCD.drawString("Distance : "+ultra.getDistance(), 0, 0);
}
Button.waitForAnyPress();
}
}
My old code, which also didn't work:
import lejos.nxt.*;
public class test {
public static void main(String [] args) throws InterruptedException {
UltrasonicSensor ultra = new UltrasonicSensor(SensorPort.S1);
try {
Motor.B.rotate(-720);
Motor.C.rotate(-720);
} catch (Exception E){}
for (int i = 0; i < 5; i++)
{
LCD.drawString("Distance : "+ultra.getDistance(), 0, i);
Thread.sleep(2000);
int maxDistance = ultra.getDistance();
if (maxDistance < 15){
Motor.B.stop();
Motor.C.stop();
}
}
Button.waitForAnyPress();
}
}
Assumptions
Okay, from the looks of things, your code is probably not doing what you want. (In the future, when writing a question on Stack Overflow, please clarify in detail what the expected behavior is, as well as what erroneous behavior you're seeing. Those are usually the first two questions we would ask of you, anyway.)
First of all, you're going to want to ensure that your NXT kit has been set up properly, with your two motors on B and C, and your sensor on S1. If this is so, continue reading.
Code Interpretation
The motor commands:
try {
Motor.B.rotate(-1500, true);
Motor.C.rotate(-1500, true);
} catch (Exception E) {}
look like they're valid motor commands... but wait! You're using a two-wheeled robot, with the motors connected to two wheels that point in opposite directions? But you're using the same distance and direction for your motor's limit angle! If your wheels oppose each other, then this will do nothing but make the robot spin in a circle.
NOTE: Since your motors are configured properly, as written in your comments, ignore this part.
If you change the direction of one of the motors by changing the positive to a negative, then you'll have them both working in unison to move your robot forward (or backwards, if you change the wrong one!)
Also, keep in mind that passing true as the second argument in
Motor.B.rotate(-1500, true);
Motor.C.rotate(-1500, true);
makes this function in a very specific fashion, according to the Javadoc (emphasis mine):
If immediateReturn is true, method returns immediately and the motor stops by itself.
If any motor method is called before the limit is reached, the rotation is canceled.
The first sentence means that this does what we want it to: It tells our motor to find the right limit angle by itself, but don't make our program wait for it. However, the second sentence means that if any other motor commands are called, it will stop moving to the given limit angle. Yeah, that's right. Those next few lines make us stop moving the motors and do what they say instead.
Now, this code is problematic for two reasons:
while (ultra.getDistance() < 30) {
Motor.B.backward();
Motor.C.backward();
}
First, these commands will IMMEDIATELY stop our previous two motor commands from executing, which basically means the motors will jump straight to going "backwards" and looping until the distance sensor reads greater than or equal to 30. This is actually what we want, but we need a bit more...
Second, after your sensor reads the distance greater than 30, your motors are never told to stop! So even when your program is showing you the distance, and waiting for your button to be pressed, it'll still be moving!
A Solution
Okay, there's a few things that need to change:
Your initial motor command is being blocked out by the later commands that tell it to move to the correct position.
Your motors don't stop when they're supposed to.
Below is your code, edited to address each of these issues. I've included notes where I've made changes to show you what I've changed.
import lejos.nxt.*;
public class test {
public static void main(String [] args) throws InterruptedException {
UltrasonicSensor ultra = new UltrasonicSensor(SensorPort.S1);
for (int i = 0; i < 5; i++) {
// No initial motor movement (because it did nothing anyway)
// We change this to approach from either direction.
while (ultra.getDistance() != 30) {
// Check whether it's behind or ahead of it.
// Assuming that B- and C- increase distance, and B+ and C+ decrease it (depends on robot configuration).
// This is called a feedback loop, by the way.
if (ultra.getDistance() < 30) { // Move forward (distance+)
Motor.B.backward();
Motor.C.backward();
} else { // Move backward (distance-)
Motor.B.forward();
Motor.C.forward();
}
}
// We only get here when the distance is right, so stop the motors.
Motor.B.stop();
Motor.C.stop();
LCD.clear();
LCD.drawString("Distance : "+ultra.getDistance(), 0, 0);
}
Button.waitForAnyPress();
}
}
Now, this code isn't perfect; it may have a tendency to oscillate between forward and backward on slippery surfaces (which may turn it slightly to the left or right due to differences in applied torque), or if the sensor misses the correct position and the robot overshoots it.
This code also doesn't wait until the robot stabilizes at the given position, just until the sensor first reports the correct one. Again, this may result in sliding around a bit if the wheels don't have decent traction, the motors are set to smooth acceleration, or if the motors run at too high of a speed.
To correct these flaws, you'd need a more advanced type of feedback loop which accounts for acceleration and slip, and you'd need to wait until the robot stabilizes at the correct position for a short period of time before stopping the motors.
However, this should get you moving in the right direction, so to speak.
EDIT Corrected drive motor directionality, as specified in the comments.
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!
I know J2ME is pretty outdated, but I have to do this for an assignment. Currently, I am using the GameCanvas class, and my game is a thread, so my code looks something like this..
class Game extends GameCanvas implements Runnable {
public GameCanvas() {
super(false);
}
public void run() {
while (true) {
draw();
flushGraphics();
}
}
protected void keyPressed(int keyCode) {
System.out.println("Hey, it actually worked.");
// other code to handle key press...
}
}
The sad thing is that the keyPressed method never gets called no matter how hard I spam hits on the emulator's numpad. I know of the getKeyStates() method that GameCanvas has, but I don't want to use it because I want to capture not just the game keys, but also the number keys 1~9.
Does anyone have any idea why my code doesn't go into my keyPressed() method, and what I can do about it? Many thanks.
Don't know where I went wrong... but after tweaking a little here and there, it started working perfectly fine. Thanks a lot guys! :)
You have a busy wait within Game#run method which most likely causes device ignore all your hits, making your UI loose responsiveness.
For simple test if above assumption is correct, just insert sleep within the loop, about like below:
while (true) {
draw();
flushGraphics();
try { Thread.sleep(100); } // sleep for 1/10 sec
catch (InterruptedException ie) { System.out.println(ie); }
}
If above helps to recover UI responsiveness, redesign your application to avoid busy waits - MIDP API provides a couple of ways to achieve that.
The MIDP documentation excerpt for GameCanvas(...)
If the developer only needs to query key status using the getKeyStates method, the regular key event mechanism can be suppressed for game keys while this GameCanvas is shown. If not needed by the application, the suppression of key events may improve performance by eliminating unnecessary system calls to keyPressed, keyRepeated and keyReleased methods.
Note that key events can be suppressed only for the defined game keys (UP, DOWN, FIRE, etc.); key events are always generated for all other keys.
So super(false) will suppress the Canvas key event listener methods in GameCanvas. In which case if you still want to register the key events use getKeyEvents(...) in your run(), the example is as under
// Get the Graphics object for the off-screen buffer
Graphics g = getGraphics();
while (true) {
// Check user input and update positions if necessary
int keyState = getKeyStates();
if ((keyState & LEFT_PRESSED) != 0) {
sprite.move(-1, 0);
}
else if ((keyState & RIGHT_PRESSED) != 0) {
sprite.move(1, 0);
}
// Clear the background to white
g.setColor(0xFFFFFF);
g.fillRect(0,0,getWidth(), getHeight());
// Draw the Sprite
sprite.paint(g);
// Flush the off-screen buffer
flushGraphics();
}
I am writing a game for Android and I think I have a fatal flaw in my architecture and/or code.
The architecture is basically two threads, one that just continuously draws the screen and the other than controls the movement of the drawables on the screen based on the user's touch screen input.
In the latter thread, I'm basically doing manual animation (insert correct term here), meaning I move the drawables on the screen via methods that change the Drawables Rect as the game progresses. The "no-no" I believe I'm doing is inserting delays into these methods, so that it "appears like an actual animation."
This all works fine and dandy because I have no reason to process any user input during these animations, but it's really bugging me that I'm designing a flaw into a lot of hard work with my game. I'm sure many of you can relate to this in terms of code design.
What is the proper way to design this "manual animation" so my code can still process user events (like touching the screen), while the animation is occurring?
Here is a reference example of one of my methods:
public void BadAnimation() {
for (int k = 0; k < mAnimationHeight; k++) {
for (int i = 0; i < mRows; i++) {
for (int j = 0; j < mCols; j++) {
if (myObject.mFlag[i][j]) {
myObject[i][j].mRect.top++
}
}
}
try {
Thread.sleep(3);
}
catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
This is an excellent tutorial that will show you how to implement the delay correctly:
http://www.droidnova.com/playing-with-graphics-in-android-part-i,147.html
It also covers how to accept user input whilst simultaneously changing the game state and drawing to the screen.
The best way to do this is inside your game loop. You have a timer that you have running if it has elapsed perform your draw, reset the timer. Otherwise do nothing.