Bullets become super fast as the angle gets closer 90 - java

so I'm developing a game in Java and I'm trying to shoot bullets towards the cursor position- the shooting part is OK but as the angle between the cursor and the player (the player shoots the bullets so essentially its the bullet's first coordinates) get closer to 90 (or -90) the bullets going super fast and I have no idea why
The red zone is the super-speed zone
Code:
public void run() {
super.run();
final double currentY=y;
final double currentX=x;
double slope = (cursorPos.y - currentY) / (cursorPos.x - currentX);
double angle= Math.toDegrees(Math.atan2(cursorPos.y - currentY, cursorPos.x - currentX));
System.out.println(angle);
while (true){
try {
double newY;// y = m * (x-x.) + y.
newY = ((slope*(x - currentX))+currentY);
y= (int) newY;
if ((angle <=-90 && angle>=-180) || (angle >=90 && angle<=180) )
{
x--;
}
else {
x++;
}
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

You're stepping x in time by fixed increments of one. So when the bullet lines are near vertical, the slope m is a big number, and they're covering m pixels per step in y. The closer to vertical, the faster they go.
Instead you need to step in fixed increments of distance along the line segment the bullet is following. If the endpoints are (x0,y0) and (x1,y1), then varying t from 0 to 1 in the equations x = t*(x1-x0)+x0; and y=t*(y1-y0)+y0 will sweep the line segment. To sweep by units of 1 pixel, you need to know how many pixels there are along the line. That's L = sqrt(sqr(x1-x0) + sqr(y1-y0)), so the values of t are i / L for i = 0 to L.
You'll need to do these computations with floating point numbers.
Another note is that you'll probably eventually have trouble using Sleep as you are. This prevents the Swing event handling loop from doing any work while it's waiting.

Related

Changing direction while moving

I am making a game with a space ship that rotates when the left and right keys are pressed and moves forward when the up key is pressed.
Currently the ship can rotate while its moving forward but it will continue in the same direction that it is going in.
How would i make it so that the ship can change the direction its is moving while the up key is being held down?
This is the update method for the SpaceShip class:
public void update(){
radians += ri;
System.out.println(radians);
if(radians < 0){
radians = 2 * Math.PI;
}if(radians > (2 * Math.PI)){
radians = 0;
}
x += xx;
y += yy;
}
this is the right event:
public void actionPerformed(ActionEvent e) {
if(pressed){
Board.getShip().setRI(0.05);
}else{
Board.getShip().setRI(0);
}
}
and this is the up event:
public void actionPerformed(ActionEvent e) {
if(pressed){
Board.getShip().setXX(Math.cos(Board.getShip().getRadians()) * Board.getShip().getSpeed());
Board.getShip().setYY(Math.sin(Board.getShip().getRadians()) * Board.getShip().getSpeed());
}else{
Board.getShip().setXX(0);
Board.getShip().setYY(0);
}
}
Rockets
A rocket defined as
// pseudo code
rocket = {
mass : 1000,
position : { // world coordinate position
x : 0,
y : 0,
},
deltaPos : { // the change in position per frame
x : 0,
y : 0,
},
direction : 0, // where the front points in radians
thrust: 100, // the force applied by the rockets
velocity : ?, // this is calculated
}
The formula for movement is
deltaVelocity = mass / thrust;
The direction of the thrust is along the direction the ship is pointing. As there are two components to the change in position per frame and that thrust changes the deltas the way to apply thrust is;
// deltaV could be a constant but I like to use mass so when I add stuff
// or upgrade rockets it has a better feel.
float deltaV = this.mass / this.thrust;
this.deltaPos.x += Math.sin(this.direction) * deltaV;
this.deltaPos.y += Math.cos(this.direction) * deltaV;
As the thrust delta is added to the position deltas the result is acceleration in the direction the ship is pointing.
Each frame you then update the position by the delta pos.
this.position.x += this.deltaPos.x;
this.position.y += this.deltaPos.y;
You may want to add some drag to slow the ship over time. You can add a simple drag coefficient
rocket.drag = 0.99; // 1 no drag 0 100% drag as soon as you stop thrust the ship will stop.
To apply the drag
this.deltaPos.x *= this.drag;
this.deltaPos.y *= this.drag;
To get the current velocity, though not needed in the caculations.
this.velocity = Math.sqrt( this.deltaPos.x * this.deltaPos.x + this.deltaPos.y * this.deltaPos.y);
This will produce rocket behaviour that is the same as in the game Asteroids. If you want behaviour that is more like a boat on water, or car (ie the changing direction changes the deltas to match the direction) let me know as it is a simple modification of the above.

Make a rectangle move to point B one pixel at a time in a strait line

I have a rectangle which when I hold down the mouse button I want that rectangle to move to that point following a strait line 1 pixel at a time.
This is my code so far (I put comments in it so you can understand)
float distanceX = finalX - x; //the number of pixels needed to get to destination on the X axis
float distanceY = finalY - y; // same as above but Y axis
float moveX = distanceX > 0 ? 1 : -1; // I only want it to move 1 pixel per render
float moveY = distanceY > 0 ? 1 : -1; // same as above
Array<Stuff> collidedX = new Array<Stuff>(); //saves collisions seperately for x and y
Array<Stuff> collidedY = new Array<Stuff>(); //because I want the square to move where the mouse is pointing even if it means only aligning one axis
for (Stuff s : collidables) {
if (overlapsT(s, x + moveX, y)) {
collidedX.add(s);
}
}
if (collidedX.size < 1) {
if (distanceX != 0)
x += moveX;
}
for (Stuff s : collidables) {
if (overlapsT(s, x, y + moveY)) {
collidedY.add(s);
}
}
if (collidedY.size < 1) {
if (distanceY != 0)
y += moveY;
}
right now the problem is it goes perfectly diagonal until it lines up with one of the axis and then moves up down left or right to the destination.
I don't want to move fractions of pixels. The way my custom physics engine works is each pixel matters, fractional pixels are no good so I am trying to figure out how to smooth the path or rather how to decide when to add 1 to x and then y.
Currently I can't comment, so I have to answer. I think the Bresenham's line algorithm will help you out. It's for drawing rasterize lines.
Bresenham

A fast collision detection between a circle and a sprite in pixel perfect

I've been thinking on some fast and brilliant pixel - perfect collision detection between a circle and any sprite. I need to get 2 points of collision to be able to calculate a normal vector out of them later. I managed to come up with some solution but the more scaling is done in my game, the more inaccurate and unprecise this collision is...It seems as if the code I posted below, was good and correct becouse I have been checking it already a few times and spent a few days reading it again and again... I also checked visually that the collision masks and areas of collision are calculated perfectly fine in the code below so the problem definitely doesn't lay there but in this method.
So I guess that the problem here is the loss of data in floating point arithmetic unless somebody finds a flaw in this method?
If however the problem is really with the float loss of data, what other solution would you recommend to find 2 points of collision between circle and any other sprite in pixel perfect? I really liked my solution becouse it was relatively fast
int xOffset1 = (int)colRectLeft; // left boundary of the collision area for the first sprite
int xOffset2 = (int)colCircleLeft; // left boundary of the collision area for the circle sprite
int yOffset1 = (int)colRectBottom; // bottom boundary of the collision area for the first sprite
int yOffset2 = (int)colCircleBottom; // bottom boundary of the collision area for the circle sprite
int width = (int)(colCircleRight - colCircleLeft); //width of the collision area - same for both sprites
int height = (int)(colCircleTop - colCircleBottom); // height of the collision area same for both sprites
// Pixel-perfect COLLISION DETECTION between circle and a sprite
// my custom vector classes - nothing special
Math2D.Vector_2 colRightPoint = new Math2D.Vector_2(-1, -1); // The right point of collision lying on the circle's circumference
Math2D.Vector_2 colLeftPoint = new Math2D.Vector_2(-1, -1); // the left point of collision lying on the circle's circumference
boolean colRightFound = false;
boolean colLeftFound = false;
// I'm going through y in the circle's area of collision
for (float y = yOffset2; y < yOffset2 + height; y += 1)
{
// from equation: (x-Sx)^2 + (y-Sy)^2 = r^2
// x1/2 = (+-)sqrt(r^2 - (y - Sy)^2) + Sx
//(Sx, Sy) is (circle's radius, circle's radius) becouse I want the points on the circle's circumference to have positive coordinates
float x1 = (float) (Math.sqrt(radius*radius - (y - radius)*(y - radius)) + radius); // the right pixel on the circumference
float x2 = (float) (-x1 + 2*radius); // the left pixel on the circumference
//first I check if the calculated x is inside of the previously calculated area of collision for both circle's area and a sprite's area
if (x1 >= xOffset2 &&
x1 <= xOffset2 + width &&
xOffset1 + x1 - xOffset2 < rectFrameW &&
yOffset1 + (int)y-yOffset2 < rectFrameH &&
yOffset1 + (int)y-yOffset2 > 0 &&
xOffset1 + x1 - xOffset2 > 0)
{
//I don't have to check if the point on the circle's circumference is opaque becouse it's always so just check if the same point translated to sprite's area of collision is opaque
boolean opaqueRectPixel = go.gameData.images.get(go.pic_nr)
.collision_mask[(int)((yOffset1 + (int)y-yOffset2)*rectFrameW +
(xOffset1 + x1 - xOffset2))];
if(opaqueRectPixel)
{
if(!colRightFound)
{
colRightPoint.x = (xOffset1 + x1 - xOffset2);
colRightPoint.y = (yOffset1 + (int)y - yOffset2);
colRightFound = true;
}
else if(!colLeftFound)
{
colLeftPoint.x = (xOffset1 + x1 - xOffset2);
colLeftPoint.y = (yOffset1 + (int)y - yOffset2);
}
}
}
//the same logic for the left point on the circle's circumference
if (x2 >= xOffset2 &&
x2 <= xOffset2 + width &&
xOffset1 + x2 - xOffset2 < rectFrameW &&
yOffset1 + (int)y-yOffset2 < rectFrameH &&
yOffset1 + (int)y-yOffset2 > 0 &&
xOffset1 + x2 - xOffset2 > 0)
{
boolean opaqueRectPixel = go.gameData.images.get(go.pic_nr)
.collision_mask[(int)((yOffset1 + (int)y-yOffset2)*rectFrameW +
(xOffset1 + x2 - xOffset2))];
if(opaqueRectPixel)
{
if(!colLeftFound)
{
colLeftPoint.x = (xOffset1 + x2 - xOffset2);
colLeftPoint.y = (yOffset1 + (int)y - yOffset2);
colLeftFound = true;
}
else if(!colRightFound)
{
colRightPoint.x = (xOffset1 + x2 - xOffset2);
colRightPoint.y = (yOffset1 + (int)y - yOffset2);
}
}
}
// if both points are already found, finish
if(colLeftFound && colRightFound)
break;
}
edit: Actually, what I'm doing in this method is finding points of intersection between circle and a sprite
edit: Ok, I'm uploading images to describe my algorithm a bit better. I really tried my best to explain it but if there's still something missing, let me know please!
Also I would accept any other good solutions to find intersection points between a circle and any sprite in pixel perfect, if you don't want to check my code :(... Eh, I'm always having problems with collisions...
If you absolutely want (or need) pixel perfect, your solution looks good.
don't forget to first make a rectangle-to-rectangle collision before testing a pixel perfect detection, to avoid unneeded processings.
If you want another accurate method which maybe more efficient, look for Separating Axis Theorem.
You can find more information about it here :
http://rocketmandevelopment.com/blog/separation-of-axis-theorem-for-collision-detection/
and here :
http://www.metanetsoftware.com/technique/tutorialA.html
The last one have nice interactive explanation and demonstration. Enjoy :)
...as I was not able to show the raster in the comments:
I did not mentally parse your code, however from the image I see that you try to detect borderline collisions. Putting round or diagonal (border)lines into a raster may cause occasions, where two crossing lines do not overlay each other - like this:
1 2
2 1
whereby 1 would be line 1 and 2 would be line 2.
However I still like the idea of checking border lines combined with rectangle pre-checks. If you would render an array of raster proved-closed line coordinates by sprites you could check them against each other. This could also be enriched by border line segmenting (such as North, East, West and South or a bit more fine grain - I guess there is an optimum). A diagonal proved-closed line in the check data set must represent something like this:
x _
x x
whereby the x represent the pixels of your line and the _ is an empty raster seat.

Java - How to check of a point is inside a sliceof a circle

I have a circle drawn, and I want to make it so I can have more slices than four. I can easily do four quadrants because I just check if the mouse in in the circle and inside a box.
This is how I am checking if the point is in the circle.
if( Math.sqrt((xx-x)*(xx-x) + (yy-y)*(yy-y)) <= radius)
{
return true;
}
else
{
return false;
}
How can I modify this if the circle is divided into more than 4 regions?
For radial slices (circular sectors), you have a couple of alternatives:
Use Math.atan2 to calculate the 4-quadrant angle of the line from the circle center to the point. Compare to the slice angles to determine the slice index.
For a particular slice, you can test which side of each slice edge the point falls. Classify the point accordingly. This is more complicated to calculate but probably faster (for a single slice) than calling Math.atan2.
The following sample code calculates the slice index for a particular point:
int sliceIndex(double xx, double yy, double x, double y, int nSlices) {
double angle = Math.atan2(yy - y, xx - x);
double sliceAngle = 2.0 * Math.PI / nSlices;
return (int) (angle / sliceAngle);
}
The above code makes the following assumptions:
slices are all the same (angular) width
slices are indexed counter-clockwise
slice 0 starts at the +x axis
slices own their right edge but not their left edge
You can adjust the calculations if these assumptions do not apply. (For instance, you can subtract the start angle from angle to eliminate assumption 3.)
First we can check that the point is within the circle as you did. But I woudln't combine this with a check for which quadrant (is that why you have radius/2 ?)
if( (xx-x)*(xx-x) + (yy-y)*(yy-y) > radius*radius)
return false;
Now we can look to see which region the point is in by using the atan2 function. atan2 is like Arctan except the Arctangent function always returns a value between -pi/2 and pi/2 (-90 and +90 degrees). We need the actual angle in polar coordinate fashion. Now assuming that (x,y) is the center of your circle and we are interested in the location of the point (xx,yy) we have
double theta = Math.atan2(yy-y,xx-x);
//theta is now in the range -Math.PI to Math.PI
if(theta<0)
theta = Math.PI - theta;
//Now theta is in the range [0, 2*pi]
//Use this value to determine which slice of the circle the point resides in.
//For example:
int numSlices = 8;
int whichSlice = 0;
double sliceSize = Math.PI*2 / numSlices;
double sliceStart;
for(int i=1; i<=numSlices; i++) {
sliceStart = i*sliceSize;
if(theta < sliceStart) {
whichSlice = i;
break;
}
}
//whichSlice should now be a number from 1 to 8 representing which part of the circle
// the point is in, where the slices are numbered 1 to numSlices starting with
// the right middle (positive x-axis if the center is (0,0).
It is more a trig problem Try something like this.
int numberOfSlices=8;
double angleInDegrees=(Math.toDegrees(Math.atan2(xx-x ,yy-y)));
long slice= Math.round(( numberOfSlices*angleInDegrees )/360 );

Player going toward top left corner

I have this code in my game loop:
new Thread(new Runnable() {
#Override
public void run() {
while (true) {
if (aPress) {
rotate -= rotSpd;
rotate = rotate < 0 ? 360 + rotate : rotate;
}
if (dPress) {
rotate += rotSpd;
rotate = rotate > 360 ? rotate - 360 : rotate;
}
if (wPress) {
x += (rotate < 180 ? speed : -speed) * Math.abs(Math.sin(Math.toRadians(rotate)));
y += (rotate < 270 ? (rotate < 90 ? -speed : speed) : -speed) * Math.abs(Math.cos(Math.toRadians(rotate)));
}
if (sPress) {
x -= (rotate < 180 ? speed : -speed) * Math.abs(Math.sin(Math.toRadians(rotate)));
y -= (rotate < 270 ? (rotate < 90 ? -speed : speed) : -speed) * Math.abs(Math.cos(Math.toRadians(rotate)));
}
repaint();
try {
Thread.sleep(20);
} catch (InterruptedException annoyingUncheckedException) {}
}
}
}).start();
It does what it should do: when you press A, it turns counterclockwise. When you press D, it turns clockwise. When you press W, it goes forward, and when you press S, it goes backwards. However, if I hold W and D, at first it goes in a circle like it should, but it slowly starts going in the direction of the top left corner. How can I fix this?
First of all, I would make use of the modulo operator, for a better understanding of the code overall (and perhaps it is also less error-prone).
if (aPress) {
rotate = (rotate - rotSpd + 360) % 360;
}
if (dPress) {
rotate = (rotate + rotSpd) % 360;
}
if (wPress) {
x += Math.cos(Math.toRadians(rotate));
y += Math.sin(Math.toRadians(rotate));
}
if (sPress) {
x -= Math.cos(Math.toRadians(rotate));
y -= Math.sin(Math.toRadians(rotate));
}
Concerning your issue, I made a quick jsFiddle to see if I could reproduce the problem but I cannot, so I guess this is related to the way key press (down/up/hold) events are handled. Perhaps you can try to log the set of pressed keys on each iteration and try to see If there is some kind of inconsistency.
NB : W/A/S/D keys have been replaced by UP/DOWN/LEFT/RIGHT in my jsFiddle to minimize keyboard layout issues. I also refactored the code of the loop to reflect my logic and the SJuan76 answer.
This is really a comment, but for a better formatting I will use an answer.
I find this code (and the rest like it) a little odd:
x += (rotate < 180 ? speed : -speed) * Math.abs(Math.sin(Math.toRadians(rotate)));
The x (horizontal value) should depend of the cosinus, not of the sinus. Also, it is strange that you have to change the sign of the speed and then make the value of sin absolute.
Wouldn't it be better?
x+= speed * Math.cos(Math.toRadians(rotate))
Of course you might want to use a 0 heading that points vertically, but even that I find easier
x+= speed * Math.cos(Math.toRadians(rotate) + Math.PI/4)
You could dictate the behavior you want by doing something like this right?
if(wPress && dPress)
{
//code for both being hit
}
My guess is that your current issue is caused by the fact that the speeds are not equal for a w and d press resulting in a non circular movement path.

Categories

Resources