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.
Related
so lately i've been learning about abstract classes and interfaces, and i saw a neat yt video about falling sand games (think Noita) so i wanted to take a crack at it as some particles share a characteristic that can be abstracted, and behavior that's not specific to only particles that can be interfaced. only going to be implementing 2, maybe 3 types of "particle" but it seems im having trouble with gravity.
i've spent a few hours trying to figure out a good way of doing it and this is what i got so far out of my attempts (which include: gravity somehow working backwards, collision being ignored, and working, but sunk a few pixels into the object)
// the drop method is called every second, and force == 1
// drop is a method from the Gravity interface, Sand implements gravity and overrides drop.
#Override
public void drop(Particle self) {
for (Particle p : Controller.particle) {
if (p.equals(self)) {
continue;
}
if (self.rectangle.getBoundsInParent().intersects(p.rectangle.getBoundsInParent())) {
Behavior.repulseUpward(self, force);
}
}
Behavior.gravityDrop(self, force);
}
public class Behavior {
static void gravityDrop(Particle p, double force) {
// in the future ill replace bottomLine with what's inside it but for now its more
// readable to me.
double bottomLine = (Controller.height - p.rectangle.getHeight());
if (p.rectangle.getLayoutY() < bottomLine) {
p.setPosition(p.rectangle.getLayoutX(), p.rectangle.getLayoutY() + force);
}
}
static void repulseUpward(Particle p, double force) {
p.setPosition(p.rectangle.getLayoutX(), p.rectangle.getLayoutY() - force);
}
}
(if i've forgotton any code that you think would be relevant ill add it)
this gives me the behavior i was hoping for! exceeeeeept if say two shapes are 20 pix wide, and are spaced 20 pix apart, it will still detect a collision and cause themselves to suspend in air. Is there any fix to get around this situation or do i have to scrap the drop method again?
Picture of the problem here
Situation: The tank has a Shot() method and a code that checks if the bullet hits the obstacle after being fired. The bullet flies up the X-coordinate. The do...while loop checks how far the bullet can travel without obstacles. After that, the animation of the bullet flight itself takes place through TranslateTransition. And the last loop goes through all the game obstacles and checks for contact through intersects and, if successful, removes the obstacle.
do {
y = (int) (bulletObj.getImageBullet().getTranslateX() + register) / PlayField.BRICK_SIZE;
x = (int) bulletObj.getImageBullet().getTranslateY() / PlayField.BRICK_SIZE;
line = LevelData.LevelOne[x][y];
register += 1;
} while (line.equals("0"));
System.out.println(System.currentTimeMillis()); // 1643642047472 ms.
bulletTranslate = new TranslateTransition();
bulletTranslate.setFromX(bulletObj.getImageBullet().getTranslateX());
bulletTranslate.setToX(bulletObj.getImageBullet().getTranslateX() + register - 18);
bulletTranslate.setNode(bulletObj.getImageBullet());
bulletTranslate.setDuration(Duration.millis(register)); // Let register = 300 мs.
bulletTranslate.play();
System.out.println(System.currentTimeMillis()); // 1643642047474 ms.
bulletObj.getImageBullet().setTranslateX(bulletObj.getImageBullet().getTranslateX() + register - 18);
for (GameObject platform: PlayField.platforms) {
if (platform.getImage().getBoundsInParent().intersects(bulletObj.getImageBullet().getBoundsInParent()))
{
System.out.println(System.currentTimeMillis()); // 1643642047474 ms.
tankRoot.getChildren().remove(platform.getImage());
PlayField.platforms.remove(platform);
LevelData.LevelOne[x][y] = "0";
break;
}
}
Everything works as expected, but there is only one problem.
The problem is that the obstacle objects are removed faster than the animation bullets pass.
And it is necessary that after contact they are deleted simultaneously.
How to solve the problem?
Before the shot
After the shot, the object disappeared during the flight of the bullet
P.S Sorry, I used google translate.
As #jewelsea suggested, you need to supply a minimal reproducible example to get some proper help.
Having said that, based on the code you provided, below is my initial analysis. Note that this is a rough analysis by reading the code.. so everything is just an assumption ;)
The first part of the code (do-while) is to determine the distance the bullet needs to travel (determining the value of register)
The second part is to initiate animation of bullet to translate from its current position (using the register value).
The third part is to set the final translate value to the bullet and the final part is to check if the bounds are intersected and delete the node.
I think the thrid part (below line of code) is not needed and could be the cause for your issue. You are updating the value to the end value and immediately checking if it is intersected.. which will be true and will delete instantly.
bulletObj.getImageBullet().setTranslateX(bulletObj.getImageBullet().getTranslateX() + register - 18);
Try moving your 'for' loop code to onFinished of translation. Something like..
bulletTranslate = new TranslateTransition();
bulletTranslate.setFromX(bulletObj.getImageBullet().getTranslateX());
bulletTranslate.setToX(bulletObj.getImageBullet().getTranslateX() + register - 18);
bulletTranslate.setNode(bulletObj.getImageBullet());
bulletTranslate.setDuration(Duration.millis(register)); // Let register = 300 мs.
bulletTranslate.setOnFinished(e->{
for (GameObject platform: PlayField.platforms) {
if (platform.getImage().getBoundsInParent().intersects(bulletObj.getImageBullet().getBoundsInParent()))
{
tankRoot.getChildren().remove(platform.getImage());
PlayField.platforms.remove(platform);
LevelData.LevelOne[x][y] = "0";
break;
}
}
});
bulletTranslate.play();
First of all, check this out (link): it's an example program taken from the Processing Box2D library on github, which displays a box at the center of the screen (it can be moved around with the mouse) while a cascade of little balls fall on it; whenever a ball hits the box, that ball turns red. I copypasted the four .pde files into a single .pde sketch, run it, and it works perfectly.
Now, onto my problem. I'm currently making a game in Processing: you have a ball, a player 1 and a player 2 (both of which can be moved around using the keyboard). Box2D is in charge of the physical interactions between each player and the ball, and I must say it handles them pretty well. Each of the three main objects has its own class. Now, I want stuff to happen as soon as player 1 makes contact with the ball. And that example code seems perfect for this scope, right? It works on my computer, after all.
So, I started copying the relevant parts:
I copypasted all the 'import's at the beginning of my code;
I added box2d.listenForCollisions(); inside setup();
I also added these two functions at the very bottom of my sketch:
void beginContact(Contact cp) {
Fixture f1 = cp.getFixtureA();
Fixture f2 = cp.getFixtureB();
Body b1 = f1.getBody();
Body b2 = f2.getBody();
Object o1 = b1.getUserData();
Object o2 = b2.getUserData();
if (o1.getClass() == Box.class) {
Particle p = (Particle) o2;
p.change();
}
else if (o2.getClass() == Box.class) {
Particle p = (Particle) o1;
p.change();
}
}
void endContact(Contact cp) {
}
Which I promptly changed into this (I basically renamed the classes, and substituted p.change(), the method that turned the balls red in that sketch, with what I want to happen when contact is being made):
(... same ...)
if (o1.getClass() == Player.class) {
Ball p = (Ball) o2;
//do stuff when contact happens
}
else if (o2.getClass() == Player.class) {
Ball p = (Ball) o1;
//do stuff when contact happens
}
}
void endContact(Contact cp) {
}
But guess what? I get a 'Could not invoke the "beginContact()" method for some reason' error! I don't think I'm lacking any crucial files or libraries, since that example worked fine in my computer and all I did was just copypaste and run the code.
I cannot paste here my whole code because it's huge, but I swear the Player class (player 1 class), the Enemy class (player 2 class) and the Ball class all have their fixtures, and there's literally nothing in my classes that differs in a substantial way from those from the example sketch. The bodies are all of dynamic type, the players are rectangular boxes like the box in the example, and the ball is a pure circle like the little balls that turned red there.
What is happening? Did I miss an important line from that code? Even though the example code runs perfectly without needing any additional files, I should mention that the console also prints an 'at shiffman.box2d.Box2DContactListener.beginContact(Box2DContactListener.java:54)' error: now, like I said, I don't need that Box2DContactListener.java file in my computer to run the example sketch... but anyway, if I read it up online (link), I can see this is what it's referring to:
public void beginContact(Contact c) {
if (beginMethod != null) {
try {
beginMethod.invoke(parent, new Object[] { c });
} catch (Exception e) {
System.out.println("Could not invoke the \"beginContact()\" method for some reason.");
e.printStackTrace();
beginMethod = null;
}
}
}
Do you have any idea what's going on here?
Basically, before the if statement, you have to check if either of the objects is 'null'.
In case it is, you just have to break.
You can just simply paste this:
if (o1==null || o2==null)
return;
I have figured the problem out before posting this question. Nevertheless I think this might help someone else, so I am posting it anyway. If anyone knows a better or more elegant way to do this, please post an answer. If I agree, I will of course accept your answer. Here is my original problem:
I am trying to get the following to work with JSyn:
I want to play an 800Hz sine wave on two channels and switch on or off the individual channels whenever I want. So, for instance, I want the following sequence:
time 0s 0.3s 0.8s 1s 1s 1.1s 1.5s
left |--------------------| |-----------|
right |----| |------------------------|
I can generate the sine wave on either one, or both channels, but I have not figured out how to turn them on/off. Here is some code:
import com.jsyn.JSyn;
import com.jsyn.Synthesizer;
import com.jsyn.unitgen.LineOut;
import com.jsyn.unitgen.SineOscillator;
public class MusicTest {
public static void main(String[] args) {
playSound();
}
public static void playSound() {
Synthesizer synth = JSyn.createSynthesizer();
try {
synth.start();
SineOscillator sineOsc1 = new SineOscillator(500, 0.2);
SineOscillator sineOsc2 = new SineOscillator(500, 0.2);
LineOut lineOut= new LineOut();
synth.add(lineOut);
synth.add(sineOsc1);
synth.add(sineOsc2);
sineOsc1.output.connect(0, lineOut.input, 0); // left channel
sineOsc2.output.connect(0, lineOut.input, 1); // right channel
lineOut.start();
sineOsc1.start(); // start left
sleep(1000);
sineOsc2.start(); // after 1s, start right
sleep(1000);
sineOsc1.stop(); // after 1s, stop left
sleep(1000);
sineOsc2.stop(); // after 1s, stop
lineOut.stop();
} finally {
synth.stop();
}
}
private static void sleep(int ms) {
try {
Thread.sleep(ms);
} catch (InterruptedException e) {}
}
}
Unfortunately, this does not work...
The reason the first example did not work is because JSyn "pulls" data through connected unit generators.
The start() method add a unit to a list of units that are executed directly by the engine. You start() the end of a chain, eg the LineOut. When a unit executes it asks for data from any units connected to its inputs. Those units, in turn, ask for data from any units connected to them. This guarantees that the units execute in an order that minimizes latency. JSyn checks for circular connections to prevent infinite loops.
Even though you stop()ped the SineOscillators, the LineOut was still pulling on them. So they made sound.
Instead of calling start() or stop() on the oscillators, call setEnabled(f). This will block the pulling of data. Try this code snippet in your first example:
sineOsc1.setEnabled( false ); // begin disabled
sineOsc2.setEnabled( false );
lineOut.start();
sineOsc1.setEnabled(true); // start left
sleep(1000);
sineOsc2.setEnabled(true); // after 1s, start right
sleep(1000);
sineOsc1.setEnabled( false ); // after 1s, stop left
sleep(1000);
sineOsc2.setEnabled( false ); // after 1s, stop
Another approach is to use one oscillator and connect it through a Multiply unit to each channel of the LineOut. Then you can turn on or off the sine on that channel by multiplying by 1.0 or 0.0. This allows you to multiply by a scalar, eg. 0.5, if you want to be half on.
sineOsc ---> multiply1 ---> lineOut(0) // left channel
`--> multiply2 ---> lineOut(1) // right channel
I realize the setEnabled() and start() methods are not well documented. I will fix that now.
The solution is to not try to turn on/off the sine wave, but just use two LineOut. Like this:
public static void playSound() {
Synthesizer synth = JSyn.createSynthesizer();
try {
synth.start();
SineOscillator sineOsc1 = new SineOscillator(500, 0.2);
LineOut lineOut1 = new LineOut();
LineOut lineOut2 = new LineOut();
synth.add(lineOut1);
synth.add(lineOut2);
synth.add(sineOsc1);
sineOsc1.output.connect(0, lineOut1.input, 0); // connect to left channel
sineOsc1.output.connect(0, lineOut2.input, 1); // connect to right channel
lineOut1.start(); // start left
sleep(1000);
lineOut2.start(); // after 1s, start right
sleep(1000);
lineOut1.stop(); // after 1s, stop left
sleep(1000);
lineOut2.stop(); // after 1s, stop
} finally {
synth.stop();
}
}
I don't know if that is the most elegant solution, but it works really great. I hope this is of help for someone else.
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