Rev 6 | Details | Compare with Previous | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 2 | chris | 1 | package com.gebauz.pingK.entities; |
| 2 | |||
| 7 | chris | 3 | import java.util.Vector; |
| 4 | chris | 4 | |
| 2 | chris | 5 | import com.gebauz.pingK.GameMain; |
| 4 | chris | 6 | import com.gebauz.pingK.R; |
| 7 | import com.gebauz.pingK.util.Vector2D; |
||
| 8 | import com.gebauz.pingK.util.MathUtil; |
||
| 2 | chris | 9 | |
| 4 | chris | 10 | import android.graphics.Bitmap; |
| 11 | import android.graphics.BitmapFactory; |
||
| 2 | chris | 12 | import android.graphics.Canvas; |
| 7 | chris | 13 | import android.graphics.Color; |
| 4 | chris | 14 | import android.graphics.Matrix; |
| 15 | import android.graphics.Paint; |
||
| 2 | chris | 16 | |
| 17 | public class Ball extends BaseEntity { |
||
| 18 | |||
| 4 | chris | 19 | public static final int DIRECTION_RANDOM = 0; |
| 20 | public static final int DIRECTION_UP = 1; |
||
| 21 | public static final int DIRECTION_DOWN = 2; |
||
| 22 | |||
| 23 | public static final float BALL_INITIAL_SPEED = 300.0f; |
||
| 24 | public static final float HIT_ZONE_DEVIATION = 0.40f; |
||
| 25 | public static final float BALL_STEEPEST_ANGLE_REFLECT_FROM_BOARD = 1.0f; |
||
| 6 | chris | 26 | public static final float BALL_STEEPEST_ANGLE_HIT_PADDLE = 1.0f; |
| 4 | chris | 27 | public static final float TOP_SPIN_INTENSITY = 0.18f; |
| 28 | public static final float BALL_MAXIMUM_ANGULAR_SPEED = 500.0f; |
||
| 29 | |||
| 30 | public static final float PLAYFIELD_BORDER = 8.0f; |
||
| 7 | chris | 31 | public static final float PADDLE_HIT_OFFSET = 0.0f; |
| 4 | chris | 32 | |
| 33 | Bitmap mBall; |
||
| 34 | |||
| 35 | Vector2D position = new Vector2D(); |
||
| 36 | Vector2D lastPosition = new Vector2D(); |
||
| 6 | chris | 37 | int lastPaddleTouched = -1; |
| 4 | chris | 38 | float size; |
| 39 | |||
| 40 | float speed; |
||
| 41 | float rotation; |
||
| 42 | float angularVelocity; |
||
| 43 | |||
| 7 | chris | 44 | public static final int TRAIL_LENGTH = 15; |
| 45 | Vector<Vector2D> mTrail = new Vector<Vector2D>(); |
||
| 46 | |||
| 2 | chris | 47 | public Ball(GameMain gameMain) { |
| 48 | super(gameMain); |
||
| 7 | chris | 49 | } |
| 2 | chris | 50 | |
| 51 | @Override |
||
| 52 | public void init() { |
||
| 4 | chris | 53 | mBall = BitmapFactory.decodeResource(getResources(), R.drawable.ball); |
| 2 | chris | 54 | |
| 4 | chris | 55 | size = mBall.getWidth(); |
| 56 | |||
| 57 | reset(DIRECTION_RANDOM); |
||
| 2 | chris | 58 | } |
| 59 | |||
| 4 | chris | 60 | public void reset(int direction) { |
| 7 | chris | 61 | resetTrail(); |
| 62 | |||
| 4 | chris | 63 | position.x = mGameMain.getWidth() / 2.0f; |
| 64 | position.y = mGameMain.getHeight() / 2.0f; |
||
| 65 | |||
| 66 | speed = BALL_INITIAL_SPEED; |
||
| 67 | if (direction == DIRECTION_UP) { |
||
| 68 | rotation = 0.0f; |
||
| 69 | |||
| 70 | } else if (direction == DIRECTION_DOWN) { |
||
| 71 | rotation = 180.0f; |
||
| 72 | } else { |
||
| 73 | if (Math.random() > 0.5) |
||
| 74 | rotation = 0.0f; |
||
| 75 | else |
||
| 76 | rotation = 180.0f; |
||
| 77 | } |
||
| 78 | |||
| 79 | angularVelocity = 0.0f; |
||
| 6 | chris | 80 | lastPaddleTouched = -1; |
| 4 | chris | 81 | } |
| 82 | |||
| 2 | chris | 83 | @Override |
| 84 | public void destroy() { |
||
| 85 | |||
| 86 | } |
||
| 87 | |||
| 88 | @Override |
||
| 89 | public void update(float deltaTime) { |
||
| 4 | chris | 90 | // move forward, calculating collisions with other entities |
| 2 | chris | 91 | |
| 4 | chris | 92 | lastPosition.x = position.x; |
| 93 | lastPosition.y = position.y; |
||
| 94 | |||
| 95 | processAngularVelocity(deltaTime); |
||
| 96 | moveForward(deltaTime); |
||
| 97 | //collectPowerUps; |
||
| 98 | //reflectFromCrystals; |
||
| 99 | reflectFromBoard(deltaTime); |
||
| 100 | reflectFromEntities(deltaTime); |
||
| 101 | //updateBallSparks; |
||
| 102 | |||
| 103 | //updateTrail |
||
| 104 | checkPlayerGainsScore(); |
||
| 2 | chris | 105 | } |
| 106 | |||
| 4 | chris | 107 | public void checkPlayerGainsScore() { |
| 108 | if (position.y < PLAYFIELD_BORDER) { |
||
| 6 | chris | 109 | // reached top -> bottom player scores |
| 110 | mGameMain.playerGainsScore(Paddle.PADDLE_BOTTOM, lastPaddleTouched); |
||
| 4 | chris | 111 | return; |
| 112 | } |
||
| 113 | if (position.y > (mGameMain.getHeight() - PLAYFIELD_BORDER)) { |
||
| 6 | chris | 114 | // reached bottom -> top player scores |
| 115 | mGameMain.playerGainsScore(Paddle.PADDLE_TOP, lastPaddleTouched); |
||
| 4 | chris | 116 | return; |
| 117 | } |
||
| 118 | } |
||
| 119 | |||
| 120 | public void processAngularVelocity(float deltaTime) { |
||
| 121 | rotation += angularVelocity * deltaTime; |
||
| 122 | } |
||
| 123 | |||
| 124 | public void moveForward(float deltaTime) { |
||
| 125 | Vector2D move = new Vector2D(); |
||
| 126 | move.x = (float)Math.sin(Math.toRadians(rotation)); |
||
| 127 | move.y = (float)Math.cos(Math.toRadians(rotation)); |
||
| 128 | |||
| 129 | move.setLength(speed * deltaTime); |
||
| 130 | |||
| 131 | position.addVector(move); |
||
| 7 | chris | 132 | |
| 133 | Vector2D newPos = new Vector2D(position.x, position.y); |
||
| 134 | mTrail.add(newPos); |
||
| 135 | while (mTrail.size() > TRAIL_LENGTH) |
||
| 136 | mTrail.remove(0); |
||
| 4 | chris | 137 | } |
| 138 | |||
| 139 | public void reflectFromBoard(float deltaTime) { |
||
| 140 | float impactDirection = -1; |
||
| 141 | |||
| 142 | // left bounce |
||
| 143 | if (position.x < PLAYFIELD_BORDER) { |
||
| 144 | reflectAlongNormal(90.0f); |
||
| 145 | impactDirection = 0.0f; |
||
| 146 | clampToAngle(BALL_STEEPEST_ANGLE_REFLECT_FROM_BOARD); // clamp to steepest angle from board |
||
| 6 | chris | 147 | mGameMain.playSound(R.raw.wallimpact); |
| 7 | chris | 148 | mGameMain.getWallImpactEffect().spawnInstance(PLAYFIELD_BORDER, position.y, 90.0f); |
| 4 | chris | 149 | } |
| 150 | |||
| 151 | // right bounce |
||
| 152 | if (position.x > (mGameMain.getWidth() - PLAYFIELD_BORDER)) { |
||
| 153 | reflectAlongNormal(270.0f); |
||
| 154 | impactDirection = 180.0f; |
||
| 155 | clampToAngle(BALL_STEEPEST_ANGLE_REFLECT_FROM_BOARD); // clamp to steepest angle from board |
||
| 6 | chris | 156 | mGameMain.playSound(R.raw.wallimpact); |
| 7 | chris | 157 | mGameMain.getWallImpactEffect().spawnInstance(mGameMain.getWidth() - PLAYFIELD_BORDER, position.y, 270.0f); |
| 4 | chris | 158 | } |
| 159 | |||
| 160 | position.x = MathUtil.clampToRange(position.x, 0, mGameMain.getWidth()); |
||
| 5 | chris | 161 | |
| 162 | // if (impactdirection >- 1) then generateImpact(settings.pos,impactdirection,SX_EIMPACT_MODE_WALL); |
||
| 4 | chris | 163 | } |
| 164 | |||
| 165 | public void reflectFromEntities(float deltaTime) { |
||
| 166 | // entities in this case are the paddles |
||
| 167 | Paddle[] paddles = mGameMain.getPaddles(); |
||
| 168 | reflectFromPaddle(paddles[Paddle.PADDLE_TOP]); |
||
| 169 | reflectFromPaddle(paddles[Paddle.PADDLE_BOTTOM]); |
||
| 170 | } |
||
| 171 | |||
| 172 | public void reflectFromPaddle(Paddle paddle) { |
||
| 173 | // if hit |
||
| 174 | if (Math.abs(position.x - paddle.position.x) < (paddle.getWidth()/2.0f)) { |
||
| 175 | float paddleY = paddle.position.y; |
||
| 176 | if (paddleY < (mGameMain.getHeight() / 2.0f)) { |
||
| 177 | paddleY -= PADDLE_HIT_OFFSET; |
||
| 178 | if ((position.y <= paddleY) && (lastPosition.y > paddleY)) { |
||
| 179 | ballHitsPaddle(paddle); |
||
| 6 | chris | 180 | lastPaddleTouched = paddle.getMode(); |
| 181 | mGameMain.playSound(R.raw.paddleimpact); |
||
| 4 | chris | 182 | } |
| 183 | } else { |
||
| 184 | paddleY += PADDLE_HIT_OFFSET; |
||
| 185 | if ((position.y >= paddleY) && (lastPosition.y < paddleY)) { |
||
| 186 | ballHitsPaddle(paddle); |
||
| 6 | chris | 187 | lastPaddleTouched = paddle.getMode(); |
| 188 | mGameMain.playSound(R.raw.paddleimpact); |
||
| 4 | chris | 189 | } |
| 190 | } |
||
| 191 | } |
||
| 192 | } |
||
| 193 | |||
| 194 | public void ballHitsPaddle(Paddle paddle) { |
||
| 7 | chris | 195 | |
| 196 | paddle.startImpactEffect(); |
||
| 197 | |||
| 4 | chris | 198 | float newY = position.y; |
| 199 | float xDifference = (position.x - paddle.position.x) * HIT_ZONE_DEVIATION; |
||
| 200 | |||
| 201 | // TODO: concave handling |
||
| 202 | float direction = 0.0f; |
||
| 203 | float paddleDirection = 0.0f; |
||
| 204 | int invertPlayer2Paddle = 0; |
||
| 205 | |||
| 206 | if (paddle.getMode() == Paddle.PADDLE_TOP) { |
||
| 207 | paddleDirection = 0.0f; |
||
| 208 | //direction = 90.0f - xDifference; |
||
| 209 | direction = xDifference; |
||
| 210 | newY += 6; |
||
| 211 | invertPlayer2Paddle = 1; |
||
| 212 | } else { |
||
| 213 | paddleDirection = 180.0f; |
||
| 214 | //direction = 270.0f + xDifference; |
||
| 215 | direction = 180.0f + xDifference; |
||
| 216 | newY -= 6; |
||
| 217 | invertPlayer2Paddle = -1; |
||
| 218 | } |
||
| 219 | |||
| 220 | // todo: belong to player |
||
| 221 | position.y = newY; |
||
| 222 | reflectAlongNormal(direction); |
||
| 5 | chris | 223 | |
| 224 | // todo: generate impact |
||
| 225 | // generateImpact(paddle.settings.pos,paddledirection, SX_EIMPACT_MODE_PADDLE); |
||
| 226 | |||
| 4 | chris | 227 | float angularSpeed = paddle.getMovementSpeed() * TOP_SPIN_INTENSITY * invertPlayer2Paddle; |
| 228 | setAngularVelocity(angularVelocity + angularSpeed); |
||
| 229 | clampToAngle(BALL_STEEPEST_ANGLE_HIT_PADDLE); |
||
| 230 | } |
||
| 231 | |||
| 232 | public void reflectAlongNormal(float rot) { |
||
| 233 | rotation += 180.0f; |
||
| 234 | rotation = MathUtil.stayInDegrees0to360(rotation); |
||
| 235 | |||
| 236 | float difference = rot - rotation; |
||
| 237 | |||
| 238 | rotation = rot + difference; |
||
| 239 | rotation = MathUtil.stayInDegrees0to360(rotation); |
||
| 240 | |||
| 241 | if (MathUtil.turnDegrees(rotation, rot) >= 90.0f) { |
||
| 242 | rotation = (float)(2*Math.PI) - rotation; |
||
| 243 | rotation = MathUtil.stayInDegrees0to360(rotation); |
||
| 244 | } |
||
| 245 | } |
||
| 246 | |||
| 247 | public void clampToAngle(float steepestAngle) { |
||
| 248 | rotation = MathUtil.stayInDegrees0to360(rotation); |
||
| 249 | if (steepestAngle <= 0) |
||
| 250 | return; |
||
| 251 | |||
| 252 | if (rotation < steepestAngle) |
||
| 253 | rotation = steepestAngle; |
||
| 254 | if (rotation > (360.0f - steepestAngle)) |
||
| 255 | rotation = 360.0f - steepestAngle; |
||
| 256 | if ((rotation > (180.0f - steepestAngle)) && (rotation <= 180.0f)) |
||
| 257 | rotation = 180.0f - steepestAngle; |
||
| 258 | if ((rotation < 180.0f + steepestAngle) && (rotation >= 180.0f)) |
||
| 259 | rotation = 180.0f + steepestAngle; |
||
| 260 | } |
||
| 261 | |||
| 2 | chris | 262 | @Override |
| 263 | public void render(Canvas canvas) { |
||
| 4 | chris | 264 | Paint paint = new Paint(); |
| 2 | chris | 265 | |
| 4 | chris | 266 | Matrix matrix = new Matrix(); |
| 267 | matrix.setTranslate(position.x - mBall.getWidth()/2, position.y - mBall.getHeight()/2); |
||
| 268 | |||
| 269 | canvas.drawBitmap(mBall, matrix, paint); |
||
| 7 | chris | 270 | |
| 271 | renderTrail(canvas); |
||
| 2 | chris | 272 | } |
| 4 | chris | 273 | |
| 7 | chris | 274 | public void renderTrail(Canvas canvas) { |
| 275 | Paint paint = new Paint(); |
||
| 276 | paint.setStrokeWidth(10.0f); |
||
| 277 | |||
| 278 | for (int i = 0; i < mTrail.size()-1; i++) { |
||
| 279 | int alpha = (int)(((float)i / (float)mTrail.size()) * 128.0f); |
||
| 280 | paint.setColor(Color.argb(alpha, 255, 0, 153)); |
||
| 281 | Vector2D pt1 = mTrail.get(i); |
||
| 282 | Vector2D pt2 = mTrail.get(i+1); |
||
| 283 | canvas.drawLine(pt1.x, pt1.y, pt2.x, pt2.y, paint); |
||
| 284 | } |
||
| 285 | } |
||
| 286 | |||
| 4 | chris | 287 | public void setAngularVelocity(float angularSpeed) { |
| 288 | angularVelocity = MathUtil.clampToRange(angularSpeed, -BALL_MAXIMUM_ANGULAR_SPEED, BALL_MAXIMUM_ANGULAR_SPEED); |
||
| 289 | } |
||
| 7 | chris | 290 | |
| 291 | public void resetTrail() { |
||
| 292 | mTrail.removeAllElements(); |
||
| 293 | } |
||
| 294 | |||
| 295 | public Vector2D getPosition() { |
||
| 296 | return position; |
||
| 297 | } |
||
| 2 | chris | 298 | |
| 299 | } |