Compare commits

..

54 Commits

Author SHA1 Message Date
a9bca04f1c Move Utils class to utils package 2018-11-22 21:13:01 +04:00
84cdddc4b9 Write a CompositeIterator and use it to clean up entity iteration 2018-11-22 21:12:35 +04:00
4d27a2671a Cleanup Brick.render() 2018-11-22 20:52:20 +04:00
451ca13566 Add missing semicolon. 2018-11-22 18:55:15 +04:00
9e44b1e93f Change desktop resolution to 1/5th of native resolution 2018-11-22 17:39:13 +04:00
7ff4d3307a Add gdx-tools dependency, add TextureSetup class 2018-11-22 17:36:28 +04:00
9e8e00e9af Add random shape and type control to GridLevelLoader 2018-11-22 17:34:18 +04:00
44c3311cd9 Set powerup segment count to 20. 2018-11-22 17:31:28 +04:00
28eb4149ee Change background to black, paddle to white 2018-11-22 17:31:00 +04:00
36ae71afeb Add FileLevelLoader and FileLayout to load levels from json files
Add two levels to assets
2018-11-22 17:30:48 +04:00
90b92b1026 Remember to call super.dispose() in FieldState's dispose() 2018-11-22 17:23:53 +04:00
53feedf407 Pass spritebatch instance to all entity's render methods
Scale spritebatch and shaperenderer projections to PIXEL_PER_METER,
no longer have to scale in each entity's render method
Use a separate spritebatch for font rendering
Dispose the texture atlas
2018-11-22 15:44:13 +04:00
70f142c2fa Render bricks and ball using sprites instead of constructed polygons
Changed field width to 10 blocks instead of 11
2018-11-22 14:55:13 +04:00
bb8d4a41e3 Add hidden (press F12 at main menu) level editor mode
Saves a level.json in the external storage
2018-11-21 15:38:32 +04:00
157a247516 Calculate PIXEL_PER_METER 2018-11-21 15:26:19 +04:00
87f91cc2bc Reorder and add new brick shapes 2018-11-21 15:26:19 +04:00
3fdd394e36 Decreased brick size, increased edge padding, removed brick padding
Field is now 11 bricks wide instead of 10
Extended edge padding to Y=0
2018-11-21 15:26:19 +04:00
2a40bcabdf Use bit-or comparisons to create brick vertices, makes for shorter code
Added new brick shapes
2018-11-21 15:26:19 +04:00
ef698a38c1 Add new brick shapes. 2018-11-21 15:26:19 +04:00
2a398725f5 Override setX and setY in Brick to update box2d body's position.
Clean up Ball and Paddle's overrides
2018-11-21 15:26:19 +04:00
f90f5d5452 Add new abstract FieldState, which contains the Box2d world instance.
PlayState now extends FieldState instead of State.
Entity now takes a FieldState instead of a State instance.
Casts from State to PlayState where necessary in entities.
2018-11-21 15:26:13 +04:00
958232b2ff Remove unused Color import from Brick 2018-11-20 16:14:42 +04:00
989bb46d64 Add LevelLoader interface and Level class.
Add GridLevelLoader implementation, use to load initial level
Add logic to return to main menu when there's no more levels, or load the next
the next level
2018-11-20 16:14:15 +04:00
1aeab73cd0 Delete bricks that go below the bottom of the play field 2018-11-20 12:15:55 +04:00
5611bbc611 Add BrickLayout interace with an implementing GridLayout
Use new GridLayout to generate initial level
Remove some constructors in Birck
Add Color to BrickType enum, alter Brick to use it
2018-11-20 11:01:54 +04:00
719c32502c No longer allow balls to collect powerups 2018-11-19 23:41:48 +04:00
9df81ee854 Add dispose() to Entity and implement in Brick 2018-11-15 23:15:07 +04:00
e116f1064d Add additional brick shapes 2018-11-15 22:04:29 +04:00
4fa5841fb2 Fix correction impulse on ball 2018-11-15 22:03:12 +04:00
f70dc67878 Begin adding support for rendering different shaped bricks 2018-11-15 20:09:48 +04:00
64e00c1a1a Treat brick coordinates as their center 2018-11-15 18:19:54 +04:00
cb90c949ff New MINIMUM_ANGLE constant in Ball, currently PI/16
Apply impulses to ball when angle relative to horizontal axis is less
than MINIMUM_ANGLE. Prevents balls getting stuck going horizontally
2018-11-15 16:31:42 +04:00
998286de8d Ensure balls collide with the shield 2018-11-15 14:53:14 +04:00
9eac3353b6 Use instance bodies array 2018-11-15 14:48:32 +04:00
adebc42c16 Add Box2d collision filtering, allow collecting power ups with balls 2018-11-15 14:22:42 +04:00
74aa10de04 Refactor to use libGdx Array instead of java ArrayList
Render bricks with their box2d angle
Add ball density = 1f
2018-11-15 13:43:48 +04:00
b44b48097c Update paddle.update() to handle edge collision more nicely + cleaner code
Calculates an adjust variable which locates the paddle at exactly the
screen edge if a regular speed * dt move would put it past. Applies
the same adjust to stuck balls so they don't drift.
2018-11-15 12:24:33 +04:00
e4000998cc Complete box2d implementation, all collisions handled with box2d
Shield re-implemented as an entity with a box2d body
Multiball powerup now spawns balls at paddle instead of from block location
2018-11-15 10:34:22 +04:00
92201411b9 Refactor entity.PhysicsBody to physics.PhysicsBody
Create CollisionListener interface and Box2dContactListener
Register the Box2dContactListener with the box2d world
2018-11-14 22:26:02 +04:00
0994289c7e Begin box2d physics implementation.
Created box2d bodies for all game entities (except shield)
Scale units down to box2d friendly units (40:1 <-> pixels:box2d)
Use floats in places integers were used for position and sizing
2018-11-14 22:04:41 +04:00
fe2c68d39a Add FPS limit back 2018-11-13 22:55:39 +04:00
b3cbe0cda5 Remove magic value 2018-11-13 19:01:14 +04:00
e2c0bad9e0 Altered powerup weights (decreased glue, increased multi ball)
Allow bricks to have their colour changed.
2018-11-13 18:58:55 +04:00
ea6240bd2c Add PowerUpType enum
Handles weighted random seletion, color and instantiation, cleaning up
other sections of Brick and PlayState code.
2018-11-13 18:42:27 +04:00
0abae880d3 Add MenuState, start with it and switch back when a game is finished 2018-11-13 17:58:42 +04:00
fa7e93953b Speed ball up 2018-11-13 17:21:23 +04:00
409735d94e Scale entity units to new board units.
Rename Brick.BLOCK_ to Brick.BRICK_
2018-11-13 17:17:19 +04:00
1fe82cb5b2 Change viewport to FitViewport, board height/width to 4K
Enable font anti-aliasing
2018-11-13 17:14:33 +04:00
a56d721050 Move away from using pixel coordinates to "board coordinates"
Changed aspect ratio from 4:3 to 9:16
2018-11-13 15:58:01 +04:00
1c7e20f8e9 Move any stuck balls along with paddle when shields get added/removed 2018-11-13 12:54:09 +04:00
7588a0986b Refactored to use states
Various other refactoring, no functional differences
2018-11-13 11:51:00 +04:00
3fad2085b6 Refactor power up chance to be a variable 2018-11-13 10:21:05 +04:00
70ece88a89 Allow shields to stack.
Refactor Ball.render to keep a local instance of BrickBuster
2018-11-12 21:46:02 +04:00
a199c82fd8 Fixed ball DEFAULT_SPEED being used when BOOST_SPEED should have been 2018-11-12 21:24:02 +04:00
77 changed files with 2979 additions and 389 deletions

View File

@ -44,8 +44,10 @@ project(":desktop") {
dependencies { dependencies {
compile project(":core") compile project(":core")
compile "com.badlogicgames.gdx:gdx-tools:$gdxVersion"
compile "com.badlogicgames.gdx:gdx-backend-lwjgl:$gdxVersion" compile "com.badlogicgames.gdx:gdx-backend-lwjgl:$gdxVersion"
compile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop" compile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"
compile "com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-desktop"
} }
} }
@ -56,7 +58,9 @@ project(":core") {
dependencies { dependencies {
compile "com.badlogicgames.gdx:gdx:$gdxVersion" 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:0.13.4"
compile "net.dermetfan.libgdx-utils:libgdx-utils-box2d:0.13.4"
} }
} }

View File

@ -0,0 +1,358 @@
{
bricks: [
{
row: 15
type: HARD
shape: RECTANGLE
}
{
row: 16
type: STANDARD_50
shape: RECTANGLE
}
{
row: 17
type: STANDARD_80
shape: RECTANGLE
}
{
row: 18
type: STANDARD_60
shape: RECTANGLE
}
{
row: 19
type: STANDARD_70
shape: RECTANGLE
}
{
row: 20
type: STANDARD_40
shape: RECTANGLE
}
{
col: 1
row: 15
type: HARD
shape: RECTANGLE
}
{
col: 1
row: 16
type: STANDARD_50
shape: RECTANGLE
}
{
col: 1
row: 17
type: STANDARD_80
shape: RECTANGLE
}
{
col: 1
row: 18
type: STANDARD_60
shape: RECTANGLE
}
{
col: 1
row: 19
type: STANDARD_70
shape: RECTANGLE
}
{
col: 1
row: 20
type: STANDARD_40
shape: RECTANGLE
}
{
col: 2
row: 15
type: HARD
shape: RECTANGLE
}
{
col: 2
row: 16
type: STANDARD_50
shape: RECTANGLE
}
{
col: 2
row: 17
type: STANDARD_80
shape: RECTANGLE
}
{
col: 2
row: 18
type: STANDARD_60
shape: RECTANGLE
}
{
col: 2
row: 19
type: STANDARD_70
shape: RECTANGLE
}
{
col: 2
row: 20
type: STANDARD_40
shape: RECTANGLE
}
{
col: 3
row: 15
type: HARD
shape: RECTANGLE
}
{
col: 3
row: 16
type: STANDARD_50
shape: RECTANGLE
}
{
col: 3
row: 17
type: STANDARD_80
shape: RECTANGLE
}
{
col: 3
row: 18
type: STANDARD_60
shape: RECTANGLE
}
{
col: 3
row: 19
type: STANDARD_70
shape: RECTANGLE
}
{
col: 3
row: 20
type: STANDARD_40
shape: RECTANGLE
}
{
col: 4
row: 15
type: HARD
shape: RECTANGLE
}
{
col: 4
row: 16
type: STANDARD_50
shape: RECTANGLE
}
{
col: 4
row: 17
type: STANDARD_80
shape: RECTANGLE
}
{
col: 4
row: 18
type: STANDARD_60
shape: RECTANGLE
}
{
col: 4
row: 19
type: STANDARD_70
shape: RECTANGLE
}
{
col: 4
row: 20
type: STANDARD_40
shape: RECTANGLE
}
{
col: 5
row: 15
type: HARD
shape: RECTANGLE
}
{
col: 5
row: 16
type: STANDARD_50
shape: RECTANGLE
}
{
col: 5
row: 17
type: STANDARD_80
shape: RECTANGLE
}
{
col: 5
row: 18
type: STANDARD_60
shape: RECTANGLE
}
{
col: 5
row: 19
type: STANDARD_70
shape: RECTANGLE
}
{
col: 5
row: 20
type: STANDARD_40
shape: RECTANGLE
}
{
col: 6
row: 15
type: HARD
shape: RECTANGLE
}
{
col: 6
row: 16
type: STANDARD_50
shape: RECTANGLE
}
{
col: 6
row: 17
type: STANDARD_80
shape: RECTANGLE
}
{
col: 6
row: 18
type: STANDARD_60
shape: RECTANGLE
}
{
col: 6
row: 19
type: STANDARD_70
shape: RECTANGLE
}
{
col: 6
row: 20
type: STANDARD_40
shape: RECTANGLE
}
{
col: 7
row: 15
type: HARD
shape: RECTANGLE
}
{
col: 7
row: 16
type: STANDARD_50
shape: RECTANGLE
}
{
col: 7
row: 17
type: STANDARD_80
shape: RECTANGLE
}
{
col: 7
row: 18
type: STANDARD_60
shape: RECTANGLE
}
{
col: 7
row: 19
type: STANDARD_70
shape: RECTANGLE
}
{
col: 7
row: 20
type: STANDARD_40
shape: RECTANGLE
}
{
col: 8
row: 15
type: HARD
shape: RECTANGLE
}
{
col: 8
row: 16
type: STANDARD_50
shape: RECTANGLE
}
{
col: 8
row: 17
type: STANDARD_80
shape: RECTANGLE
}
{
col: 8
row: 18
type: STANDARD_60
shape: RECTANGLE
}
{
col: 8
row: 19
type: STANDARD_70
shape: RECTANGLE
}
{
col: 8
row: 20
type: STANDARD_40
shape: RECTANGLE
}
{
col: 9
row: 15
type: HARD
shape: RECTANGLE
}
{
col: 9
row: 16
type: STANDARD_50
shape: RECTANGLE
}
{
col: 9
row: 17
type: STANDARD_80
shape: RECTANGLE
}
{
col: 9
row: 18
type: STANDARD_60
shape: RECTANGLE
}
{
col: 9
row: 19
type: STANDARD_70
shape: RECTANGLE
}
{
col: 9
row: 20
type: STANDARD_40
shape: RECTANGLE
}
]
}

View File

@ -0,0 +1,598 @@
{
bricks: [
{
row: 5
type: STANDARD_40
shape: HALF_LOWER_RECTANGLE
}
{
row: 6
type: STANDARD_40
shape: DIAMOND
}
{
row: 7
type: STANDARD_40
shape: DIAMOND
}
{
row: 8
type: STANDARD_40
shape: RECTANGLE
}
{
row: 9
type: STANDARD_40
shape: FULL_LEFT_UP_RIGHT_TRIANGLE
}
{
row: 10
type: STANDARD_40
shape: RIGHT_UP_RIGHT_TRIANGLE
}
{
col: 1
row: 5
type: STANDARD_40
shape: HALF_LOWER_RECTANGLE
}
{
col: 1
row: 6
type: STANDARD_60
shape: DIAMOND
}
{
col: 1
row: 7
type: STANDARD_40
shape: DIAMOND
}
{
col: 1
row: 8
type: STANDARD_40
shape: RECTANGLE
}
{
col: 1
row: 9
type: STANDARD_50
shape: RECTANGLE
}
{
col: 1
row: 10
type: STANDARD_50
shape: RECTANGLE
}
{
col: 1
row: 11
type: STANDARD_40
shape: FULL_LEFT_UP_RIGHT_TRIANGLE
}
{
col: 1
row: 12
type: STANDARD_40
shape: RIGHT_UP_RIGHT_TRIANGLE
}
{
col: 2
row: 5
type: STANDARD_40
shape: HALF_LOWER_RECTANGLE
}
{
col: 2
row: 6
type: STANDARD_60
shape: DIAMOND
}
{
col: 2
row: 7
type: STANDARD_50
shape: DIAMOND
}
{
col: 2
row: 8
type: STANDARD_40
shape: RECTANGLE
}
{
col: 2
row: 9
type: STANDARD_50
shape: RECTANGLE
}
{
col: 2
row: 10
type: STANDARD_50
shape: RECTANGLE
}
{
col: 2
row: 11
type: STANDARD_50
shape: RECTANGLE
}
{
col: 2
row: 12
type: STANDARD_50
shape: RECTANGLE
}
{
col: 2
row: 13
type: STANDARD_40
shape: FULL_LEFT_UP_RIGHT_TRIANGLE
}
{
col: 2
row: 14
type: STANDARD_40
shape: RIGHT_UP_RIGHT_TRIANGLE
}
{
col: 3
row: 5
type: STANDARD_40
shape: HALF_LOWER_RECTANGLE
}
{
col: 3
row: 6
type: STANDARD_60
shape: DIAMOND
}
{
col: 3
row: 7
type: STANDARD_40
shape: DIAMOND
}
{
col: 3
row: 8
type: STANDARD_40
shape: RECTANGLE
}
{
col: 3
row: 9
type: STANDARD_50
shape: RECTANGLE
}
{
col: 3
row: 10
type: STANDARD_50
shape: RECTANGLE
}
{
col: 3
row: 11
type: STANDARD_50
shape: RECTANGLE
}
{
col: 3
row: 12
type: STANDARD_50
shape: RECTANGLE
}
{
col: 3
row: 13
type: STANDARD_80
shape: RECTANGLE
}
{
col: 3
row: 14
type: STANDARD_80
shape: RECTANGLE
}
{
col: 3
row: 15
type: STANDARD_40
shape: FULL_LEFT_UP_RIGHT_TRIANGLE
}
{
col: 3
row: 16
type: STANDARD_40
shape: RIGHT_UP_RIGHT_TRIANGLE
}
{
col: 4
row: 5
type: STANDARD_40
shape: HALF_LOWER_RECTANGLE
}
{
col: 4
row: 6
type: STANDARD_40
shape: DIAMOND
}
{
col: 4
row: 7
type: STANDARD_50
shape: DIAMOND
}
{
col: 4
row: 8
type: STANDARD_40
shape: RECTANGLE
}
{
col: 4
row: 9
type: STANDARD_50
shape: RECTANGLE
}
{
col: 4
row: 10
type: STANDARD_50
shape: RECTANGLE
}
{
col: 4
row: 11
type: STANDARD_50
shape: RECTANGLE
}
{
col: 4
row: 12
type: STANDARD_50
shape: RECTANGLE
}
{
col: 4
row: 13
type: STANDARD_80
shape: RECTANGLE
}
{
col: 4
row: 14
type: STANDARD_80
shape: RECTANGLE
}
{
col: 4
row: 15
type: STANDARD_70
shape: RECTANGLE
}
{
col: 4
row: 16
type: STANDARD_60
shape: RECTANGLE
}
{
col: 4
row: 17
type: STANDARD_40
shape: FULL_LEFT_UP_RIGHT_TRIANGLE
}
{
col: 4
row: 18
type: STANDARD_40
shape: RIGHT_UP_RIGHT_TRIANGLE
}
{
col: 5
row: 5
type: STANDARD_40
shape: HALF_LOWER_RECTANGLE
}
{
col: 5
row: 6
type: STANDARD_40
shape: DIAMOND
}
{
col: 5
row: 7
type: STANDARD_50
shape: DIAMOND
}
{
col: 5
row: 8
type: STANDARD_40
shape: RECTANGLE
}
{
col: 5
row: 9
type: STANDARD_50
shape: RECTANGLE
}
{
col: 5
row: 10
type: STANDARD_50
shape: RECTANGLE
}
{
col: 5
row: 11
type: STANDARD_50
shape: RECTANGLE
}
{
col: 5
row: 12
type: STANDARD_50
shape: RECTANGLE
}
{
col: 5
row: 13
type: STANDARD_80
shape: RECTANGLE
}
{
col: 5
row: 14
type: STANDARD_80
shape: RECTANGLE
}
{
col: 5
row: 15
type: STANDARD_70
shape: RECTANGLE
}
{
col: 5
row: 16
type: STANDARD_60
shape: RECTANGLE
}
{
col: 5
row: 17
type: STANDARD_40
shape: FULL_UP_LEFT_TRIANGLE
}
{
col: 5
row: 18
type: STANDARD_40
shape: LEFT_UP_LEFT_TRIANGLE
}
{
col: 6
row: 5
type: STANDARD_40
shape: HALF_LOWER_RECTANGLE
}
{
col: 6
row: 6
type: STANDARD_60
shape: DIAMOND
}
{
col: 6
row: 7
type: STANDARD_40
shape: DIAMOND
}
{
col: 6
row: 8
type: STANDARD_40
shape: RECTANGLE
}
{
col: 6
row: 9
type: STANDARD_50
shape: RECTANGLE
}
{
col: 6
row: 10
type: STANDARD_50
shape: RECTANGLE
}
{
col: 6
row: 11
type: STANDARD_50
shape: RECTANGLE
}
{
col: 6
row: 12
type: STANDARD_50
shape: RECTANGLE
}
{
col: 6
row: 13
type: STANDARD_80
shape: RECTANGLE
}
{
col: 6
row: 14
type: STANDARD_80
shape: RECTANGLE
}
{
col: 6
row: 15
type: STANDARD_40
shape: FULL_UP_LEFT_TRIANGLE
}
{
col: 6
row: 16
type: STANDARD_40
shape: LEFT_UP_LEFT_TRIANGLE
}
{
col: 7
row: 5
type: STANDARD_40
shape: HALF_LOWER_RECTANGLE
}
{
col: 7
row: 6
type: STANDARD_60
shape: DIAMOND
}
{
col: 7
row: 7
type: STANDARD_50
shape: DIAMOND
}
{
col: 7
row: 8
type: STANDARD_40
shape: RECTANGLE
}
{
col: 7
row: 9
type: STANDARD_50
shape: RECTANGLE
}
{
col: 7
row: 10
type: STANDARD_50
shape: RECTANGLE
}
{
col: 7
row: 11
type: STANDARD_50
shape: RECTANGLE
}
{
col: 7
row: 12
type: STANDARD_50
shape: RECTANGLE
}
{
col: 7
row: 13
type: STANDARD_40
shape: FULL_UP_LEFT_TRIANGLE
}
{
col: 7
row: 14
type: STANDARD_40
shape: LEFT_UP_LEFT_TRIANGLE
}
{
col: 8
row: 5
type: STANDARD_40
shape: HALF_LOWER_RECTANGLE
}
{
col: 8
row: 6
type: STANDARD_60
shape: DIAMOND
}
{
col: 8
row: 7
type: STANDARD_40
shape: DIAMOND
}
{
col: 8
row: 8
type: STANDARD_40
shape: RECTANGLE
}
{
col: 8
row: 9
type: STANDARD_50
shape: RECTANGLE
}
{
col: 8
row: 10
type: STANDARD_50
shape: RECTANGLE
}
{
col: 8
row: 11
type: STANDARD_40
shape: FULL_UP_LEFT_TRIANGLE
}
{
col: 8
row: 12
type: STANDARD_40
shape: LEFT_UP_LEFT_TRIANGLE
}
{
col: 9
row: 5
type: STANDARD_40
shape: HALF_LOWER_RECTANGLE
}
{
col: 9
row: 6
type: STANDARD_40
shape: DIAMOND
}
{
col: 9
row: 7
type: STANDARD_40
shape: DIAMOND
}
{
col: 9
row: 8
type: STANDARD_40
shape: RECTANGLE
}
{
col: 9
row: 9
type: STANDARD_40
shape: FULL_UP_LEFT_TRIANGLE
}
{
col: 9
row: 10
type: STANDARD_40
shape: LEFT_UP_LEFT_TRIANGLE
}
]
}

BIN
core/assets/playBtn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1013 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 963 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 678 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 732 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 683 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 534 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

257
core/assets/textures.atlas Normal file
View File

@ -0,0 +1,257 @@
textures.png
size: 1024,1024
format: RGBA8888
filter: MipMapLinearLinear,MipMapLinearLinear
repeat: none
ball
rotate: false
xy: 800, 824
size: 128, 128
orig: 128, 128
offset: 0, 0
index: -1
brick_DIAMOND
rotate: false
xy: 8, 824
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
brick_DOWN_LEFT_TRIANGLE
rotate: false
xy: 8, 688
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
brick_DOWN_RIGHT_TRIANGLE
rotate: false
xy: 272, 824
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
brick_DOWN_TRIANGLE
rotate: false
xy: 8, 552
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
brick_FULL_DOWN_LEFT_TRIANGLE
rotate: false
xy: 272, 688
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
brick_FULL_HALF_DOWN_TRIANGLE
rotate: false
xy: 536, 824
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
brick_FULL_HALF_LEFT_TRIANGLE
rotate: false
xy: 8, 416
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
brick_FULL_HALF_RIGHT_TRIANGLE
rotate: false
xy: 272, 552
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
brick_FULL_HALF_UP_TRIANGLE
rotate: false
xy: 536, 688
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
brick_FULL_LEFT_DOWN_RIGHT_TRIANGLE
rotate: false
xy: 8, 280
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
brick_FULL_LEFT_UP_RIGHT_TRIANGLE
rotate: false
xy: 272, 416
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
brick_FULL_UP_LEFT_TRIANGLE
rotate: false
xy: 536, 552
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
brick_HALF_DOWN_TRIANGLE
rotate: false
xy: 8, 144
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
brick_HALF_LEFT_SQUARE
rotate: false
xy: 272, 280
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
brick_HALF_LEFT_TRIANGLE
rotate: false
xy: 536, 416
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
brick_HALF_LOWER_RECTANGLE
rotate: false
xy: 8, 8
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
brick_HALF_RIGHT_SQUARE
rotate: false
xy: 272, 144
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
brick_HALF_RIGHT_TRIANGLE
rotate: false
xy: 536, 280
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
brick_HALF_UPPER_RECTANGLE
rotate: false
xy: 272, 8
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
brick_HALF_UP_TRIANGLE
rotate: false
xy: 536, 144
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
brick_LEFT_DOWN_LEFT_TRIANGLE
rotate: false
xy: 536, 8
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
textures2.png
size: 1024,1024
format: RGBA8888
filter: MipMapLinearLinear,MipMapLinearLinear
repeat: none
brick_LEFT_DOWN_RIGHT_TRIANGLE
rotate: false
xy: 8, 688
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
brick_LEFT_TRIANGLE
rotate: false
xy: 8, 552
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
brick_LEFT_UP_LEFT_TRIANGLE
rotate: false
xy: 272, 688
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
brick_LEFT_UP_RIGHT_TRIANGLE
rotate: false
xy: 8, 416
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
brick_RECTANGLE
rotate: false
xy: 272, 552
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
brick_RIGHT_DOWN_LEFT_TRIANGLE
rotate: false
xy: 536, 688
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
brick_RIGHT_DOWN_RIGHT_TRIANGLE
rotate: false
xy: 8, 280
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
brick_RIGHT_TRIANGLE
rotate: false
xy: 272, 416
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
brick_RIGHT_UP_LEFT_TRIANGLE
rotate: false
xy: 536, 552
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
brick_RIGHT_UP_RIGHT_TRIANGLE
rotate: false
xy: 8, 144
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
brick_UP_LEFT_TRIANGLE
rotate: false
xy: 272, 280
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
brick_UP_RIGHT_TRIANGLE
rotate: false
xy: 536, 416
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1
brick_UP_TRIANGLE
rotate: false
xy: 8, 8
size: 256, 128
orig: 256, 128
offset: 0, 0
index: -1

BIN
core/assets/textures.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
core/assets/textures2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -1,200 +1,75 @@
package com.me.brickbuster; package com.me.brickbuster;
import com.badlogic.gdx.ApplicationAdapter; import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.PolygonSpriteBatch;
import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer; import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.viewport.FitViewport;
import com.me.brickbuster.entity.*; import com.badlogic.gdx.utils.viewport.Viewport;
import com.me.brickbuster.entity.powerup.*; import com.me.brickbuster.state.MenuState;
import com.me.brickbuster.state.PlayState;
import java.util.*; public class BrickBuster extends Game {
public class BrickBuster extends ApplicationAdapter {
public static final int WIDTH = 800;
public static final int HEIGHT = 600;
public static final String TITLE = "Brick Buster"; public static final String TITLE = "Brick Buster";
// 9*16 board area
public static final int BOARD_WIDTH = 2160;
public static final int BOARD_HEIGHT = 3840;
public static final int SHIELD_HEIGHT = 5; public OrthographicCamera cam;
public Viewport viewport;
public static final Vector2 HORIZONTAL_EDGE = new Vector2(1, 0); public BitmapFont font;
public static final Vector2 VERTICAL_EDGE = new Vector2(0, 1); public SpriteBatch sb;
public SpriteBatch fb;
public ShapeRenderer sr;
private static final Map<Class<? extends PowerUp>, Integer> powerUpWeights; public AssetManager assets;
private static final int weightSum;
private BitmapFont font;
private SpriteBatch batch;
private ShapeRenderer sr;
private Paddle paddle;
private ArrayList<Ball> balls;
private ArrayList<Brick> bricks;
private ArrayList<PowerUp> powerUps;
private boolean shieldActive = false;
@Override @Override
public void create () { public void create () {
cam = new OrthographicCamera();
viewport = new FitViewport(BOARD_WIDTH, BOARD_HEIGHT, cam);
viewport.apply(true);
font = new BitmapFont(); font = new BitmapFont();
batch = new SpriteBatch(); font.getData().setScale(4);
font.getRegion().getTexture().setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
sb = new SpriteBatch();
fb = new SpriteBatch();
sr = new ShapeRenderer(); sr = new ShapeRenderer();
paddle = new Paddle(this); assets = new AssetManager();
powerUps = new ArrayList<PowerUp>(); setScreen(new MenuState(this));
bricks = new ArrayList<Brick>();
for (int col = 0; col < 13; col++) {
for (int row = 0; row < 7; row++) {
int x = 15 + (col * (Brick.BLOCK_WIDTH + 10));
int y = 15 + Brick.BLOCK_HEIGHT + (row * (Brick.BLOCK_HEIGHT + 10));
Class<? extends PowerUp> powerUpType = null;
if (MathUtils.randomBoolean(0.15f)) {
powerUpType = getWeightedPowerUp();
}
bricks.add(new Brick(this, powerUpType, x, HEIGHT - y));
}
}
balls = new ArrayList<Ball>();
balls.add(new Ball(this));
} }
@Override @Override
public void render () { public void render () {
Gdx.gl.glClearColor(0.5f,1,1,1); Gdx.gl.glClearColor(0f,0f,0f,1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// Render the current Screen (State)
super.render();
long start_update = System.nanoTime();
update(Gdx.graphics.getDeltaTime());
long finish_update = System.nanoTime() - start_update;
long start_render = System.nanoTime();
for (Brick block : bricks) {
block.render(sr);
}
for (PowerUp powerUp : powerUps) {
powerUp.render(sr);
}
for (Ball ball : balls) {
ball.render(sr);
}
paddle.render(sr);
if (shieldActive) {
sr.begin(ShapeRenderer.ShapeType.Filled);
sr.setColor(Color.SALMON);
sr.rect(0, 0, WIDTH, SHIELD_HEIGHT);
sr.end();
}
long finish_render = System.nanoTime() - start_render;
batch.begin();
//batch.setColor(Color.BLACK);
font.draw(batch, String.format("FPS: %d Update: %.2f ms Render: %.2f ms",
Gdx.graphics.getFramesPerSecond(), finish_update/1000000f, finish_render/1000000f), 0, 13);
batch.end();
}
public void update(float dt) {
for (Iterator<Ball> it = balls.iterator(); it.hasNext();) {
Ball ball = it.next();
ball.update(dt);
if (ball.isDead()) {
it.remove();
}
}
if (balls.isEmpty()) {
reset();
}
paddle.update(dt);
for (Iterator<PowerUp> it = powerUps.iterator(); it.hasNext();) {
PowerUp powerUp = it.next();
powerUp.update(dt);
if(powerUp.isCaught()) {
it.remove();
}
}
if (getBricks().isEmpty()) {
create();
}
}
public void reset() {
Paddle paddle = getPaddle();
Ball ball = new Ball(this);
ball.setX(paddle.getX());
ball.setY(Paddle.PADDLE_Y + Paddle.PADDLE_HEIGHT + Ball.RADIUS);
balls.add(ball);
paddle.setSticky(false);
paddle.setWidth(Paddle.DEFAULT_WIDTH);
} }
@Override @Override
public void dispose () { public void resize(int width, int height) {
viewport.update(width, height);
Matrix4 projection = cam.combined.cpy().scl(PlayState.PIXEL_PER_METER);
sb.setProjectionMatrix(projection);
fb.setProjectionMatrix(cam.combined);
sr.setProjectionMatrix(projection);
super.resize(width, height);
} }
private static final Class<? extends PowerUp> getWeightedPowerUp() {
int remaining = MathUtils.random(weightSum);
for (Map.Entry<Class<? extends PowerUp>, Integer> entry : powerUpWeights.entrySet()) {
remaining -= entry.getValue();
if (remaining < 0) {
return entry.getKey();
}
}
return null;
}
public void setShieldActive(boolean shieldActive) {
this.shieldActive = shieldActive;
}
public boolean isShieldActive() {
return shieldActive;
}
public ArrayList<Ball> getBalls() {
return balls;
}
public Paddle getPaddle() {
return paddle;
}
public ArrayList<Brick> getBricks() {
return bricks;
}
public void addPowerUp(PowerUp powerUp) {
this.powerUps.add(powerUp);
}
static {
Map<Class<? extends PowerUp>, Integer> tmp = new HashMap<Class<? extends PowerUp>, Integer>();
// Assign PowerUp weights here
tmp.put(GluePowerUp.class, 30);
tmp.put(LongerPaddlePowerUp.class, 40);
tmp.put(MultiBallPowerUp.class, 20);
tmp.put(ShieldPowerUp.class, 40);
powerUpWeights = Collections.unmodifiableMap(tmp);
int sum = 0;
for (int x : powerUpWeights.values()) {
sum += x;
}
weightSum = sum;
}
} }

View File

@ -2,128 +2,171 @@ package com.me.brickbuster.entity;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer; import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType; import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.me.brickbuster.BrickBuster; import com.badlogic.gdx.physics.box2d.*;
import com.me.brickbuster.Utils; import com.me.brickbuster.physics.CollisionListener;
import net.dermetfan.utils.Pair; import com.me.brickbuster.physics.EntityType;
import com.me.brickbuster.physics.PhysicsBody;
import com.me.brickbuster.state.PlayState;
import java.util.Iterator; public class Ball extends Entity implements PhysicsBody, CollisionListener {
public class Ball extends Entity { public static final float RADIUS = 1.2f;
public static final int RADIUS = 12;
public static final Color BALL_COLOR = Color.CHARTREUSE; public static final Color BALL_COLOR = Color.CHARTREUSE;
public static final float DEFAULT_SPEED = 350; public static final float DEFAULT_SPEED = 45;
public static final float BOOST_SPEED = 450; public static final float BOOST_SPEED = 55;
public static final int BLOCKS_FOR_BOOST = 39; public static final int BLOCKS_FOR_BOOST = 39;
public Vector2 direction; public static final float MINIMUM_ANGLE = MathUtils.PI/16;
public static final float CORRECTION_IMPULSE = 3f;
private float speed; private float speed;
private boolean isStuck = true; private boolean isStuck = true;
private boolean isDead = false; private boolean touchedPaddle = false;
public Ball(BrickBuster brickBuster) { private TextureRegion texture;
super(brickBuster,BrickBuster.WIDTH/2, Paddle.PADDLE_Y + Paddle.PADDLE_HEIGHT + RADIUS);
this.speed = getBrickBuster().getBricks().size() > BLOCKS_FOR_BOOST? DEFAULT_SPEED : DEFAULT_SPEED; private Body body;
public Ball(PlayState state) {
super(state, state.paddle.getX(), state.paddle.getY() + Paddle.PADDLE_HEIGHT + RADIUS);
this.speed = state.bricks.size > BLOCKS_FOR_BOOST? DEFAULT_SPEED : BOOST_SPEED;
texture = state.textures.findRegion("ball");
createBody();
} }
@Override @Override
public void render(ShapeRenderer sr) { public void render(SpriteBatch sb, ShapeRenderer sr) {
sr.begin(ShapeType.Filled); sb.setColor(Color.WHITE);
sr.setColor(BALL_COLOR); sb.draw(texture, pos.x - RADIUS,
sr.circle(getPos().x, getPos().y, RADIUS); pos.y - RADIUS,
sr.end(); RADIUS*2,
RADIUS*2);
} }
@Override @Override
public void update(float dt) { public void update(float dt) {
if (isStuck || isDead) { if (isStuck || deleted) {
if (!isDead && Gdx.input.justTouched()) { if (!deleted && Gdx.input.justTouched()) {
launch(); launch();
} else { } else {
return; return;
} }
} }
if (touchedPaddle) {
Vector2 new_pos = getPos().cpy().add(direction.cpy().scl(speed * dt)); paddleCollision();
touchedPaddle = false;
boolean brickCollision = false;
Iterator<Brick> brickIterator = getBrickBuster().getBricks().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 (new_pos.x + RADIUS > BrickBuster.WIDTH || new_pos.x - RADIUS < 0) { if (pos.y + RADIUS < 0) {
Utils.reflect(direction, BrickBuster.VERTICAL_EDGE); deleted = true;
} else if (new_pos.y + RADIUS > BrickBuster.HEIGHT) {
Utils.reflect(direction, BrickBuster.HORIZONTAL_EDGE);
} else if (new_pos.y - RADIUS < BrickBuster.SHIELD_HEIGHT && getBrickBuster().isShieldActive()) {
Utils.reflect(direction, BrickBuster.HORIZONTAL_EDGE);
getBrickBuster().setShieldActive(false);
} else if (new_pos.y + RADIUS < 0) {
isDead = true;
return; return;
} else if (direction.y < 0 && new_pos.y <= Paddle.PADDLE_Y + Paddle.PADDLE_HEIGHT + RADIUS) {
Pair<Vector2, Vector2> paddle = getBrickBuster().getPaddle().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 (getBrickBuster().getPaddle().isSticky()) {
return;
}
}
} }
getPos().add(direction.cpy().scl(speed * dt)); Vector2 velocity = body.getLinearVelocity();
body.setLinearVelocity(velocity.nor().scl(speed));
float rad = velocity.angleRad();
if (Math.abs(rad) < MINIMUM_ANGLE || Math.abs(rad) > MathUtils.PI - MINIMUM_ANGLE) {
Vector2 impulse = new Vector2(0, rad > 0? CORRECTION_IMPULSE : -CORRECTION_IMPULSE);
body.applyLinearImpulse(impulse, pos, true);
}
}
@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;
ballFixture.density = 1f;
ballFixture.filter.categoryBits = EntityType.BALL;
ballFixture.filter.maskBits = EntityType.BOUNDARY | EntityType.BRICK
| EntityType.PADDLE | EntityType.SHIELD;
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() { public Vector2 paddleReflectAngle() {
Paddle paddle = getBrickBuster().getPaddle(); Paddle paddle = ((PlayState) state).paddle;
float rel = MathUtils.clamp((getX() - paddle.getX()) + (paddle.getWidth()/2), 5, paddle.getWidth()-5); float rel = (pos.x - paddle.getX()) + (paddle.getWidth()/2);
float newAngle = MathUtils.PI - (MathUtils.PI * (rel / paddle.getWidth())); float newAngle = MathUtils.PI - (MathUtils.PI * (rel / paddle.getWidth()));
newAngle = MathUtils.clamp(newAngle, MINIMUM_ANGLE, MathUtils.PI-MINIMUM_ANGLE);
return new Vector2(MathUtils.cos(newAngle), MathUtils.sin(newAngle)); return new Vector2(MathUtils.cos(newAngle), MathUtils.sin(newAngle));
} }
public void launch() { public void launch() {
if (getBrickBuster().getPaddle().isSticky()) { Vector2 direction;
if (((PlayState) state).paddle.isSticky()) {
direction = paddleReflectAngle(); direction = paddleReflectAngle();
} else { } else {
// launch at random angle between 135 and 45 degrees // launch at random angle between 135 and 45 degrees
float angle = MathUtils.random(MathUtils.PI/2) + MathUtils.PI/4; float angle = MathUtils.random(MathUtils.PI/2) + MathUtils.PI/4;
direction = new Vector2(MathUtils.cos(angle), MathUtils.sin(angle)); direction = new Vector2(MathUtils.cos(angle), MathUtils.sin(angle));
} }
body.setLinearVelocity(direction.scl(speed));
isStuck = false; isStuck = false;
} }
public void paddleCollision() { public void paddleCollision() {
Paddle paddle = getBrickBuster().getPaddle(); if (((PlayState) state).paddle.isSticky()) {
if (paddle.isSticky()) {
isStuck = true; isStuck = true;
setY(Paddle.PADDLE_Y + Paddle.PADDLE_HEIGHT + RADIUS); body.setLinearVelocity(new Vector2());
setY(((PlayState) state).paddle.getY() + Paddle.PADDLE_HEIGHT + RADIUS);
return; return;
} }
direction = paddleReflectAngle(); body.setLinearVelocity(paddleReflectAngle().scl(speed));
}
@Override
public void setX(float x) {
super.setX(x);
body.setTransform(pos, 0);
}
@Override
public void setY(float y) {
super.setY(y);
body.setTransform(pos, 0);
} }
public void setSpeed(float speed) { public void setSpeed(float speed) {
@ -134,15 +177,4 @@ public class Ball extends Entity {
return isStuck; return isStuck;
} }
public boolean isDead() {
return isDead;
}
public void setDirection(Vector2 direction) {
this.direction = direction;
}
public void setStuck(boolean stuck) {
isStuck = stuck;
}
} }

View File

@ -1,65 +1,176 @@
package com.me.brickbuster.entity; package com.me.brickbuster.entity;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer; 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.badlogic.gdx.math.Vector2;
import com.me.brickbuster.BrickBuster; import com.badlogic.gdx.physics.box2d.Body;
import com.me.brickbuster.entity.powerup.PowerUp; 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.EntityType;
import com.me.brickbuster.physics.PhysicsBody;
import com.me.brickbuster.state.FieldState;
import com.me.brickbuster.state.PlayState;
public class Brick extends Entity { public class Brick extends Entity implements PhysicsBody, CollisionListener {
public static final Color BLOCK_COLOR = Color.FOREST;
public static final int BLOCK_WIDTH = 50;
public static final int BLOCK_HEIGHT = 20;
private Class<? extends PowerUp> powerUpType; public static final float BRICK_WIDTH = 5.2f;
public static final float BRICK_HEIGHT = 2.6f;
private Vector2[] vertices; private BrickType type;
private BrickShape.Shape shape;
private PowerUpType powerUpType;
public Brick(BrickBuster brickBuster, Class<? extends PowerUp> powerUpType, int x, int y) { private TextureRegion region;
super(brickBuster, x, y);
private Body body;
private boolean hitByBall = false;
public Brick(FieldState state, BrickType type, BrickShape.Shape shape, PowerUpType powerUpType, float x, float y) {
super(state, x, y);
this.type = type;
this.shape = shape;
this.powerUpType = powerUpType; this.powerUpType = powerUpType;
this.region = state.textures.findRegion("brick_" + shape.name());
this.vertices = new Vector2[] { createBody();
new Vector2(x, y),
new Vector2(x + BLOCK_WIDTH, y),
new Vector2(x + BLOCK_WIDTH, y + BLOCK_HEIGHT),
new Vector2(x, y + BLOCK_HEIGHT)
};
} }
@Override @Override
public void render(ShapeRenderer sr) { public void render(SpriteBatch sb, ShapeRenderer sr) {
sr.begin(ShapeType.Filled); sb.setColor(type.getColor());
sr.setColor(BLOCK_COLOR); sb.draw(region, pos.x - BRICK_WIDTH/2, pos.y - BRICK_HEIGHT/2,
sr.rect(getX(), getY(), BLOCK_WIDTH, BLOCK_HEIGHT); BRICK_WIDTH/2, BRICK_HEIGHT/2,
sr.end(); BRICK_WIDTH, BRICK_HEIGHT,
1f, 1f,
MathUtils.radiansToDegrees * body.getAngle());
} }
@Override @Override
public void update(float dt) { public void update(float dt) {
if (hitByBall) {
hit();
hitByBall = false;
}
if (getY() + BRICK_HEIGHT < 0) {
delete();
}
}
@Override
public Body getBody() {
return body;
}
@Override
public void createBody() {
BodyDef brickBody = new BodyDef();
brickBody.type = BodyDef.BodyType.StaticBody;
brickBody.position.set(pos.cpy());
int shapeMask = shape.getMask();
Vector2[] vertices = new Vector2[Integer.bitCount(shapeMask)];
int v = 0;
if ((shapeMask & BrickShape.BOTTOM_LEFT) != 0) {
vertices[v++] = new Vector2(-BRICK_WIDTH/2, -BRICK_HEIGHT/2);
}
if ((shapeMask & BrickShape.MIDDLE_LEFT) != 0) {
vertices[v++] = new Vector2(-BRICK_WIDTH/2, 0);
}
if ((shapeMask & BrickShape.TOP_LEFT) != 0) {
vertices[v++] = new Vector2(-BRICK_WIDTH/2, BRICK_HEIGHT/2);
}
if ((shapeMask & BrickShape.TOP_MIDDLE) != 0) {
vertices[v++] = new Vector2(0, BRICK_HEIGHT/2);
}
if ((shapeMask & BrickShape.CENTER) != 0) {
vertices[v++] = new Vector2(0, 0);
}
if ((shapeMask & BrickShape.TOP_RIGHT) != 0) {
vertices[v++] = new Vector2(BRICK_WIDTH/2, BRICK_HEIGHT/2);
}
if ((shapeMask & BrickShape.MIDDLE_RIGHT) != 0) {
vertices[v++] = new Vector2(BRICK_WIDTH/2, 0);
}
if ((shapeMask & BrickShape.BOTTOM_RIGHT) != 0) {
vertices[v++] = new Vector2(BRICK_WIDTH/2, -BRICK_HEIGHT/2);
}
if ((shapeMask & BrickShape.BOTTOM_MIDDLE) != 0) {
vertices[v++] = new Vector2(0, -BRICK_HEIGHT/2);
}
PolygonShape brickShape = new PolygonShape();
brickShape.set(vertices);
FixtureDef brickFixture = new FixtureDef();
brickFixture.shape = brickShape;
brickFixture.friction = 0f;
brickFixture.density = 0.5f;
brickFixture.filter.categoryBits = EntityType.BRICK;
brickFixture.filter.maskBits = EntityType.BALL | EntityType.BRICK | EntityType.BOUNDARY;
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) {
}
@Override
public void setX(float x) {
super.setX(x);
body.setTransform(pos, 0);
}
@Override
public void setY(float y) {
super.setY(y);
body.setTransform(pos, 0);
}
public BrickType getType() {
return type;
}
public BrickShape.Shape getShape() {
return shape;
}
public PowerUpType getPowerUpType() {
return powerUpType;
} }
public boolean hit() { public boolean hit() {
if (getBrickBuster().getBricks().size()-1 <= Ball.BLOCKS_FOR_BOOST) { if (((PlayState) state).bricks.size-1 <= Ball.BLOCKS_FOR_BOOST) {
for (Ball ball : getBrickBuster().getBalls()) { for (Ball ball : ((PlayState) state).balls) {
ball.setSpeed(Ball.BOOST_SPEED); ball.setSpeed(Ball.BOOST_SPEED);
} }
} }
if (powerUpType != null) { if (powerUpType != null) {
try { ((PlayState) state).powerUps.add(powerUpType.createInstance((PlayState) state, this));
PowerUp powerUp = powerUpType.getConstructor(BrickBuster.class, Brick.class).newInstance(getBrickBuster(), this);
getBrickBuster().addPowerUp(powerUp);
} catch(Exception e) {
}
} }
deleted = true;
return true; return true;
} }
public Vector2[] getVertices() {
return vertices;
}
} }

View File

@ -0,0 +1,84 @@
package com.me.brickbuster.entity;
public class BrickShape {
public static final int BOTTOM_LEFT = 0x1;
public static final int MIDDLE_LEFT = 0x1 << 1;
public static final int TOP_LEFT = 0x1 << 2;
public static final int TOP_MIDDLE = 0x1 << 3;
public static final int CENTER = 0x1 << 4;
public static final int TOP_RIGHT = 0x1 << 5;
public static final int MIDDLE_RIGHT = 0x1 << 6;
public static final int BOTTOM_RIGHT = 0x1 << 7;
public static final int BOTTOM_MIDDLE = 0x1 << 8;
public enum Shape {
RECTANGLE(0, 0, BOTTOM_LEFT | TOP_LEFT | TOP_RIGHT | BOTTOM_RIGHT),
HALF_LEFT_SQUARE(1, 0, BOTTOM_LEFT | TOP_LEFT | TOP_MIDDLE | BOTTOM_MIDDLE),
HALF_RIGHT_SQUARE(2, 0, BOTTOM_MIDDLE | TOP_MIDDLE | TOP_RIGHT | BOTTOM_RIGHT),
HALF_UPPER_RECTANGLE(4, 0, MIDDLE_LEFT | TOP_LEFT | TOP_RIGHT | MIDDLE_RIGHT),
HALF_LOWER_RECTANGLE(5, 0, BOTTOM_LEFT | MIDDLE_LEFT | MIDDLE_RIGHT | BOTTOM_RIGHT),
DIAMOND(2, 1, MIDDLE_LEFT | TOP_MIDDLE | MIDDLE_RIGHT | BOTTOM_MIDDLE),
UP_TRIANGLE(2, 2, BOTTOM_LEFT | TOP_MIDDLE | BOTTOM_RIGHT),
HALF_UP_TRIANGLE(3, 5, BOTTOM_LEFT | CENTER | BOTTOM_RIGHT),
FULL_HALF_UP_TRIANGLE(1, 5, BOTTOM_LEFT | MIDDLE_LEFT | TOP_MIDDLE | MIDDLE_RIGHT | BOTTOM_RIGHT),
DOWN_TRIANGLE(2, 3, TOP_LEFT | TOP_RIGHT | BOTTOM_MIDDLE),
HALF_DOWN_TRIANGLE(2, 5, TOP_LEFT | TOP_RIGHT | CENTER),
FULL_HALF_DOWN_TRIANGLE(0, 5, MIDDLE_LEFT | TOP_LEFT | TOP_RIGHT | MIDDLE_RIGHT | BOTTOM_MIDDLE),
LEFT_TRIANGLE(3, 4, MIDDLE_LEFT | TOP_RIGHT | BOTTOM_RIGHT),
HALF_LEFT_TRIANGLE(2, 4, CENTER | TOP_RIGHT | BOTTOM_RIGHT),
FULL_HALF_LEFT_TRIANGLE(1, 3, MIDDLE_LEFT | TOP_MIDDLE | TOP_RIGHT | BOTTOM_RIGHT | BOTTOM_MIDDLE),
RIGHT_TRIANGLE(0, 4, BOTTOM_LEFT | TOP_LEFT | MIDDLE_RIGHT),
HALF_RIGHT_TRIANGLE(1, 4, BOTTOM_LEFT | TOP_LEFT | CENTER),
FULL_HALF_RIGHT_TRIANGLE(0, 3, BOTTOM_LEFT | TOP_LEFT | TOP_MIDDLE | MIDDLE_RIGHT | BOTTOM_MIDDLE),
DOWN_RIGHT_TRIANGLE(0, 1, BOTTOM_LEFT | TOP_RIGHT | BOTTOM_RIGHT),
LEFT_DOWN_RIGHT_TRIANGLE(0, 6, BOTTOM_LEFT | TOP_MIDDLE | BOTTOM_MIDDLE),
FULL_LEFT_DOWN_RIGHT_TRIANGLE(3, 1, BOTTOM_LEFT | TOP_MIDDLE | TOP_RIGHT | BOTTOM_RIGHT),
RIGHT_DOWN_RIGHT_TRIANGLE(2, 6, BOTTOM_MIDDLE | TOP_RIGHT | BOTTOM_RIGHT),
UP_RIGHT_TRIANGLE(0, 2, TOP_LEFT | TOP_RIGHT | BOTTOM_RIGHT),
LEFT_UP_RIGHT_TRIANGLE(0, 7, TOP_LEFT | TOP_MIDDLE | BOTTOM_MIDDLE),
FULL_LEFT_UP_RIGHT_TRIANGLE(3, 0, TOP_LEFT | TOP_RIGHT | BOTTOM_RIGHT | BOTTOM_MIDDLE),
RIGHT_UP_RIGHT_TRIANGLE(2, 7, TOP_MIDDLE | TOP_RIGHT | BOTTOM_RIGHT),
UP_LEFT_TRIANGLE(1, 2, BOTTOM_LEFT | TOP_LEFT | TOP_RIGHT),
LEFT_UP_LEFT_TRIANGLE(1, 7, BOTTOM_LEFT | TOP_LEFT | TOP_MIDDLE),
FULL_UP_LEFT_TRIANGLE(3, 2, BOTTOM_LEFT | TOP_LEFT | TOP_RIGHT | BOTTOM_MIDDLE),
RIGHT_UP_LEFT_TRIANGLE(3, 7, BOTTOM_MIDDLE | TOP_MIDDLE | TOP_RIGHT),
DOWN_LEFT_TRIANGLE(1, 1, BOTTOM_LEFT | TOP_LEFT | BOTTOM_RIGHT),
LEFT_DOWN_LEFT_TRIANGLE(1, 6, BOTTOM_LEFT | TOP_LEFT | BOTTOM_MIDDLE),
FULL_DOWN_LEFT_TRIANGLE(3, 3, BOTTOM_LEFT | TOP_LEFT | TOP_MIDDLE | BOTTOM_RIGHT),
RIGHT_DOWN_LEFT_TRIANGLE(3, 6, BOTTOM_MIDDLE | TOP_MIDDLE | BOTTOM_RIGHT),
;
private int row;
private int col;
private int mask;
Shape(int row, int col, int mask) {
this.row = row;
this.col = col;
this.mask = mask;
}
public int getRow() {
return row;
}
public int getCol() {
return col;
}
public int getMask() {
return mask;
}
}
}

View File

@ -0,0 +1,31 @@
package com.me.brickbuster.entity;
import com.badlogic.gdx.graphics.Color;
public enum BrickType {
STANDARD_10(new Color(0xf1f1f1ff)),
STANDARD_20(new Color(0xff8f00ff)),
STANDARD_30(Color.CYAN),
STANDARD_40(Color.GREEN),
STANDARD_50(Color.RED),
STANDARD_60(new Color(0x0070ffff)),
STANDARD_70(new Color(0xff00ffff)),
STANDARD_80(new Color(0xffff00ff)),
EXPLOSIVE(Color.GRAY),
HARD(new Color(0x9d9d9dff)),
HARDER(new Color(0xbcae00ff)),
UNBREAKABLE(Color.BLACK)
;
private Color color;
BrickType(Color color) {
this.color = color;
}
public Color getColor() {
return color;
}
}

View File

@ -1,26 +1,30 @@
package com.me.brickbuster.entity; package com.me.brickbuster.entity;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer; import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.me.brickbuster.BrickBuster; import com.me.brickbuster.state.FieldState;
public abstract class Entity { public abstract class Entity {
private BrickBuster brickBuster; protected FieldState state;
private Vector2 pos; protected Vector2 pos;
protected boolean deleted = false;
public Entity(BrickBuster brickBuster, float x, float y) { public Entity(FieldState state, Vector2 pos) {
this.brickBuster = brickBuster; this(state, pos.x, pos.y);
}
public Entity(FieldState state, float x, float y) {
this.state = state;
this.pos = new Vector2(x, y); this.pos = new Vector2(x, y);
} }
public abstract void render(ShapeRenderer sr); public abstract void render(SpriteBatch sb, ShapeRenderer sr);
public abstract void update(float dt); public abstract void update(float dt);
public BrickBuster getBrickBuster() { public void dispose() {}
return brickBuster;
}
public Vector2 getPos() { public Vector2 getPos() {
return pos; return pos;
@ -46,4 +50,12 @@ public abstract class Entity {
pos.y = y; pos.y = y;
} }
public void delete() {
this.deleted = true;
}
public boolean isDeleted() {
return deleted;
}
} }

View File

@ -3,76 +3,130 @@ package com.me.brickbuster.entity;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input; import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer; import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType; import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2; 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.EntityType;
import com.me.brickbuster.physics.PhysicsBody;
import com.me.brickbuster.state.PlayState;
import net.dermetfan.utils.Pair; import net.dermetfan.utils.Pair;
public class Paddle extends Entity { public class Paddle extends Entity implements PhysicsBody {
public static final Color PADDLE_COLOR = Color.BLACK; public static final Color STICKY_COLOR = Color.GRAY;
public static final int DEFAULT_WIDTH = 100; public static final Color PADDLE_COLOR = Color.WHITE;
public static final int PADDLE_HEIGHT = 10;
public static final int PADDLE_Y = 15;
public static final int PADDLE_SPEED = 375;
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 boolean sticky = false;
private int width = DEFAULT_WIDTH;
public Paddle(BrickBuster brickBuster) { private Body body;
super(brickBuster, BrickBuster.WIDTH / 2, PADDLE_Y);
public Paddle(PlayState brickBuster) {
super(brickBuster, PlayState.BOARD_WIDTH/2, PADDLE_Y);
createBody();
} }
@Override @Override
public void render(ShapeRenderer sr) { public void render(SpriteBatch sb, ShapeRenderer sr) {
sr.begin(ShapeType.Filled); sr.begin(ShapeType.Filled);
sr.setColor(sticky? Color.GRAY : PADDLE_COLOR); sr.setColor(sticky? STICKY_COLOR : PADDLE_COLOR);
sr.rect(getX() - width/2, getY(), width, PADDLE_HEIGHT); sr.rect(pos.x - width/2, pos.y, width, PADDLE_HEIGHT);
sr.end(); sr.end();
} }
@Override @Override
public void update(float dt) { public void update(float dt) {
float displacement = PADDLE_SPEED * dt;
float adjust = 0;
if (Gdx.input.isKeyPressed(Input.Keys.LEFT)) { if (Gdx.input.isKeyPressed(Input.Keys.LEFT)) {
if (getX() - width/2 - PADDLE_SPEED * dt < 0) { if (pos.x - width / 2 - displacement < PlayState.EDGE_PADDING) {
setX(width/2); adjust = -(pos.x - width/2 - PlayState.EDGE_PADDING);
return; } else {
} adjust = -displacement;
setX(getX() - PADDLE_SPEED * dt);
for (Ball ball : getBrickBuster().getBalls()) {
if (ball.isStuck()) {
ball.setX(ball.getX() - PADDLE_SPEED * dt);
}
} }
} }
if (Gdx.input.isKeyPressed(Input.Keys.RIGHT)) { if (Gdx.input.isKeyPressed(Input.Keys.RIGHT)) {
if (getX() + width/2 + PADDLE_SPEED * dt > BrickBuster.WIDTH) { if (pos.x + width / 2 + displacement > PlayState.BOARD_WIDTH-PlayState.EDGE_PADDING) {
setX(BrickBuster.WIDTH - width/2); adjust = PlayState.BOARD_WIDTH - PlayState.EDGE_PADDING - width/2 - pos.x;
return; } else {
adjust = displacement;
} }
setX(getX() + PADDLE_SPEED * dt); }
if (!MathUtils.isZero(adjust)) {
for (Ball ball : getBrickBuster().getBalls()) { setX(pos.x + adjust);
for (Ball ball : ((PlayState) state).balls) {
if (ball.isStuck()) { if (ball.isStuck()) {
ball.setX(ball.getX() + PADDLE_SPEED * dt); ball.setX(ball.getX() + adjust);
} }
} }
} }
} }
public Pair<Vector2, Vector2> getTopEdge() { @Override
return new Pair<Vector2, Vector2>( public void createBody() {
new Vector2(getX() - width/2, getY() + PADDLE_HEIGHT), BodyDef paddleBody = new BodyDef();
new Vector2(getX() + width/2, getY() + PADDLE_HEIGHT) 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.filter.categoryBits = EntityType.PADDLE;
paddleFixture.filter.maskBits = EntityType.BALL | EntityType.POWER_UP;
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);
body.setTransform(pos, 0);
}
@Override
public void setY(float y) {
super.setY(y);
body.setTransform(pos, 0);
}
public float getWidth() {
return width; return width;
} }
public void setWidth(float width) {
if (this.width == width) {
return;
}
((PlayState) state).world.destroyBody(body);
this.width = width;
createBody();
}
public boolean isSticky() { public boolean isSticky() {
return sticky; return sticky;
} }
@ -81,7 +135,7 @@ public class Paddle extends Entity {
this.sticky = sticky; this.sticky = sticky;
} }
public void setWidth(int width) {
this.width = width;
}
} }

View File

@ -0,0 +1,70 @@
package com.me.brickbuster.entity;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
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.EntityType;
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(SpriteBatch sb, ShapeRenderer sr) {
sr.begin(ShapeRenderer.ShapeType.Filled);
sr.setColor(PowerUpType.SHIELD.getColor());
sr.rect(0, pos.y, PlayState.BOARD_WIDTH, SHIELD_HEIGHT);
sr.end();
}
@Override
public void update(float dt) {
if (deleted) {
((PlayState) 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;
brickFixture.filter.categoryBits = EntityType.SHIELD;
brickFixture.filter.maskBits = EntityType.BALL;
body = state.world.createBody(brickBody);
body.createFixture(brickFixture);
body.setUserData(this);
brickShape.dispose();
}
@Override
public Body getBody() {
return body;
}
}

View File

@ -1,18 +1,18 @@
package com.me.brickbuster.entity.powerup; package com.me.brickbuster.entity.powerup;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.me.brickbuster.BrickBuster; import com.badlogic.gdx.math.Vector2;
import com.me.brickbuster.entity.Brick; import com.me.brickbuster.state.PlayState;
public class GluePowerUp extends PowerUp { public class GluePowerUp extends PowerUp {
public GluePowerUp(BrickBuster brickBuster, Brick brick) { public GluePowerUp(PlayState state, Vector2 brick, Color color) {
super(brickBuster, brick, Color.WHITE); super(state, brick, color);
} }
@Override @Override
public void activate() { public void activate() {
getBrickBuster().getPaddle().setSticky(true); ((PlayState) state).paddle.setSticky(true);
} }
} }

View File

@ -1,20 +1,20 @@
package com.me.brickbuster.entity.powerup; package com.me.brickbuster.entity.powerup;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.me.brickbuster.BrickBuster; import com.badlogic.gdx.math.Vector2;
import com.me.brickbuster.entity.Brick;
import com.me.brickbuster.entity.Paddle; import com.me.brickbuster.entity.Paddle;
import com.me.brickbuster.state.PlayState;
public class LongerPaddlePowerUp extends PowerUp { public class LongerPaddlePowerUp extends PowerUp {
public LongerPaddlePowerUp(BrickBuster brickBuster, Brick brick) {
super(brickBuster, brick, Color.OLIVE); public LongerPaddlePowerUp(PlayState state, Vector2 brick, Color color) {
super(state, brick, color);
} }
@Override @Override
public void activate() { public void activate() {
Paddle paddle = getBrickBuster().getPaddle(); if (((PlayState) state).paddle.getWidth() < Paddle.DEFAULT_WIDTH*2.5) {
if (paddle.getWidth() < 250) { ((PlayState) state).paddle.setWidth(((PlayState) state).paddle.getWidth() + Paddle.DEFAULT_WIDTH/2);
paddle.setWidth(paddle.getWidth() + 50);
} }
} }
} }

View File

@ -3,28 +3,25 @@ package com.me.brickbuster.entity.powerup;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.me.brickbuster.BrickBuster;
import com.me.brickbuster.entity.Ball; import com.me.brickbuster.entity.Ball;
import com.me.brickbuster.entity.Brick; import com.me.brickbuster.entity.Paddle;
import com.me.brickbuster.state.PlayState;
public class MultiBallPowerUp extends PowerUp { public class MultiBallPowerUp extends PowerUp {
private Vector2 pos; private Vector2 pos;
public MultiBallPowerUp(BrickBuster brickBuster, Brick brick) { public MultiBallPowerUp(PlayState state, Vector2 brick, Color color) {
super(brickBuster, brick, Color.ROYAL); super(state, brick, color);
this.pos = getPos().cpy(); this.pos = getPos().cpy();
} }
@Override @Override
public void activate() { public void activate() {
for (int x = 0; x < 2; x++) { for (int x = 0; x < 2; x++) {
Ball ball = new Ball(getBrickBuster()); Ball ball = new Ball((PlayState) state);
ball.setPos(pos.cpy()); ball.launch();
float angle = MathUtils.random(MathUtils.PI*2); ((PlayState) state).balls.add(ball);
ball.setDirection(new Vector2(MathUtils.cos(angle), MathUtils.sin(angle)));
ball.setStuck(false);
getBrickBuster().getBalls().add(ball);
} }
} }
} }

View File

@ -1,57 +1,98 @@
package com.me.brickbuster.entity.powerup; package com.me.brickbuster.entity.powerup;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer; import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType; import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.me.brickbuster.BrickBuster; import com.badlogic.gdx.physics.box2d.Body;
import com.me.brickbuster.Utils; import com.badlogic.gdx.physics.box2d.BodyDef;
import com.me.brickbuster.entity.Brick; import com.badlogic.gdx.physics.box2d.CircleShape;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.me.brickbuster.entity.Ball;
import com.me.brickbuster.entity.Entity; import com.me.brickbuster.entity.Entity;
import net.dermetfan.utils.Pair; import com.me.brickbuster.entity.Paddle;
import com.me.brickbuster.physics.CollisionListener;
import com.me.brickbuster.physics.EntityType;
import com.me.brickbuster.physics.PhysicsBody;
import com.me.brickbuster.state.PlayState;
public abstract class PowerUp extends Entity { public abstract class PowerUp extends Entity implements PhysicsBody, CollisionListener {
public static final int RADIUS = 10; public static final float RADIUS = 1.2f;
public static final int FALL_SPEED = 100; public static final float FALL_SPEED = 15f;
private Color color; private Color color;
private boolean isCaught; private boolean isCaught;
public PowerUp(BrickBuster brickBuster, Brick brick, Color color) { private Body body;
super(brickBuster, brick.getX() + Brick.BLOCK_WIDTH/2, brick.getY() + Brick.BLOCK_HEIGHT/2);
public PowerUp(PlayState state, Vector2 pos, Color color) {
super(state, pos);
this.color = color; this.color = color;
createBody();
} }
@Override @Override
public void render(ShapeRenderer sr) { public void render(SpriteBatch sb, ShapeRenderer sr) {
sr.begin(ShapeType.Filled); sr.begin(ShapeType.Filled);
sr.setColor(color); sr.setColor(color);
sr.circle(getX(), getY(), RADIUS); sr.circle(pos.x, pos.y, RADIUS, 20);
sr.end(); sr.end();
} }
@Override @Override
public void update(float dt) { public void update(float dt) {
setY(getY() - FALL_SPEED * dt); if (isCaught) {
Pair<Vector2, Vector2> paddle = getBrickBuster().getPaddle().getTopEdge();
Vector2 lineDir = paddle.getValue().sub(paddle.getKey());
Vector2 nearest = Utils.nearestPoint(paddle.getKey().cpy(), lineDir, getPos().cpy());
if (nearest.dst(getX(), getY()) <= RADIUS) {
activate(); activate();
isCaught = true; delete();
} }
if (getY() + RADIUS < 0) { 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;
ballFixture.filter.categoryBits = EntityType.POWER_UP;
ballFixture.filter.maskBits = EntityType.PADDLE;
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 || contacted instanceof Ball) {
isCaught = true; isCaught = true;
} }
} }
@Override
public void endContact(Entity contacted) {}
public abstract void activate(); public abstract void activate();
public boolean isCaught() {
return isCaught;
}
} }

View File

@ -0,0 +1,66 @@
package com.me.brickbuster.entity.powerup;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2;
import com.me.brickbuster.entity.Brick;
import com.me.brickbuster.state.PlayState;
public enum PowerUpType {
GLUE(GluePowerUp.class, Color.WHITE, 20),
LONGER_PADDLE(LongerPaddlePowerUp.class, Color.OLIVE, 40),
MULTI_BALL(MultiBallPowerUp.class, Color.ROYAL, 30),
SHIELD(ShieldPowerUp.class, Color.SALMON, 40),
;
private static final int WEIGHT_SUM;
private Class<? extends PowerUp> cls;
private Color color;
private int weight;
PowerUpType(Class<? extends PowerUp> cls, Color color, int weight) {
this.cls = cls;
this.color = color;
this.weight = weight;
}
public Color getColor() {
return color;
}
public static PowerUpType getWeightedRandom() {
int distance = MathUtils.random(WEIGHT_SUM);
for (PowerUpType type : values()) {
distance -= type.weight;
if (distance < 0) {
return type;
}
}
return null;
}
public PowerUp createInstance(PlayState state, Brick brick) {
return createInstance(state, new Vector2(brick.getX(), brick.getY()));
}
public PowerUp createInstance(PlayState state, Vector2 pos) {
try {
return cls.getConstructor(PlayState.class, Vector2.class, Color.class)
.newInstance(state, pos, color);
} catch(Exception e) {
System.out.println("Error instantiating PoewrUp: " + e.getMessage());
}
return null;
}
static {
int weightSum = 0;
for (PowerUpType type : values()) {
weightSum += type.weight;
}
WEIGHT_SUM = weightSum;
}
}

View File

@ -1,18 +1,18 @@
package com.me.brickbuster.entity.powerup; package com.me.brickbuster.entity.powerup;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.me.brickbuster.BrickBuster; import com.badlogic.gdx.math.Vector2;
import com.me.brickbuster.entity.Brick; import com.me.brickbuster.state.PlayState;
public class ShieldPowerUp extends PowerUp { public class ShieldPowerUp extends PowerUp {
public ShieldPowerUp(BrickBuster brickBuster, Brick brick) { public ShieldPowerUp(PlayState state, Vector2 brick, Color color) {
super(brickBuster, brick, Color.SALMON); super(state, brick, color);
} }
@Override @Override
public void activate() { public void activate() {
getBrickBuster().setShieldActive(true); ((PlayState) state).addShield();
} }
} }

View File

@ -0,0 +1,12 @@
package com.me.brickbuster.layout;
import com.badlogic.gdx.utils.Array;
import com.me.brickbuster.entity.Brick;
public interface BrickLayout {
void initialize();
Array<Brick> getBricks();
}

View File

@ -0,0 +1,41 @@
package com.me.brickbuster.layout;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Json;
import com.me.brickbuster.entity.Brick;
import com.me.brickbuster.state.PlayState;
public class FileLayout implements BrickLayout {
private static final Json JSON = new Json();
private PlayState state;
private Array<Brick> bricks;
private FileHandle file;
public FileLayout(PlayState state, FileHandle file) {
this.state = state;
this.file = file;
}
@Override
public void initialize() {
bricks = new Array<Brick>();
LevelJsonTemplate level = JSON.fromJson(LevelJsonTemplate.class, file);
for (LevelJsonTemplate.BrickJsonTemplate brick : level.bricks) {
final float x = PlayState.EDGE_PADDING + Brick.BRICK_WIDTH/2 + brick.col * Brick.BRICK_WIDTH;
final float y = PlayState.EDGE_PADDING + Brick.BRICK_HEIGHT/2 + brick.row * Brick.BRICK_HEIGHT;
bricks.add(new Brick(state, brick.type, brick.shape, brick.powerUp, x, PlayState.BOARD_HEIGHT - y));
}
}
@Override
public Array<Brick> getBricks() {
return bricks;
}
}

View File

@ -0,0 +1,26 @@
package com.me.brickbuster.layout;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import com.me.brickbuster.state.PlayState;
public class FileLevelLoader implements LevelLoader {
private PlayState state;
private int level = 1;
public FileLevelLoader(PlayState state) {
this.state = state;
}
@Override
public Level getNextLevel() {
FileHandle file = Gdx.files.internal("levels/level_" + level + ".json");
if (file.exists()) {
level++;
return new Level(new FileLayout(state, file), null, null);
}
return null;
}
}

View File

@ -0,0 +1,80 @@
package com.me.brickbuster.layout;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.utils.Array;
import com.me.brickbuster.entity.Brick;
import com.me.brickbuster.entity.BrickShape;
import com.me.brickbuster.entity.BrickType;
import com.me.brickbuster.entity.powerup.PowerUpType;
import com.me.brickbuster.state.PlayState;
public class GridLayout implements BrickLayout {
private final PlayState state;
private final int cols;
private final int rows;
private final float powerUpChance;
private BrickShape.Shape shape = null;
private BrickType type = null;
private boolean randomShape = false;
private boolean randomType = false;
private final Array<Brick> bricks;
public GridLayout(PlayState state, int cols, int rows, float powerUpChance) {
this(state, cols, rows, powerUpChance, false, false);
}
public GridLayout(PlayState state, int cols, int rows, float powerUpChance, boolean randomShape, boolean randomType) {
this(state, cols, rows, powerUpChance, BrickShape.Shape.RECTANGLE, BrickType.STANDARD_10);
this.randomShape = randomShape;
this.randomType = randomType;
}
public GridLayout(PlayState state, int cols, int rows, float powerUpChance, BrickShape.Shape shape, BrickType type) {
this.state = state;
this.cols = cols;
this.rows = rows;
this.powerUpChance = powerUpChance;
this.shape = shape;
this.type = type;
this.bricks = new Array<Brick>(cols * rows);
}
@Override
public void initialize() {
for (int col = 0; col < cols; col++) {
for (int row = 0; row < rows; row++) {
bricks.add(getBrickForCell(row, col));
}
}
}
protected Brick getBrickForCell(int row, int col) {
final float x = PlayState.EDGE_PADDING + Brick.BRICK_WIDTH/2 + col * Brick.BRICK_WIDTH;
final float y = PlayState.EDGE_PADDING + Brick.BRICK_HEIGHT/2 + row * Brick.BRICK_HEIGHT;
PowerUpType powerUpType = null;
if (MathUtils.randomBoolean(powerUpChance)) {
powerUpType = PowerUpType.getWeightedRandom();
}
if (randomType) {
type = BrickType.values()[MathUtils.random(BrickType.values().length-1)];
}
if (randomShape) {
shape = BrickShape.Shape.values()[MathUtils.random(BrickShape.Shape.values().length-1)];
}
return new Brick(state, type, shape, powerUpType, x, PlayState.BOARD_HEIGHT - y);
}
@Override
public Array<Brick> getBricks() {
return bricks;
}
}

View File

@ -0,0 +1,35 @@
package com.me.brickbuster.layout;
import com.me.brickbuster.state.PlayState;
public class GridLevelLoader implements LevelLoader {
public static final float POWER_UP_CHANCE = 0.15f;
public static final int COLUMNS = 10;
public static final int ROWS = 20;
public static final int ROUNDS = 2;
private PlayState state;
private boolean randomShape;
private boolean randomType;
private int playCount = 0;
public GridLevelLoader(PlayState state) {
this(state, false, false);
}
public GridLevelLoader(PlayState state, boolean randomShape, boolean randomType) {
this.state = state;
this.randomShape = randomShape;
this.randomType = randomType;
}
@Override
public Level getNextLevel() {
if (playCount++ < ROUNDS) {
return new Level(new GridLayout(state, COLUMNS, ROWS, POWER_UP_CHANCE, randomShape, randomType));
}
return null;
}
}

View File

@ -0,0 +1,33 @@
package com.me.brickbuster.layout;
import com.badlogic.gdx.graphics.Texture;
public class Level {
private final BrickLayout layout;
private final Texture background;
private final Texture border;
public Level(BrickLayout layout) {
this(layout, null, null);
}
public Level(BrickLayout layout, Texture background, Texture border) {
this.layout = layout;
this.background = background;
this.border = border;
}
public BrickLayout getLayout() {
return layout;
}
public Texture getBackground() {
return background;
}
public Texture getBorder() {
return border;
}
}

View File

@ -0,0 +1,23 @@
package com.me.brickbuster.layout;
import com.me.brickbuster.entity.BrickShape;
import com.me.brickbuster.entity.BrickType;
import com.me.brickbuster.entity.powerup.PowerUpType;
import java.util.List;
public class LevelJsonTemplate {
public String backgroundFile;
public String borderFile;
public List<BrickJsonTemplate> bricks;
public static class BrickJsonTemplate {
public int col;
public int row;
public BrickType type;
public BrickShape.Shape shape;
public PowerUpType powerUp;
}
}

View File

@ -0,0 +1,7 @@
package com.me.brickbuster.layout;
public interface LevelLoader {
Level getNextLevel();
}

View File

@ -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) {}
}

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

View File

@ -0,0 +1,12 @@
package com.me.brickbuster.physics;
public final class EntityType {
public static final short BOUNDARY = 0x1;
public static final short BALL = 0x1 << 1;
public static final short BRICK = 0x1 << 2;
public static final short PADDLE = 0x1 << 3;
public static final short POWER_UP = 0x1 << 4;
public static final short SHIELD = 0x1 << 5;
}

View File

@ -0,0 +1,11 @@
package com.me.brickbuster.physics;
import com.badlogic.gdx.physics.box2d.Body;
public interface PhysicsBody {
void createBody();
Body getBody();
}

View File

@ -0,0 +1,233 @@
package com.me.brickbuster.state;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputAdapter;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.utils.Json;
import com.me.brickbuster.BrickBuster;
import com.me.brickbuster.entity.Brick;
import com.me.brickbuster.entity.BrickShape;
import com.me.brickbuster.entity.BrickType;
import com.me.brickbuster.entity.powerup.PowerUpType;
import com.me.brickbuster.layout.LevelJsonTemplate;
import com.me.brickbuster.physics.Box2dContactListener;
import java.util.ArrayList;
public class EditorState extends FieldState {
public static final long BLINK_INTERVAL = 250l;
public static final int COLUMNS = 10;
public static final int ROWS = 33;
private Brick placeHolder;
private Brick[][] bricks = new Brick[COLUMNS][ROWS];
private int col = 0;
private int row = 0;
private PowerUpType curPowerUpType;
private BrickShape.Shape curShape;
private BrickType curType;
private Box2DDebugRenderer debugRenderer;
private long lastBlink = System.currentTimeMillis();
private boolean blink = false;
public EditorState(BrickBuster game) {
super(game);
}
@Override
public void setup() {
super.setup();
world = new World(new Vector2(), false);
world.setContactListener(new Box2dContactListener());
curPowerUpType = null;
curShape = BrickShape.Shape.RECTANGLE;
curType = BrickType.STANDARD_10;
updatePlaceHolder();
Gdx.input.setInputProcessor(new InputProcessor());
debugRenderer = new Box2DDebugRenderer();
}
@Override
public void render() {
game.sb.begin();
for (int col = 0; col < bricks.length; col++) {
for (int row = 0; row < bricks[col].length; row++) {
if (col == this.col && row == this.row && !blink) {
continue;
}
Brick brick = bricks[col][row];
if (brick != null) {
brick.render(game.sb, game.sr);
}
}
}
if (!blink) {
placeHolder.render(game.sb, game.sr);
}
game.sb.end();
debugRenderer.render(world, game.cam.combined.cpy().scl(PlayState.PIXEL_PER_METER));
game.fb.begin();
game.font.setColor(Color.GRAY);
game.font.draw(game.fb, String.format("Current powerup: %s", curPowerUpType), 10, 230);
game.font.draw(game.fb, "WASD: Move brick", 10, 170);
game.font.draw(game.fb, "ENTER: Set brick BACKSPACE: Delete brick F12: Save", 10, 110);
game.font.draw(game.fb, "UP/DOWN: type LEFT/RIGHT: shape SPACE: powerup", 10, 55);
game.fb.end();
}
@Override
public void update(float dt) {
long now = System.currentTimeMillis();
if (now - lastBlink > BLINK_INTERVAL) {
blink = !blink;
lastBlink = now;
}
}
private void updatePlaceHolder() {
if (placeHolder != null) {
world.destroyBody(placeHolder.getBody());
}
placeHolder = createBrick();
}
private Brick createBrick() {
return new Brick(this, curType, curShape, curPowerUpType, getBrickX(), getBrickY());
}
private float getBrickX() {
return PlayState.EDGE_PADDING + Brick.BRICK_WIDTH/2 + col * Brick.BRICK_WIDTH;
}
private float getBrickY() {
return PlayState.BOARD_HEIGHT - (PlayState.EDGE_PADDING + Brick.BRICK_HEIGHT/2 + row * Brick.BRICK_HEIGHT);
}
private void saveToFile() {
Json json = new Json();
FileHandle file = Gdx.files.external("level.json");
LevelJsonTemplate level = new LevelJsonTemplate();
level.bricks = new ArrayList<LevelJsonTemplate.BrickJsonTemplate>();
for (int col = 0; col < bricks.length; col++) {
for (int row = 0; row < bricks[col].length; row++) {
Brick brick = bricks[col][row];
if (brick == null) {
continue;
}
LevelJsonTemplate.BrickJsonTemplate brickJson = new LevelJsonTemplate.BrickJsonTemplate();
brickJson.col = col;
brickJson.row = row;
brickJson.type = brick.getType();
brickJson.shape = brick.getShape();
brickJson.powerUp = brick.getPowerUpType();
level.bricks.add(brickJson);
}
}
file.writeString(json.prettyPrint(level), false);
System.out.println("level.json saved to " + file.file().getAbsolutePath());
}
private final class InputProcessor extends InputAdapter {
@Override
public boolean keyDown(int keycode) {
int next = -1;
switch (keycode) {
case Input.Keys.UP:
next = curType.ordinal() == BrickType.values().length-1? 0 : curType.ordinal()+1;
curType = BrickType.values()[next];
updatePlaceHolder();
break;
case Input.Keys.DOWN:
next = curType.ordinal() == 0? BrickType.values().length-1 : curType.ordinal()-1;
curType = BrickType.values()[next];
updatePlaceHolder();
break;
case Input.Keys.LEFT:
next = curShape.ordinal() == 0? BrickShape.Shape.values().length-1 : curShape.ordinal()-1;
curShape = BrickShape.Shape.values()[next];
updatePlaceHolder();
break;
case Input.Keys.RIGHT:
next = curShape.ordinal() == BrickShape.Shape.values().length-1? 0 : curShape.ordinal()+1;
curShape = BrickShape.Shape.values()[next];
updatePlaceHolder();
break;
case Input.Keys.W:
if (row > 0) {
row--;
placeHolder.setY(getBrickY());
}
break;
case Input.Keys.A:
if (col > 0) {
col--;
placeHolder.setX(getBrickX());
}
break;
case Input.Keys.S:
if (row < ROWS-1) {
row++;
placeHolder.setY(getBrickY());
}
break;
case Input.Keys.D:
if (col < COLUMNS-1) {
col++;
placeHolder.setX(getBrickX());
}
break;
case Input.Keys.SPACE:
next = curPowerUpType == null? 0 : curPowerUpType.ordinal()+1;
if (next >= curPowerUpType.values().length) {
curPowerUpType = null;
} else {
curPowerUpType = PowerUpType.values()[next];
}
break;
case Input.Keys.ENTER:
if (bricks[col][row] != null) {
world.destroyBody(bricks[col][row].getBody());
bricks[col][row].dispose();
}
bricks[col][row] = createBrick();
break;
case Input.Keys.BACKSPACE:
if (bricks[col][row] != null) {
world.destroyBody(bricks[col][row].getBody());
bricks[col][row].dispose();
bricks[col][row] = null;
}
break;
case Input.Keys.F12:
saveToFile();
break;
default:
return false;
}
return true;
}
}
}

View File

@ -0,0 +1,30 @@
package com.me.brickbuster.state;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.physics.box2d.World;
import com.me.brickbuster.BrickBuster;
public abstract class FieldState extends State {
public World world;
public TextureAtlas textures;
public FieldState(BrickBuster game) {
super(game);
}
@Override
public void setup() {
game.assets.load("textures.atlas", TextureAtlas.class);
game.assets.finishLoading();
textures = game.assets.get("textures.atlas", TextureAtlas.class);
}
@Override
public void dispose() {
super.dispose();
textures.dispose();
}
}

View File

@ -0,0 +1,57 @@
package com.me.brickbuster.state;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.math.Vector3;
import com.me.brickbuster.BrickBuster;
public class MenuState extends State {
private Texture playButton;
public MenuState(BrickBuster game) {
super(game);
}
@Override
public void setup() {
this.playButton = new Texture(Gdx.files.internal("playBtn.png"));
}
@Override
public void render() {
game.sb.setProjectionMatrix(game.cam.combined);
game.sb.begin();
game.sb.draw(playButton,
BrickBuster.BOARD_WIDTH/2-playButton.getWidth()/2,
BrickBuster.BOARD_HEIGHT/2-playButton.getHeight()/2);
game.sb.end();
game.sb.setProjectionMatrix(game.cam.combined.cpy().scl(PlayState.PIXEL_PER_METER));
}
@Override
public void update(float dt) {
if (Gdx.input.justTouched()) {
Vector3 clickLoc = game.cam.unproject(new Vector3(Gdx.input.getX(), Gdx.input.getY(), 0));
if (clickLoc.x >= BrickBuster.BOARD_WIDTH/2-playButton.getWidth()/2
&& clickLoc.x <= BrickBuster.BOARD_WIDTH/2+playButton.getWidth()/2
&& clickLoc.y >= BrickBuster.BOARD_HEIGHT/2-playButton.getHeight()/2
&& clickLoc.y <= BrickBuster.BOARD_HEIGHT/2+playButton.getHeight()/2) {
game.setScreen(new PlayState(game));
dispose();
}
}
if(Gdx.input.isKeyJustPressed(Input.Keys.F12)) {
game.setScreen(new EditorState(game));
dispose();
}
}
@Override
public void dispose() {
super.dispose();
playButton.dispose();
}
}

View File

@ -0,0 +1,234 @@
package com.me.brickbuster.state;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
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.utils.CompositeIterator;
import com.me.brickbuster.entity.*;
import com.me.brickbuster.entity.powerup.PowerUp;
import com.me.brickbuster.layout.BrickLayout;
import com.me.brickbuster.layout.FileLevelLoader;
import com.me.brickbuster.layout.Level;
import com.me.brickbuster.layout.LevelLoader;
import com.me.brickbuster.physics.Box2dContactListener;
import com.me.brickbuster.physics.EntityType;
import com.me.brickbuster.physics.PhysicsBody;
import java.util.Iterator;
public class PlayState extends FieldState {
public static final float BOARD_WIDTH = 54f;
public static final float BOARD_HEIGHT = 96f;
public static final float PIXEL_PER_METER = BrickBuster.BOARD_WIDTH / BOARD_WIDTH;
public static final float EDGE_PADDING = 1f;
public static final Vector2 lowerLeftCorner =
new Vector2(EDGE_PADDING,0);
public static final Vector2 lowerRightCorner =
new Vector2(BrickBuster.BOARD_WIDTH/PIXEL_PER_METER-EDGE_PADDING,0);
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 Body playArea;
public Array<Body> bodies;
public LevelLoader levelLoader;
public Level currentLevel;
public Array<PowerUp> powerUps;
public Paddle paddle;
public Array<Ball> balls;
public Array<Brick> bricks;
public Array<Shield> shields;
private float updateTime = 0f;
private final Box2DDebugRenderer debugRenderer;
public PlayState(BrickBuster game) {
super(game);
debugRenderer = new Box2DDebugRenderer();
}
@Override
public void setup() {
super.setup();
// Initialize a world with no gravity
world = new World(new Vector2(), false);
world.setContactListener(new Box2dContactListener());
bodies = new Array<Body>();
levelLoader = new FileLevelLoader(this);
currentLevel = levelLoader.getNextLevel();
initializeLevel();
}
@Override
public void render() {
world.getBodies(bodies);
for (Body b : bodies) {
Entity e = (Entity) b.getUserData();
if (e instanceof Ball || e instanceof PowerUp || e instanceof Brick) {
e.setPos(b.getPosition());
}
}
long start = System.nanoTime();
game.sb.begin();
Iterator<? extends Entity> it = CompositeIterator.concat(balls, bricks);
while (it.hasNext()) {
it.next().render(game.sb, game.sr);
}
game.sb.end();
it = CompositeIterator.concat(powerUps, shields);
while (it.hasNext()) {
it.next().render(game.sb, game.sr);
}
paddle.render(game.sb, game.sr);
//debugRenderer.render(world, game.cam.combined.cpy().scl(PIXEL_PER_METER));
long renderTime = System.nanoTime() - start;
game.fb.begin();
game.font.setColor(Color.GRAY);
game.font.draw(game.fb, String.format("FPS: %d Update: %.2f ms Render: %.2f ms",
Gdx.graphics.getFramesPerSecond(), updateTime/1000000f, renderTime/1000000f), 10, BrickBuster.BOARD_HEIGHT-10);
game.fb.end();
}
@Override
public void update(float dt) {
long start = System.nanoTime();
paddle.update(dt);
Iterator<? extends Entity> it = CompositeIterator.concat(balls, powerUps, shields, bricks);
while (it.hasNext()) {
Entity ent = it.next();
ent.update(dt);
if (ent.isDeleted()) {
it.remove();
if (ent instanceof PhysicsBody) {
world.destroyBody(((PhysicsBody) ent).getBody());
}
ent.dispose();
}
}
if (balls.size == 0) {
ballReset();
}
if (bricks.size == 0) {
currentLevel = levelLoader.getNextLevel();
if (currentLevel == null) {
game.setScreen(new MenuState(game));
dispose();
} else {
initializeLevel();
}
return;
}
world.step(dt, 6, 2);
updateTime = System.nanoTime() - start;
}
@Override
public void dispose() {
super.dispose();
world.dispose();
bodies.clear();
powerUps.clear();
powerUps = null;
balls.clear();
balls = null;
bricks.clear();
bricks = null;
paddle = null;
}
private void initializeLevel() {
world.getBodies(bodies);
for (Body b : bodies) {
world.destroyBody(b);
}
// Set up the borders
BodyDef playAreaDef = new BodyDef();
playAreaDef.type = BodyDef.BodyType.StaticBody;
playAreaDef.position.set(new Vector2());
EdgeShape screenEdge = new EdgeShape();
FixtureDef playAreaFixture = new FixtureDef();
playAreaFixture.shape = screenEdge;
playAreaFixture.filter.categoryBits = EntityType.BOUNDARY;
playAreaFixture.filter.maskBits = EntityType.BALL | EntityType.BRICK;
playArea = world.createBody(playAreaDef);
screenEdge.set(lowerRightCorner, upperRightCorner);
playArea.createFixture(playAreaFixture);
screenEdge.set(upperRightCorner, upperLeftCorner);
playArea.createFixture(playAreaFixture);
screenEdge.set(upperLeftCorner, lowerLeftCorner);
playArea.createFixture(playAreaFixture);
screenEdge.dispose();
powerUps = new Array<PowerUp>();
paddle = new Paddle(this);
BrickLayout layout = currentLevel.getLayout();
layout.initialize();
bricks = layout.getBricks();
balls = new Array<Ball>();
balls.add(new Ball(this));
shields = new Array<Shield>();
}
public int getShieldCount() {
return shields.size;
}
public void addShield() {
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.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.SHIELD_HEIGHT);
}
}
}
public void ballReset() {
Ball ball = new Ball(this);
ball.setX(paddle.getX());
ball.setY(Paddle.PADDLE_Y + Paddle.PADDLE_HEIGHT + Ball.RADIUS);
balls.add(ball);
paddle.setSticky(false);
paddle.setWidth(Paddle.DEFAULT_WIDTH);
}
}

View File

@ -0,0 +1,39 @@
package com.me.brickbuster.state;
import com.badlogic.gdx.ScreenAdapter;
import com.me.brickbuster.BrickBuster;
public abstract class State extends ScreenAdapter {
protected final BrickBuster game;
private boolean disposed;
public State(BrickBuster game) {
this.game = game;
this.setup();
}
@Override
public final void render(float delta) {
update(delta);
if (!disposed) {
render();
}
}
@Override
public void dispose() {
this.disposed = true;
}
public BrickBuster getGame() {
return game;
}
public abstract void setup();
public abstract void render();
public abstract void update(float dt);
}

View File

@ -0,0 +1,43 @@
package com.me.brickbuster.utils;
import java.util.Iterator;
public class CompositeIterator<T> implements Iterator<T> {
private short index = 0;
private Iterator<? extends T> iterator;
private Iterable<? extends T>[] iterables;
private CompositeIterator(Iterable<? extends T>... iterables) {
this.iterables = iterables;
this.iterator = iterables[index].iterator();
}
@Override
public boolean hasNext() {
if (iterator.hasNext()) {
return true;
} else {
if (++index < iterables.length) {
iterator = iterables[index].iterator();
return hasNext();
}
}
return false;
}
@Override
public T next() {
return iterator.next();
}
@Override
public void remove() {
iterator.remove();
}
public static <E> CompositeIterator<E> concat(Iterable<? extends E>... iterables) {
return new CompositeIterator<E>(iterables);
}
}

View File

@ -1,10 +1,13 @@
package com.me.brickbuster; package com.me.brickbuster.utils;
import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
public class Utils { public class Utils {
public static final Vector2 HORIZONTAL_EDGE = new Vector2(1, 0);
public static final Vector2 VERTICAL_EDGE = new Vector2(0, 1);
public static Vector2 reflect(Vector2 incoming, Vector2 surface) { public static Vector2 reflect(Vector2 incoming, Vector2 surface) {
Vector2 normal = new Vector2(-surface.y, surface.x); Vector2 normal = new Vector2(-surface.y, surface.x);
return incoming.sub(normal.scl(normal.dot(incoming) * 2)); return incoming.sub(normal.scl(normal.dot(incoming) * 2));

View File

@ -7,8 +7,8 @@ import com.me.brickbuster.BrickBuster;
public class DesktopLauncher { public class DesktopLauncher {
public static void main (String[] arg) { public static void main (String[] arg) {
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration(); LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
config.width = BrickBuster.WIDTH; config.width = 432;
config.height = BrickBuster.HEIGHT; config.height = 768;
config.title = BrickBuster.TITLE; config.title = BrickBuster.TITLE;
new LwjglApplication(new BrickBuster(), config); new LwjglApplication(new BrickBuster(), config);
} }

View File

@ -0,0 +1,17 @@
package com.me.brickbuster.desktop;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.tools.texturepacker.TexturePacker;
public class TextureSetup {
public static void main(String[] args) {
TexturePacker.Settings settings = new TexturePacker.Settings();
settings.paddingX = 8;
settings.paddingY = 8;
settings.filterMin = Texture.TextureFilter.MipMapLinearLinear;
settings.filterMag = Texture.TextureFilter.MipMapLinearLinear;
TexturePacker.process(settings,"sprites", "./", "textures");
}
}