Skip to content

Commit 57a4900

Browse files
authored
Add vibe-coding falling blocks game (#142)
1 parent e5c1d3c commit 57a4900

File tree

9 files changed

+873
-3
lines changed

9 files changed

+873
-3
lines changed
30.4 KB
Binary file not shown.

examples/assets/scripts/falling-blocks.mjs

Lines changed: 692 additions & 0 deletions
Large diffs are not rendered by default.

examples/assets/sounds/clear1.mp3

7.16 KB
Binary file not shown.

examples/assets/sounds/clear4.mp3

7.36 KB
Binary file not shown.

examples/assets/sounds/drop.mp3

6.55 KB
Binary file not shown.

examples/assets/sounds/gameover.mp3

10.8 KB
Binary file not shown.

examples/assets/sounds/rotate.mp3

4.51 KB
Binary file not shown.

examples/vibe-falling-blocks.html

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
6+
<title>PlayCanvas Web Components - Vibe-Coded Falling Blocks Game</title>
7+
<script type="importmap">
8+
{
9+
"imports": {
10+
"playcanvas": "../node_modules/playcanvas/build/playcanvas.mjs"
11+
}
12+
}
13+
</script>
14+
<script type="module" src="../dist/pwc.mjs"></script>
15+
<link rel="stylesheet" href="css/example.css">
16+
<style>
17+
#game-ui {
18+
position: absolute;
19+
top: 0;
20+
left: 0;
21+
width: 100%;
22+
height: 100%;
23+
pointer-events: none;
24+
font-family: Arial, sans-serif;
25+
}
26+
27+
#score, #level, #lines {
28+
position: absolute;
29+
background-color: rgba(0, 0, 0, 0.5);
30+
color: white;
31+
padding: 10px 20px;
32+
border-radius: 5px;
33+
font-size: 18px;
34+
}
35+
36+
#score {
37+
top: 20px;
38+
right: 20px;
39+
}
40+
41+
#level {
42+
top: 70px;
43+
right: 20px;
44+
}
45+
46+
#lines {
47+
top: 120px;
48+
right: 20px;
49+
}
50+
51+
#game-over {
52+
position: absolute;
53+
top: 50%;
54+
left: 50%;
55+
transform: translate(-50%, -50%);
56+
background-color: rgba(0, 0, 0, 0.8);
57+
color: white;
58+
padding: 30px;
59+
border-radius: 10px;
60+
text-align: center;
61+
pointer-events: auto;
62+
display: none;
63+
}
64+
65+
#restart-button {
66+
background-color: #4CAF50;
67+
border: none;
68+
color: white;
69+
padding: 15px 32px;
70+
text-align: center;
71+
text-decoration: none;
72+
display: inline-block;
73+
font-size: 16px;
74+
margin: 20px 0 0 0;
75+
cursor: pointer;
76+
border-radius: 5px;
77+
transition: background-color 0.3s;
78+
}
79+
80+
#restart-button:hover {
81+
background-color: #45a049;
82+
}
83+
84+
#controls {
85+
position: absolute;
86+
bottom: 20px;
87+
left: 20px;
88+
background-color: rgba(0, 0, 0, 0.5);
89+
color: white;
90+
padding: 10px 20px;
91+
border-radius: 5px;
92+
font-size: 16px;
93+
}
94+
</style>
95+
</head>
96+
<body>
97+
<pc-app>
98+
<!-- Assets -->
99+
<pc-asset src="assets/scripts/falling-blocks.mjs"></pc-asset>
100+
<pc-asset src="assets/skies/shanghai-riverside-4k.hdr" id="shanghai"></pc-asset>
101+
<pc-asset src="assets/sounds/clear1.mp3" id="clear1-sound"></pc-asset>
102+
<pc-asset src="assets/sounds/clear4.mp3" id="clear4-sound"></pc-asset>
103+
<pc-asset src="assets/sounds/rotate.mp3" id="rotate-sound"></pc-asset>
104+
<pc-asset src="assets/sounds/drop.mp3" id="drop-sound"></pc-asset>
105+
<pc-asset src="assets/sounds/gameover.mp3" id="gameover-sound"></pc-asset>
106+
<pc-asset src="assets/models/rounded-box.glb" id="rounded-box"></pc-asset>
107+
108+
<!-- Materials for Blocks -->
109+
<pc-material id="block-i" diffuse="0 1 1"></pc-material>
110+
<pc-material id="block-j" diffuse="0 0 1"></pc-material>
111+
<pc-material id="block-l" diffuse="1 0.5 0"></pc-material>
112+
<pc-material id="block-o" diffuse="1 1 0"></pc-material>
113+
<pc-material id="block-s" diffuse="0 1 0"></pc-material>
114+
<pc-material id="block-t" diffuse="0.5 0 0.5"></pc-material>
115+
<pc-material id="block-z" diffuse="1 0 0"></pc-material>
116+
<pc-material id="block-grid" diffuse="0.2 0.2 0.2"></pc-material>
117+
<pc-material id="block-wall" diffuse="0.3 0.3 0.3"></pc-material>
118+
119+
<!-- Scene -->
120+
<pc-scene>
121+
<!-- Sky -->
122+
<pc-sky asset="shanghai" type="infinite" level="3" lighting></pc-sky>
123+
124+
<!-- Camera -->
125+
<pc-entity name="camera" position="0 9.5 27">
126+
<pc-camera></pc-camera>
127+
</pc-entity>
128+
129+
<pc-entity name="ambient">
130+
<pc-light type="ambient" intensity="0.2"></pc-light>
131+
</pc-entity>
132+
133+
<!-- Game Board -->
134+
<pc-entity name="game-board" position="0 0 0">
135+
<pc-scripts>
136+
<pc-script name="fallingBlocksGame" attributes='{
137+
"boardWidth": 10,
138+
"boardHeight": 20,
139+
"blockSize": 1
140+
}'></pc-script>
141+
</pc-scripts>
142+
<pc-sounds>
143+
<pc-sound name="clear1" asset="clear1-sound" overlap></pc-sound>
144+
<pc-sound name="clear4" asset="clear4-sound" overlap></pc-sound>
145+
<pc-sound name="rotate" asset="rotate-sound" overlap></pc-sound>
146+
<pc-sound name="drop" asset="drop-sound" overlap></pc-sound>
147+
<pc-sound name="gameover" asset="gameover-sound" overlap></pc-sound>
148+
</pc-sounds>
149+
</pc-entity>
150+
</pc-scene>
151+
</pc-app>
152+
153+
<!-- UI Overlay -->
154+
<div id="game-ui">
155+
<div id="score">Score: <span id="score-value">0</span></div>
156+
<div id="level">Level: <span id="level-value">1</span></div>
157+
<div id="lines">Lines: <span id="lines-value">0</span></div>
158+
159+
<div id="controls">
160+
<div>← → : Move left/right</div>
161+
<div>↑ : Rotate</div>
162+
<div>↓ : Soft drop</div>
163+
<div>Space : Hard drop</div>
164+
<div>P : Pause</div>
165+
</div>
166+
167+
<div id="game-over">
168+
<h2>Game Over</h2>
169+
<p>Final Score: <span id="final-score">0</span></p>
170+
<button id="restart-button">Play Again</button>
171+
</div>
172+
</div>
173+
174+
<script type="module" src="js/example.mjs"></script>
175+
</body>
176+
</html>

src/components/sound-slot.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ class SoundSlotElement extends AsyncElement {
5050

5151
this.soundSlot = this.soundElement!.component!.addSlot(this._name, options);
5252
this.asset = this._asset;
53-
this.soundSlot!.play();
5453

5554
this._onReady();
5655
}
@@ -245,14 +244,17 @@ class SoundSlotElement extends AsyncElement {
245244
}
246245

247246
static get observedAttributes() {
248-
return ['asset', 'autoPlay', 'duration', 'loop', 'name', 'overlap', 'pitch', 'startTime', 'volume'];
247+
return ['asset', 'auto-play', 'duration', 'loop', 'name', 'overlap', 'pitch', 'start-time', 'volume'];
249248
}
250249

251250
attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
252251
switch (name) {
253252
case 'asset':
254253
this.asset = newValue;
255254
break;
255+
case 'auto-play':
256+
this.autoPlay = this.hasAttribute('auto-play');
257+
break;
256258
case 'duration':
257259
this.duration = parseFloat(newValue);
258260
break;
@@ -268,7 +270,7 @@ class SoundSlotElement extends AsyncElement {
268270
case 'pitch':
269271
this.pitch = parseFloat(newValue);
270272
break;
271-
case 'startTime':
273+
case 'start-time':
272274
this.startTime = parseFloat(newValue);
273275
break;
274276
case 'volume':

0 commit comments

Comments
 (0)