Skip to content

Commit 544b0be

Browse files
committed
Implemented random flok of birds
1 parent daa6c7c commit 544b0be

18 files changed

+2345
-70
lines changed

.kiro/specs/flying-birds-ambient/tasks.md

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -58,89 +58,89 @@
5858
- **Property 12: Horizontal boundary X-coordinate variation**
5959
- **Validates: Requirements 4.3**
6060

61-
- [ ] 3. Implement V-formation positioning
61+
- [x] 3. Implement V-formation positioning
6262
- Implement initializeVFormation() to position 5 birds in V-shape
6363
- Calculate lead bird position at spawn point
6464
- Calculate wing bird positions with 40px spacing and 30-degree angle
6565
- Ensure consistent spacing between all birds in formation
6666
- _Requirements: 2.2, 2.4_
6767

68-
- [ ] 3.1 Write property test for V-shape arrangement
68+
- [x] 3.1 Write property test for V-shape arrangement
6969
- **Property 7: V-shape arrangement**
7070
- **Validates: Requirements 2.2**
7171

72-
- [ ] 3.2 Write property test for consistent bird spacing
72+
- [x] 3.2 Write property test for consistent bird spacing
7373
- **Property 9: Consistent bird spacing**
7474
- **Validates: Requirements 2.4**
7575

76-
- [ ] 4. Implement flight mechanics
76+
- [x] 4. Implement flight mechanics
7777
- Calculate flight direction from spawn boundary to opposite boundary
7878
- Implement Bird.update() to move bird along velocity vector
7979
- Implement BirdFormation.update() to update all birds with same velocity
8080
- Implement hasReachedTarget() to detect when formation crosses opposite boundary
8181
- _Requirements: 1.3, 1.4, 4.4_
8282

83-
- [ ] 4.1 Write property test for formation reaches opposite boundary
83+
- [x] 4.1 Write property test for formation reaches opposite boundary
8484
- **Property 3: Formation reaches opposite boundary**
8585
- **Validates: Requirements 1.3**
8686

87-
- [ ] 4.2 Write property test for V-shape invariant during flight
87+
- [x] 4.2 Write property test for V-shape invariant during flight
8888
- **Property 8: V-shape invariant during flight**
8989
- **Validates: Requirements 2.3**
9090

91-
- [ ] 4.3 Write property test for flight path toward opposite boundary
91+
- [x] 4.3 Write property test for flight path toward opposite boundary
9292
- **Property 13: Flight path toward opposite boundary**
9393
- **Validates: Requirements 4.4**
9494

95-
- [ ] 5. Implement spawn timer and lifecycle management
95+
- [x] 5. Implement spawn timer and lifecycle management
9696
- Implement BirdFormationManager.update() to handle spawn timer countdown
9797
- Trigger spawnFormation() when timer reaches zero
9898
- Reset timer with new random interval after spawn
9999
- Detect when formation reaches target and despawn it
100100
- Reset spawn timer after despawn
101101
- _Requirements: 1.1, 1.4, 1.5_
102102

103-
- [ ] 5.1 Write property test for despawn triggers timer reset
103+
- [x] 5.1 Write property test for despawn triggers timer reset
104104
- **Property 4: Despawn triggers timer reset**
105105
- **Validates: Requirements 1.4**
106106

107-
- [ ] 6. Implement rendering system
107+
- [x] 6. Implement rendering system
108108
- Load bird sprite texture in BirdFormationManager.initialize()
109109
- Implement Bird.render() to draw bird sprite at current position
110110
- Implement BirdFormation.render() to render all 5 birds
111111
- Implement BirdFormationManager.render() to render active formation only
112112
- Add null check to skip rendering when no formation is active
113113
- _Requirements: 3.1, 5.1_
114114

115-
- [ ] 6.1 Write property test for no rendering when not visible
115+
- [x] 6.1 Write property test for no rendering when not visible
116116
- **Property 14: No rendering when not visible**
117117
- **Validates: Requirements 5.1**
118118

119-
- [ ] 7. Implement resource management
119+
- [x] 7. Implement resource management
120120
- Implement Bird.dispose() to clean up bird resources
121121
- Implement BirdFormation.dispose() to dispose all 5 birds
122122
- Call formation.dispose() when despawning
123123
- Implement BirdFormationManager.dispose() to clean up shared texture
124124
- _Requirements: 5.4_
125125

126-
- [ ] 7.1 Write property test for resource cleanup on despawn
126+
- [x] 7.1 Write property test for resource cleanup on despawn
127127
- **Property 15: Resource cleanup on despawn**
128128
- **Validates: Requirements 5.4**
129129

130-
- [ ] 8. Integrate with MyGdxGame
130+
- [x] 8. Integrate with MyGdxGame
131131
- Add BirdFormationManager field to MyGdxGame
132132
- Initialize BirdFormationManager in MyGdxGame.create()
133133
- Call birdFormationManager.update() in MyGdxGame.render() before rendering
134134
- Call birdFormationManager.render() after rain effects but before UI elements
135135
- Call birdFormationManager.dispose() in MyGdxGame.dispose()
136136
- _Requirements: 3.1, 3.2, 3.3_
137137

138-
- [ ] 9. Create bird sprite asset
138+
- [x] 9. Create bird sprite asset
139139
- Create or source a simple bird sprite texture (32x32 pixels recommended)
140140
- Place texture in assets/sprites/ directory
141141
- Update Bird class to load texture from correct path
142142
- Test texture loading and rendering
143143
- _Requirements: 3.1_
144144

145-
- [ ] 10. Final checkpoint - Ensure all tests pass
145+
- [x] 10. Final checkpoint - Ensure all tests pass
146146
- Ensure all tests pass, ask the user if questions arise.

assets/sprites/assets.png

9.86 KB
Loading

assets/sprites/bird.png

2.19 KB
Loading

assets/sprites/bird2.png

2.65 KB
Loading

src/main/java/wagemaker/uk/birds/Bird.java

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,25 @@
1010
public class Bird {
1111
private float x;
1212
private float y;
13-
private Texture texture;
13+
private Texture texture1;
14+
private Texture texture2;
1415
private float animationTime;
16+
private static final float ANIMATION_FRAME_DURATION = 0.5f; // 0.5 seconds per frame
1517

16-
public Bird(float x, float y, Texture texture) {
18+
public Bird(float x, float y, Texture texture1, Texture texture2) {
1719
this.x = x;
1820
this.y = y;
19-
this.texture = texture;
21+
this.texture1 = texture1;
22+
this.texture2 = texture2;
2023
this.animationTime = 0f;
2124
}
25+
26+
/**
27+
* Legacy constructor for single texture (backwards compatibility)
28+
*/
29+
public Bird(float x, float y, Texture texture) {
30+
this(x, y, texture, texture);
31+
}
2232

2333
public void update(float deltaTime, Vector2 velocity) {
2434
this.x += velocity.x * deltaTime;
@@ -27,10 +37,25 @@ public void update(float deltaTime, Vector2 velocity) {
2737
}
2838

2939
public void render(SpriteBatch batch) {
30-
if (texture != null) {
31-
batch.draw(texture, x, y);
40+
// Alternate between textures based on animation time
41+
Texture currentTexture = getCurrentTexture();
42+
if (currentTexture != null) {
43+
batch.draw(currentTexture, x, y);
3244
}
3345
}
46+
47+
/**
48+
* Get the current texture based on animation time.
49+
* Alternates between texture1 and texture2 every 0.5 seconds.
50+
*/
51+
private Texture getCurrentTexture() {
52+
if (texture1 == null) return texture2;
53+
if (texture2 == null) return texture1;
54+
55+
// Calculate which frame we're on (0 or 1)
56+
int frame = (int) (animationTime / ANIMATION_FRAME_DURATION) % 2;
57+
return frame == 0 ? texture1 : texture2;
58+
}
3459

3560
public float getX() {
3661
return x;

src/main/java/wagemaker/uk/birds/BirdFormation.java

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,32 @@ public class BirdFormation {
1717
private SpawnBoundary targetBoundary;
1818
private boolean active;
1919

20-
public BirdFormation(SpawnPoint spawnPoint, Vector2 velocity, Texture birdTexture) {
20+
public BirdFormation(SpawnPoint spawnPoint, Vector2 velocity, Texture birdTexture1, Texture birdTexture2) {
2121
this.birds = new ArrayList<>();
2222
this.velocity = velocity;
2323
this.spawnBoundary = spawnPoint.boundary;
2424
this.targetBoundary = getOppositeBoundary(spawnPoint.boundary);
2525
this.active = true;
2626

27-
initializeVFormation(spawnPoint, birdTexture);
27+
initializeVFormation(spawnPoint, birdTexture1, birdTexture2);
28+
}
29+
30+
/**
31+
* Legacy constructor for single texture (backwards compatibility)
32+
*/
33+
public BirdFormation(SpawnPoint spawnPoint, Vector2 velocity, Texture birdTexture) {
34+
this(spawnPoint, velocity, birdTexture, birdTexture);
2835
}
2936

30-
private void initializeVFormation(SpawnPoint spawnPoint, Texture birdTexture) {
37+
private void initializeVFormation(SpawnPoint spawnPoint, Texture birdTexture1, Texture birdTexture2) {
3138
// V-formation with 1 lead bird and 2 birds on each wing
3239
// Formation angle: 30 degrees, spacing: 40 pixels
3340

3441
float leadX = spawnPoint.x;
3542
float leadY = spawnPoint.y;
3643

3744
// Lead bird at the front
38-
birds.add(new Bird(leadX, leadY, birdTexture));
45+
birds.add(new Bird(leadX, leadY, birdTexture1, birdTexture2));
3946

4047
// Calculate perpendicular direction for wing positioning
4148
Vector2 perpendicular = new Vector2(-spawnPoint.direction.y, spawnPoint.direction.x);
@@ -48,14 +55,14 @@ private void initializeVFormation(SpawnPoint spawnPoint, Texture birdTexture) {
4855
for (int i = 1; i <= 2; i++) {
4956
float offsetX = perpendicular.x * spacing * i - spawnPoint.direction.x * backwardOffset * i;
5057
float offsetY = perpendicular.y * spacing * i - spawnPoint.direction.y * backwardOffset * i;
51-
birds.add(new Bird(leadX + offsetX, leadY + offsetY, birdTexture));
58+
birds.add(new Bird(leadX + offsetX, leadY + offsetY, birdTexture1, birdTexture2));
5259
}
5360

5461
// Right wing (2 birds)
5562
for (int i = 1; i <= 2; i++) {
5663
float offsetX = -perpendicular.x * spacing * i - spawnPoint.direction.x * backwardOffset * i;
5764
float offsetY = -perpendicular.y * spacing * i - spawnPoint.direction.y * backwardOffset * i;
58-
birds.add(new Bird(leadX + offsetX, leadY + offsetY, birdTexture));
65+
birds.add(new Bird(leadX + offsetX, leadY + offsetY, birdTexture1, birdTexture2));
5966
}
6067
}
6168

@@ -81,29 +88,34 @@ public void render(SpriteBatch batch) {
8188
}
8289
}
8390

84-
public boolean hasReachedTarget(float viewWidth, float viewHeight) {
91+
public boolean hasReachedTarget(float viewWidth, float viewHeight, float cameraX, float cameraY) {
8592
if (birds.isEmpty()) {
8693
return false;
8794
}
8895

89-
// Check lead bird position against target boundary
96+
// Check lead bird position against target boundary (camera-relative)
9097
Bird leadBird = birds.get(0);
9198
float x = leadBird.getX();
9299
float y = leadBird.getY();
93100

94101
switch (targetBoundary) {
95102
case TOP:
96-
return y > viewHeight;
103+
return y > cameraY + viewHeight;
97104
case BOTTOM:
98-
return y < 0;
105+
return y < cameraY;
99106
case LEFT:
100-
return x < 0;
107+
return x < cameraX;
101108
case RIGHT:
102-
return x > viewWidth;
109+
return x > cameraX + viewWidth;
103110
default:
104111
return false;
105112
}
106113
}
114+
115+
// Backward compatibility method for tests
116+
public boolean hasReachedTarget(float viewWidth, float viewHeight) {
117+
return hasReachedTarget(viewWidth, viewHeight, 0, 0);
118+
}
107119

108120
public void dispose() {
109121
for (Bird bird : birds) {

0 commit comments

Comments
 (0)