I'm trying to move an object on the click of a mouse while the object remains animated. There are a few similar posts on this website, and I've based my code off this answer:
An efficient algorithm to move ostrichs along a line at a constant speed
But I want to use a thread to keep the object animated. How should I do this? Here's my code:
public void movePlayer(Graphics g, int finalX, int finalY)
{
int length = finalX - xpos;
int height = finalY - ypos;
int oldXpos = xpos;
int oldYpos = ypos;
double speed = 20;
double distanceX = (length)/speed;
double distanceY = (height)/speed;
double distance = (Math.hypot(length,height));
double distanceTraveled = 0;
//This currently doesn't work:
move = new Thread(this);
{
while (distanceTraveled<distance)
{
//move the object by increments
xpos += distanceX;
ypos += distanceY;
distanceTraveled = Math.hypot(xpos-oldXpos, ypos - oldYpos);
drawPlayer(img, g);
for(int x = 0; x < 100000; x ++);
}
}
}
If this is Swing, why not simply use a MouseListener to help you drag the object? If you want to animate separate from the mouse, don't use a while(true) loop unless you want to freeze the event thread. Use a Swing Timer instead. If this isn't Swing, tell us more details (shoot, do this anyway)!
Related
Let me describe to you the context of my problem before I outline the issue. I'm currently in the middle of writing a game engine level editor and I'm working on the class that is going to act as the screen that the user interacts with in order to build their levels. I want to make it so the screen is proportional to the size of the editor.
The issue in question occurs when I begin resizing my screen and drawing on it at the same time. I draw from one thread and at the same time I'm editing the size of the raw pixel array that I'm drawing onto, from another thread (the EDT). I know this is a big no-no so naturally, with no safety in place, I get the occasional IndexOutOfBounds Exception on a resize.
My thought was, I could add a synchronize block on both the resizing code and the drawing code. This way, there would be no conflict and the issue should be avoided. However, the synchronization is being ignored completely. I'm still getting the same error and I'm really quite confused on why it isn't working. Below are the two methods of interest:
public void setPixel(int r, int g, int b, int x, int y) {
synchronized (pixels){
System.out.println("Start Draw...");
int color = (r << 16) | (g << 8) | b;
pixels[y * screenWidth + x] = color;
System.out.println("End Draw...");
}
}
#Override
public void componentResized(ComponentEvent e) {
synchronized (pixels) {
System.out.println("Start resize");
int width = e.getComponent().getWidth();
int height = e.getComponent().getHeight();
float aspectRatio = 4 / 3f;
if (width > height) {
width = (int) (height * aspectRatio);
} else if (height > width) {
height = width;
}
if (width < 0 || height < 0) {
width = 1;
height = 1;
}
this.screenWidth = width;
this.screenHeight = height;
image = new BufferedImage(screenWidth, screenHeight, BufferedImage.TYPE_INT_RGB);
pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
System.out.println("End Resize");
}
}
I don't know if it matters (it shouldn't right?) but my screen class extends a AWT Canvas. Also it is a listener on its parent component, so when that gets resized, it fires an event that triggers componentResized to be called. Anyway, thank you, any help is appreciated.
EDIT: My drawing code can be found below.
new Thread(new Runnable(){
#Override
public void run() {
while(true) {
for (int y = 0; y < screen.getHeight(); y++) {
for(int x = 0; x < screen.getWidth(); x++){
int r = (int) (Math.random() * 255);
int g = (int) (Math.random() * 255);
int b = (int) (Math.random() * 255);
screen.setPixel(r, g, b, x, y);
}
}
}
}
}).start();
There is something else I can think of. Not only what is in the comments of the question.
Let me modify first the setPixel method:
public void setPixel(int r, int g, int b, int x, int y) {
System.out.println("Called with: " + x + ", " + y); //Just added a print statement.
synchronized (mySyncGuard){
System.out.println("Start Draw...");
int color = (r << 16) | (g << 8) | b;
pixels[y * screenWidth + x] = color;
System.out.println("End Draw...");
}
}
Where mySyncGuard is a final property used as the synchronization guard to both setPixel and componentResized.
And now imagine the following scenario:
There is a loop which calls the setPixel method: this loop calls the method starting from x = 0 and y = 0 up to x < screenWidth and y < screenHeight! Like the following one:
for (int x = 0; x < screenWidth; ++x)
for (int y = 0; y < screenHeight; ++y) {
int r = calculateNextR(),
g = calculateNextG(),
b = calculateNextB();
setPixel(r, g, b, x, y);
}
Where calculateNextR(), calculateNextG() and calculateNextB() are methods that produce the next red, green and blue color components respectively.
Now, for example, let screenWidth be 200 and screenHeight be also 200 and at some point resized to 100x100.
Now the problem is that x and y would be lets say 150 and 150 respectively while the component was about to be resized to 100, 100.
Our custom setPixel method is now called with x==150 and y==150. But before the printing statement we added which shows the x and the y values, the componentResized manages to be called and take the lock of the synchronization guard property mySyncGuard! So componentResized is now doing its job changing the size of the image to 100x100, which, in turn, changes the pixels array to something smaller than the previous data array.
In parallel, setPixel now prints "Called with 150, 150" and waits at the synchronized block to obtain the lock of mySyncGuard, because componentResized has already currently obtained it and changing the image to 100x100 and the pixels array accordingly.
So now the pixels array is smaller, the componentResized finishes resizing and finally setPixel can obtain the lock of mySyncGuard.
And now is the problem: the array pixels is traversed at location 150,150 while it actually has size 100x100. So there you go! IndexOutOfBoundsException...
Conclusion:
We need more of your code to determine what's wrong.
The variables changing (such as screenWidth, screenHeight, image and pixels) need to be synchronized everywhere, not only inside the setPixel and componentResized methods. For example, if your case is like the scenario I just described, then unfortunately the for loops should also be in a synchronized (mySyncGuard) block.
This wouldn't fit in a comment. If you post some more code, then we can probably tell you what went wrong (and I may delete this answer if it is not needed).
I'm trying to move an object (rectangle for our purposes), from point A to D. However, along the way I want to stop the motion at different segments and wait some time.
For example, I want to move from A to B, wait 5 seconds, then move from B to C, wait 2 seconds. Finally, move from C to D.
I've tried a couple of things. First I moved my object by giving it a "speed" (xspeed) and increasing its position (xpos) by xspeed. Then using an if statement I stopped the position when it reaches 100. I then count 5 seconds and begin motion again. However, since the motion starts past the x-position 100, the if-statement does not allow me to move forward. I don't know if I can override this if statement. Below is the code of what I did:
class Pipe {
color c;
float xpos;
float ypos;
float xspeed;
int time;
Pipe(color c_, float xpos_, float ypos_, float xspeed_) {
c = c_;
xpos = xpos_;
ypos = ypos_;
xspeed = xspeed_;
}
void display() {
noStroke();
fill(c);
rectMode(CENTER);
rect(xpos,ypos,10,200);
}
void pipeMove() {
xpos = xpos + xspeed;
if (xpos > 100) {
xpos = 100;
if (millis() > time) {
time = millis()+5000;
xpos = xpos + xspeed;
}
}
}
}
Pipe myPipe1;
Pipe myPipe2;
void setup() {
size(1500,500);
myPipe1 = new Pipe(color(85,85,85),0,height/2,2);
// myPipe2 = new Pipe(color(85,85,85),-100,height/2,2);
}
void draw() {
background(255);
myPipe1.display();
myPipe1.pipeMove();
// myPipe2.display();
// myPipe2.pipeMove();
}
The other option I tried was stopping the automatic looping in Processing by using noLoop(), and looping the position within my class. However, this does not move my object. See code below of my for-loop.
class Pipe {
color c;
float xpos;
float ypos;
float xspeed;
int time;
Pipe(color c_, float xpos_, float ypos_, float xspeed_) {
c = c_;
xpos = xpos_;
ypos = ypos_;
xspeed = xspeed_;
}
void display() {
noStroke();
fill(c);
rectMode(CENTER);
rect(xpos,ypos,10,200);
}
void pipeMove() {
for (int i = 0; i<100; i = i+1) {
xpos = xpos + i;
}
}
}
I think you're on the right track with your first approach. You've got the first part working, now you just need to add in the other steps. One way to solve this might be to use states in your Pipe class.
By that I mean, you just have to keep track of which step the pipe is currently on, and then do the right thing depending on which step you're on. The simplest way to do this might be to add booleans into your Pipe class:
class Pipe {
color c;
float xpos;
float ypos;
float xspeed;
int time;
boolean movingToA = true;
boolean waitingAtA = false;
boolean movingToB = false;
//...
And then in your pipeMove() function, just do the right thing depending on which state you're in, and change the state to change the behavior:
void pipeMove() {
if (movingToA) {
xpos = xpos + xspeed;
if (xpos > 100) {
xpos = 100;
movingToA = false;
waitingAtA = true;
time = millis();
}
} else if (waitingAtA) {
if (millis() > time + 5000) {
waitingAtA = false;
movingToB = true;
}
} else if (movingToB) {
xpos = xpos + xspeed;
if (xpos > 200) {
xpos = 200;
movingToB = false;
}
}
}
}
There are some pretty obvious improvements to be made here- you could use enum values, or a data structure of potential actions, or parameterize the behavior, for example. But the basics are the same: perform different actions depending on which state you're in, and then change that state to change the behavior.
I had a quick question, and wondered if anyone had any ideas or libraries I could use for this. I am making a java game, and need to make 2d images concave. The problem is, 1: I don't know how to make an image concave. 2: I need the concave effect to be somewhat of a post process, think Oculus Rift. Everything is normal, but the camera of the player distorts the normal 2d images to look 3d. I am a Sophmore, so I don't know very complex math to accomplish this.
Thanks,
-Blue
If you're not using any 3D libraries or anything like that, just implement it as a simple 2D distortion. It doesn't have to be 100% mathematically correct as long as it looks OK. You can create a couple of arrays to store the distorted texture co-ordinates for your bitmap, which means you can pre-calculate the distortion once (which will be slow but only happens once) and then render multiple times using the pre-calculated values (which will be faster).
Here's a simple function using a power formula to generate a distortion field. There's nothing 3D about it, it just sucks in the center of the image to give a concave look:
int distortionU[][];
int distortionV[][];
public void computeDistortion(int width, int height)
{
// this will be really slow but you only have to call it once:
int halfWidth = width / 2;
int halfHeight = height / 2;
// work out the distance from the center in the corners:
double maxDistance = Math.sqrt((double)((halfWidth * halfWidth) + (halfHeight * halfHeight)));
// allocate arrays to store the distorted co-ordinates:
distortionU = new int[width][height];
distortionV = new int[width][height];
for(int y = 0; y < height; y++)
{
for(int x = 0; x < width; x++)
{
// work out the distortion at this pixel:
// find distance from the center:
int xDiff = x - halfWidth;
int yDiff = y - halfHeight;
double distance = Math.sqrt((double)((xDiff * xDiff) + (yDiff * yDiff)));
// distort the distance using a power function
double invDistance = 1.0 - (distance / maxDistance);
double distortedDistance = (1.0 - Math.pow(invDistance, 1.7)) * maxDistance;
distortedDistance *= 0.7; // zoom in a little bit to avoid gaps at the edges
// work out how much to multiply xDiff and yDiff by:
double distortionFactor = distortedDistance / distance;
xDiff = (int)((double)xDiff * distortionFactor);
yDiff = (int)((double)yDiff * distortionFactor);
// save the distorted co-ordinates
distortionU[x][y] = halfWidth + xDiff;
distortionV[x][y] = halfHeight + yDiff;
// clamp
if(distortionU[x][y] < 0)
distortionU[x][y] = 0;
if(distortionU[x][y] >= width)
distortionU[x][y] = width - 1;
if(distortionV[x][y] < 0)
distortionV[x][y] = 0;
if(distortionV[x][y] >= height)
distortionV[x][y] = height - 1;
}
}
}
Call it once passing the size of the bitmap that you want to distort. You can play around with the values or use a totally different formula to get the effect you want. Using an exponent less than one for the pow() function should give the image a convex look.
Then when you render your bitmap, or copy it to another bitmap, use the values in distortionU and distortionV to distort your bitmap, e.g.:
for(int y = 0; y < height; y++)
{
for(int x = 0; x < width; x++)
{
// int pixelColor = bitmap.getPixel(x, y); // gets undistorted value
int pixelColor = bitmap.getPixel(distortionU[x][y], distortionV[x][y]); // gets distorted value
canvas.drawPixel(x + offsetX, y + offsetY, pixelColor);
}
}
I don't know what your actual function for drawing a pixel to the canvas is called, the above is just pseudo-code.
So, I'm trying to make a game in LWJGL and it seems to work fine for me. Although, I ran into some issues in moving my entities around on the screen. I want to make it go from one point to another, at the same speed. Also, I'm animating my sprite according to the direction the entity is moving.
But! I have some issues:
1# It flickers because the movement is defined a modifier: delta (to make smooth movement defined by the FPS). Actually, it never really reaches it's point (because it recalculates and never hits the position). How can I solve this?
2# When 2 players join the same server, my character on the fastest computer runs faster. I think it's because of the FPS, how can that be solved?
private String name;
private float positionx,positiony; // Current
private int targetx,targety; // Target
private int dx, dy; // Direction
private int pointx, pointy; // Direction
private float speed;
private Sprite sprite;
public Entity(String name, int positionx, int positiony, Sprite sprite){
this.name = name;
this.speed = 0.1f;
this.positionx = 720;
this.positiony = 450;
this.targetx = 1000; // fix this
this.targety = 10; // this for testing.
this.sprite = sprite;
this.dx = 0;
this.dy = 0;
}
//double distance = Math.sqrt((vx * vx) + (vy * vy));
public void move(long delta){
if(positionx < targetx){
dx = 1;
pointx = 1;
}else if(positionx > targetx){
dx = -1;
pointx = -1;
}else{
dx = 0;
}
if(positiony < targety){
dy = 1;
pointy = 1;
}else if(positiony > targety){
dy = -1;
pointy = -1;
}else{
dy = 0;
}
//Set animations:
if(positionx==targetx && positiony==targety){
if(pointx<0){
sprite.setAnimation(5, 2, 100); // Standing left
}else if(pointx>0){
sprite.setAnimation(6, 2, 100); // Standing right
}else if(pointy<0){
sprite.setAnimation(7, 2, 100); // Standing up
}else if(pointy>0){
sprite.setAnimation(4, 2, 100); // Standing down
}
}else{
if(pointx<0){
sprite.setAnimation(1, 2, 100); // Walking left
}else if(pointx>0){
sprite.setAnimation(2, 2, 100); // Walking right
}else if(pointy<0){
sprite.setAnimation(3, 2, 100); // Walking up
}else if(pointy>0){
sprite.setAnimation(0, 2, 100); // Walking down
}
}
//movement here.
positionx += dx*delta*speed;
positiony += dy*delta*speed;
System.out.println(dx*delta*speed);
sprite.setPosition((int)positionx, (int)positiony);
}
1# It flickers because the movement is defined a modifier: delta (to make smooth movement defined by the FPS). Actually, it never really reaches it's point (because it recalculates and never hits the position). How can I solve this?
If you store point A and point B between which it moves, you can set a time interval. Each time interval a set distance will be travelled and if at one iteration the object goes too far you can set its coordinates for point B. This can be easily done with a Timer. That way, after a certain amount of time, it will be on your specified position.
2# When 2 players join the same server, my character on the fastest computer runs faster. I think it's because of the FPS, how can that be solved?
Same answer as question #1, if you use a Timer. Each player will move at the same speed (because the elapsed time is the same for each gamer).
Bottom line:
fps is variable, while elapsed time is the same for everyone.
I was trying to create a 2D Animation in Java of a moving line on Panel(A line moving from one point to another in the Panel). I hope its possible. Here's the code I used.
private void movingline(int length) throws InterruptedException {
for(int i = 0; i + length < width; i++){
for(int j = 0; j + length < height; j++){
eraseline();
drawLine(Color.cyan, i, j, i+length, j+length);
erase = true;
}
}
}
private void eraseline() {
if(erase){
fillCanvas(Color.BLUE);
}
}
On running the code, the Panel doesn't show up.
Here's the code to draw the line.
public void drawLine(Color c, int x1, int y1, int x2, int y2) {
int pix = c.getRGB();
int dx = x2 - x1;
int dy = y2 - y1;
canvas.setRGB(x1, y1, pix);
if (dx != 0) {
float m = (float) dy / (float) dx;
float b = y1 - m*x1;
dx = (x2 > x1) ? 1 : -1;
while (x1 != x2) {
x1 += dx;
y1 = Math.round(m*x1 + b);
canvas.setRGB(x1, y1, pix);
}
}
repaint();
}
On running the code the Panel doesn't show up with the moving line. Any help would be much appreciated.
I think the biggest problem is that you're trying to change the appearance of the GUI from (I'm guessing) a Thread that's not the Event Dispatching Thread.
The solution is to wrap the activity (specifically, the calls to eraseLine and drawLine) in a Runnable and call that Runnable using SwingUtilities.invokeAndWait().
EDIT: Java's graphics components don't really let you manipulate the canvas yourself. Only the components themselves do any drawing, and then only when called on to paint themselves. Directly drawing on the canvas, even if you could get it to work, would work badly because you'd be interfering with what the component does.
Rather than go into a lot more explanation, I've gone and implemented what I think is the "proper" way to do this.
http://pastebin.com/etfmKbjj
The coding's commented where necessary, I hope it gives you some ideas. For more background, read the official tutorials on Swing and Graphics.