aspose file tools*
The moose likes Applets and the fly likes Animation Applet Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Java » Applets
Bookmark "Animation Applet" Watch "Animation Applet" New topic
Author

Animation Applet

Matthew Gaunt
Greenhorn

Joined: Mar 12, 2001
Posts: 12
Am trying to make my animated object chase after the mouse pointer in my applet. The applet implements the MouseMotionListener, and when the mouseMoved() method is invoked, then another method is called which controls how much the animated image should move on its way to the pointer's position. This is done in a while loop and the call repaint() made on each iteration. But for some reason, the animated object is not repainted until the final iteration.
If I call this same method from the run() method (as it is in an applet), the animation works perfectly. Does anyone have any ideas.
Best Regards
Matt Gaunt
Frank Carver
Sheriff

Joined: Jan 07, 1999
Posts: 6920
This is not a servlets question, so I've moved it to the Applets forum.


Read about me at frankcarver.me ~ Raspberry Alpha Omega ~ Frank's Punchbarrel Blog
Rahul Rathore
Ranch Hand

Joined: Sep 30, 2000
Posts: 324
Matthew, can you be more specific? What exactly is your code? What are the methods you have implemented and how? What do you mean by calling from run() method? Have you created another thread?
Matthew Gaunt
Greenhorn

Joined: Mar 12, 2001
Posts: 12
I have created a thread to run the animated object (ie: Thread t=new Thread(this) ). As usual, the thread is started using thread.start(). Implicitly invoking the run() method which is required in the code as I am implementing the Runnable interface.
Now, this applet object also implements the MouseMotionListener. What I want to happen is:
when the mouse pointer positions itself over the applet, the mouseMoved() method will call another method (lets call it imageMove() ) which will cause the applet to repaint() through a loop to make it appear as though the image is moving towards the pointer. Sounds simple enough. But for some reason, when the imageMove() method is called from the mouseMoved() method, it does everything in the loop except the actual repaint() of the image. Yet, if I call the imageMove() method from the run() method, it works fine.
Hope this sounds a little clearer.
Best Regards
Matt
Nathan Pruett
Bartender

Joined: Oct 18, 2000
Posts: 4121

Matthew,
From what you have described, it sounds like what you need to do is this :
Since you are implementing Runnable, have your thread call imageMove(), repaint(), then Thread.sleep() with the amount of time you want your thread to sleep inside a loop. Inside mouseMoved() you will save the mouse coordinates, and then in imageMove() you will move the image toward the coordinates you have saved. The image may move a little jerkily if you move the mouse really fast, or if the thread sleeps a long time, but it should work ok...
HTH,
-Nate


-Nate
Write once, run anywhere, because there's nowhere to hide! - /. A.C.
Matthew Gaunt
Greenhorn

Joined: Mar 12, 2001
Posts: 12
Thanks Nate,
That is pretty much what I have now. At the moment, after the thread is started, if I call the imageMove() method from the run() method, then the imageMove() method works perfectly. Repainting the image as required. But, when I call the same imageMove() method from the mouseMoved() method (or any of the other mouse listener methods), it performs the whole imageMove() method EXCEPT for the repaint() calls. For some reason, it doesn't do any of them, other than the last one.
Very frustrating.
Any suggestions?!
Best Regards
Matt Gaunt
Nathan Pruett
Bartender

Joined: Oct 18, 2000
Posts: 4121

Matt,
Do you have the calls to repaint() inside the imageMove() method? Are you only calling repaint() in the run() method? It would really help if you posted source code...
From what you've said it sounds like a thread scheduling problem... the run() method executes in it's own thread, whereas I think the mouse listeners execute in the main thread... I think repaint() excutes in a seperate thread too... though I am not sure... if the thread sleeps between calls it may give repaint() time to catch up each time... whereas in the mouse listener, it may try to repaint() before the last call to repaint() has made it to the screen...
HTH,
-Nate
Rahul Rathore
Ranch Hand

Joined: Sep 30, 2000
Posts: 324
Matthew maybe I have cracked it. But without seeing your source code I cannot be sure.
Note carefully:-
1. All AWT events are added to an Event Queue and handled in a SINGLE Event Dispatch Thread.
2. Both the mouse events and paint events are AWT events and hence both are processed in the SAME Event Dispatch thread.
3.repaint() does NOT directly call paint(). Rather it schedules calls to paint() by causing paint events to be added to Event Queue.

Now the reason you are facing problems in the single-threaded version is this:-
Your mouse move event is picked up by the Event Dispatch thread for processing. mouseMove() calls imageMove() in the SAME Event Dispatch thread. In imageMove() the repaint calls ultimately causes paint events to be added to the Event queue- but these paint events CANNOT be processed by the Event Dispatch thread because that thread is still processing the imageMove() method. So the Event dispatch thread becomes free to process your paint event only AFTER it has returned from the imageMove() and mouseMove() methods. Thus when the Event dispatch thread processes the paint event imageMove() has already completed execution.
Another fact is that when the multiple paint events generated in the imageMove() method are being added to the Event queue, they all coalesce so that only the last paint event survives. So when the Event dispatch thread gets the chance to execute the paint event (AFTER returning from imageMove(), mouseMoved()), only the last paint will take effect.
Also complicating the situation is the fact that along with multiple paint events, multiple mouse move events are being continously generated and added to the event queue.
The only way to rectify the situation is to define your own private queue. The mouseMove() should simply add to your queue and return immediately so that the Event Dispatch thread becomes free to process subsequent events (like paint events). Your queue should be processed in a different thread, which will call imageMove(). Of course access to your queue will have to be synchronized.

[This message has been edited by Rahul Rathore (edited March 12, 2001).]
Matthew Gaunt
Greenhorn

Joined: Mar 12, 2001
Posts: 12
Thank you Rahul,
It sounds pretty much right to me, what you have said. I have posted my code as requested by Nate. I think this will just verify what Rahul has explained.
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
/**
* Insert the type's description here.
* Creation date: (09/03/01 16:11:33)
* @author:
*/
public class Neko extends Applet implements MouseListener, MouseMotionListener, Runnable {
Thread thread;
public java.awt.Image[] catPics = new java.awt.Image[9];
private java.lang.String[] cat = {"awake.gif","right1.gif","right2.gif","scratch1.gif","scratch2.gif","sleep1.gif","sleep2.gif","stop.gif","yawn.gif"};
public java.awt.Image currentImage;
private int x = 0;
public int endX;
public int currentX = 0;
private int y = 20;
public int currentY = 20;
/**
* Initializes the applet.
*/
public void init() {
addMouseListener(this);
addMouseMotionListener(this);
for (int i=0; i<catPics.length;i++)
catPics[i] = getImage(getDocumentBase(),"images/"+cat[i]);
currentImage=catPics[0];
}
public void mouseMoved(MouseEvent e) {
endX=e.getX();
System.out.println("Am at horizontal position "+endX+" and current position is "+currentX);
if (endX>currentX){
System.out.println("Am about to run to the right");
runRight(endX);
}
else{
System.out.println("Am about to run to the left");
runLeft(endX);
}
}
public void paint(Graphics g) {
System.out.println("Am in the paint method now");
if (currentImage!=null){
System.out.println(currentX+"\t"+currentY);
g.drawImage(currentImage,currentX,currentY,this);
}
// insert code to paint the applet here
}
/**
* Insert the method's description here.
* Creation date: (12/03/01 09:17:10)
* @param time long
*/
public void pause(long time) {
try{
Thread.sleep(time);
}
catch (InterruptedException e){}
}
/**
* Insert the method's description here.
* Creation date: (12/03/01 14:02:20)
* @param numZs int
*/
public void rest(int numZs) {
currentImage=catPics[8];
repaint();
pause(400);
for (int i=0; i<numZs; i++){
if (currentImage!=catPics[5])
currentImage=catPics[5];
else
currentImage=catPics[6];
repaint();
pause(800);
}
currentImage=catPics[0];
repaint();
pause(600);
}
/**
* Contains the thread execution loop.
*/
public void run() {
repaint();
double x=getSize().width;
runRight((int)x/2);
rest(4);
scratch(6);
runLeft(0);
}
/**
* Insert the method's description here.
* Creation date: (12/03/01 12:43:00)
* @param endPos int
*/
public void runLeft(int endPos) {
System.out.println("Am about to run from "+currentX+" to "+endPos);
while (currentX>endPos){
if (currentImage!=catPics[2]){
currentImage=catPics[2];
repaint();}
else{
currentImage=catPics[1];
repaint();}
currentX-=5;
pause(200);
}
currentX=endPos;
currentImage=catPics[7];
repaint();
pause(400);
}
/**
* Insert the method's description here.
* Creation date: (09/03/01 16:54:33)
*/
public void runRight(int endPos) {
System.out.println("Am about to run from "+currentX+" to "+endPos);
while (currentX<endPos){
if (currentImage!=catPics[2])
currentImage=catPics[2];
else
currentImage=catPics[1];
currentX+=5;
System.out.println("currentX = "+currentX);
if (currentX>=endPos) System.out.println("last iteration");
repaint();
pause(250);
}
currentX=endPos;
currentImage=catPics[7];
repaint();
pause(700);
}
/**
* Insert the method's description here.
* Creation date: (12/03/01 14:08:03)
* @param numScratch int
*/
public void scratch(int numScratch) {
for (int i=0; i<numScratch; ++i){
if (currentImage!=catPics[3])
currentImage=catPics[3];
else
currentImage=catPics[4];
repaint();
pause(350);
}
currentImage=catPics[7];
repaint();
pause(600);
}
/**
* Starts up the thread.
*/
public void start() {
if (thread == null){
thread = new Thread(this);
thread.start();
}
}
/**
* Terminates the thread and leaves it for garbage collection.
*/
public void stop() {
if (thread != null){
thread.stop();
thread = null;
}
}
}

Best Regards
Matt Gaunt
Matthew Gaunt
Greenhorn

Joined: Mar 12, 2001
Posts: 12
Thanks Rahul,
Have sorted it by creating a new Thread in the mouseMove() method and then calling the imageMove() method. Seems to be working better than before.
Then sychronized the code inside the mouseMove() method, so as the imageMove() method is only being called once at any time (since imangeMove() is only called from mouseMove()), so all seems to be fine now.
Thank you so much for your help.
Best Regards
Matt Gaunt
Nathan Pruett
Bartender

Joined: Oct 18, 2000
Posts: 4121

Matt,
I have basically hacked up your code, but really all that I changed to get it to run correctly was :
1.) Made the runRight() and runLeft() methods only move once toward the endX position.
2.) Moved almost all the code out of the mouseMoved() method and put all of it into the run() method.
3.) Changed the run() method alot... all that your run() method usually needs to do is keep calling repaint() and sleep()... all the other logic in your program should be just involved in updating currentX and endX and should not bother itself with repaint().
4.) I messed around with some other parts too... mainly adding a MediaTracker to ensure images are loaded. I made the little animation at the first uninterruptable... I may have messed around with images somewhat because I had to create my own ( MS Paint all the way bay-bee!! Man they looked like crap... )
Anyway, the code works, but be sure to back up your old version before completly overwriting it...

HTH,
-Nate
[This message has been edited by Nathan Pruett (edited March 13, 2001).]
Rahul Rathore
Ranch Hand

Joined: Sep 30, 2000
Posts: 324
Hi Matthew
You're welcome.
I am not clear about how exactly how you have rectified. But there may still be problems.
If I am understanding you right, you are creating a new thread in every call to mouseMove(), and then synchronizing access to your imageMove() methods.
The problem here may be that :-
1. Multiple threads will be unnecessarily created drawing system resources and impairing performance.
2. When multiple threads are seeking locks, there is no guarantee which particular thread will acquire the next lock for executing imageMove() methods. Lock/monitor is granted to seeking threads in an arbitrary order, and not on first-come first-served basis. So a thread caused by a later mouse move event may get the lock earlier than a thread from an earlier mouse move event- which may make a nonsense of the animation. The cat movement may not correspond to the mouse movement, especially when there is a lot of mouse movement.
I totally agree with Nate's approach. Its clean, simple and efficient.
As Nate shows only 1 animation thread is required in the program. The mouseMove() method should simply update the member variable (endX). The animation thread (run() method) will use endX and call all imageMove() methods containing repaints.

[This message has been edited by Rahul Rathore (edited March 14, 2001).]
Matthew Gaunt
Greenhorn

Joined: Mar 12, 2001
Posts: 12
So simple,
Yet I was making it so difficult. Thanks Nate for showing me the light. I didn't even consider using a while loop in the run() method. But looking at it, it makes a hell of a lot more sense than creating new threads.
And Rahul, you were right. I did start having Thread execution problems, as in executing in the right order.
Thank you both for all your help. You have both taught me heaps.
Best Regards
Matt Gaunt
Patrick Lentz
Greenhorn

Joined: Oct 04, 2000
Posts: 23
Hey guys,
I'm curious to see this one in action...Would it be too much to ask to have you send me the gif's to my emailaddress which is ing_patrick@hotmail.com ?
I'd appreciate it )
Patrick
Nathan Pruett
Bartender

Joined: Oct 18, 2000
Posts: 4121

Ok, patrick... You asked for it! I'm sending you the gifs I used to test it out... they were hacked together with MS Paint and converted to GIFs so they look like crap...
Hope you like 'em
-Nate
P.S. - Matthew, could you send me the original GIFs so I can see what the applet is really supposed to look like? Thanks in advance!
Patrick Lentz
Greenhorn

Joined: Oct 04, 2000
Posts: 23
Thanx for the gifs guys. I now tried to run the code, but the code didn't work correctly on my Windows 2000 machine. Without moving the mouse at all the DOS box showed a lot of activity. The x position was shown as going from 20 to 200 and once there it stayed there. No images were shown on the screen either.
Did anyone else try this code? If anyone has the code working I would appreciate the codefile.
My emailaddress is ing_patrick@hotmail.com
Appreciate it
Patrick
Rahul Rathore
Ranch Hand

Joined: Sep 30, 2000
Posts: 324
Patrick
Note this statement in Nate's code:-
catPics[i] = getImage(getCodeBase(),"images/"+cat[i]);
In which directory have you placed the .gif files? The url in getImage() must point to the directory where you have placed the .gif files. Otherwise the catPics array will contain only nulls- hence no images will be painted.
For the above code you should place the image in an images subdirectory under the directory containing your .class file.

[This message has been edited by Rahul Rathore (edited March 15, 2001).]
 
 
subject: Animation Applet