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.
Related
I have a "physics ball" that can bounce around the screen off the edges which works fine. But I wanted to be able to add boxes and have my ball be able to bounce of those, too. I have tried to create something and it feels like it is quite close, but there is flaw that I understand why it exists, but am unsure on how I can get around it.
if (colliding(ball, block))
{
if (ball.velocity.x > 0)
{
ball.velocity.x *= -ball.restitution;
ball.vector.x = block.vector.x - ball.radius;
}
else
{
ball.velocity.x *= -ball.restitution;
ball.vector.x = block.vector.x + block.width + ball.radius;
}
if (ball.velocity.y > 0)
{
ball.velocity.y *= -ball.restitution;
ball.vector.y = block.vector.y - ball.radius;
}
else
{
ball.velocity.y *= -ball.restitution;
ball.vector.y = block.vector.y + block.height + ball.radius;
}
}
colliding():
boolean colliding(MassEntity ball, Block block)
{
return PVector.dist(ball.vector, block.vector) < ball.radius
|| PVector.dist(ball.vector, new PVector(block.vector.x + block.width, block.vector.y)) < ball.radius
|| PVector.dist(ball.vector, new PVector(block.vector.x + block.width, block.vector.y + block.height)) < ball.radius
|| PVector.dist(ball.vector, new PVector(block.vector.x, block.vector.y + block.height)) < ball.radius
|| (ball.vector.x - ball.radius < block.vector.x + block.width && ball.vector.x + ball.radius > block.vector.x
&& ball.vector.y - ball.radius < block.vector.y + block.height && ball.vector.y + ball.radius > block.vector.y);
}
(I know the if statement is a little monstrous, but I don't know what happened to the formatting honestly)
The issue is that when the ball collides with the rectangle, since the ball is "teleported" to outside of the rectangle (so it doesn't stay inside the rectangle due to the velocity being flipped), it teleports on both axes so pretty much the ball will weirdly teleport to the end of one of the edges.
I just need to somehow make if statements for the respective axes to only be considered in the appropriate situation.
First, compute P, the nearest point to the ball that is in the box :
PVector P = new PVector(
max(min(ball.vector.x, box.vector.x + box.width / 2), box.vector.x - box.width / 2),
max(min(ball.vector.y, box.vector.y + box.height / 2), box.vector.y - box.height / 2)
);
To check if there is a collision, you can check if the distance between P and the center of the ball is smaller than the radius of the ball.
To update the speed of the ball, you can do this :
PVector n = normalize(ball.vector.copy().sub(P));
ball.velocity.sub(n.mult(2 * n.dot(ball.velocity)));
n is the normal vector a the collision's position and to reflect the speed on the surface you have to delete the component of the velocity that is parallel to it. Then you have to add this same component multiplied by -1. As those two operations are the same, you can just do it one time with a factor 2.
A last precision I have to make is that you may need to check if the ball is going away from the box to avoid reversing the speed in that case :
PVector n = normalize(ball.vector.copy().sub(P));
float f = n.dot(ball.velocity);
if (f < 0)
ball.velocity.sub(n.mult(2 * f));
I haven't tested my code so tell me if there is a problem.
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.
Is there some sort of formula for rotating the arrow in this link right here to make sure it's always pointing toward the red? Each time I've tried, I'd always get a number that's off, and the arrow's rotation is not in sync with the arrow's turning and movement.
A couple of snippets of code to show what I'm working with:
arrow.moveX(true);
arrow.moveY(true);
if(arrow.turning) {
arrow.turn(player.direction / (0.75 * arrow.getSpeed()));
}
The arrow's speed is 12.5 units/time if that's important. As for the movement itself:
public void moveX(boolean turn) {
if(turn) {
x += speed * Math.cos(angle);
} else {
x += speed;
}
}
public void moveY(boolean turn) {
if(turn) {
y += speed * Math.sin(angle);
} else {
y += speed;
}
}
I'm trying to figure out how to render the arrow's sprite make sure that the pointer itself is facing that "forward" direction that it's moving in, no matter how much it rotates. Here is the render method itself if that's necessary:
#Override
public void render(Canvas c, Paint p) {
matrix.setTranslate((float)x, (float)y);
if(alive) {
matrix.postRotate(drawnAngle, (float) (x + width / 2), (float) (y + height / 2));
} else {
matrix.postRotate(angle, (float) (x + width / 2), (float) (y + height / 2));
angle += speed * 2;
}
c.drawBitmap(getSprite(), matrix, p);
}
The variable drawnAngle has a value of 0 right now, it's a placeholder. It was just my attempt of trying to find the right number to rotate the arrow by.
So I've actually spent hours trying to figure this out, and the moment I decide to post for help, I figured it out! It turns out that while I was using radians in the first snippet of code (the actual movement), I was supposed to be using degrees in the actual rotate in that last snippet!
I changed drawnAngle to (float)(angle * (180 / Math.PI)) and this worked as a solution for me!
Hopefully no one else has this problem.
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
I wrote a program to solve the following:
Implement a diffusion limited aggregation simulation on a toroid plane where seeds are randomly created, and particles move randomly. they move if they particles do not land near a seed or a particle. where user inputs seeds (red pixels), particles (black pixels), steps (no or iterations), plane size.
My code is very slow. How can I make it faster?
I randomly created x and y coordinates and drew red pixels (seeds), then randomly created x and y for black pixels (particles), if a black pixel lands where there is a red or black pixel it can stay, otherwise it moves randomly again until there are no more particles . If the pixel lands out of borders like x > border then x=0; if x <1 then x= border. The same for y.
This just means that if it lands on the border I move it to the opposite border. Then checks for the neighboring pixels again. I have an outer loop to create the seeds, and inner loop for the particles. In the inner loop I check for the x,y positions:
//Place, move, and "stick" the particles; stop if either steps or particles = 0
for (int p = 0; p < particles; p++) {
for (int s = 0; s < steps; s++) {
if (xPos > image.getWidth() ) {
do something
}
else if (xPos < 1) {
do something
}
if (yPos > image.getHeight() - 2) {
do something
}
else if (yPos < 1) {
do something
}
else if (xPos > image.getWidth() && yPos > image.getHeight()) {
do something
}
else if (xPos < 1 && yPos < 1) {
do something
}
//If the surrounding pixels' color is not white, make that particle stick.
if (moveValid()) {
image.setRGB(xPos, yPos, 0xFF000000);
}
//Otherwise, move in a random direction
else {
if(xPos == 1 && image.getRGB(size - 2, yPos) != 0xFFFFFFFF){
draw(xPos,yPos);
}
else if(xPos == size - 2 && image.getRGB(1,yPos) != 0xFFFFFFFF){
draw(xPos,yPos);
}
if(yPos == 1 && image.getRGB(xPos, size - 2) != 0xFFFFFFFF){
draw(xPos,yPos);
}
else if(yPos == size - 2 && image.getRGB(xPos,1) != 0xFFFFFFFF){
draw(xPos,yPos);
}
else {
move();
}
}
}
//Regenerate random x and y positions for the next particle
xPos = random.nextInt(size);
yPos = random.nextInt(size);
}
Although the implementation of draw() is not shown, it looks like you're updating a BufferedImage and then rendering it.
The first step is always to profile your existing code, looking for easily implemented optimizations.
The second step is sometimes to set aside the existing code and try a different approach.
You may be able to leverage the Mode-View-Controller pattern, outlined here and discussed here. In particular, let your DLA model evolve on a background thread at full speed, while updating your view at a more sustainable rate. This article suggests several approaches to synchronization and includes a related example that uses javax.swing.Timer to pace the updates.