Created
May 12, 2019 22:32
-
-
Save peterkos/a1084c5b5af58c5d0360ef8e3ac361c5 to your computer and use it in GitHub Desktop.
CSE 340, Ex 1. Doodle - Snake
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* RESOURCES: | |
* http://www.curious-creature.com/2013/12/21/android-recipe-4-path-tracing/ | |
* (Implementation came from this SO link, however I derived something similar w/ ViewAnimator first) | |
* https://stackoverflow.com/questions/50674847/how-to-draw-several-lines-slowly-in-constant-velocity-on-canvas-by-android | |
* https://stackoverflow.com/questions/15010156/android-using-paint-to-draw-dashed-line-with-two-different-colors | |
*/ | |
// Runner class omitted | |
// We need to create our own Path. | |
// Why? Well, in order to create a tracing effect, | |
// the ObjectAnimator needs to modify the phase parameter of the DashPathEffect. | |
// In the chaos of being intelligent, no Android developer added a setPhase() method to it. | |
// Alas, we need to simulate this method in an effective wrapper around Path. | |
class SnakePath extends View { | |
Path snakePath; | |
Path foodDotsBasePath; // Duplicate base of snakePath | |
Path altSnakePath; // Effect of "Eating" pellets | |
Path snakeOutlinePath; // Outline of snake path to follow | |
Paint snakePaint; | |
Paint foodPaint; | |
Paint altSnakePaint; | |
Paint snakeOutlinePaint; | |
float length; | |
public SnakePath(Context context) { | |
super(context); | |
} | |
public void init() { | |
snakePath = new Path(); | |
snakePath.moveTo(0, 600); | |
snakePath.lineTo(100, 600); | |
snakePath.lineTo(100, 2000); | |
snakePath.lineTo(1000, 2000); | |
snakePath.lineTo(1000, 1800); | |
snakePath.lineTo(300, 1800); | |
snakePath.lineTo(300, 1000); | |
snakePath.lineTo(500, 1000); | |
snakePath.lineTo(500, 1600); | |
snakePath.lineTo(1200, 1600); | |
snakePath.lineTo(1200, 2000); | |
snakePath.lineTo(1300, 2000); | |
snakePath.lineTo(1300, 1400); | |
snakePath.lineTo(700, 1400); | |
snakePath.lineTo(700, 800); | |
snakePath.lineTo(300, 800); | |
snakePath.lineTo(300, 600); | |
snakePath.lineTo(900, 600); | |
snakePath.lineTo(900, 1200); | |
snakePath.lineTo(1100, 1200); | |
snakePath.lineTo(1100, 600); | |
snakePath.lineTo(PHONE_DIMS.x, 600); | |
// Configure paint of snake | |
snakePaint = new Paint(); | |
snakePaint.setStrokeWidth(18); | |
snakePaint.setColor(Color.BLUE); | |
snakePaint.setStyle(Paint.Style.STROKE); | |
// Now, we'll use the DashPathEffect to *setup* a trace effect | |
PathMeasure snakeLength = new PathMeasure(snakePath, false); | |
length = snakeLength.getLength(); | |
// Finally, build the animator. | |
ObjectAnimator animator1 = ObjectAnimator.ofFloat(this, "phase", length, 0.0f); | |
animator1.setInterpolator(new LinearInterpolator()); | |
animator1.setDuration(13000); | |
animator1.setRepeatCount(ObjectAnimator.INFINITE); | |
animator1.setRepeatMode(ObjectAnimator.RESTART); | |
// Here's another path that is layered under the snake, to simulate eating the pellets. | |
// (offsetting wouldn't work) | |
altSnakePath = new Path(snakePath); | |
altSnakePaint = new Paint(); | |
altSnakePaint.setStrokeWidth(40f); | |
altSnakePaint.setColor(Color.parseColor("#FAFAFA")); | |
altSnakePaint.setStyle(Paint.Style.STROKE); | |
// In addition, here is another path to outline the path the snake will take. | |
// This helps the dots look more... aligned. | |
snakeOutlinePath = new Path(snakePath); | |
snakeOutlinePaint = new Paint(); | |
snakeOutlinePaint.setStrokeWidth(4); | |
snakeOutlinePaint.setStyle(Paint.Style.STROKE); | |
// Now, as the snake eats things, we can use the PathDashPathEffect to stamp these | |
// along the snake's travel path, with no extra positioning required! | |
// (I can't figure out how to add multiple pathEffects :c ) | |
// Copy the base path as duplicate | |
foodDotsBasePath = new Path(snakePath); | |
// These are the actual dots -- foodDotsPath acts as a base | |
Path foodDotsPath = new Path(); | |
foodDotsPath.addCircle(0f, 0f, 20f, Path.Direction.CW); | |
// Apply the stamped dash effect | |
PathDashPathEffect dotEffect = new PathDashPathEffect(foodDotsPath, length / 14, 2000f, PathDashPathEffect.Style.TRANSLATE); | |
// Configure food paint | |
foodPaint = new Paint(); | |
foodPaint.setPathEffect(dotEffect); | |
foodPaint.setStyle(Paint.Style.STROKE); | |
foodPaint.setColor(Color.RED); | |
// Start the animator as now both snakePath and foodDotsPath (which it controls) | |
// have been instantiated. | |
animator1.start(); | |
} | |
public void setPhase(float phase) { | |
// Since we don't have a real setPhase() method in the PathEffect (grr), we'll | |
// have to simulate it by re-instantiating a new PaintEffect with each new phase | |
// we want to assign. | |
int lengthDiv = 2; | |
snakePaint.setPathEffect(new DashPathEffect(new float[] {length / lengthDiv, length / lengthDiv}, phase)); | |
altSnakePaint.setPathEffect(new DashPathEffect(new float[] {length / lengthDiv, length / lengthDiv}, phase)); | |
// Now, we force onDraw() to call, redrawing the path! | |
invalidate(); | |
} | |
@Override | |
public void onDraw(Canvas canvas) { | |
super.onDraw(canvas); | |
// The order of the below paths matters! | |
canvas.drawPath(foodDotsBasePath, foodPaint); | |
canvas.drawPath(altSnakePath, altSnakePaint); | |
// This line draws the background path for debugging. | |
// canvas.drawPath(snakeOutlinePath, snakeOutlinePaint); | |
canvas.drawPath(snakePath, snakePaint); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment