Subversion Repositories AndroidProjects

Rev

Rev 6 | Blame | Compare with Previous | Last modification | View Log | RSS feed

package com.gebauz.pingK.entities;

import java.util.Vector;

import com.gebauz.pingK.GameMain;
import com.gebauz.pingK.R;
import com.gebauz.pingK.util.Vector2D;
import com.gebauz.pingK.util.MathUtil;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;

public class Ball extends BaseEntity {
       
        public static final int DIRECTION_RANDOM = 0;
        public static final int DIRECTION_UP = 1;
        public static final int DIRECTION_DOWN = 2;
       
        public static final float BALL_INITIAL_SPEED = 300.0f;
        public static final float HIT_ZONE_DEVIATION = 0.40f;
        public static final float BALL_STEEPEST_ANGLE_REFLECT_FROM_BOARD = 1.0f;
        public static final float BALL_STEEPEST_ANGLE_HIT_PADDLE = 1.0f;
        public static final float TOP_SPIN_INTENSITY = 0.18f;
        public static final float BALL_MAXIMUM_ANGULAR_SPEED = 500.0f;
       
        public static final float PLAYFIELD_BORDER = 8.0f;
        public static final float PADDLE_HIT_OFFSET = 0.0f;
               
        Bitmap mBall;
       
        Vector2D position = new Vector2D();
        Vector2D lastPosition = new Vector2D();
        int lastPaddleTouched = -1;
        float size;    
       
        float speed;
        float rotation;
        float angularVelocity;
       
        public static final int TRAIL_LENGTH = 15;
        Vector<Vector2D> mTrail = new Vector<Vector2D>();
       
        public Ball(GameMain gameMain) {
                super(gameMain);
        }      

        @Override
        public void init() {
                mBall = BitmapFactory.decodeResource(getResources(), R.drawable.ball);
               
                size = mBall.getWidth();
               
                reset(DIRECTION_RANDOM);
        }
       
        public void reset(int direction) {
                resetTrail();
               
                position.x = mGameMain.getWidth() / 2.0f;
                position.y = mGameMain.getHeight() / 2.0f;
               
                speed = BALL_INITIAL_SPEED;
                if (direction == DIRECTION_UP) {
                        rotation = 0.0f;
                       
                } else if (direction == DIRECTION_DOWN) {
                        rotation = 180.0f;
                } else {
                        if (Math.random() > 0.5)
                                rotation = 0.0f;
                        else
                                rotation = 180.0f;
                }
               
                angularVelocity = 0.0f;
                lastPaddleTouched = -1;
        }
       
        @Override
        public void destroy() {
               
        }
       
        @Override
        public void update(float deltaTime) {
                // move forward, calculating collisions with other entities
               
                lastPosition.x = position.x;
                lastPosition.y = position.y;

            processAngularVelocity(deltaTime);
            moveForward(deltaTime);
            //collectPowerUps;
            //reflectFromCrystals;
            reflectFromBoard(deltaTime);
            reflectFromEntities(deltaTime);
            //updateBallSparks;
           
            //updateTrail
            checkPlayerGainsScore();
        }
       
        public void checkPlayerGainsScore() {
                if (position.y < PLAYFIELD_BORDER) {
                        // reached top -> bottom player scores                 
                        mGameMain.playerGainsScore(Paddle.PADDLE_BOTTOM, lastPaddleTouched);
                        return;
                }
                if (position.y > (mGameMain.getHeight() - PLAYFIELD_BORDER)) {
                        // reached bottom -> top player scores
                        mGameMain.playerGainsScore(Paddle.PADDLE_TOP, lastPaddleTouched);
                        return;
                }                      
        }
       
        public void processAngularVelocity(float deltaTime) {
                rotation += angularVelocity * deltaTime;
        }
       
        public void moveForward(float deltaTime) {
                Vector2D move = new Vector2D();
                move.x = (float)Math.sin(Math.toRadians(rotation));
                move.y = (float)Math.cos(Math.toRadians(rotation));
               
                move.setLength(speed * deltaTime);
               
                position.addVector(move);
               
                Vector2D newPos = new Vector2D(position.x, position.y);
                mTrail.add(newPos);
                while (mTrail.size() > TRAIL_LENGTH)
                        mTrail.remove(0);                      
        }
       
        public void reflectFromBoard(float deltaTime) {
                float impactDirection = -1;
               
                // left bounce
                if (position.x < PLAYFIELD_BORDER) {
                        reflectAlongNormal(90.0f);
                        impactDirection = 0.0f;
                        clampToAngle(BALL_STEEPEST_ANGLE_REFLECT_FROM_BOARD);   // clamp to steepest angle from board
                        mGameMain.playSound(R.raw.wallimpact);
                        mGameMain.getWallImpactEffect().spawnInstance(PLAYFIELD_BORDER, position.y, 90.0f);
                }
               
                // right bounce
                if (position.x > (mGameMain.getWidth() - PLAYFIELD_BORDER)) {
                        reflectAlongNormal(270.0f);
                        impactDirection = 180.0f;
                        clampToAngle(BALL_STEEPEST_ANGLE_REFLECT_FROM_BOARD);   // clamp to steepest angle from board
                        mGameMain.playSound(R.raw.wallimpact);
                        mGameMain.getWallImpactEffect().spawnInstance(mGameMain.getWidth() - PLAYFIELD_BORDER, position.y, 270.0f);
                }
               
                position.x = MathUtil.clampToRange(position.x, 0, mGameMain.getWidth());
               
                // if (impactdirection >- 1) then generateImpact(settings.pos,impactdirection,SX_EIMPACT_MODE_WALL);
        }
       
        public void reflectFromEntities(float deltaTime) {
                // entities in this case are the paddles
                Paddle[] paddles = mGameMain.getPaddles();
                reflectFromPaddle(paddles[Paddle.PADDLE_TOP]);
                reflectFromPaddle(paddles[Paddle.PADDLE_BOTTOM]);
        }
       
        public void reflectFromPaddle(Paddle paddle) {
                // if hit
                if (Math.abs(position.x - paddle.position.x) < (paddle.getWidth()/2.0f)) {
                        float paddleY = paddle.position.y;
                        if (paddleY < (mGameMain.getHeight() / 2.0f)) {
                                paddleY -= PADDLE_HIT_OFFSET;
                                if ((position.y <= paddleY) && (lastPosition.y > paddleY)) {
                                        ballHitsPaddle(paddle);
                                        lastPaddleTouched = paddle.getMode();
                                        mGameMain.playSound(R.raw.paddleimpact);
                                }
                        } else {
                                paddleY += PADDLE_HIT_OFFSET;
                                if ((position.y >= paddleY) && (lastPosition.y < paddleY)) {
                                        ballHitsPaddle(paddle);
                                        lastPaddleTouched = paddle.getMode();
                                        mGameMain.playSound(R.raw.paddleimpact);
                                }
                        }
                }
        }
       
        public void ballHitsPaddle(Paddle paddle) {
               
                paddle.startImpactEffect();
               
                float newY = position.y;
                float xDifference = (position.x - paddle.position.x) * HIT_ZONE_DEVIATION;
               
                // TODO: concave handling
                float direction = 0.0f;
                float paddleDirection = 0.0f;
                int invertPlayer2Paddle = 0;
               
                if (paddle.getMode() == Paddle.PADDLE_TOP) {
                        paddleDirection = 0.0f;
                        //direction = 90.0f - xDifference;
                        direction = xDifference;
                        newY += 6;
                        invertPlayer2Paddle = 1;
                } else {
                        paddleDirection = 180.0f;
                        //direction = 270.0f + xDifference;
                        direction = 180.0f + xDifference;
                        newY -= 6;
                        invertPlayer2Paddle = -1;
                }
               
                // todo: belong to player
                position.y = newY;
                reflectAlongNormal(direction);

                // todo: generate impact               
                // generateImpact(paddle.settings.pos,paddledirection, SX_EIMPACT_MODE_PADDLE);

                float angularSpeed = paddle.getMovementSpeed() * TOP_SPIN_INTENSITY * invertPlayer2Paddle;
                setAngularVelocity(angularVelocity + angularSpeed);
                clampToAngle(BALL_STEEPEST_ANGLE_HIT_PADDLE);
        }
       
        public void reflectAlongNormal(float rot) {
                rotation += 180.0f;
                rotation = MathUtil.stayInDegrees0to360(rotation);
               
                float difference = rot - rotation;
               
                rotation = rot + difference;
                rotation = MathUtil.stayInDegrees0to360(rotation);
               
                if (MathUtil.turnDegrees(rotation, rot) >= 90.0f) {
                        rotation = (float)(2*Math.PI) - rotation;
                        rotation = MathUtil.stayInDegrees0to360(rotation);
                }              
        }
       
        public void clampToAngle(float steepestAngle) {
                rotation = MathUtil.stayInDegrees0to360(rotation);
                if (steepestAngle <= 0)
                        return;
               
                if (rotation < steepestAngle)
                        rotation = steepestAngle;
                if (rotation > (360.0f - steepestAngle))
                        rotation = 360.0f - steepestAngle;
                if ((rotation > (180.0f - steepestAngle)) && (rotation <= 180.0f))
                        rotation = 180.0f - steepestAngle;
                if ((rotation < 180.0f + steepestAngle) && (rotation >= 180.0f))
                        rotation = 180.0f + steepestAngle;
        }
       
        @Override
        public void render(Canvas canvas) {
                Paint paint = new Paint();
               
                Matrix matrix = new Matrix();
                matrix.setTranslate(position.x - mBall.getWidth()/2, position.y - mBall.getHeight()/2);
               
                canvas.drawBitmap(mBall, matrix, paint);

                renderTrail(canvas);
        }
       
        public void renderTrail(Canvas canvas) {
                Paint paint = new Paint();
                paint.setStrokeWidth(10.0f);
               
                for (int i = 0; i < mTrail.size()-1; i++) {
                        int alpha = (int)(((float)i / (float)mTrail.size()) * 128.0f);
                        paint.setColor(Color.argb(alpha, 255, 0, 153));
                        Vector2D pt1 = mTrail.get(i);
                        Vector2D pt2 = mTrail.get(i+1);
                        canvas.drawLine(pt1.x, pt1.y, pt2.x, pt2.y, paint);
                }
        }
       
        public void setAngularVelocity(float angularSpeed) {
                angularVelocity = MathUtil.clampToRange(angularSpeed, -BALL_MAXIMUM_ANGULAR_SPEED, BALL_MAXIMUM_ANGULAR_SPEED);        
        }
       
        public void resetTrail() {
                mTrail.removeAllElements();
        }
       
        public Vector2D getPosition() {
                return position;
        }

}