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
;
}
}