📄 lunarview.scala
字号:
if (speed <= mGoalSpeed) { mScratchRect.set(4 + BAR + 4, 4, 4 + BAR + 4 + speedWidth, 4 + BAR_HEIGHT) canvas.drawRect(mScratchRect, mLinePaint) } else { // Draw the bad color in back, with the good color in front of it mScratchRect.set(4 + BAR + 4, 4, 4 + BAR + 4 + speedWidth, 4 + BAR_HEIGHT) canvas.drawRect(mScratchRect, mLinePaintBad) val goalWidth = (BAR * mGoalSpeed / SPEED_MAX) mScratchRect.set(4 + BAR + 4, 4, 4 + BAR + 4 + goalWidth, 4 + BAR_HEIGHT) canvas.drawRect(mScratchRect, mLinePaint) } // Draw the landing pad canvas.drawLine(mGoalX, 1 + screenHeight - PAD_HEIGHT, mGoalX + mGoalWidth, 1 + screenHeight - PAD_HEIGHT, mLinePaint) // Draw the ship with its current rotation canvas.save() canvas.rotate(mHeading.toFloat, mX.toFloat, screenHeight - mY.toFloat) if (mMode == LOSE) { mCrashedImage.setBounds(xLeft, yTop, xLeft+mLanderWidth, yTop+mLanderHeight) mCrashedImage.draw(canvas) } else if (mEngineFiring) { mFiringImage.setBounds(xLeft, yTop, xLeft+mLanderWidth, yTop+mLanderHeight) mFiringImage.draw(canvas) } else { mLanderImage.setBounds(xLeft, yTop, xLeft+mLanderWidth, yTop+mLanderHeight) mLanderImage.draw(canvas) } /* * Our animation strategy is that each draw() does an invalidate(), * so we get a series of draws. This is a known animation strategy * within Android, and the system throttles the draws down to match * the refresh rate. */ if (mMode == RUNNING) { // Invalidate a space around the current lander + the bars at the top. // Note: invalidating a relatively small part of the screen to draw // is a good optimization. In this case, the bars and the ship // may be far apart, limiting the value of the optimization. invalidate(xLeft-20, yTop-20, xLeft+mLanderWidth+20, yTop+mLanderHeight+20) invalidate(0, 0, screenWidth, 4 + BAR_HEIGHT) } canvas.restore() } /** * Figures the lander state (x, y, fuel, ...) based on the passage of * realtime. Does not invalidate(). Called at the start * of draw(). Detects the end-of-game and sets the UI to the next state. */ def updatePhysics() { val now = System.currentTimeMillis // Do nothing if mLastTime is in the future. // This allows the game-start to delay the start of the physics // by 100ms or whatever. if (mLastTime > now) return val elapsed = (now - mLastTime) / 1000.0 // mRotating -- update heading if (mRotating != 0) { mHeading += mRotating * (SLEW_SEC * elapsed) // Bring things back into the range 0..360 if (mHeading < 0) mHeading += 360 else if (mHeading >= 360) mHeading -= 360 } // Base accelerations -- 0 for x, gravity for y var ddx = 0.0 var ddy = -DOWN_ACCEL_SEC * elapsed if (mEngineFiring) { // taking 0 as up, 90 as to the right // cos(deg) is ddy component, sin(deg) is ddx component var elapsedFiring = elapsed var fuelUsed = elapsedFiring * FUEL_SEC // tricky case where we run out of fuel partway through the elapsed if (fuelUsed > mFuel) { elapsedFiring = mFuel / fuelUsed * elapsed fuelUsed = mFuel // Oddball case where we adjust the "control" from here mEngineFiring = false } mFuel -= fuelUsed // have this much acceleration from the engine val accel = FIRE_ACCEL_SEC * elapsedFiring val radians = 2 * Math.Pi * mHeading / 360 ddx = Math.sin(radians) * accel ddy += Math.cos(radians) * accel } val dxOld = mDX val dyOld = mDY // figure speeds for the end of the period mDX += ddx mDY += ddy // figure position based on average speed during the period mX += elapsed * (mDX + dxOld)/2 mY += elapsed * (mDY + dyOld)/2 mLastTime = now checkLanding() } def checkLanding() { val yLowerBound = PAD_HEIGHT + mLanderHeight/2 - BOTTOM_PADDING if (mY <= yLowerBound) { mY = yLowerBound val res = getContext.getResources val speed = Math.sqrt(mDX*mDX + mDY*mDY) val onGoal = (mGoalX <= mX - mLanderWidth/2 && mX + mLanderWidth/2 <= mGoalX + mGoalWidth) // "Hyperspace" win -- upside down, going fast, // puts you back at the top. if (onGoal && Math.abs(mHeading - 180) < mGoalAngle && speed > SPEED_HYPERSPACE) { mWinsInARow += 1 doStart() } else { val (result, message) = if (!onGoal) (LOSE, res.getText(R.string.message_off_pad)) else if (!(mHeading <= mGoalAngle || mHeading >= 360 - mGoalAngle)) (LOSE, res.getText(R.string.message_bad_angle)) else if (speed > mGoalSpeed) (LOSE, res.getText(R.string.message_too_fast)) else { mWinsInARow += 1 (WIN, "") } setMode(result, message) } } } /** * Sets if the engine is currently firing. */ def isFiring_=(firing: Boolean) { mEngineFiring = firing } def isFiring = mEngineFiring /** * Sets the game mode, RUNNING, PAUSED, etc. * @param mode RUNNING, PAUSED, ... */ def mode_=(mode: Int) { setMode(mode, null) } def mode = mMode /** * Sets the game mode, RUNNING, PAUSED, etc. * @param mode RUNNING, PAUSED, ... * @param message string to add to screen or null */ def setMode(mode: Int, message: CharSequence) { mMode = mode invalidate() if (mMode == RUNNING) { mStatusText.setVisibility(View.INVISIBLE) } else { mRotating = 0 mEngineFiring = false val res = getContext.getResources var str = mMode match { case READY => res.getText(R.string.mode_ready) case PAUSE => res.getText(R.string.mode_pause) case LOSE => mWinsInARow = 0; res.getText(R.string.mode_lose) case WIN => res.getString(R.string.mode_win_prefix) + mWinsInARow + " " + res.getString(R.string.mode_win_suffix) case _ => "" } mStatusText setText (if (message != null) message + "\n" + str else str) mStatusText setVisibility View.VISIBLE } } /** * Starts the game, setter parameters for the current * difficulty. */ def doStart() { // First set the game for Medium difficulty mFuel = FUEL_INIT mEngineFiring = false mGoalWidth = (mLanderWidth * TARGET_WIDTH).toInt mGoalSpeed = TARGET_SPEED mGoalAngle = TARGET_ANGLE var speedInit = SPEED_INIT // Adjust difficulty params for EASY/HARD if (mDifficulty == EASY) { mFuel = mFuel * 3 / 2 mGoalWidth = mGoalWidth * 4 / 3 mGoalSpeed = mGoalSpeed * 3 / 2 mGoalAngle = mGoalAngle * 4 / 3 speedInit = speedInit * 3 / 4 } else if (mDifficulty == HARD) { mFuel = mFuel * 7 / 8 mGoalWidth = mGoalWidth * 3 / 4 mGoalSpeed = mGoalSpeed * 7 / 8 speedInit = speedInit * 4 / 3 } mX = getWidth/2 mY = getHeight - mLanderHeight/2 // start with a little random motion mDY = Math.random * -speedInit mDX = Math.random * 2*speedInit - speedInit mHeading = 0 // Figure initial spot for landing, not too near center do { mGoalX = (Math.random * (getWidth - mGoalWidth)).toInt } while (Math.abs(mGoalX - (mX - mLanderWidth/2)) <= getWidth/6) mLastTime = System.currentTimeMillis + 100 mode = RUNNING } /** * Resumes from a pause. */ def doResume() { // Move the real time clock up to now mLastTime = System.currentTimeMillis + 100 mode = RUNNING } /** * Pauses from the running state. */ def doPause() { if (mMode == RUNNING) mode = PAUSE } /** * Standard override to get key events. */ override def onKeyDown(keyCode: Int, msg: KeyEvent): Boolean = { var handled = false val okStart = keyCode == KeyEvent.KEYCODE_DPAD_UP || keyCode == KeyEvent.KEYCODE_DPAD_DOWN || keyCode == KeyEvent.KEYCODE_S val center = keyCode == KeyEvent.KEYCODE_DPAD_UP // ready-to-start -> start if (okStart && (mMode == READY || mMode == LOSE || mMode == WIN)) { doStart() handled = true } // paused -> running else if (mMode == PAUSE && okStart) { doResume() handled = true } else if (mMode == RUNNING) { // center/space -> fire if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_SPACE) { isFiring = true handled = true // left/q -> left } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_Q) { mRotating = -1 handled = true // right/w -> right } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT || keyCode == KeyEvent.KEYCODE_W) { mRotating = 1 handled = true // up -> pause } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) { doPause() handled = true } } handled } /** * Standard override for key-up. We actually care about these, * so we can turn off the engine or stop rotating. */ override def onKeyUp(keyCode: Int, msg: KeyEvent): Boolean = if (mMode == RUNNING) { if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_SPACE) { isFiring = false true } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_Q || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT || keyCode == KeyEvent.KEYCODE_W) { mRotating = 0 true } else false } else false def difficulty_=(d: Int) { mDifficulty = d } def difficulty = mDifficulty}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -