Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2014 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | package com.android.systemui.egg; |
| 18 | |
| 19 | import android.animation.TimeAnimator; |
| 20 | import android.content.Context; |
| 21 | import android.content.res.Resources; |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 22 | import android.graphics.Canvas; |
| 23 | import android.graphics.Color; |
| 24 | import android.graphics.Matrix; |
| 25 | import android.graphics.Outline; |
| 26 | import android.graphics.Paint; |
| 27 | import android.graphics.Path; |
| 28 | import android.graphics.PorterDuff; |
| 29 | import android.graphics.Rect; |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 30 | import android.graphics.drawable.Drawable; |
| 31 | import android.graphics.drawable.GradientDrawable; |
| 32 | import android.util.AttributeSet; |
| 33 | import android.util.Log; |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 34 | import android.view.Gravity; |
| 35 | import android.view.KeyEvent; |
| 36 | import android.view.MotionEvent; |
| 37 | import android.view.View; |
| 38 | import android.view.ViewOutlineProvider; |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 39 | import android.view.animation.DecelerateInterpolator; |
| 40 | import android.widget.FrameLayout; |
| 41 | import android.widget.ImageView; |
| 42 | import android.widget.TextView; |
| 43 | |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 44 | import com.android.systemui.R; |
| 45 | |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 46 | import java.util.ArrayList; |
| 47 | |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 48 | public class LLand extends FrameLayout { |
| 49 | public static final String TAG = "LLand"; |
| 50 | |
| 51 | public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 52 | public static final boolean DEBUG_DRAW = false; // DEBUG |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 53 | |
| 54 | public static final void L(String s, Object ... objects) { |
| 55 | if (DEBUG) { |
| 56 | Log.d(TAG, String.format(s, objects)); |
| 57 | } |
| 58 | } |
| 59 | |
| 60 | public static final boolean AUTOSTART = true; |
| 61 | public static final boolean HAVE_STARS = true; |
| 62 | |
| 63 | public static final float DEBUG_SPEED_MULTIPLIER = 1f; // 0.1f; |
| 64 | public static final boolean DEBUG_IDDQD = false; |
| 65 | |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 66 | final static int[] POPS = { |
| 67 | // resid // spinny! |
| 68 | R.drawable.pop_belt, 0, |
| 69 | R.drawable.pop_droid, 0, |
| 70 | R.drawable.pop_pizza, 1, |
| 71 | R.drawable.pop_stripes, 0, |
| 72 | R.drawable.pop_swirl, 1, |
| 73 | R.drawable.pop_vortex, 1, |
| 74 | R.drawable.pop_vortex2, 1, |
| 75 | }; |
| 76 | |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 77 | private static class Params { |
| 78 | public float TRANSLATION_PER_SEC; |
| 79 | public int OBSTACLE_SPACING, OBSTACLE_PERIOD; |
| 80 | public int BOOST_DV; |
| 81 | public int PLAYER_HIT_SIZE; |
| 82 | public int PLAYER_SIZE; |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 83 | public int OBSTACLE_WIDTH, OBSTACLE_STEM_WIDTH; |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 84 | public int OBSTACLE_GAP; |
| 85 | public int OBSTACLE_MIN; |
| 86 | public int BUILDING_WIDTH_MIN, BUILDING_WIDTH_MAX; |
| 87 | public int BUILDING_HEIGHT_MIN; |
| 88 | public int CLOUD_SIZE_MIN, CLOUD_SIZE_MAX; |
| 89 | public int STAR_SIZE_MIN, STAR_SIZE_MAX; |
| 90 | public int G; |
| 91 | public int MAX_V; |
| 92 | public float SCENERY_Z, OBSTACLE_Z, PLAYER_Z, PLAYER_Z_BOOST, HUD_Z; |
| 93 | public Params(Resources res) { |
| 94 | TRANSLATION_PER_SEC = res.getDimension(R.dimen.translation_per_sec); |
| 95 | OBSTACLE_SPACING = res.getDimensionPixelSize(R.dimen.obstacle_spacing); |
| 96 | OBSTACLE_PERIOD = (int) (OBSTACLE_SPACING / TRANSLATION_PER_SEC); |
| 97 | BOOST_DV = res.getDimensionPixelSize(R.dimen.boost_dv); |
| 98 | PLAYER_HIT_SIZE = res.getDimensionPixelSize(R.dimen.player_hit_size); |
| 99 | PLAYER_SIZE = res.getDimensionPixelSize(R.dimen.player_size); |
| 100 | OBSTACLE_WIDTH = res.getDimensionPixelSize(R.dimen.obstacle_width); |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 101 | OBSTACLE_STEM_WIDTH = res.getDimensionPixelSize(R.dimen.obstacle_stem_width); |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 102 | OBSTACLE_GAP = res.getDimensionPixelSize(R.dimen.obstacle_gap); |
| 103 | OBSTACLE_MIN = res.getDimensionPixelSize(R.dimen.obstacle_height_min); |
| 104 | BUILDING_HEIGHT_MIN = res.getDimensionPixelSize(R.dimen.building_height_min); |
| 105 | BUILDING_WIDTH_MIN = res.getDimensionPixelSize(R.dimen.building_width_min); |
| 106 | BUILDING_WIDTH_MAX = res.getDimensionPixelSize(R.dimen.building_width_max); |
| 107 | CLOUD_SIZE_MIN = res.getDimensionPixelSize(R.dimen.cloud_size_min); |
| 108 | CLOUD_SIZE_MAX = res.getDimensionPixelSize(R.dimen.cloud_size_max); |
| 109 | STAR_SIZE_MIN = res.getDimensionPixelSize(R.dimen.star_size_min); |
| 110 | STAR_SIZE_MAX = res.getDimensionPixelSize(R.dimen.star_size_max); |
| 111 | |
| 112 | G = res.getDimensionPixelSize(R.dimen.G); |
| 113 | MAX_V = res.getDimensionPixelSize(R.dimen.max_v); |
| 114 | |
| 115 | SCENERY_Z = res.getDimensionPixelSize(R.dimen.scenery_z); |
| 116 | OBSTACLE_Z = res.getDimensionPixelSize(R.dimen.obstacle_z); |
| 117 | PLAYER_Z = res.getDimensionPixelSize(R.dimen.player_z); |
| 118 | PLAYER_Z_BOOST = res.getDimensionPixelSize(R.dimen.player_z_boost); |
| 119 | HUD_Z = res.getDimensionPixelSize(R.dimen.hud_z); |
| 120 | } |
| 121 | } |
| 122 | |
| 123 | private TimeAnimator mAnim; |
| 124 | |
| 125 | private TextView mScoreField; |
| 126 | private View mSplash; |
| 127 | |
| 128 | private Player mDroid; |
| 129 | private ArrayList<Obstacle> mObstaclesInPlay = new ArrayList<Obstacle>(); |
| 130 | |
| 131 | private float t, dt; |
| 132 | |
| 133 | private int mScore; |
| 134 | private float mLastPipeTime; // in sec |
| 135 | private int mWidth, mHeight; |
| 136 | private boolean mAnimating, mPlaying; |
| 137 | private boolean mFrozen; // after death, a short backoff |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 138 | private boolean mFlipped; |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 139 | |
| 140 | private int mTimeOfDay; |
| 141 | private static final int DAY = 0, NIGHT = 1, TWILIGHT = 2, SUNSET = 3; |
| 142 | private static final int[][] SKIES = { |
| 143 | { 0xFFc0c0FF, 0xFFa0a0FF }, // DAY |
| 144 | { 0xFF000010, 0xFF000000 }, // NIGHT |
| 145 | { 0xFF000040, 0xFF000010 }, // TWILIGHT |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 146 | { 0xFFa08020, 0xFF204080 }, // SUNSET |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 147 | }; |
| 148 | |
| 149 | private static Params PARAMS; |
| 150 | |
| 151 | public LLand(Context context) { |
| 152 | this(context, null); |
| 153 | } |
| 154 | |
| 155 | public LLand(Context context, AttributeSet attrs) { |
| 156 | this(context, attrs, 0); |
| 157 | } |
| 158 | |
| 159 | public LLand(Context context, AttributeSet attrs, int defStyle) { |
| 160 | super(context, attrs, defStyle); |
| 161 | setFocusable(true); |
| 162 | PARAMS = new Params(getResources()); |
| 163 | mTimeOfDay = irand(0, SKIES.length); |
| 164 | } |
| 165 | |
| 166 | @Override |
| 167 | public boolean willNotDraw() { |
| 168 | return !DEBUG; |
| 169 | } |
| 170 | |
| 171 | public int getGameWidth() { return mWidth; } |
| 172 | public int getGameHeight() { return mHeight; } |
| 173 | public float getGameTime() { return t; } |
| 174 | public float getLastTimeStep() { return dt; } |
| 175 | |
| 176 | public void setScoreField(TextView tv) { |
| 177 | mScoreField = tv; |
| 178 | if (tv != null) { |
| 179 | tv.setTranslationZ(PARAMS.HUD_Z); |
| 180 | if (!(mAnimating && mPlaying)) { |
| 181 | tv.setTranslationY(-500); |
| 182 | } |
| 183 | } |
| 184 | } |
| 185 | |
| 186 | public void setSplash(View v) { |
| 187 | mSplash = v; |
| 188 | } |
| 189 | |
| 190 | @Override |
| 191 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { |
| 192 | stop(); |
| 193 | reset(); |
| 194 | if (AUTOSTART) { |
| 195 | start(false); |
| 196 | } |
| 197 | } |
| 198 | |
| 199 | final float hsv[] = {0, 0, 0}; |
| 200 | |
| 201 | private void reset() { |
| 202 | L("reset"); |
| 203 | final Drawable sky = new GradientDrawable( |
| 204 | GradientDrawable.Orientation.BOTTOM_TOP, |
| 205 | SKIES[mTimeOfDay] |
| 206 | ); |
| 207 | sky.setDither(true); |
| 208 | setBackground(sky); |
| 209 | |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 210 | mFlipped = frand() > 0.5f; |
| 211 | setScaleX(mFlipped ? -1 : 1); |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 212 | |
| 213 | setScore(0); |
| 214 | |
| 215 | int i = getChildCount(); |
| 216 | while (i-->0) { |
| 217 | final View v = getChildAt(i); |
| 218 | if (v instanceof GameView) { |
| 219 | removeViewAt(i); |
| 220 | } |
| 221 | } |
| 222 | |
| 223 | mObstaclesInPlay.clear(); |
| 224 | |
| 225 | mWidth = getWidth(); |
| 226 | mHeight = getHeight(); |
| 227 | |
| 228 | boolean showingSun = (mTimeOfDay == DAY || mTimeOfDay == SUNSET) && frand() > 0.25; |
| 229 | if (showingSun) { |
| 230 | final Star sun = new Star(getContext()); |
| 231 | sun.setBackgroundResource(R.drawable.sun); |
| 232 | final int w = getResources().getDimensionPixelSize(R.dimen.sun_size); |
| 233 | sun.setTranslationX(frand(w, mWidth-w)); |
| 234 | if (mTimeOfDay == DAY) { |
| 235 | sun.setTranslationY(frand(w, (mHeight * 0.66f))); |
| 236 | sun.getBackground().setTint(0); |
| 237 | } else { |
| 238 | sun.setTranslationY(frand(mHeight * 0.66f, mHeight - w)); |
| 239 | sun.getBackground().setTintMode(PorterDuff.Mode.SRC_ATOP); |
| 240 | sun.getBackground().setTint(0xC0FF8000); |
| 241 | |
| 242 | } |
| 243 | addView(sun, new LayoutParams(w, w)); |
| 244 | } |
| 245 | if (!showingSun) { |
| 246 | final boolean dark = mTimeOfDay == NIGHT || mTimeOfDay == TWILIGHT; |
| 247 | final float ff = frand(); |
| 248 | if ((dark && ff < 0.75f) || ff < 0.5f) { |
| 249 | final Star moon = new Star(getContext()); |
| 250 | moon.setBackgroundResource(R.drawable.moon); |
| 251 | moon.getBackground().setAlpha(dark ? 255 : 128); |
| 252 | moon.setScaleX(frand() > 0.5 ? -1 : 1); |
| 253 | moon.setRotation(moon.getScaleX() * frand(5, 30)); |
| 254 | final int w = getResources().getDimensionPixelSize(R.dimen.sun_size); |
| 255 | moon.setTranslationX(frand(w, mWidth - w)); |
| 256 | moon.setTranslationY(frand(w, mHeight - w)); |
| 257 | addView(moon, new LayoutParams(w, w)); |
| 258 | } |
| 259 | } |
| 260 | |
| 261 | final int mh = mHeight / 6; |
| 262 | final boolean cloudless = frand() < 0.25; |
| 263 | final int N = 20; |
| 264 | for (i=0; i<N; i++) { |
| 265 | final float r1 = frand(); |
| 266 | final Scenery s; |
| 267 | if (HAVE_STARS && r1 < 0.3 && mTimeOfDay != DAY) { |
| 268 | s = new Star(getContext()); |
| 269 | } else if (r1 < 0.6 && !cloudless) { |
| 270 | s = new Cloud(getContext()); |
| 271 | } else { |
| 272 | s = new Building(getContext()); |
| 273 | |
| 274 | s.z = (float)i/N; |
| 275 | s.setTranslationZ(PARAMS.SCENERY_Z * (1+s.z)); |
| 276 | s.v = 0.85f * s.z; // buildings move proportional to their distance |
| 277 | hsv[0] = 175; |
| 278 | hsv[1] = 0.25f; |
| 279 | hsv[2] = 1 * s.z; |
| 280 | s.setBackgroundColor(Color.HSVToColor(hsv)); |
| 281 | s.h = irand(PARAMS.BUILDING_HEIGHT_MIN, mh); |
| 282 | } |
| 283 | final LayoutParams lp = new LayoutParams(s.w, s.h); |
| 284 | if (s instanceof Building) { |
| 285 | lp.gravity = Gravity.BOTTOM; |
| 286 | } else { |
| 287 | lp.gravity = Gravity.TOP; |
| 288 | final float r = frand(); |
| 289 | if (s instanceof Star) { |
| 290 | lp.topMargin = (int) (r * r * mHeight); |
| 291 | } else { |
| 292 | lp.topMargin = (int) (1 - r*r * mHeight/2) + mHeight/2; |
| 293 | } |
| 294 | } |
| 295 | |
| 296 | addView(s, lp); |
| 297 | s.setTranslationX(frand(-lp.width, mWidth + lp.width)); |
| 298 | } |
| 299 | |
| 300 | mDroid = new Player(getContext()); |
| 301 | mDroid.setX(mWidth / 2); |
| 302 | mDroid.setY(mHeight / 2); |
| 303 | addView(mDroid, new LayoutParams(PARAMS.PLAYER_SIZE, PARAMS.PLAYER_SIZE)); |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 304 | |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 305 | mAnim = new TimeAnimator(); |
| 306 | mAnim.setTimeListener(new TimeAnimator.TimeListener() { |
| 307 | @Override |
| 308 | public void onTimeUpdate(TimeAnimator timeAnimator, long t, long dt) { |
| 309 | step(t, dt); |
| 310 | } |
| 311 | }); |
| 312 | } |
| 313 | |
| 314 | private void setScore(int score) { |
| 315 | mScore = score; |
| 316 | if (mScoreField != null) mScoreField.setText(String.valueOf(score)); |
| 317 | } |
| 318 | |
| 319 | private void addScore(int incr) { |
| 320 | setScore(mScore + incr); |
| 321 | } |
| 322 | |
| 323 | private void start(boolean startPlaying) { |
| 324 | L("start(startPlaying=%s)", startPlaying?"true":"false"); |
| 325 | if (startPlaying) { |
| 326 | mPlaying = true; |
| 327 | |
| 328 | t = 0; |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 329 | // there's a sucker born every OBSTACLE_PERIOD |
| 330 | mLastPipeTime = getGameTime() - PARAMS.OBSTACLE_PERIOD; |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 331 | |
| 332 | if (mSplash != null && mSplash.getAlpha() > 0f) { |
| 333 | mSplash.setTranslationZ(PARAMS.HUD_Z); |
| 334 | mSplash.animate().alpha(0).translationZ(0).setDuration(400); |
| 335 | |
| 336 | mScoreField.animate().translationY(0) |
| 337 | .setInterpolator(new DecelerateInterpolator()) |
| 338 | .setDuration(1500); |
| 339 | } |
| 340 | |
| 341 | mScoreField.setTextColor(0xFFAAAAAA); |
| 342 | mScoreField.setBackgroundResource(R.drawable.scorecard); |
| 343 | mDroid.setVisibility(View.VISIBLE); |
| 344 | mDroid.setX(mWidth / 2); |
| 345 | mDroid.setY(mHeight / 2); |
| 346 | } else { |
| 347 | mDroid.setVisibility(View.GONE); |
| 348 | } |
| 349 | if (!mAnimating) { |
| 350 | mAnim.start(); |
| 351 | mAnimating = true; |
| 352 | } |
| 353 | } |
| 354 | |
| 355 | private void stop() { |
| 356 | if (mAnimating) { |
| 357 | mAnim.cancel(); |
| 358 | mAnim = null; |
| 359 | mAnimating = false; |
| 360 | mScoreField.setTextColor(0xFFFFFFFF); |
| 361 | mScoreField.setBackgroundResource(R.drawable.scorecard_gameover); |
| 362 | mTimeOfDay = irand(0, SKIES.length); // for next reset |
| 363 | mFrozen = true; |
| 364 | postDelayed(new Runnable() { |
| 365 | @Override |
| 366 | public void run() { |
| 367 | mFrozen = false; |
| 368 | } |
| 369 | }, 250); |
| 370 | } |
| 371 | } |
| 372 | |
| 373 | public static final float lerp(float x, float a, float b) { |
| 374 | return (b - a) * x + a; |
| 375 | } |
| 376 | |
| 377 | public static final float rlerp(float v, float a, float b) { |
| 378 | return (v - a) / (b - a); |
| 379 | } |
| 380 | |
| 381 | public static final float clamp(float f) { |
| 382 | return f < 0f ? 0f : f > 1f ? 1f : f; |
| 383 | } |
| 384 | |
| 385 | public static final float frand() { |
| 386 | return (float) Math.random(); |
| 387 | } |
| 388 | |
| 389 | public static final float frand(float a, float b) { |
| 390 | return lerp(frand(), a, b); |
| 391 | } |
| 392 | |
| 393 | public static final int irand(int a, int b) { |
| 394 | return (int) lerp(frand(), (float) a, (float) b); |
| 395 | } |
| 396 | |
| 397 | private void step(long t_ms, long dt_ms) { |
| 398 | t = t_ms / 1000f; // seconds |
| 399 | dt = dt_ms / 1000f; |
| 400 | |
| 401 | if (DEBUG) { |
| 402 | t *= DEBUG_SPEED_MULTIPLIER; |
| 403 | dt *= DEBUG_SPEED_MULTIPLIER; |
| 404 | } |
| 405 | |
| 406 | // 1. Move all objects and update bounds |
| 407 | final int N = getChildCount(); |
| 408 | int i = 0; |
| 409 | for (; i<N; i++) { |
| 410 | final View v = getChildAt(i); |
| 411 | if (v instanceof GameView) { |
| 412 | ((GameView) v).step(t_ms, dt_ms, t, dt); |
| 413 | } |
| 414 | } |
| 415 | |
| 416 | // 2. Check for altitude |
| 417 | if (mPlaying && mDroid.below(mHeight)) { |
| 418 | if (DEBUG_IDDQD) { |
| 419 | poke(); |
| 420 | } else { |
| 421 | L("player hit the floor"); |
| 422 | stop(); |
| 423 | } |
| 424 | } |
| 425 | |
| 426 | // 3. Check for obstacles |
| 427 | boolean passedBarrier = false; |
| 428 | for (int j = mObstaclesInPlay.size(); j-->0;) { |
| 429 | final Obstacle ob = mObstaclesInPlay.get(j); |
| 430 | if (mPlaying && ob.intersects(mDroid) && !DEBUG_IDDQD) { |
| 431 | L("player hit an obstacle"); |
| 432 | stop(); |
| 433 | } else if (ob.cleared(mDroid)) { |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 434 | if (ob instanceof Stem) passedBarrier = true; |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 435 | mObstaclesInPlay.remove(j); |
| 436 | } |
| 437 | } |
| 438 | |
| 439 | if (mPlaying && passedBarrier) { |
| 440 | addScore(1); |
| 441 | } |
| 442 | |
| 443 | // 4. Handle edge of screen |
| 444 | // Walk backwards to make sure removal is safe |
| 445 | while (i-->0) { |
| 446 | final View v = getChildAt(i); |
| 447 | if (v instanceof Obstacle) { |
| 448 | if (v.getTranslationX() + v.getWidth() < 0) { |
| 449 | removeViewAt(i); |
| 450 | } |
| 451 | } else if (v instanceof Scenery) { |
| 452 | final Scenery s = (Scenery) v; |
| 453 | if (v.getTranslationX() + s.w < 0) { |
| 454 | v.setTranslationX(getWidth()); |
| 455 | } |
| 456 | } |
| 457 | } |
| 458 | |
| 459 | // 3. Time for more obstacles! |
| 460 | if (mPlaying && (t - mLastPipeTime) > PARAMS.OBSTACLE_PERIOD) { |
| 461 | mLastPipeTime = t; |
| 462 | final int obstacley = (int) (Math.random() |
| 463 | * (mHeight - 2*PARAMS.OBSTACLE_MIN - PARAMS.OBSTACLE_GAP)) + PARAMS.OBSTACLE_MIN; |
| 464 | |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 465 | final int inset = (PARAMS.OBSTACLE_WIDTH - PARAMS.OBSTACLE_STEM_WIDTH) / 2; |
| 466 | final int yinset = PARAMS.OBSTACLE_WIDTH/2; |
| 467 | |
| 468 | final int d1 = irand(0,250); |
| 469 | final Obstacle s1 = new Stem(getContext(), obstacley - yinset, false); |
| 470 | addView(s1, new LayoutParams( |
| 471 | PARAMS.OBSTACLE_STEM_WIDTH, |
| 472 | (int) s1.h, |
| 473 | Gravity.TOP|Gravity.LEFT)); |
| 474 | s1.setTranslationX(mWidth+inset); |
| 475 | s1.setTranslationY(-s1.h-yinset); |
| 476 | s1.setTranslationZ(PARAMS.OBSTACLE_Z*0.75f); |
| 477 | s1.animate() |
| 478 | .translationY(0) |
| 479 | .setStartDelay(d1) |
| 480 | .setDuration(250); |
| 481 | mObstaclesInPlay.add(s1); |
| 482 | |
| 483 | final Obstacle p1 = new Pop(getContext(), PARAMS.OBSTACLE_WIDTH); |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 484 | addView(p1, new LayoutParams( |
| 485 | PARAMS.OBSTACLE_WIDTH, |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 486 | PARAMS.OBSTACLE_WIDTH, |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 487 | Gravity.TOP|Gravity.LEFT)); |
| 488 | p1.setTranslationX(mWidth); |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 489 | p1.setTranslationY(-PARAMS.OBSTACLE_WIDTH); |
| 490 | p1.setTranslationZ(PARAMS.OBSTACLE_Z); |
| 491 | p1.setScaleX(0.25f); |
| 492 | p1.setScaleY(0.25f); |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 493 | p1.animate() |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 494 | .translationY(s1.h-inset) |
| 495 | .scaleX(1f) |
| 496 | .scaleY(1f) |
| 497 | .setStartDelay(d1) |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 498 | .setDuration(250); |
| 499 | mObstaclesInPlay.add(p1); |
| 500 | |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 501 | final int d2 = irand(0,250); |
| 502 | final Obstacle s2 = new Stem(getContext(), |
| 503 | mHeight - obstacley - PARAMS.OBSTACLE_GAP - yinset, |
| 504 | true); |
| 505 | addView(s2, new LayoutParams( |
| 506 | PARAMS.OBSTACLE_STEM_WIDTH, |
| 507 | (int) s2.h, |
| 508 | Gravity.TOP|Gravity.LEFT)); |
| 509 | s2.setTranslationX(mWidth+inset); |
| 510 | s2.setTranslationY(mHeight+yinset); |
| 511 | s2.setTranslationZ(PARAMS.OBSTACLE_Z*0.75f); |
| 512 | s2.animate() |
| 513 | .translationY(mHeight-s2.h) |
| 514 | .setStartDelay(d2) |
| 515 | .setDuration(400); |
| 516 | mObstaclesInPlay.add(s2); |
| 517 | |
| 518 | final Obstacle p2 = new Pop(getContext(), PARAMS.OBSTACLE_WIDTH); |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 519 | addView(p2, new LayoutParams( |
| 520 | PARAMS.OBSTACLE_WIDTH, |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 521 | PARAMS.OBSTACLE_WIDTH, |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 522 | Gravity.TOP|Gravity.LEFT)); |
| 523 | p2.setTranslationX(mWidth); |
| 524 | p2.setTranslationY(mHeight); |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 525 | p2.setTranslationZ(PARAMS.OBSTACLE_Z); |
| 526 | p2.setScaleX(0.25f); |
| 527 | p2.setScaleY(0.25f); |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 528 | p2.animate() |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 529 | .translationY(mHeight-s2.h-yinset) |
| 530 | .scaleX(1f) |
| 531 | .scaleY(1f) |
| 532 | .setStartDelay(d2) |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 533 | .setDuration(400); |
| 534 | mObstaclesInPlay.add(p2); |
| 535 | } |
| 536 | |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 537 | if (DEBUG_DRAW) invalidate(); |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 538 | } |
| 539 | |
| 540 | @Override |
| 541 | public boolean onTouchEvent(MotionEvent ev) { |
| 542 | if (DEBUG) L("touch: %s", ev); |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 543 | switch (ev.getAction()) { |
| 544 | case MotionEvent.ACTION_DOWN: |
| 545 | poke(); |
| 546 | return true; |
| 547 | case MotionEvent.ACTION_UP: |
| 548 | unpoke(); |
| 549 | return true; |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 550 | } |
| 551 | return false; |
| 552 | } |
| 553 | |
| 554 | @Override |
| 555 | public boolean onTrackballEvent(MotionEvent ev) { |
| 556 | if (DEBUG) L("trackball: %s", ev); |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 557 | switch (ev.getAction()) { |
| 558 | case MotionEvent.ACTION_DOWN: |
| 559 | poke(); |
| 560 | return true; |
| 561 | case MotionEvent.ACTION_UP: |
| 562 | unpoke(); |
| 563 | return true; |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 564 | } |
| 565 | return false; |
| 566 | } |
| 567 | |
| 568 | @Override |
| 569 | public boolean onKeyDown(int keyCode, KeyEvent ev) { |
| 570 | if (DEBUG) L("keyDown: %d", keyCode); |
| 571 | switch (keyCode) { |
| 572 | case KeyEvent.KEYCODE_DPAD_CENTER: |
| 573 | case KeyEvent.KEYCODE_DPAD_UP: |
| 574 | case KeyEvent.KEYCODE_SPACE: |
| 575 | case KeyEvent.KEYCODE_ENTER: |
| 576 | case KeyEvent.KEYCODE_BUTTON_A: |
| 577 | poke(); |
| 578 | return true; |
| 579 | } |
| 580 | return false; |
| 581 | } |
| 582 | |
| 583 | @Override |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 584 | public boolean onKeyUp(int keyCode, KeyEvent ev) { |
| 585 | if (DEBUG) L("keyDown: %d", keyCode); |
| 586 | switch (keyCode) { |
| 587 | case KeyEvent.KEYCODE_DPAD_CENTER: |
| 588 | case KeyEvent.KEYCODE_DPAD_UP: |
| 589 | case KeyEvent.KEYCODE_SPACE: |
| 590 | case KeyEvent.KEYCODE_ENTER: |
| 591 | case KeyEvent.KEYCODE_BUTTON_A: |
| 592 | unpoke(); |
| 593 | return true; |
| 594 | } |
| 595 | return false; |
| 596 | } |
| 597 | |
| 598 | @Override |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 599 | public boolean onGenericMotionEvent (MotionEvent ev) { |
| 600 | if (DEBUG) L("generic: %s", ev); |
| 601 | return false; |
| 602 | } |
| 603 | |
| 604 | private void poke() { |
| 605 | L("poke"); |
| 606 | if (mFrozen) return; |
| 607 | if (!mAnimating) { |
| 608 | reset(); |
| 609 | start(true); |
| 610 | } else if (!mPlaying) { |
| 611 | start(true); |
| 612 | } |
| 613 | mDroid.boost(); |
| 614 | if (DEBUG) { |
| 615 | mDroid.dv *= DEBUG_SPEED_MULTIPLIER; |
| 616 | mDroid.animate().setDuration((long) (200/DEBUG_SPEED_MULTIPLIER)); |
| 617 | } |
| 618 | } |
| 619 | |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 620 | private void unpoke() { |
| 621 | L("unboost"); |
| 622 | if (mFrozen) return; |
| 623 | if (!mAnimating) return; |
| 624 | mDroid.unboost(); |
| 625 | } |
| 626 | |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 627 | @Override |
| 628 | public void onDraw(Canvas c) { |
| 629 | super.onDraw(c); |
| 630 | |
| 631 | if (!DEBUG_DRAW) return; |
| 632 | |
| 633 | final Paint pt = new Paint(); |
| 634 | pt.setColor(0xFFFFFFFF); |
| 635 | final int L = mDroid.corners.length; |
| 636 | final int N = L/2; |
| 637 | for (int i=0; i<N; i++) { |
| 638 | final int x = (int) mDroid.corners[i*2]; |
| 639 | final int y = (int) mDroid.corners[i*2+1]; |
| 640 | c.drawCircle(x, y, 4, pt); |
| 641 | c.drawLine(x, y, |
| 642 | mDroid.corners[(i*2+2)%L], |
| 643 | mDroid.corners[(i*2+3)%L], |
| 644 | pt); |
| 645 | } |
| 646 | |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 647 | pt.setStyle(Paint.Style.STROKE); |
| 648 | pt.setStrokeWidth(getResources().getDisplayMetrics().density); |
| 649 | |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 650 | final int M = getChildCount(); |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 651 | pt.setColor(0x8000FF00); |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 652 | for (int i=0; i<M; i++) { |
| 653 | final View v = getChildAt(i); |
| 654 | if (v == mDroid) continue; |
| 655 | if (!(v instanceof GameView)) continue; |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 656 | if (v instanceof Pop) { |
| 657 | final Pop p = (Pop) v; |
| 658 | c.drawCircle(p.cx, p.cy, p.r, pt); |
| 659 | } else { |
| 660 | final Rect r = new Rect(); |
| 661 | v.getHitRect(r); |
| 662 | c.drawRect(r, pt); |
| 663 | } |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 664 | } |
| 665 | |
| 666 | pt.setColor(Color.BLACK); |
| 667 | final StringBuilder sb = new StringBuilder("obstacles: "); |
| 668 | for (Obstacle ob : mObstaclesInPlay) { |
| 669 | sb.append(ob.hitRect.toShortString()); |
| 670 | sb.append(" "); |
| 671 | } |
| 672 | pt.setTextSize(20f); |
| 673 | c.drawText(sb.toString(), 20, 100, pt); |
| 674 | } |
| 675 | |
| 676 | static final Rect sTmpRect = new Rect(); |
| 677 | |
| 678 | private interface GameView { |
| 679 | public void step(long t_ms, long dt_ms, float t, float dt); |
| 680 | } |
| 681 | |
| 682 | private class Player extends ImageView implements GameView { |
| 683 | public float dv; |
| 684 | |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 685 | private boolean mBoosting; |
| 686 | |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 687 | private final float[] sHull = new float[] { |
| 688 | 0.3f, 0f, // left antenna |
| 689 | 0.7f, 0f, // right antenna |
| 690 | 0.92f, 0.33f, // off the right shoulder of Orion |
| 691 | 0.92f, 0.75f, // right hand (our right, not his right) |
| 692 | 0.6f, 1f, // right foot |
| 693 | 0.4f, 1f, // left foot BLUE! |
| 694 | 0.08f, 0.75f, // sinistram |
| 695 | 0.08f, 0.33f, // cold shoulder |
| 696 | }; |
| 697 | public final float[] corners = new float[sHull.length]; |
| 698 | |
| 699 | public Player(Context context) { |
| 700 | super(context); |
| 701 | |
| 702 | setBackgroundResource(R.drawable.android); |
| 703 | getBackground().setTintMode(PorterDuff.Mode.SRC_ATOP); |
| 704 | getBackground().setTint(0xFF00FF00); |
| 705 | setOutlineProvider(new ViewOutlineProvider() { |
| 706 | @Override |
| 707 | public void getOutline(View view, Outline outline) { |
| 708 | final int w = view.getWidth(); |
| 709 | final int h = view.getHeight(); |
| 710 | final int ix = (int) (w * 0.3f); |
| 711 | final int iy = (int) (h * 0.2f); |
| 712 | outline.setRect(ix, iy, w - ix, h - iy); |
| 713 | } |
| 714 | }); |
| 715 | } |
| 716 | |
| 717 | public void prepareCheckIntersections() { |
| 718 | final int inset = (PARAMS.PLAYER_SIZE - PARAMS.PLAYER_HIT_SIZE)/2; |
| 719 | final int scale = PARAMS.PLAYER_HIT_SIZE; |
| 720 | final int N = sHull.length/2; |
| 721 | for (int i=0; i<N; i++) { |
| 722 | corners[i*2] = scale * sHull[i*2] + inset; |
| 723 | corners[i*2+1] = scale * sHull[i*2+1] + inset; |
| 724 | } |
| 725 | final Matrix m = getMatrix(); |
| 726 | m.mapPoints(corners); |
| 727 | } |
| 728 | |
| 729 | public boolean below(int h) { |
| 730 | final int N = corners.length/2; |
| 731 | for (int i=0; i<N; i++) { |
| 732 | final int y = (int) corners[i*2+1]; |
| 733 | if (y >= h) return true; |
| 734 | } |
| 735 | return false; |
| 736 | } |
| 737 | |
| 738 | public void step(long t_ms, long dt_ms, float t, float dt) { |
| 739 | if (getVisibility() != View.VISIBLE) return; // not playing yet |
| 740 | |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 741 | if (mBoosting) { |
| 742 | dv = -PARAMS.BOOST_DV; |
| 743 | } else { |
| 744 | dv += PARAMS.G; |
| 745 | } |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 746 | if (dv < -PARAMS.MAX_V) dv = -PARAMS.MAX_V; |
| 747 | else if (dv > PARAMS.MAX_V) dv = PARAMS.MAX_V; |
| 748 | |
| 749 | final float y = getTranslationY() + dv * dt; |
| 750 | setTranslationY(y < 0 ? 0 : y); |
| 751 | setRotation( |
| 752 | 90 + lerp(clamp(rlerp(dv, PARAMS.MAX_V, -1 * PARAMS.MAX_V)), 90, -90)); |
| 753 | |
| 754 | prepareCheckIntersections(); |
| 755 | } |
| 756 | |
| 757 | public void boost() { |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 758 | mBoosting = true; |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 759 | dv = -PARAMS.BOOST_DV; |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 760 | |
| 761 | animate().cancel(); |
| 762 | animate() |
| 763 | .scaleX(1.25f) |
| 764 | .scaleY(1.25f) |
| 765 | .translationZ(PARAMS.PLAYER_Z_BOOST) |
| 766 | .setDuration(100); |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 767 | setScaleX(1.25f); |
| 768 | setScaleY(1.25f); |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 769 | } |
| 770 | |
| 771 | public void unboost() { |
| 772 | mBoosting = false; |
| 773 | |
| 774 | animate().cancel(); |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 775 | animate() |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 776 | .scaleX(1f) |
| 777 | .scaleY(1f) |
| 778 | .translationZ(PARAMS.PLAYER_Z) |
| 779 | .setDuration(200); |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 780 | } |
| 781 | } |
| 782 | |
| 783 | private class Obstacle extends View implements GameView { |
| 784 | public float h; |
| 785 | |
| 786 | public final Rect hitRect = new Rect(); |
| 787 | |
| 788 | public Obstacle(Context context, float h) { |
| 789 | super(context); |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 790 | setBackgroundColor(0xFFFF0000); |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 791 | this.h = h; |
| 792 | } |
| 793 | |
| 794 | public boolean intersects(Player p) { |
| 795 | final int N = p.corners.length/2; |
| 796 | for (int i=0; i<N; i++) { |
| 797 | final int x = (int) p.corners[i*2]; |
| 798 | final int y = (int) p.corners[i*2+1]; |
| 799 | if (hitRect.contains(x, y)) return true; |
| 800 | } |
| 801 | return false; |
| 802 | } |
| 803 | |
| 804 | public boolean cleared(Player p) { |
| 805 | final int N = p.corners.length/2; |
| 806 | for (int i=0; i<N; i++) { |
| 807 | final int x = (int) p.corners[i*2]; |
| 808 | if (hitRect.right >= x) return false; |
| 809 | } |
| 810 | return true; |
| 811 | } |
| 812 | |
| 813 | @Override |
| 814 | public void step(long t_ms, long dt_ms, float t, float dt) { |
| 815 | setTranslationX(getTranslationX()-PARAMS.TRANSLATION_PER_SEC*dt); |
| 816 | getHitRect(hitRect); |
| 817 | } |
| 818 | } |
| 819 | |
Dan Sandler | 2200f86 | 2014-10-06 01:04:47 -0400 | [diff] [blame] | 820 | private class Pop extends Obstacle { |
| 821 | int mRotate; |
| 822 | int cx, cy, r; |
| 823 | public Pop(Context context, float h) { |
| 824 | super(context, h); |
| 825 | int idx = 2*irand(0, POPS.length/2); |
| 826 | setBackgroundResource(POPS[idx]); |
| 827 | setScaleX(frand() < 0.5f ? -1 : 1); |
| 828 | mRotate = POPS[idx+1] == 0 ? 0 : (frand() < 0.5f ? -1 : 1); |
| 829 | setOutlineProvider(new ViewOutlineProvider() { |
| 830 | @Override |
| 831 | public void getOutline(View view, Outline outline) { |
| 832 | final int pad = (int) (getWidth() * 0.02f); |
| 833 | outline.setOval(pad, pad, getWidth()-pad, getHeight()-pad); |
| 834 | } |
| 835 | }); |
| 836 | } |
| 837 | |
| 838 | public boolean intersects(Player p) { |
| 839 | final int N = p.corners.length/2; |
| 840 | for (int i=0; i<N; i++) { |
| 841 | final int x = (int) p.corners[i*2]; |
| 842 | final int y = (int) p.corners[i*2+1]; |
| 843 | if (Math.hypot(x-cx, y-cy) <= r) return true; |
| 844 | } |
| 845 | return false; |
| 846 | } |
| 847 | |
| 848 | @Override |
| 849 | public void step(long t_ms, long dt_ms, float t, float dt) { |
| 850 | super.step(t_ms, dt_ms, t, dt); |
| 851 | if (mRotate != 0) { |
| 852 | setRotation(getRotation() + dt * 45 * mRotate); |
| 853 | } |
| 854 | |
| 855 | cx = (hitRect.left + hitRect.right)/2; |
| 856 | cy = (hitRect.top + hitRect.bottom)/2; |
| 857 | r = getWidth()/2; |
| 858 | } |
| 859 | } |
| 860 | |
| 861 | private class Stem extends Obstacle { |
| 862 | Paint mPaint = new Paint(); |
| 863 | Path mShadow = new Path(); |
| 864 | boolean mDrawShadow; |
| 865 | |
| 866 | public Stem(Context context, float h, boolean drawShadow) { |
| 867 | super(context, h); |
| 868 | mDrawShadow = drawShadow; |
| 869 | mPaint.setColor(0xFFAAAAAA); |
| 870 | setBackground(null); |
| 871 | } |
| 872 | |
| 873 | @Override |
| 874 | public void onAttachedToWindow() { |
| 875 | super.onAttachedToWindow(); |
| 876 | setWillNotDraw(false); |
| 877 | setOutlineProvider(new ViewOutlineProvider() { |
| 878 | @Override |
| 879 | public void getOutline(View view, Outline outline) { |
| 880 | outline.setRect(0, 0, getWidth(), getHeight()); |
| 881 | } |
| 882 | }); |
| 883 | } |
| 884 | @Override |
| 885 | public void onDraw(Canvas c) { |
| 886 | final int w = c.getWidth(); |
| 887 | final int h = c.getHeight(); |
| 888 | final GradientDrawable g = new GradientDrawable(); |
| 889 | g.setOrientation(GradientDrawable.Orientation.LEFT_RIGHT); |
| 890 | g.setGradientCenter(w * 0.75f, 0); |
| 891 | g.setColors(new int[] { 0xFFFFFFFF, 0xFFAAAAAA }); |
| 892 | g.setBounds(0, 0, w, h); |
| 893 | g.draw(c); |
| 894 | if (!mDrawShadow) return; |
| 895 | mShadow.reset(); |
| 896 | mShadow.moveTo(0,0); |
| 897 | mShadow.lineTo(w, 0); |
| 898 | mShadow.lineTo(w, PARAMS.OBSTACLE_WIDTH/2+w*1.5f); |
| 899 | mShadow.lineTo(0, PARAMS.OBSTACLE_WIDTH/2); |
| 900 | mShadow.close(); |
| 901 | c.drawPath(mShadow, mPaint); |
| 902 | } |
| 903 | } |
| 904 | |
Dan Sandler | 4455da9 | 2014-09-14 16:28:58 -0400 | [diff] [blame] | 905 | private class Scenery extends FrameLayout implements GameView { |
| 906 | public float z; |
| 907 | public float v; |
| 908 | public int h, w; |
| 909 | public Scenery(Context context) { |
| 910 | super(context); |
| 911 | } |
| 912 | |
| 913 | @Override |
| 914 | public void step(long t_ms, long dt_ms, float t, float dt) { |
| 915 | setTranslationX(getTranslationX() - PARAMS.TRANSLATION_PER_SEC * dt * v); |
| 916 | } |
| 917 | } |
| 918 | |
| 919 | private class Building extends Scenery { |
| 920 | public Building(Context context) { |
| 921 | super(context); |
| 922 | |
| 923 | w = irand(PARAMS.BUILDING_WIDTH_MIN, PARAMS.BUILDING_WIDTH_MAX); |
| 924 | h = 0; // will be setup later, along with z |
| 925 | |
| 926 | setTranslationZ(PARAMS.SCENERY_Z); |
| 927 | } |
| 928 | } |
| 929 | |
| 930 | private class Cloud extends Scenery { |
| 931 | public Cloud(Context context) { |
| 932 | super(context); |
| 933 | setBackgroundResource(frand() < 0.01f ? R.drawable.cloud_off : R.drawable.cloud); |
| 934 | getBackground().setAlpha(0x40); |
| 935 | w = h = irand(PARAMS.CLOUD_SIZE_MIN, PARAMS.CLOUD_SIZE_MAX); |
| 936 | z = 0; |
| 937 | v = frand(0.15f,0.5f); |
| 938 | } |
| 939 | } |
| 940 | |
| 941 | private class Star extends Scenery { |
| 942 | public Star(Context context) { |
| 943 | super(context); |
| 944 | setBackgroundResource(R.drawable.star); |
| 945 | w = h = irand(PARAMS.STAR_SIZE_MIN, PARAMS.STAR_SIZE_MAX); |
| 946 | v = z = 0; |
| 947 | } |
| 948 | } |
| 949 | } |