I am making a game which needs an "arrow" to be shot from a stationary location (a set coordinate). The arrow's trajectory is based on the location that the user Clicks in the GUI. This is essentially an Aiming feature. I cant get the arrow to follow a working path, any equations ie used have led to weird, glitchy, and buggy results.
public class ReShoot implements ActionListener
{
public void actionPerformed(ActionEvent e){
ArrowShoot shoot = new ArrowShoot();
shoot.ReShoot();
}
}
public class ArrowShoot implements ActionListener
{
public Timer T = new Timer(5, this);
Arrow A = new Arrow();
public void ReShoot(){
T.start();
arrow_x=0;
arrow_y=200;
A.setBounds(0,200,10,10);
}
// MAIN: y=-16t^2 + Vy * t + h
//Vy = v * sin(a)
//Vx = v * cos(a)
//a = arctan( (200-mouse_y)/v
//v = Mouse_x - Arrow_x
//t = x / Vx
public void actionPerformed(ActionEvent e)
{//arrow_y = 0.0025 * Math.pow((mouse_x-arrow_x), 2)+ mouse_y;
Container container_arrow = getContentPane();
container_arrow.setLayout(null);
container_arrow.add(A);
A.setBounds(0,200,10,10);
arrow_x++;
double v = mouse_x/2; //height change
double a = 50* Math.atan((200-mouse_y) / (v));
double Vy = v * Math.sin(a);
double Vx = v * Math.cos(a);
double t = arrow_x/Vx;
double h = 200;
arrow_y = (16) * Math.pow(t, 2) + (Vy * t) + h;
int x = (int)Math.round(arrow_x);
int y = (int)Math.round(arrow_y);
A.setBounds(x, y,10,10);
if (arrow_y>=500)
T.stop();
}
I am pretty sure im doing this all wrong, and there has to be a more effective method to accomplish this task.
It doesn't look like you are calculating the trajectory path correctly. In actionPerformed, you are incrementing the x coordinate of the arrow, and then calculating the corresponding y. This will not work at all, since even though you can calculate y as a function of x, x is itself a function of t (time). Hence you have to calculate x at time t instead of assuming that x will always increase by 1 at the next invocation.
Given that you can calculate the angle, you can calculate the position of x and y as a function of time and the angle using the following formulas:
So your algorithm will essentially be:
time++; //time variable that you maintain
arrow_x = ... //calculate using (1)
arrow_y = ... //calculate using (2)
Related
import java.awt.Color;
import acm.graphics.GOval;
import acm.program.GraphicsProgram;
public class Clock extends GraphicsProgram {
private static final long serialVersionUID = 1L;
public void run() {
GOval tofig = createFilledCircle(getWidth()/2, getHeight()/2, 200, Color.black);
add(tofig);
GOval lala = createFilledCircle(getWidth()/2, getHeight()/2, 180, Color.white);
add(lala);
}
private GOval createFilledCircle(double x, double y, double r, Color color) {
GOval circle = new GOval(x - r, y - r, 2 * r, 2 * r);
circle.setFilled(true);
circle.setColor(color);
return circle;
}
// Ignore this;
public static void main(String[] args) {
new Clock().start();
}
}
Here's the code for the trigonometry part of what you're trying to do that you and I have kinda worked on together:
public class DrawCircle {
static final double twopi = Math.PI * 2;
static final double fudge = 0.000001;
private static void drawHourLabels(double center_x, double center_y, double radius) {
int steps = 12;
for (double angle = 0.0; angle < (twopi - fudge); angle += twopi/steps) {
double x_offset = Math.sin(angle) * radius;
double y_offset = Math.cos(angle) * radius;
double x = center_x + x_offset;
double y = center_y + y_offset;
// Here you'd do the actual drawing of each hour label at the coordinates x,y. We'll just print them for now.
System.out.println(String.format("%f %f", x, y));
}
}
public static void main(String... args) {
// drawHourLabels(getWidth()/2, getHeight()/2, 220); // <-- you'd do something like this in your "run" method.
// Draw clock labels around circle with center at 400x200 of radius 220
drawHourLabels(400, 600, 220);
}
}
The 'fudge' value is used because floating point arithmetic isn't perfectly precise. By the time we've added 12 floating point values together to get to 2 * Math.PI, we might be a little over or under. We want to make sure we don't process the 12:00 position again at the end of the loop because we computed a value just a little smaller than 2 * Math.PI. So we add a "fudge factor" that's really small, but guaranteed to be bigger than any floating point inaccuracy we've accumulated.
Take a look at this simple example from me.
/**
* Draw text elements from 1 to 12 inside a circle
* #return a group of text elements from 1 to 12
*/
public strictfp static Group drawText() {
//a list for storing text numbers
List<Text> numbersInClock = new ArrayList<>();
//create a group of shapes
Group group = new Group();
//set it to x,y positions
group.setLayoutX(150);
group.setLayoutY(150);
//numbers corresponding to angle calculations
int[] numbers = {3,4,5,6,7,8,9,10,11,12,1,2};
double[] angles = {0, 0.166666666667, 0.333333333334,0.5, 0.666666666667, 0.833333333334, 1, 1.166666666667, 1.33333333334, 1.5, 1.666666666667, 1.83333333334};
int i = 0;
for (double angle : angles) {
//Calculated formula for x,y positions of each number within a circle
//(x,y) = (rcos(angle),rsin(angle))
int r = 90; // length of radius, a bit shorter to put it inside a circle
//calculate x and y positions based on formula for numbers within a circle
double x = r * Math.cos(angle*Math.PI);
double y = r * Math.sin(angle*Math.PI);
//create a text element consiting a coressponding number
Text text = new Text(x, y, String.valueOf(numbers[i++]));
//add it to a list
numbersInClock.add(text);
}
//add all text elements to a group
group.getChildren().addAll(numbersInClock);
//return a group
return group;
}
Full code can be acquired from here: https://github.com/MomirSarac/JavaFX-Circle-Clock-Image
You can use sin and cos to know where to set your text.
Sine and Cosine are two math operators that take in an angle in radians and give out number beetween -1 and 1 to know how to multiply to get coordinates
you can learn more here
public class SimpleHarmonic {
public static void main(String[] args) {
StdDraw.setXscale(0,900);
StdDraw.setYscale(0,700);
while (true) {
StdDraw.setPenColor(StdDraw.BLACK);
StdDraw.line(0,350,900,350); // x-axis
StdDraw.line(450,0,450,900); // y-axis
StdDraw.setPenColor(StdDraw.RED);
for (double x = -450; x <= 450; x += 0.5) {
double y = 50 * Math.sin(x * (Math.PI / 180));
int Y = (int) y;
int X = (int) x;
StdDraw.line(450 + X, 350 - Y, 450 + X, 350 - Y);
}
StdDraw.clear();
}
}
}
In this code I am attempting to simulate simple harmonic motion. However, I have only been able to draw a static graph, but I need it to move continously.
I believe I need to use a loop to contionusly redraw the points, but I am not sure how to do that.
How can I make my current sine graph move contionusly?
Edit: Voted to close as non-programming? what?
I took a look at the StdDraw class you are using and it looks like what you want is the
StdDRaw.show(int) method, this method comment states:
/**
* Display on screen, pause for t milliseconds, and turn on
* <em>animation mode</em>: subsequent calls to
* drawing methods such as {#code line()}, {#code circle()}, and {#code square()}
* will not be displayed on screen until the next call to {#code show()}.
* This is useful for producing animations (clear the screen, draw a bunch of shapes,
* display on screen for a fixed amount of time, and repeat). It also speeds up
* drawing a huge number of shapes (call {#code show(0)} to defer drawing
* on screen, draw the shapes, and call {#code show(0)} to display them all
* on screen at once).
* #param t number of milliseconds
*/
In this library any time you call a draw method such as line or circle it conditionally repaints the frame. By passing the int param to the draw method it will turn all painting methods into "animation mode" and defer repainting the frame until you call draw() (no params).
To make it animate you must make each iteration of your while loop 1 animation frame, each frame will need to differ from the previous one. You can do this by using a variable outside your loop to offset each frame by a small ammount. Ill call this offset
With this information you can alter your loop to look like:
double offset = 0;
while (true) {
offset+=1; // move the frame slightly
StdDraw.show(10); // defer repainting for 10 milisecoinds
StdDraw.clear(); // clear before painting
StdDraw.setPenColor(StdDraw.BLACK);
StdDraw.line(0,350,900,350); // x-axis
StdDraw.line(450,0,450,900); // y-axis
StdDraw.setPenColor(StdDraw.RED);
for (double x = -450; x <= 450; x += 0.5) {
// apply the offset inside of calculation of Y only such that it
// slowly "moves" the sin wave
double y = 50 * Math.sin((offset+x) * (Math.PI / 180));
int Y = (int) y;
int X = (int) x;
StdDraw.line(450 + X, 350 - Y, 450 + X, 350 - Y);
}
StdDraw.show(); // end animation frame. force a repaint
}
A few improvements in your code
1 Inside your loop where you draw each "dot" you are increnting by .5. Because that X value is literally 1 pixel you arent gaining anything by going to .5 instead of 1. 1 is quite literally the smallest you can visually see in this enviroment. I recommend making it at least be x+=1
for (double x = -450; x <= 450; x += 1)
2 You are using the .line method but drawing to the same point. You could significantly speed up your program by only calculating every 3rd pixels Y value and connecting the dots. For instance
double prevX = -450;
double prevY = 50 * Math.sin((prevX+offset) * (Math.PI / 180)); // seed the previous Y to start
for (double x = 0; x <= 450; x += 3) {
double y = 50 * Math.sin((x+offset) * (Math.PI / 180));
StdDraw.line(450 + (int)prevX, 350 - (int)prevY, 450 + (int)x, 350 - (int)y);
prevX = x;
prevY = y;
}
3 This isnt your code but in the StdDraw.init method you can set some rendering hints to allow for cleaner lines. This should make it look alot nicer
offscreen.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
RenderingHints.VALUE_STROKE_PURE);
Combining all those things heres what I wrote
public static void main(String[] args) {
StdDraw.setXscale(0,900);
StdDraw.setYscale(0,700);
double offset = 0;
while (true) {
StdDraw.show(10);
StdDraw.clear();
offset-=1;
StdDraw.setPenColor(StdDraw.BLACK);
StdDraw.line(0,350,900,350); // x-axis
StdDraw.line(450,0,450,900); // y-axis
StdDraw.setPenColor(StdDraw.RED);
double prevX = 0;
double prevY = 50 * Math.sin((prevX+offset) * (Math.PI / 180)); // seed the previous Y to start
StdDraw.filledCircle(450 + prevX, 350 - prevY, 5);
for (double x = 0; x <= 450; x += 3) {
double y = 50 * Math.sin((x+offset) * (Math.PI / 180));
StdDraw.line(450 + (int)prevX, 350 - (int)prevY, 450 + (int)x, 350 - (int)y);
prevX = x;
prevY = y;
}
StdDraw.show();
}
}
I dont have an animation recorder so heres a picture
I have an object that I am trying to get to move in a smooth "arc". The x value of the object does not actually change, but the y value does.
The y value starts at -108, and needs to get to -37.5. Every iteration I want it to move the same distance in a shorter time. The issue is that it needs to decelerate and stop at -37.5 (this would be for half of the motion). When the current code runs the time stays the same, the distance increases. this does work for the first iteration.
This is what I have:
private void jump() {
int startV = 10;
float yVal;
float velocity;
int airTime = 1;
float currY = -108;
Main main = New Main();
// main.iter is the iteration
velocity = startV * (1 + (main.iter / 10));
while (airtime != 0) {
yVal = yVal + velocity;
velocity = velocity - (1 + (main.iter / 10));
if (currY < yVal) {
airtime++;
}
else {
airtime--;
}
}
yVal = -108;
}
EDIT:
main.iter is an int that increments by one
I am trying to calculate the net acceleration due to gravity in order to build a simple space flight sim using (G*(m1 * m2) / d * d) / m1. The ship tends to go in a semi-correct direction in a stair step pattern.
The update function of the main class
public void update()
{
double[] accels = new double[bodies.size()];//acceleration of the planets
double[][] xyaccels = new double[bodies.size()][2];//storing the x and y
for(Body n: bodies){
int count = 0;
double dist = distance(ship.loc.x,ship.loc.y,n.loc.x,n.loc.y);
double acel = getAccel(n.mass, ship.mass, dist);
accels[count] = acel;
double alpha = getAngle(ship.loc.x,ship.loc.y,n.loc.x,n.loc.y);
//xyaccels[count][0] = Math.cos(alpha) * acel;
//xyaccels[count][1] = Math.sin(alpha) * acel;
//split the acceleration into the x and y
XA += Math.cos(alpha) * acel;
YA += Math.sin(alpha) * acel;
count++;
}
ship.update(XA, YA);
//XA = 0;
//YA = 0;
accels = null;
xyaccels = null;
}
update function for the spaceship
public void update(double XA, double YA){
xAccel += XA;
yAccel += YA;
//add the x-acceleration and the y-acceleration to the loc
loc.x += Math.round(xAccel);
loc.y += Math.round(yAccel);
}
You don't update location from acceleration. You don't have any equations relating velocity to acceleration that I can see. Your physics are wrong.
The 2D n body problem requires four coupled ordinary differential equations for each body. Acceleration, velocity, and displacement are all 2D vector quantities.
dv/dt = F/m // Newton's F = ma
ds/dt = v // Definition of velocity that you'll update to get position.
You have to integrate all of them together.
I'm assuming you know something about calculus and physics. If you don't, it's a better idea to find a library written by someone else who does: something like JBox2D.
Alright, I'm trying to do some simple object moving in the direction of where you touched the screen.
If I touch directly northwest of the object, it'll kind of move into the direction of the touch position. If I touch directly southeast of the object, it will kind of move into the direction of the touch position as well. However, if I touch directly northeast of the object, it'll move into the opposite direction towards the southwest. If I touch directly southwest of the object, it'll also move to the opposite direction towards northeast.
Also, if I touch north of the object, but just a little to the west, it will go straight west with a little to the north. Same with touching west of the object with a little bit to the north, it'll go straight north with a little bit to the west. Same thing for other directions.
Really, all the directions are from somewhat to obviously incorrect. I've been doing some paper calculations as well and I've seemed to be getting some correct angles, but at this point I'm completely stumped.
Does anyone know what the problem may be?
package com.badlogic.androidgames.texasholdem;
import java.util.List;
import android.util.FloatMath;
import com.badlogic.androidgames.framework.Game;
import com.badlogic.androidgames.framework.Graphics;
import com.badlogic.androidgames.framework.Input.TouchEvent;
import com.badlogic.androidgames.framework.Screen;
public class MainMenuScreen extends Screen {
public static float TO_RADIANS = (1 / 180.0f) * (float) Math.PI;
public static float TO_DEGREES = (1 / (float) Math.PI) * 180;
float num_x = 0; // Position of object on X axis
float num_y = 0; // Position of object on Y axis
float angle = 0;
public MainMenuScreen(Game game) {
super(game);
}
public void update(float deltaTime) {
Graphics g = game.getGraphics();
List<TouchEvent> touchEvents = game.getInput().getTouchEvents();
game.getInput().getKeyEvents();
int len = touchEvents.size();
for(int i = 0; i < len; i++) {
TouchEvent event = touchEvents.get(i);
if(event.type == TouchEvent.TOUCH_UP) {
if(inBounds(event, 0, 0, g.getWidth(), g.getHeight()) ) {
// Calculate the angle of the direction between two points
angle = (float) Math.atan2(event.x - num_x, event.y - num_y) * TO_DEGREES;
if (angle < 0)
angle += 360;
// This is just to give me numbers on the Math.atan2 result, angle, to/from X position, and to/from Y position
System.out.println("Pressed! - ATAN: " + Math.atan2(event.x - num_x, event.y - num_y)
+ " - ANGLE:" + angle + " - POS: " + event.x + "tx/"
+ (int)num_x + "fx " + event.y + "ty/" + (int)num_y + "fy");
}
}
}
// Moving object in direction at 1f speed
num_x += (1f * (float) Math.cos(angle * TO_RADIANS));
num_y += (1f * (float) Math.sin(angle * TO_RADIANS));
}
private boolean inBounds(TouchEvent event, int x, int y, int width, int height) {
if(event.x > x && event.x < x + width - 1 &&
event.y > y && event.y < y + height - 1)
return true;
else
return false;
}
public void present(float deltaTime) {
Graphics g = game.getGraphics();
g.drawPixmap(Assets.background, 0, 0);
g.drawPixmap(Assets.backcard, (int)num_x, (int)num_y);
}
public void pause() {
Settings.save(game.getFileIO());
}
public void resume() {
}
public void dispose() {
}
}
if event x> x then x must be positive to move toward event.x
the problem here is that when event.x< x then your moving x must be negative
int dx,dy;
dx = (1f * (float) Math.cos(angle * TO_RADIANS));
dy = (1f * (float) Math.sin(angle * TO_RADIANS));
if(event.x<x){
dx=-dx;}
if(event.y<y){
dy=-dy;}
num_x+=dx;
num_y+=dy;
this way is simpler but less precise....
public void update(){
//(find dif between item x, and touch x)
float xdif=destx-x;
float ydif=desty-y;
if(x<destx){
dx=xdif/8;
}
else if(x>destx){
//we devide both x and y differences by the same number
dx=xdif/8;
}
else if(x==destx){
dx=0;
}
if(y<desty){
dy=ydif/5;
}
else if(y>desty){
dy=ydif/5;
}
else if(y==desty){
dy=0;
}
x+=dx;
y+=dy;
there u go, pathing in a straight line between two points, item.x and touch x.
Firstly, the math - I think the problem is that, for example, tan(135deg) = tan (-45deg) = -1. Therefore, atan has return values ranging between -90deg and 90deg as a resolution to ambiguity (look at its graph here). I think La5t5tarfighter's solution - negating the x movement in some cases - is on the right track, but you need to negate the y component in those cases as well. You could try that, but it would be much simpler if you used libGDX's Vector2 class. This is how I'd do it:
move.set(touchX, touchY); // y should be through flipping or unproject() before this
move.sub(objectPos); // move now points from object to where you touched
move.nor(); // now 1 unit long
move.scl(SPEED*deltaTime); // multiplied by a constant and delta - framerate-independent
objectPos.add(move);
You could even chain it into just one line if you want:
objectPos.add(move.set(x,y).sub(objectPos).nor().scl(SPEED*deltaTime));
Secondly, you're not using a Camera. I'm not completely sure what the default coordinate system is, but I believe the y axis points up which is not the same as the one used for inputs - Input.getY() is given with an y axis pointing down from the top left corner. If you had a Camera, you'd do this:
cam.unproject(someVector.set(Gdx.input.getX(), Gdx.input.getY(), 0));
Lacking that, you might need to flip the y axis:
event.y = Gdx.graphics.getHeight() - event.y;
Still, this could be wrong. Try drawing the object right at the touch position - if I'm right in this, it'll seem mirrored vertically. If it draws correctly where you touch, ignore this part.