Compare commits
3 Commits
fe2c68d39a
...
e4000998cc
Author | SHA1 | Date | |
---|---|---|---|
e4000998cc | |||
92201411b9 | |||
0994289c7e |
@ -46,6 +46,7 @@ project(":desktop") {
|
||||
compile project(":core")
|
||||
compile "com.badlogicgames.gdx:gdx-backend-lwjgl:$gdxVersion"
|
||||
compile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"
|
||||
compile "com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-desktop"
|
||||
|
||||
}
|
||||
}
|
||||
@ -56,7 +57,9 @@ project(":core") {
|
||||
|
||||
dependencies {
|
||||
compile "com.badlogicgames.gdx:gdx:$gdxVersion"
|
||||
compile "com.badlogicgames.gdx:gdx-box2d:$gdxVersion"
|
||||
compile "net.dermetfan.libgdx-utils:libgdx-utils:0.13.4"
|
||||
compile "net.dermetfan.libgdx-utils:libgdx-utils-box2d:0.13.4"
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -6,102 +6,108 @@ import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
|
||||
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
|
||||
import com.badlogic.gdx.math.MathUtils;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.me.brickbuster.BrickBuster;
|
||||
import com.me.brickbuster.Utils;
|
||||
import com.badlogic.gdx.physics.box2d.Body;
|
||||
import com.badlogic.gdx.physics.box2d.BodyDef;
|
||||
import com.badlogic.gdx.physics.box2d.CircleShape;
|
||||
import com.badlogic.gdx.physics.box2d.FixtureDef;
|
||||
import com.me.brickbuster.physics.CollisionListener;
|
||||
import com.me.brickbuster.physics.PhysicsBody;
|
||||
import com.me.brickbuster.state.PlayState;
|
||||
import net.dermetfan.utils.Pair;
|
||||
|
||||
import java.util.Iterator;
|
||||
public class Ball extends Entity implements PhysicsBody, CollisionListener {
|
||||
|
||||
public class Ball extends Entity {
|
||||
|
||||
public static final int RADIUS = 45;
|
||||
public static final float RADIUS = 1.2f;
|
||||
public static final Color BALL_COLOR = Color.CHARTREUSE;
|
||||
public static final float DEFAULT_SPEED = 1800;
|
||||
public static final float BOOST_SPEED = 2200;
|
||||
public static final float DEFAULT_SPEED = 45;
|
||||
public static final float BOOST_SPEED = 55;
|
||||
public static final int BLOCKS_FOR_BOOST = 39;
|
||||
|
||||
public Vector2 direction;
|
||||
private float speed;
|
||||
private boolean isStuck = true;
|
||||
private boolean isDead = false;
|
||||
private boolean touchedPaddle = false;
|
||||
|
||||
private Body body;
|
||||
|
||||
public Ball(PlayState state) {
|
||||
super(state,BrickBuster.BOARD_WIDTH /2, state.paddle.getY() + Paddle.PADDLE_HEIGHT + RADIUS);
|
||||
super(state, state.paddle.getX(), state.paddle.getY() + Paddle.PADDLE_HEIGHT + RADIUS);
|
||||
this.speed = state.bricks.size() > BLOCKS_FOR_BOOST? DEFAULT_SPEED : BOOST_SPEED;
|
||||
createBody();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(ShapeRenderer sr) {
|
||||
sr.begin(ShapeType.Filled);
|
||||
sr.setColor(BALL_COLOR);
|
||||
sr.circle(pos.x, pos.y, RADIUS);
|
||||
sr.circle(pos.x * PlayState.PIXEL_PER_METER,
|
||||
pos.y * PlayState.PIXEL_PER_METER,
|
||||
RADIUS * PlayState.PIXEL_PER_METER);
|
||||
sr.end();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(float dt) {
|
||||
if (isStuck || isDead) {
|
||||
if (!isDead && Gdx.input.justTouched()) {
|
||||
if (isStuck || deleted) {
|
||||
if (!deleted && Gdx.input.justTouched()) {
|
||||
launch();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 new_pos = pos.cpy().add(direction.cpy().scl(speed * dt));
|
||||
|
||||
boolean brickCollision = false;
|
||||
Iterator<Brick> brickIterator = state.bricks.iterator();
|
||||
while (!brickCollision && brickIterator.hasNext()) {
|
||||
Brick brick = brickIterator.next();
|
||||
Vector2[] vertices = brick.getVertices();
|
||||
for(int i = 0; i < vertices.length; i++) {
|
||||
Vector2 v1 = vertices[i];
|
||||
Vector2 v2 = vertices[i+1 < vertices.length? i+1 : 0];
|
||||
Vector2 segment = v2.cpy().sub(v1);
|
||||
Vector2 nearest = Utils.nearestPoint(v1.cpy(), segment, new_pos.cpy());
|
||||
|
||||
if (nearest.dst(new_pos.x, new_pos.y) <= RADIUS) {
|
||||
if (brick.hit()) {
|
||||
brickIterator.remove();
|
||||
}
|
||||
Utils.reflect(direction, segment.nor());
|
||||
brickCollision = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (touchedPaddle) {
|
||||
paddleCollision();
|
||||
touchedPaddle = false;
|
||||
}
|
||||
|
||||
if (new_pos.x + RADIUS > BrickBuster.BOARD_WIDTH || new_pos.x - RADIUS < 0) {
|
||||
Utils.reflect(direction, Utils.VERTICAL_EDGE);
|
||||
} else if (new_pos.y + RADIUS > BrickBuster.BOARD_HEIGHT) {
|
||||
Utils.reflect(direction, Utils.HORIZONTAL_EDGE);
|
||||
} else if (state.getShieldCount() > 0
|
||||
&& new_pos.y - RADIUS < PlayState.SHIELD_HEIGHT * state.getShieldCount()) {
|
||||
Utils.reflect(direction, Utils.HORIZONTAL_EDGE);
|
||||
state.removeShield();
|
||||
} else if (new_pos.y + RADIUS < 0) {
|
||||
isDead = true;
|
||||
return;
|
||||
} else if (direction.y < 0 && new_pos.y <= state.paddle.getY() + Paddle.PADDLE_HEIGHT + RADIUS) {
|
||||
Pair<Vector2, Vector2> paddle = state.paddle.getTopEdge();
|
||||
Vector2 lineDir = paddle.getValue().sub(paddle.getKey());
|
||||
Vector2 nearest = Utils.nearestPoint(paddle.getKey().cpy(), lineDir, new_pos.cpy());
|
||||
|
||||
if (nearest.dst(new_pos.x, new_pos.y) <= RADIUS) {
|
||||
paddleCollision();
|
||||
if (state.paddle.isSticky()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (getY() + RADIUS < 0) {
|
||||
deleted = true;
|
||||
}
|
||||
|
||||
pos.add(direction.cpy().scl(speed * dt));
|
||||
body.setLinearVelocity(body.getLinearVelocity().nor().scl(speed));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Body getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createBody() {
|
||||
BodyDef ballBody = new BodyDef();
|
||||
ballBody.type = BodyDef.BodyType.DynamicBody;
|
||||
ballBody.position.set(pos);
|
||||
|
||||
CircleShape ballShape = new CircleShape();
|
||||
ballShape.setRadius(RADIUS);
|
||||
|
||||
FixtureDef ballFixture = new FixtureDef();
|
||||
ballFixture.shape = ballShape;
|
||||
ballFixture.restitution = 1f;
|
||||
ballFixture.friction = 0f;
|
||||
|
||||
body = state.world.createBody(ballBody);
|
||||
body.createFixture(ballFixture);
|
||||
body.setUserData(this);
|
||||
|
||||
ballShape.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginContact(Entity contacted) {
|
||||
if (contacted instanceof Shield) {
|
||||
contacted.delete();
|
||||
}
|
||||
|
||||
if (contacted instanceof Paddle && !isStuck) {
|
||||
touchedPaddle = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endContact(Entity contacted) {
|
||||
}
|
||||
|
||||
public Vector2 paddleReflectAngle() {
|
||||
int trim = (int) (state.paddle.getWidth() * 0.10);
|
||||
float trim = state.paddle.getWidth() * 0.10f;
|
||||
float rel = MathUtils.clamp((pos.x - state.paddle.getX()) + (state.paddle.getWidth()/2),
|
||||
trim, state.paddle.getWidth()-trim);
|
||||
float newAngle = MathUtils.PI - (MathUtils.PI * (rel / state.paddle.getWidth()));
|
||||
@ -109,23 +115,43 @@ public class Ball extends Entity {
|
||||
}
|
||||
|
||||
public void launch() {
|
||||
Vector2 direction;
|
||||
if (state.paddle.isSticky()) {
|
||||
direction = paddleReflectAngle();
|
||||
} else {
|
||||
// launch at random angle between 135 and 45 degrees
|
||||
float angle = MathUtils.random(MathUtils.PI/2) + MathUtils.PI/4;
|
||||
direction = new Vector2(MathUtils.cos(angle), MathUtils.sin(angle));
|
||||
|
||||
}
|
||||
body.setLinearVelocity(direction.scl(speed));
|
||||
isStuck = false;
|
||||
}
|
||||
|
||||
public void paddleCollision() {
|
||||
if (state.paddle.isSticky()) {
|
||||
isStuck = true;
|
||||
pos.y = state.paddle.getY() + Paddle.PADDLE_HEIGHT + RADIUS;
|
||||
body.setLinearVelocity(new Vector2());
|
||||
setY(state.paddle.getY() + Paddle.PADDLE_HEIGHT + RADIUS);
|
||||
return;
|
||||
}
|
||||
direction = paddleReflectAngle();
|
||||
body.setLinearVelocity(paddleReflectAngle().scl(speed));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setX(float x) {
|
||||
super.setX(x);
|
||||
Vector2 bodyPos = body.getPosition();
|
||||
bodyPos.x = x;
|
||||
body.setTransform(bodyPos, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setY(float y) {
|
||||
super.setY(y);
|
||||
Vector2 bodyPos = body.getPosition();
|
||||
bodyPos.y = y;
|
||||
body.setTransform(bodyPos, 0);
|
||||
}
|
||||
|
||||
public void setSpeed(float speed) {
|
||||
@ -136,15 +162,4 @@ public class Ball extends Entity {
|
||||
return isStuck;
|
||||
}
|
||||
|
||||
public boolean isDead() {
|
||||
return isDead;
|
||||
}
|
||||
|
||||
public void setDirection(Vector2 direction) {
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
public void setStuck(boolean stuck) {
|
||||
isStuck = stuck;
|
||||
}
|
||||
}
|
||||
|
@ -4,50 +4,96 @@ import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
|
||||
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.physics.box2d.Body;
|
||||
import com.badlogic.gdx.physics.box2d.BodyDef;
|
||||
import com.badlogic.gdx.physics.box2d.FixtureDef;
|
||||
import com.badlogic.gdx.physics.box2d.PolygonShape;
|
||||
import com.me.brickbuster.entity.powerup.PowerUpType;
|
||||
import com.me.brickbuster.physics.CollisionListener;
|
||||
import com.me.brickbuster.physics.PhysicsBody;
|
||||
import com.me.brickbuster.state.PlayState;
|
||||
|
||||
public class Brick extends Entity {
|
||||
public class Brick extends Entity implements PhysicsBody, CollisionListener {
|
||||
|
||||
public static final Color DEFAULT_COLOR = Color.FOREST;
|
||||
public static final int BRICK_WIDTH = 200;
|
||||
public static final int BRICK_HEIGHT = 100;
|
||||
public static final float BRICK_WIDTH = 5f;
|
||||
public static final float BRICK_HEIGHT = 2.5f;
|
||||
|
||||
private PowerUpType powerUpType;
|
||||
private Color color;
|
||||
|
||||
private Vector2[] vertices;
|
||||
private Body body;
|
||||
private boolean hitByBall = false;
|
||||
|
||||
public Brick(PlayState state, PowerUpType powerUpType, int x, int y) {
|
||||
public Brick(PlayState state, PowerUpType powerUpType, float x, float y) {
|
||||
this(state, powerUpType, true, x, y);
|
||||
}
|
||||
|
||||
public Brick(PlayState state, PowerUpType powerUpType, boolean hidePowerup, int x, int y) {
|
||||
public Brick(PlayState state, PowerUpType powerUpType, boolean hidePowerup, float x, float y) {
|
||||
this(state, powerUpType, DEFAULT_COLOR, hidePowerup, x, y);
|
||||
}
|
||||
|
||||
public Brick(PlayState state, PowerUpType powerUpType, Color color, boolean hidePowerUp, int x, int y) {
|
||||
public Brick(PlayState state, PowerUpType powerUpType, Color color, boolean hidePowerUp, float x, float y) {
|
||||
super(state, x, y);
|
||||
this.powerUpType = powerUpType;
|
||||
this.color = powerUpType != null && !hidePowerUp? powerUpType.getColor() : color;
|
||||
this.vertices = new Vector2[] {
|
||||
new Vector2(x, y),
|
||||
new Vector2(x + BRICK_WIDTH, y),
|
||||
new Vector2(x + BRICK_WIDTH, y + BRICK_HEIGHT),
|
||||
new Vector2(x, y + BRICK_HEIGHT)
|
||||
};
|
||||
createBody();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(ShapeRenderer sr) {
|
||||
sr.begin(ShapeType.Filled);
|
||||
sr.setColor(color);
|
||||
sr.rect(getX(), getY(), BRICK_WIDTH, BRICK_HEIGHT);
|
||||
sr.rect(getX() * PlayState.PIXEL_PER_METER,
|
||||
getY() * PlayState.PIXEL_PER_METER,
|
||||
BRICK_WIDTH * PlayState.PIXEL_PER_METER,
|
||||
BRICK_HEIGHT * PlayState.PIXEL_PER_METER);
|
||||
sr.end();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(float dt) {
|
||||
if (hitByBall) {
|
||||
hit();
|
||||
hitByBall = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Body getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createBody() {
|
||||
BodyDef brickBody = new BodyDef();
|
||||
brickBody.type = BodyDef.BodyType.StaticBody;
|
||||
brickBody.position.set(pos.cpy());
|
||||
|
||||
PolygonShape brickShape = new PolygonShape();
|
||||
brickShape.setAsBox(BRICK_WIDTH/2, BRICK_HEIGHT/2,
|
||||
new Vector2(BRICK_WIDTH/2,BRICK_HEIGHT/2), 0);
|
||||
|
||||
FixtureDef brickFixture = new FixtureDef();
|
||||
brickFixture.shape = brickShape;
|
||||
brickFixture.friction = 0f;
|
||||
|
||||
body = state.world.createBody(brickBody);
|
||||
body.createFixture(brickFixture);
|
||||
body.setUserData(this);
|
||||
|
||||
brickShape.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginContact(Entity contacted) {
|
||||
if (contacted instanceof Ball) {
|
||||
hitByBall = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endContact(Entity contacted) {
|
||||
}
|
||||
|
||||
public boolean hit() {
|
||||
@ -60,11 +106,9 @@ public class Brick extends Entity {
|
||||
if (powerUpType != null) {
|
||||
state.powerUps.add(powerUpType.createInstance(state, this));
|
||||
}
|
||||
|
||||
deleted = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
public Vector2[] getVertices() {
|
||||
return vertices;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ public abstract class Entity {
|
||||
|
||||
protected PlayState state;
|
||||
protected Vector2 pos;
|
||||
protected boolean deleted = false;
|
||||
|
||||
public Entity(PlayState state, Vector2 pos) {
|
||||
this(state, pos.x, pos.y);
|
||||
@ -46,4 +47,12 @@ public abstract class Entity {
|
||||
pos.y = y;
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
this.deleted = true;
|
||||
}
|
||||
|
||||
public boolean isDeleted() {
|
||||
return deleted;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -6,31 +6,43 @@ import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
|
||||
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.me.brickbuster.BrickBuster;
|
||||
import com.badlogic.gdx.physics.box2d.Body;
|
||||
import com.badlogic.gdx.physics.box2d.BodyDef;
|
||||
import com.badlogic.gdx.physics.box2d.EdgeShape;
|
||||
import com.badlogic.gdx.physics.box2d.FixtureDef;
|
||||
import com.me.brickbuster.physics.CollisionListener;
|
||||
import com.me.brickbuster.physics.PhysicsBody;
|
||||
import com.me.brickbuster.state.PlayState;
|
||||
import net.dermetfan.utils.Pair;
|
||||
|
||||
public class Paddle extends Entity {
|
||||
public class Paddle extends Entity implements PhysicsBody {
|
||||
|
||||
public static final Color STICKY_COLOR = Color.GRAY;
|
||||
public static final Color PADDLE_COLOR = Color.BLACK;
|
||||
public static final int DEFAULT_WIDTH = 300;
|
||||
public static final int PADDLE_HEIGHT = 30;
|
||||
public static final int PADDLE_Y = 50;
|
||||
public static final int PADDLE_SPEED = 1500;
|
||||
|
||||
private int width = DEFAULT_WIDTH;
|
||||
public static final float DEFAULT_WIDTH = 7.5f;
|
||||
public static final float PADDLE_HEIGHT = 0.75f;
|
||||
public static final float PADDLE_Y = 1.25f;
|
||||
public static final float PADDLE_SPEED = 40f;
|
||||
|
||||
private float width = DEFAULT_WIDTH;
|
||||
private boolean sticky = false;
|
||||
|
||||
private Body body;
|
||||
|
||||
public Paddle(PlayState brickBuster) {
|
||||
super(brickBuster, BrickBuster.BOARD_WIDTH / 2, PADDLE_Y);
|
||||
super(brickBuster, PlayState.BOARD_WIDTH/2, PADDLE_Y);
|
||||
createBody();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(ShapeRenderer sr) {
|
||||
sr.begin(ShapeType.Filled);
|
||||
sr.setColor(sticky? STICKY_COLOR : PADDLE_COLOR);
|
||||
sr.rect(getX() - width/2, getY(), width, PADDLE_HEIGHT);
|
||||
sr.rect((getX() - width/2) * PlayState.PIXEL_PER_METER,
|
||||
getY() * PlayState.PIXEL_PER_METER,
|
||||
width * PlayState.PIXEL_PER_METER,
|
||||
PADDLE_HEIGHT * PlayState.PIXEL_PER_METER);
|
||||
sr.end();
|
||||
}
|
||||
|
||||
@ -41,7 +53,7 @@ public class Paddle extends Entity {
|
||||
setX(width/2);
|
||||
return;
|
||||
}
|
||||
pos.x = pos.x - PADDLE_SPEED * dt;
|
||||
setX(pos.x - PADDLE_SPEED * dt);
|
||||
|
||||
for (Ball ball : state.balls) {
|
||||
if (ball.isStuck()) {
|
||||
@ -50,11 +62,11 @@ public class Paddle extends Entity {
|
||||
}
|
||||
}
|
||||
if (Gdx.input.isKeyPressed(Input.Keys.RIGHT)) {
|
||||
if (pos.x + width/2 + PADDLE_SPEED * dt > BrickBuster.BOARD_WIDTH) {
|
||||
setX(BrickBuster.BOARD_WIDTH - width/2);
|
||||
if (pos.x + width/2 + PADDLE_SPEED * dt > PlayState.BOARD_WIDTH) {
|
||||
setX(PlayState.BOARD_WIDTH - width/2);
|
||||
return;
|
||||
}
|
||||
pos.x = pos.x + PADDLE_SPEED * dt;
|
||||
setX(pos.x + PADDLE_SPEED * dt);
|
||||
|
||||
for (Ball ball : state.balls) {
|
||||
if (ball.isStuck()) {
|
||||
@ -64,19 +76,59 @@ public class Paddle extends Entity {
|
||||
}
|
||||
}
|
||||
|
||||
public Pair<Vector2, Vector2> getTopEdge() {
|
||||
return new Pair<Vector2, Vector2>(
|
||||
new Vector2(pos.x - width/2, pos.y + PADDLE_HEIGHT),
|
||||
new Vector2(pos.x + width/2, pos.y + PADDLE_HEIGHT)
|
||||
);
|
||||
@Override
|
||||
public void createBody() {
|
||||
BodyDef paddleBody = new BodyDef();
|
||||
paddleBody.type = BodyDef.BodyType.KinematicBody;
|
||||
paddleBody.position.set(pos);
|
||||
|
||||
EdgeShape paddleShape = new EdgeShape();
|
||||
paddleShape.set(new Vector2(-width/2, PADDLE_HEIGHT),
|
||||
new Vector2(width/2, PADDLE_HEIGHT));
|
||||
|
||||
FixtureDef paddleFixture = new FixtureDef();
|
||||
paddleFixture.shape = paddleShape;
|
||||
//paddleFixture.isSensor = true;
|
||||
|
||||
body = state.world.createBody(paddleBody);
|
||||
body.createFixture(paddleFixture);
|
||||
body.setUserData(this);
|
||||
|
||||
paddleShape.dispose();
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
@Override
|
||||
public Body getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setX(float x) {
|
||||
super.setX(x);
|
||||
Vector2 bodyPos = body.getPosition();
|
||||
bodyPos.x = x;
|
||||
body.setTransform(bodyPos, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setY(float y) {
|
||||
super.setY(y);
|
||||
Vector2 bodyPos = body.getPosition();
|
||||
bodyPos.y = y;
|
||||
body.setTransform(bodyPos, 0);
|
||||
}
|
||||
|
||||
public float getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public void setWidth(int width) {
|
||||
public void setWidth(float width) {
|
||||
if (this.width == width) {
|
||||
return;
|
||||
}
|
||||
state.world.destroyBody(body);
|
||||
this.width = width;
|
||||
createBody();
|
||||
}
|
||||
|
||||
public boolean isSticky() {
|
||||
|
67
core/src/com/me/brickbuster/entity/Shield.java
Normal file
67
core/src/com/me/brickbuster/entity/Shield.java
Normal file
@ -0,0 +1,67 @@
|
||||
package com.me.brickbuster.entity;
|
||||
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.physics.box2d.Body;
|
||||
import com.badlogic.gdx.physics.box2d.BodyDef;
|
||||
import com.badlogic.gdx.physics.box2d.FixtureDef;
|
||||
import com.badlogic.gdx.physics.box2d.PolygonShape;
|
||||
import com.me.brickbuster.entity.powerup.PowerUpType;
|
||||
import com.me.brickbuster.physics.PhysicsBody;
|
||||
import com.me.brickbuster.state.PlayState;
|
||||
|
||||
public class Shield extends Entity implements PhysicsBody {
|
||||
|
||||
public static final float SHIELD_HEIGHT = 0.75f;
|
||||
|
||||
private Body body;
|
||||
|
||||
public Shield(PlayState state) {
|
||||
super(state, 0, state.getShieldCount() * SHIELD_HEIGHT);
|
||||
createBody();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(ShapeRenderer sr) {
|
||||
sr.begin(ShapeRenderer.ShapeType.Filled);
|
||||
sr.setColor(PowerUpType.SHIELD.getColor());
|
||||
sr.rect(0, pos.y * PlayState.PIXEL_PER_METER,
|
||||
PlayState.BOARD_WIDTH * PlayState.PIXEL_PER_METER,
|
||||
SHIELD_HEIGHT * PlayState.PIXEL_PER_METER);
|
||||
sr.end();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(float dt) {
|
||||
if (deleted) {
|
||||
state.removeShield(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createBody() {
|
||||
BodyDef brickBody = new BodyDef();
|
||||
brickBody.type = BodyDef.BodyType.StaticBody;
|
||||
brickBody.position.set(pos.cpy());
|
||||
|
||||
PolygonShape brickShape = new PolygonShape();
|
||||
brickShape.setAsBox(PlayState.BOARD_WIDTH/2, SHIELD_HEIGHT/2,
|
||||
new Vector2(PlayState.BOARD_WIDTH/2,SHIELD_HEIGHT/2), 0);
|
||||
|
||||
FixtureDef brickFixture = new FixtureDef();
|
||||
brickFixture.shape = brickShape;
|
||||
brickFixture.friction = 0f;
|
||||
|
||||
body = state.world.createBody(brickBody);
|
||||
body.createFixture(brickFixture);
|
||||
body.setUserData(this);
|
||||
|
||||
brickShape.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Body getBody() {
|
||||
return body;
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.math.MathUtils;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.me.brickbuster.entity.Ball;
|
||||
import com.me.brickbuster.entity.Paddle;
|
||||
import com.me.brickbuster.state.PlayState;
|
||||
|
||||
public class MultiBallPowerUp extends PowerUp {
|
||||
@ -19,10 +20,7 @@ public class MultiBallPowerUp extends PowerUp {
|
||||
public void activate() {
|
||||
for (int x = 0; x < 2; x++) {
|
||||
Ball ball = new Ball(state);
|
||||
ball.setPos(pos.cpy());
|
||||
float angle = MathUtils.random(MathUtils.PI*2);
|
||||
ball.setDirection(new Vector2(MathUtils.cos(angle), MathUtils.sin(angle)));
|
||||
ball.setStuck(false);
|
||||
ball.launch();
|
||||
state.balls.add(ball);
|
||||
}
|
||||
}
|
||||
|
@ -4,53 +4,93 @@ import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
|
||||
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.physics.box2d.Body;
|
||||
import com.badlogic.gdx.physics.box2d.BodyDef;
|
||||
import com.badlogic.gdx.physics.box2d.CircleShape;
|
||||
import com.badlogic.gdx.physics.box2d.FixtureDef;
|
||||
import com.me.brickbuster.Utils;
|
||||
import com.me.brickbuster.entity.Entity;
|
||||
import com.me.brickbuster.entity.Paddle;
|
||||
import com.me.brickbuster.physics.CollisionListener;
|
||||
import com.me.brickbuster.physics.PhysicsBody;
|
||||
import com.me.brickbuster.state.PlayState;
|
||||
import net.dermetfan.utils.Pair;
|
||||
|
||||
public abstract class PowerUp extends Entity {
|
||||
public abstract class PowerUp extends Entity implements PhysicsBody, CollisionListener {
|
||||
|
||||
public static final int RADIUS = 45;
|
||||
public static final int FALL_SPEED = 600;
|
||||
public static final float RADIUS = 1.2f;
|
||||
public static final float FALL_SPEED = 15f;
|
||||
|
||||
private Color color;
|
||||
private boolean isCaught;
|
||||
|
||||
private Body body;
|
||||
|
||||
public PowerUp(PlayState state, Vector2 pos, Color color) {
|
||||
super(state, pos);
|
||||
this.color = color;
|
||||
createBody();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(ShapeRenderer sr) {
|
||||
sr.begin(ShapeType.Filled);
|
||||
sr.setColor(color);
|
||||
sr.circle(getX(), getY(), RADIUS);
|
||||
sr.circle(getX() * PlayState.PIXEL_PER_METER,
|
||||
getY() * PlayState.PIXEL_PER_METER,
|
||||
RADIUS * PlayState.PIXEL_PER_METER);
|
||||
sr.end();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(float dt) {
|
||||
setY(getY() - FALL_SPEED * dt);
|
||||
|
||||
Pair<Vector2, Vector2> paddle = state.paddle.getTopEdge();
|
||||
Vector2 lineDir = paddle.getValue().sub(paddle.getKey());
|
||||
Vector2 nearest = Utils.nearestPoint(paddle.getKey().cpy(), lineDir, getPos().cpy());
|
||||
|
||||
if (nearest.dst(getX(), getY()) <= RADIUS) {
|
||||
if (isCaught) {
|
||||
activate();
|
||||
isCaught = true;
|
||||
delete();
|
||||
}
|
||||
|
||||
if (getY() + RADIUS < 0) {
|
||||
delete();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Body getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createBody() {
|
||||
BodyDef ballBody = new BodyDef();
|
||||
ballBody.type = BodyDef.BodyType.DynamicBody;
|
||||
ballBody.position.set(pos);
|
||||
|
||||
CircleShape ballShape = new CircleShape();
|
||||
ballShape.setRadius(RADIUS);
|
||||
|
||||
FixtureDef ballFixture = new FixtureDef();
|
||||
ballFixture.shape = ballShape;
|
||||
ballFixture.isSensor = true;
|
||||
|
||||
body = state.world.createBody(ballBody);
|
||||
body.createFixture(ballFixture);
|
||||
body.setUserData(this);
|
||||
|
||||
body.setLinearVelocity(0f, -FALL_SPEED);
|
||||
|
||||
ballShape.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginContact(Entity contacted) {
|
||||
if (contacted instanceof Paddle) {
|
||||
isCaught = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endContact(Entity contacted) {}
|
||||
|
||||
public abstract void activate();
|
||||
|
||||
public boolean isCaught() {
|
||||
return isCaught;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,53 @@
|
||||
package com.me.brickbuster.physics;
|
||||
|
||||
import com.badlogic.gdx.physics.box2d.Contact;
|
||||
import com.badlogic.gdx.physics.box2d.ContactImpulse;
|
||||
import com.badlogic.gdx.physics.box2d.ContactListener;
|
||||
import com.badlogic.gdx.physics.box2d.Manifold;
|
||||
import com.me.brickbuster.entity.Entity;
|
||||
|
||||
public class Box2dContactListener implements ContactListener {
|
||||
|
||||
@Override
|
||||
public void beginContact(Contact contact) {
|
||||
Object userDataA = contact.getFixtureA().getBody().getUserData();
|
||||
Object userDataB = contact.getFixtureB().getBody().getUserData();
|
||||
|
||||
if (userDataA == null || userDataB == null ||
|
||||
!(userDataA instanceof Entity) || !(userDataB instanceof Entity)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (userDataA instanceof CollisionListener) {
|
||||
((CollisionListener) userDataA).beginContact((Entity) userDataB);
|
||||
}
|
||||
if (userDataB instanceof CollisionListener) {
|
||||
((CollisionListener) userDataB).beginContact((Entity) userDataA);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endContact(Contact contact) {
|
||||
Object userDataA = contact.getFixtureA().getBody().getUserData();
|
||||
Object userDataB = contact.getFixtureB().getBody().getUserData();
|
||||
|
||||
if (userDataA == null || userDataB == null ||
|
||||
!(userDataA instanceof Entity) || !(userDataB instanceof Entity)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (userDataA instanceof CollisionListener) {
|
||||
((CollisionListener) userDataA).endContact((Entity) userDataB);
|
||||
}
|
||||
if (userDataB instanceof CollisionListener) {
|
||||
((CollisionListener) userDataB).endContact((Entity) userDataA);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preSolve(Contact contact, Manifold oldManifold) {}
|
||||
|
||||
@Override
|
||||
public void postSolve(Contact contact, ContactImpulse impulse) {}
|
||||
|
||||
}
|
11
core/src/com/me/brickbuster/physics/CollisionListener.java
Normal file
11
core/src/com/me/brickbuster/physics/CollisionListener.java
Normal file
@ -0,0 +1,11 @@
|
||||
package com.me.brickbuster.physics;
|
||||
|
||||
import com.me.brickbuster.entity.Entity;
|
||||
|
||||
public interface CollisionListener {
|
||||
|
||||
void beginContact(Entity contacted);
|
||||
|
||||
void endContact(Entity contacted);
|
||||
|
||||
}
|
11
core/src/com/me/brickbuster/physics/PhysicsBody.java
Normal file
11
core/src/com/me/brickbuster/physics/PhysicsBody.java
Normal file
@ -0,0 +1,11 @@
|
||||
package com.me.brickbuster.physics;
|
||||
|
||||
import com.badlogic.gdx.physics.box2d.Body;
|
||||
|
||||
public interface PhysicsBody {
|
||||
|
||||
void createBody();
|
||||
|
||||
Body getBody();
|
||||
|
||||
}
|
@ -2,63 +2,125 @@ package com.me.brickbuster.state;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
|
||||
import com.badlogic.gdx.math.MathUtils;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.physics.box2d.*;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.me.brickbuster.BrickBuster;
|
||||
import com.me.brickbuster.entity.Ball;
|
||||
import com.me.brickbuster.entity.Brick;
|
||||
import com.me.brickbuster.entity.Paddle;
|
||||
import com.me.brickbuster.entity.powerup.*;
|
||||
import com.me.brickbuster.entity.*;
|
||||
import com.me.brickbuster.entity.powerup.PowerUp;
|
||||
import com.me.brickbuster.entity.powerup.PowerUpType;
|
||||
import com.me.brickbuster.physics.Box2dContactListener;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class PlayState extends State {
|
||||
|
||||
public static final int SHIELD_HEIGHT = 30;
|
||||
public static final float PIXEL_PER_METER = 40f; // Board dimension: 54x96 meters
|
||||
public static final float BOARD_WIDTH = 54f;
|
||||
public static final float BOARD_HEIGHT = 96f;
|
||||
public static final float EDGE_PADDING = .125f;
|
||||
|
||||
public static final float POWERUP_CHANCE = 0.15f;
|
||||
|
||||
public static final int COLUMNS = 9;
|
||||
public static final int ROWS = 8;
|
||||
|
||||
public static final Vector2 lowerLeftCorner =
|
||||
new Vector2(EDGE_PADDING,EDGE_PADDING);
|
||||
public static final Vector2 lowerRightCorner =
|
||||
new Vector2(BrickBuster.BOARD_WIDTH/PIXEL_PER_METER-EDGE_PADDING,EDGE_PADDING);
|
||||
public static final Vector2 upperRightCorner =
|
||||
new Vector2(BrickBuster.BOARD_WIDTH/PIXEL_PER_METER-EDGE_PADDING, BrickBuster.BOARD_HEIGHT/PIXEL_PER_METER-EDGE_PADDING);
|
||||
public static final Vector2 upperLeftCorner =
|
||||
new Vector2(EDGE_PADDING, BrickBuster.BOARD_HEIGHT/PIXEL_PER_METER-EDGE_PADDING);
|
||||
|
||||
public World world;
|
||||
|
||||
public Body playArea;
|
||||
public Array<Body> bodies;
|
||||
|
||||
public List<PowerUp> powerUps;
|
||||
public Paddle paddle;
|
||||
public List<Ball> balls;
|
||||
public List<Brick> bricks;
|
||||
public List<Shield> shields;
|
||||
|
||||
private int shieldCount = 0;
|
||||
private float updateTime = 0f;
|
||||
|
||||
private final Box2DDebugRenderer debugRenderer;
|
||||
|
||||
public PlayState(BrickBuster game) {
|
||||
super(game);
|
||||
debugRenderer = new Box2DDebugRenderer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
// Initialize a world with no gravity
|
||||
world = new World(new Vector2(), false);
|
||||
world.setContactListener(new Box2dContactListener());
|
||||
|
||||
// define a playArea body with position set to 0
|
||||
BodyDef playAreaDef = new BodyDef();
|
||||
playAreaDef.type = BodyDef.BodyType.StaticBody;
|
||||
playAreaDef.position.set(new Vector2());
|
||||
|
||||
EdgeShape screenEdge = new EdgeShape();
|
||||
|
||||
playArea = world.createBody(playAreaDef);
|
||||
// Right edge
|
||||
screenEdge.set(lowerRightCorner, upperRightCorner);
|
||||
playArea.createFixture(screenEdge, 0f);
|
||||
// Top edge
|
||||
screenEdge.set(upperRightCorner, upperLeftCorner);
|
||||
playArea.createFixture(screenEdge, 0f);
|
||||
// Left edge
|
||||
screenEdge.set(upperLeftCorner, lowerLeftCorner);
|
||||
playArea.createFixture(screenEdge, 0f);
|
||||
// Bottom edge
|
||||
//screenEdge.set(lowerLeftCorner, lowerRightCorner);
|
||||
//playArea.createFixture(screenEdge, 0f);
|
||||
screenEdge.dispose();
|
||||
|
||||
powerUps = new ArrayList<PowerUp>();
|
||||
paddle = new Paddle(this);
|
||||
|
||||
int brick_padding = (BrickBuster.BOARD_WIDTH - COLUMNS * Brick.BRICK_WIDTH) / (COLUMNS + 1);
|
||||
float brick_padding = ((BrickBuster.BOARD_WIDTH/PIXEL_PER_METER) - COLUMNS * Brick.BRICK_WIDTH) / (COLUMNS + 1);
|
||||
bricks = new ArrayList<Brick>();
|
||||
for (int col = 0; col < COLUMNS; col++) {
|
||||
for (int row = 0; row < ROWS; row++) {
|
||||
int x = brick_padding + (col * (Brick.BRICK_WIDTH + brick_padding));
|
||||
int y = brick_padding + Brick.BRICK_HEIGHT + (row * (Brick.BRICK_HEIGHT + brick_padding));
|
||||
float x = brick_padding + (col * (Brick.BRICK_WIDTH + brick_padding));
|
||||
float y = brick_padding + Brick.BRICK_HEIGHT + (row * (Brick.BRICK_HEIGHT + brick_padding));
|
||||
|
||||
PowerUpType powerUpType = null;
|
||||
if (MathUtils.randomBoolean(POWERUP_CHANCE)) {
|
||||
powerUpType = PowerUpType.getWeightedRandom();
|
||||
}
|
||||
|
||||
bricks.add(new Brick(this, powerUpType, x, BrickBuster.BOARD_HEIGHT - y));
|
||||
bricks.add(new Brick(this, powerUpType, x, BrickBuster.BOARD_HEIGHT/PIXEL_PER_METER - y));
|
||||
}
|
||||
}
|
||||
|
||||
balls = new ArrayList<Ball>();
|
||||
balls.add(new Ball(this));
|
||||
|
||||
shields = new ArrayList<Shield>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
Array<Body> bodies = new Array<Body>();
|
||||
world.getBodies(bodies);
|
||||
for (Body b : bodies) {
|
||||
Entity e = (Entity) b.getUserData();
|
||||
if (e instanceof Ball || e instanceof PowerUp) {
|
||||
e.setPos(b.getPosition());
|
||||
}
|
||||
}
|
||||
|
||||
long start = System.nanoTime();
|
||||
for (Brick block : bricks) {
|
||||
block.render(game.sr);
|
||||
@ -69,14 +131,12 @@ public class PlayState extends State {
|
||||
for (Ball ball : balls) {
|
||||
ball.render(game.sr);
|
||||
}
|
||||
for (Shield shield : shields) {
|
||||
shield.render(game.sr);
|
||||
}
|
||||
paddle.render(game.sr);
|
||||
|
||||
if (getShieldCount() > 0) {
|
||||
game.sr.begin(ShapeType.Filled);
|
||||
game.sr.setColor(Color.SALMON);
|
||||
game.sr.rect(0, 0, BrickBuster.BOARD_WIDTH, getShieldCount() * SHIELD_HEIGHT);
|
||||
game.sr.end();
|
||||
}
|
||||
//debugRenderer.render(world, game.cam.combined.cpy().scl(PIXEL_PER_METER));
|
||||
long renderTime = System.nanoTime() - start;
|
||||
|
||||
game.sb.begin();
|
||||
@ -94,8 +154,9 @@ public class PlayState extends State {
|
||||
for (Iterator<Ball> it = balls.iterator(); it.hasNext();) {
|
||||
Ball ball = it.next();
|
||||
ball.update(dt);
|
||||
if (ball.isDead()) {
|
||||
if (ball.isDeleted()) {
|
||||
it.remove();
|
||||
world.destroyBody(ball.getBody());
|
||||
}
|
||||
}
|
||||
if (balls.isEmpty()) {
|
||||
@ -105,15 +166,36 @@ public class PlayState extends State {
|
||||
for (Iterator<PowerUp> it = powerUps.iterator(); it.hasNext();) {
|
||||
PowerUp powerUp = it.next();
|
||||
powerUp.update(dt);
|
||||
if(powerUp.isCaught()) {
|
||||
if(powerUp.isDeleted()) {
|
||||
it.remove();
|
||||
world.destroyBody(powerUp.getBody());
|
||||
}
|
||||
}
|
||||
|
||||
for (Iterator<Shield> it = shields.iterator(); it.hasNext();) {
|
||||
Shield shield = it.next();
|
||||
shield.update(dt);
|
||||
if(shield.isDeleted()) {
|
||||
it.remove();
|
||||
world.destroyBody(shield.getBody());
|
||||
}
|
||||
}
|
||||
|
||||
for (Iterator<Brick> it = bricks.iterator(); it.hasNext();) {
|
||||
Brick brick = it.next();
|
||||
brick.update(dt);
|
||||
if (brick.isDeleted()) {
|
||||
it.remove();
|
||||
world.destroyBody(brick.getBody());
|
||||
}
|
||||
}
|
||||
if (bricks.isEmpty()) {
|
||||
game.setScreen(new MenuState(game));
|
||||
dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
world.step(dt, 6, 2);
|
||||
updateTime = System.nanoTime() - start;
|
||||
}
|
||||
|
||||
@ -121,6 +203,8 @@ public class PlayState extends State {
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
|
||||
world.dispose();
|
||||
|
||||
powerUps.clear();
|
||||
powerUps = null;
|
||||
balls.clear();
|
||||
@ -131,25 +215,25 @@ public class PlayState extends State {
|
||||
}
|
||||
|
||||
public int getShieldCount() {
|
||||
return shieldCount;
|
||||
return shields.size();
|
||||
}
|
||||
|
||||
public void addShield() {
|
||||
shieldCount++;
|
||||
paddle.setY(paddle.getY() + SHIELD_HEIGHT);
|
||||
Shield shield = new Shield(this);
|
||||
shields.add(shield);
|
||||
paddle.setY(paddle.getY() + Shield.SHIELD_HEIGHT);
|
||||
for (Ball ball : balls) {
|
||||
if (ball.isStuck()) {
|
||||
ball.setY(ball.getY() + SHIELD_HEIGHT);
|
||||
ball.setY(ball.getY() + Shield.SHIELD_HEIGHT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeShield() {
|
||||
shieldCount--;
|
||||
paddle.setY(paddle.getY() - SHIELD_HEIGHT);
|
||||
public void removeShield(Shield shield) {
|
||||
paddle.setY(paddle.getY() - Shield.SHIELD_HEIGHT);
|
||||
for (Ball ball : balls) {
|
||||
if (ball.isStuck()) {
|
||||
ball.setY(ball.getY() - SHIELD_HEIGHT);
|
||||
ball.setY(ball.getY() - Shield.SHIELD_HEIGHT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user