package com.mhacks.android.pong; import android.animation.Animator; import android.animation.AnimatorSet; import android.app.Activity; import android.graphics.Color; import android.graphics.PointF; import android.os.Build; import android.util.Log; import android.view.View; import android.view.ViewTreeObserver; import android.view.animation.LinearInterpolator; import com.mhacks.android.R; import java.util.Random; /** * Created by boztalay on 1/15/14. */ public class PongPlayer { private final static int DEFAULT_GAME_COLOR = Color.WHITE; private final static int DEFAULT_HORIZONTAL_TRAVEL_TIME = 350; private boolean isReadyToPlay; private AnimatorSet currentAnimator; private Random rand; private View parentView; private PongGamePieceView leftPaddleView; private PongGamePieceView rightPaddleView; private PongGamePieceView ballView; private PointF ballOrigin; private PointF ballDestination; private PointF ballDirection; private PointF leftPaddleDestination; private PointF rightPaddleDestination; public PongPlayer(View parentView) { isReadyToPlay = false; rand = new Random(); this.parentView = parentView; leftPaddleView = (PongGamePieceView)parentView.findViewById(R.id.pong_left_paddle); leftPaddleView.setBackgroundColor(DEFAULT_GAME_COLOR); rightPaddleView = (PongGamePieceView)parentView.findViewById(R.id.pong_right_paddle); rightPaddleView.setBackgroundColor(DEFAULT_GAME_COLOR); ballView = (PongGamePieceView)parentView.findViewById(R.id.pong_ball); ballView.setBackgroundColor(DEFAULT_GAME_COLOR); ballOrigin = new PointF(); ballDestination = new PointF(); ballDirection = new PointF(); leftPaddleDestination = new PointF(); rightPaddleDestination = new PointF(); } public void onPulled(float percentagePulled) { float amountToRotate = (float)(180.0 * percentagePulled); leftPaddleView.setRotation(amountToRotate); rightPaddleView.setRotation(-amountToRotate); } public void resetGamePieces() { Log.d("PongPlayer", "resetGamePieces"); ballView.setPositionByCenter(getGameFieldCenterX(), getGameFieldCenterY()); int paddleHeight = parentView.getHeight() / 3; float leftPaddleX = ((float)parentView.getWidth() * 0.25f); leftPaddleView.setHeight(paddleHeight); leftPaddleView.setPositionByCenter(leftPaddleX, getGameFieldCenterY()); leftPaddleView.setRotation(0.0f); leftPaddleDestination.set(leftPaddleX, getGameFieldCenterY()); float rightPaddleX = ((float)parentView.getWidth() * 0.75f); rightPaddleView.setHeight(paddleHeight); rightPaddleView.setPositionByCenter(rightPaddleX, getGameFieldCenterY()); rightPaddleView.setRotation(0.0f); rightPaddleDestination.set(rightPaddleX, getGameFieldCenterY()); if(!isReadyToPlay) { isReadyToPlay = true; } } public void stopPlaying() { if(currentAnimator != null) { currentAnimator.removeAllListeners(); currentAnimator.cancel(); } leftPaddleView.clearAnimation(); rightPaddleView.clearAnimation(); ballView.clearAnimation(); } public void startPlaying() { Log.d("PongPlayer", "startPlaying"); if(isReadyToPlay) { resetPaddleRotations(); ballOrigin.set(getGameFieldCenterX(), getGameFieldCenterY()); pickRandomStartingBallDestination(); determineNextPaddleDestinations(); animateBallAndPaddlesToDestinations(); } else { parentView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { if (Build.VERSION.SDK_INT < 16) { parentView.getViewTreeObserver().removeGlobalOnLayoutListener(this); } else { parentView.getViewTreeObserver().removeOnGlobalLayoutListener(this); } resetGamePieces(); startPlaying(); } }); } } private void resetPaddleRotations() { leftPaddleView.setRotation(0.0f); rightPaddleView.setRotation(0.0f); } //BALL private void pickRandomStartingBallDestination() { float destinationX = ballView.centerXWhenContactingRightSideOf(leftPaddleView); if(rand.nextBoolean()) { destinationX = ballView.centerXWhenContactingLeftSideOf(rightPaddleView); } float destinationY = randomFloatBetween(0.0f, parentView.getHeight() - 30.0f) + 15.0f; ballDestination.set(destinationX, destinationY); ballDirection.set((ballDestination.x - ballOrigin.x), (ballDestination.y - ballOrigin.y)); normalizeVector(ballDirection); } private void determineNextBallDestination() { float newBallDestinationX; float newBallDestinationY; reflectBallDirection(); float verticalDistanceToNextWall = calculateVerticalDistanceFromBallToNextWall(); float distanceToNextWall = verticalDistanceToNextWall / ballDirection.y; float horizontalDistanceToNextWall = distanceToNextWall * ballDirection.x; float horizontalDistanceToNextPaddle = calculateHorizontalDistanceFromBallToNextPaddle(); if(Math.abs(horizontalDistanceToNextPaddle) < Math.abs(horizontalDistanceToNextWall)) { newBallDestinationX = ballDestination.x + horizontalDistanceToNextPaddle; float verticalDistanceToNextPaddle = Math.abs(horizontalDistanceToNextPaddle) * ballDirection.y; newBallDestinationY = ballDestination.y + verticalDistanceToNextPaddle; } else { newBallDestinationX = ballDestination.x + horizontalDistanceToNextWall; newBallDestinationY = ballDestination.y + verticalDistanceToNextWall; } ballOrigin.set(ballDestination); ballDestination.set(newBallDestinationX, newBallDestinationY); } private void reflectBallDirection() { if(didBallHitWall()) { ballDirection.set(ballDirection.x, -ballDirection.y); } else if(didBallHitPaddle()) { ballDirection.set(-ballDirection.x, ballDirection.y); } } private boolean didBallHitWall() { return (areFloatsEqual(ballDestination.y, ballView.centerYWhenContactingCeiling(parentView)) || areFloatsEqual(ballDestination.y, ballView.centerYWhenContactingFloor(parentView))); } private boolean didBallHitPaddle() { return (areFloatsEqual(ballDestination.x, ballView.centerXWhenContactingRightSideOf(leftPaddleView)) || areFloatsEqual(ballDestination.x, ballView.centerXWhenContactingLeftSideOf(rightPaddleView))); } private float calculateVerticalDistanceFromBallToNextWall() { if(ballDirection.y > 0.0f) { return (ballView.centerYWhenContactingFloor(parentView) - ballDestination.y); } else { return (ballView.centerYWhenContactingCeiling(parentView) - ballDestination.y); } } private float calculateHorizontalDistanceFromBallToNextPaddle() { if(ballDirection.x < 0.0f) { return (ballView.centerXWhenContactingRightSideOf(leftPaddleView) - ballDestination.x); } else { return (ballView.centerXWhenContactingLeftSideOf(rightPaddleView) - ballDestination.x); } } //PADDLES private void determineNextPaddleDestinations() { float lazySpeedFactor = 0.25f; float normalSpeedFactor = 0.5f; float holyCrapSpeedFactor = 1.0f; float leftPaddleVerticalDistanceToBallDestination = ballDestination.y - leftPaddleView.getCenterY(); float rightPaddleVerticalDistanceToBallDestination = ballDestination.y - rightPaddleView.getCenterY(); float leftPaddleOffset; float rightPaddleOffset; //Determining how far each paddle will mode if(ballDirection.x < 0.0f) { //Ball is going toward the left paddle if(isBallDestinationIsTheLeftPaddle()) { leftPaddleOffset = (leftPaddleVerticalDistanceToBallDestination * holyCrapSpeedFactor); rightPaddleOffset = (rightPaddleVerticalDistanceToBallDestination * lazySpeedFactor); } else { //Destination is a wall leftPaddleOffset = (leftPaddleVerticalDistanceToBallDestination * normalSpeedFactor); rightPaddleOffset = -(rightPaddleVerticalDistanceToBallDestination * normalSpeedFactor); } } else { //Ball is going toward the right paddle if(isBallDestinationIsTheRightPaddle()) { leftPaddleOffset = (leftPaddleVerticalDistanceToBallDestination * lazySpeedFactor); rightPaddleOffset = (rightPaddleVerticalDistanceToBallDestination * holyCrapSpeedFactor); } else { //Destination is a wall leftPaddleOffset = -(leftPaddleVerticalDistanceToBallDestination * normalSpeedFactor); rightPaddleOffset = (rightPaddleVerticalDistanceToBallDestination * normalSpeedFactor); } } leftPaddleDestination.set(leftPaddleDestination.x, leftPaddleView.getCenterY() + leftPaddleOffset); rightPaddleDestination.set(rightPaddleDestination.x, rightPaddleView.getCenterY() + rightPaddleOffset); capPaddleDestinationsToWalls(); } private boolean isBallDestinationIsTheLeftPaddle() { return areFloatsEqual(ballDestination.x, ballView.centerXWhenContactingRightSideOf(leftPaddleView)); } private boolean isBallDestinationIsTheRightPaddle() { return areFloatsEqual(ballDestination.x, ballView.centerXWhenContactingLeftSideOf(rightPaddleView)); } private void capPaddleDestinationsToWalls() { if(leftPaddleDestination.y < leftPaddleView.centerYWhenContactingCeiling(parentView)) { leftPaddleDestination.y = leftPaddleView.centerYWhenContactingCeiling(parentView); } else if(leftPaddleDestination.y > leftPaddleView.centerYWhenContactingFloor(parentView)) { leftPaddleDestination.y = leftPaddleView.centerYWhenContactingFloor(parentView); } if(rightPaddleDestination.y < rightPaddleView.centerYWhenContactingCeiling(parentView)) { rightPaddleDestination.y = rightPaddleView.centerYWhenContactingCeiling(parentView); } else if(rightPaddleDestination.y > rightPaddleView.centerYWhenContactingFloor(parentView)) { rightPaddleDestination.y = rightPaddleView.centerYWhenContactingFloor(parentView); } } //ANIMATING private void animateBallAndPaddlesToDestinations() { currentAnimator = new AnimatorSet(); currentAnimator.playTogether(leftPaddleView.createAnimationsToSetPositionByCenter(leftPaddleDestination), rightPaddleView.createAnimationsToSetPositionByCenter(rightPaddleDestination), ballView.createAnimationsToSetPositionByCenter(ballDestination)); float endToEndDistance = ballView.centerXWhenContactingLeftSideOf(rightPaddleView) - ballView.centerXWhenContactingRightSideOf(leftPaddleView); float horizontalDistanceBallWillTravel = Math.abs(ballDestination.x - ballOrigin.x); float proportionOfEndToEndDistanceBallWillTravel = horizontalDistanceBallWillTravel / endToEndDistance; long animationDuration = (long)((float)DEFAULT_HORIZONTAL_TRAVEL_TIME * proportionOfEndToEndDistanceBallWillTravel); currentAnimator.setDuration(animationDuration); currentAnimator.setInterpolator(new LinearInterpolator()); currentAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationEnd(Animator animator) { determineNextBallDestination(); determineNextPaddleDestinations(); animateBallAndPaddlesToDestinations(); } @Override public void onAnimationStart(Animator animator) {} @Override public void onAnimationCancel(Animator animator) {} @Override public void onAnimationRepeat(Animator animator) {} }); currentAnimator.start(); } private float getGameFieldCenterX() { return ((float)parentView.getWidth() / 2.0f); } private float getGameFieldCenterY() { return ((float)parentView.getHeight() / 2.0f); } //Etc private float randomFloatBetween(float min, float max) { return ((rand.nextFloat() * (max - min)) + min); } private void normalizeVector(PointF vector) { float magnitude = (float)Math.sqrt((double)(vector.x * vector.x + vector.y * vector.y)); vector.set(vector.x / magnitude, vector.y / magnitude); } private boolean areFloatsEqual(float num1, float num2) { float ellipsis = 0.01f; return (Math.abs(num1 - num2) < ellipsis); } }