Step 0: Create a Hello World with Eclipse RCP Application and add a View to it.
Step 1: Add an Image to the view . For this tutorial, an image of the moon with a transparent background called moon.png was prepared and added to the project.
Step 2: Create an inner class that implements Runnable, that updates the contents of the view at a regular interval. Later, during the initialization of the view, this worker thread is started, which calls the update() method of the view every ~15 milliseconds. The speed of the animation can be controlled by the TIMER_INTERVAL variable, which defines how long the thread should sleep before waking up and calling the update() method again.
class AnimatorThread implements Runnable{ // The timer interval in milliseconds private static final int TIMER_INTERVAL = 14; public void go(){ Thread t = new Thread(this); t.start(); } public void run() { try { while(true){ animate(); Thread.sleep(TIMER_INTERVAL); } } catch (InterruptedException e) { e.printStackTrace(); } } }
Step 3: Create the view's contents and start the worker thread. In the createPartControl() method of the view, create a Canvas where the image will be painted. Set the canvas's background color and add a paintListener to it with code defining where the image of the moon should be painted. Finally, new up the worker thread class and start it.
public void createPartControl(Composite parent) { parent.setBackground(new Color(parent.getDisplay(), 205, 38, 38)); // Create the canvas for drawing canvas = new Canvas(parent, SWT.DOUBLE_BUFFERED); canvas.setBackground(new Color(parent.getDisplay(), 0,0,0)); canvas.addPaintListener( new PaintListener() { public void paintControl(PaintEvent e) { GC gc = e.gc; Transform trans = new Transform(e.display ); gc.getTransform( trans ); trans.translate( x, y ); trans.translate( IMAGE_WIDTH / 2f, IMAGE_WIDTH / 2f ); trans.rotate( a ); trans.translate( -IMAGE_WIDTH / 2f, -IMAGE_WIDTH / 2f ); gc.setTransform( trans ); trans.dispose(); gc.drawImage( moon, 0, 0, moon.getBounds().width, moon.getBounds().height, 0, 0, IMAGE_WIDTH, IMAGE_WIDTH); // Draw the moon } }); AnimatorThread at = new AnimatorThread(); at.go(); }
Step 4: Create the physics for the bouncing of the ball. Add some constants and variables as private members of the view class and an animate() method which calculates the next position of the moon. Last but not least, force a redraw of the canvas at the end of the animate() method. This invokes the code that was defined in the canvas's PaintListener.
public void animate() { Display.getDefault().asyncExec(new Runnable(){ public void run(){ try{ float left = x; float top = y; // Determine the ball's location directionY += GRAVITY; x += directionX; y += directionY; a += directionA; // Determine out of bounds Rectangle rect = canvas.getClientArea(); if ( x > rect.width - IMAGE_WIDTH ) { x = rect.width - IMAGE_WIDTH; directionX = -directionX; directionA -= ( directionY - directionA ) * FRICTION_WALL; } if ( x < 0 ) { x = 0; directionX = -directionX; directionA += ( directionY - directionA ) * FRICTION_WALL; } if ( y > rect.height - IMAGE_WIDTH ) { directionY = (int) ( -GRAVITY * Math.sqrt( ( 1 + 8 * ( rect.height - IMAGE_WIDTH ) / GRAVITY ) ) / 2 ); y = rect.height - IMAGE_WIDTH; directionA += ( directionX - directionA ) * FRICTION_FLOOR; } float right = left + IMAGE_WIDTH; float bottom = top + IMAGE_WIDTH; if ( x < left ) left = x; else right = x + IMAGE_WIDTH; if ( y < top ) top = y; else bottom = y + IMAGE_WIDTH; // Force a redraw canvas.redraw( (int) Math.floor( left ) - 1, (int) Math.floor( top ) - 1, (int) ( Math.ceil( right ) - Math.floor( left ) ) + 2, (int) ( Math.ceil( bottom ) - Math.floor( top ) ) + 2, false ); }catch(SWTException e){ //eat it! } } }); }Step 5: Run the application and test if everything worked. Your application should now have an image of the moon in the view that bounces back and forth across the view. As you resize the view, the moon's boundaries are recalculated. Here's the full code of the view:
package com.eclipsercptutorials.animation; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTException; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.graphics.Transform; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.part.ViewPart; public class MainView extends ViewPart { public static final String ID = "com.eclipsercptutorials.animation.mainView"; // the ID needs to match the id set in the view's properties // The image private Image moon; // The image width private static int IMAGE_WIDTH = 85; // Rate of downward acceleration per frame private static final float GRAVITY = .25f; // Coefficient of friction private static final float FRICTION_FLOOR = 5f / 9f; private static final float FRICTION_WALL = 5f / 11f; // The location of the "ball" private float x = 0; private float y = 0; private float a = 0; // The direction the "ball" is moving private float directionX = 4; private float directionY = 0; private float directionA = 0; // We draw everything on this canvas private Canvas canvas; public MainView() { moon = Activator.getImageDescriptor("icons/moon.png").createImage(); } public void createPartControl(Composite parent) { parent.setBackground(new Color(parent.getDisplay(), 205, 38, 38)); // Create the canvas for drawing canvas = new Canvas(parent, SWT.DOUBLE_BUFFERED); canvas.setBackground(new Color(parent.getDisplay(), 0,0,0)); canvas.addPaintListener( new PaintListener() { public void paintControl(PaintEvent e) { GC gc = e.gc; Transform trans = new Transform(e.display ); gc.getTransform( trans ); trans.translate( x, y ); trans.translate( IMAGE_WIDTH / 2f, IMAGE_WIDTH / 2f ); trans.rotate( a ); trans.translate( -IMAGE_WIDTH / 2f, -IMAGE_WIDTH / 2f ); gc.setTransform( trans ); trans.dispose(); gc.drawImage( moon, 0, 0, moon.getBounds().width, moon.getBounds().height, 0, 0, IMAGE_WIDTH, IMAGE_WIDTH); // Draw the moon } }); AnimatorThread at = new AnimatorThread(); at.go(); } public void animate() { Display.getDefault().asyncExec(new Runnable(){ public void run(){ try{ float left = x; float top = y; // Determine the ball's location directionY += GRAVITY; x += directionX; y += directionY; a += directionA; // Determine out of bounds Rectangle rect = canvas.getClientArea(); if ( x > rect.width - IMAGE_WIDTH ) { x = rect.width - IMAGE_WIDTH; directionX = -directionX; directionA -= ( directionY - directionA ) * FRICTION_WALL; } if ( x < 0 ) { x = 0; directionX = -directionX; directionA += ( directionY - directionA ) * FRICTION_WALL; } if ( y > rect.height - IMAGE_WIDTH ) { directionY = (int) ( -GRAVITY * Math.sqrt( ( 1 + 8 * ( rect.height - IMAGE_WIDTH ) / GRAVITY ) ) / 2 ); y = rect.height - IMAGE_WIDTH; directionA += ( directionX - directionA ) * FRICTION_FLOOR; } float right = left + IMAGE_WIDTH; float bottom = top + IMAGE_WIDTH; if ( x < left ) left = x; else right = x + IMAGE_WIDTH; if ( y < top ) top = y; else bottom = y + IMAGE_WIDTH; // Force a redraw canvas.redraw( (int) Math.floor( left ) - 1, (int) Math.floor( top ) - 1, (int) ( Math.ceil( right ) - Math.floor( left ) ) + 2, (int) ( Math.ceil( bottom ) - Math.floor( top ) ) + 2, false ); }catch(SWTException e){ //eat it! } } }); } public void setFocus() {} class AnimatorThread implements Runnable{ // The timer interval in milliseconds private static final int TIMER_INTERVAL = 14; public void go(){ Thread t = new Thread(this); t.start(); } public void run() { try { while(true){ animate(); Thread.sleep(TIMER_INTERVAL); } } catch (InterruptedException e) { e.printStackTrace(); } } } }Piece of Cake!! <--- Previous - Updating a Widget in an Eclipse RCP Application from a Worker Thread ---> Next - Add a Toolbar to a View in an Eclipse RCP Application Also see: Eclipse RCP Tutorial Table of Contents
No comments:
Post a Comment