任意の曲線に沿って画像を移動する



アニメーションを作成するタスクがありました-与えられた曲線に沿って画像を移動します。 stackoverflow.comで同様のタスクを持ついくつかの質問が1年以上未回答のままであることに気付き、Googleは驚きました。 私は袖をまくり、文書を掘り下げ、適切な解決策を見つけなければなりませんでした。

曲線があります。 たとえば、一連のポイントから構築され、美しさのために平滑化されます。

//  List<PointF> aPoints = new ArrayList<PointF>(); aPoints.add(new PointF(10f, 160f)); aPoints.add(new PointF(100f, 100f)); aPoints.add(new PointF(300f, 220f)); aPoints.add(new PointF(640f, 180f)); //   Path ptCurve = new Path(); PointF point = aPoints.get(0); ptCurve.moveTo(point.x, point.y); for(int i = 0; i < aPoints.size() - 1; i++){ point = aPoints.get(i); PointF next = aPoints.get(i+1); ptCurve.quadTo( point.x, point.y, (next.x + point.x) / 2, (point.y + next.y) / 2 ); }
      
      







タスクは、曲線上の点の座標を取得して、そこに画像を描画することです。 これを行うには、 PathMeasureクラスを使用します。 このクラスを使用して、曲線の長さを「測定」します。 目的のポイントを見つけるために、このクラスのオブジェクトに、ポイントが先頭から削除される長さを渡すことができます。



そのため、たとえば、曲線の中央にあるポイントの座標を取得できます。

  PathMeasure pm = new PathMeasure(ptCurve, false); float afP[] = {0f, 0f}; //   pm.getPosTan(pm.getLength() * 0.5f, afP, null);
      
      







最後のパラメーター(ここでnull



を渡しました)は、座標と同様に、このポイントで接線パラメーターを取得できます。



さらに、既製の変換マトリックス(オフセットと目的の回転)を提供するgetMatrix



メソッドがあります。 これを使用してスプライトを表示します。

  Matrix mxTransform = new Matrix(); pm.getMatrix( pm.getLength() * 0.5f, mxTransform, PathMeasure.POSITION_MATRIX_FLAG + PathMeasure.TANGENT_MATRIX_FLAG ); mxTransform.preTranslate(-bmSprite.getWidth(), -bmSprite.getHeight()); canvas.drawBitmap(bmSprite, mxTransform, null);
      
      







まさに必要なものが判明しました。





完全なコードを以下に示すか、リポジトリからプロジェクトをダウンロードできます-SpriteAlongPathまたはMercurialを使用します- hg clone bitbucket.org/TedBeer/spritealongpath



hg clone bitbucket.org/TedBeer/spritealongpath







 /** * User: TedBeer * Date: 30/01/12 * Time: 12:32 */ package net.tedbeer; import android.app.Activity; import android.os.Bundle; import android.content.Context; import android.graphics.*; import android.util.Log; import android.view.Display; import android.view.MotionEvent; import android.view.View; import android.view.WindowManager; import android.widget.Toast; import java.util.*; public class moveSprite extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new SceneView(this)); } } public class SceneView extends View { private static Bitmap bmSprite; private static Bitmap bmBackground; private static Rect rSrc, rDest; //animation step private static int iMaxAnimationStep = 20; private int iCurStep = 0; //points defining our curve private List<PointF> aPoints = new ArrayList<PointF>(); private Paint paint; private Path ptCurve = new Path(); //curve private PathMeasure pm; //curve measure private float fSegmentLen; //curve segment length public SceneView(Context context) { super(context); //destination rectangle Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); rDest = new Rect(0, 0, display.getWidth(), display.getHeight()); //load background if (bmBackground == null) { bmBackground = BitmapFactory.decodeResource(getResources(), R.drawable.winter_mountains); rSrc = new Rect(0, 0, bmBackground.getWidth(), bmBackground.getHeight()); } //load sprite if (bmSprite == null) bmSprite = BitmapFactory.decodeResource(getResources(), R.drawable.sledge3); //init random set of points aPoints.add(new PointF(10f, 160f)); aPoints.add(new PointF(100f, 100f)); aPoints.add(new PointF(300f, 220f)); aPoints.add(new PointF(640f, 180f)); //init smooth curve PointF point = aPoints.get(0); ptCurve.moveTo(point.x, point.y); for(int i = 0; i < aPoints.size() - 1; i++){ point = aPoints.get(i); PointF next = aPoints.get(i+1); ptCurve.quadTo(point.x, point.y, (next.x + point.x) / 2, (point.y + next.y) / 2); } pm = new PathMeasure(ptCurve, false); fSegmentLen = pm.getLength() / iMaxAnimationStep;//20 animation steps //init paint object paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(3); paint.setColor(Color.rgb(0, 148, 255)); } @Override protected void onDraw(Canvas canvas) { canvas.drawBitmap(bmBackground, rSrc, rDest, null); canvas.drawPath(ptCurve, paint); //animate the sprite Matrix mxTransform = new Matrix(); if (iCurStep <= iMaxAnimationStep) { pm.getMatrix(fSegmentLen * iCurStep, mxTransform, PathMeasure.POSITION_MATRIX_FLAG + PathMeasure.TANGENT_MATRIX_FLAG); mxTransform.preTranslate(-bmSprite.getWidth(), -bmSprite.getHeight()); canvas.drawBitmap(bmSprite, mxTransform, null); iCurStep++; //advance to the next step invalidate(); } else { iCurStep = 0; } } @Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN ) { //run animation invalidate(); return true; } return false; } }
      
      







更新:ビデオを別のビデオに置き換えました。スプライトも回転していることがはっきりとわかります。

tween-engブランチがリポジトリに追加され、コメントで指定されたjava-universal-tween-engineがアニメーションに使用されます



All Articles