Skip to content

Commit 3ff03bb

Browse files
committed
game demo (WIP)
1 parent bbadb61 commit 3ff03bb

File tree

3 files changed

+266
-9
lines changed

3 files changed

+266
-9
lines changed

src/example.ts

Lines changed: 194 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,34 +34,46 @@ const jitterComponent = {
3434

3535
let xDirecton = 0
3636
let yDirection = 0
37+
let isSpacebar = false
3738

3839
const controlComponent = {
3940
name: "control",
4041
state: {
41-
moveSpeed: 2
42+
moveSpeed: 2,
43+
runSpeed: 5,
44+
controlsID: null
4245
},
4346
onAttach() {
4447
const keys = {}
4548

46-
setInterval(() => {
49+
const id = setInterval(() => {
4750
if (keys["ArrowLeft"]) xDirecton = -1
4851
if (keys["ArrowRight"]) xDirecton = 1
4952
if (keys["ArrowUp"]) yDirection = -1
5053
if (keys["ArrowDown"]) yDirection = 1
54+
if (keys[" "]) isSpacebar = true
5155
}, 20)
5256

57+
this.controlsID = id
58+
5359
document.addEventListener("keydown", (event) => {
60+
event.preventDefault()
5461
keys[event.key] = true
5562
})
5663

5764
document.addEventListener("keyup", (event) => {
65+
event.preventDefault()
5866
delete keys[event.key]
5967

6068
if (!keys["ArrowLeft"]) xDirecton = 0
6169
if (!keys["ArrowRight"]) xDirecton = 0
6270
if (!keys["ArrowUp"]) yDirection = 0
6371
if (!keys["ArrowDown"]) yDirection = 0
72+
if (!keys[" "]) isSpacebar = false
6473
})
74+
},
75+
onRemove() {
76+
6577
}
6678
}
6779

@@ -73,6 +85,60 @@ const growComponent = {
7385
}
7486
}
7587

88+
const collisionComponent = {
89+
name: "onCollisionDeleteComponent",
90+
}
91+
92+
const collisionProcessor = {
93+
name: "onCollisionDeleteProcessor",
94+
target: "onCollisionDeleteComponent",
95+
update(component, entities) {
96+
entities.forEach((entity) => {
97+
const allEntities = ECS.getEntities()
98+
99+
for (let targetEntity of allEntities) {
100+
const isPlayer = targetEntity.name === "Player"
101+
102+
if (!isPlayer) {
103+
continue
104+
}
105+
106+
107+
const targetPosition = ECS.entityHasComponent(targetEntity, "position")
108+
const targetRenderer = ECS.entityHasComponent(targetEntity, "box") || ECS.entityHasComponent(targetEntity, "playerBox")
109+
const hasPosition = ECS.entityHasComponent(entity, "position")
110+
const hasRenderer = ECS.entityHasComponent(entity, "box") || ECS.entityHasComponent(entity, "playerBox")
111+
const hasCollision = ECS.entityHasComponent(entity, "onCollisionDeleteComponent")
112+
const isSelf = targetEntity === entity
113+
114+
if (!hasPosition || !hasRenderer || isSelf || !targetPosition || !targetRenderer || !hasCollision) {
115+
continue
116+
}
117+
118+
const x = entity.state.x
119+
const y = entity.state.y
120+
const width = entity.state.width
121+
const height = entity.state.height
122+
123+
const targetX = targetEntity.state.x
124+
const targetY = targetEntity.state.y
125+
const targetWidth = targetEntity.state.width
126+
const targetHeight = targetEntity.state.height
127+
128+
const xCollision = (x + width) >= targetX && x <= (targetX + targetWidth)
129+
const yCollision = (y + height) >= targetY && y <= (targetY + targetHeight)
130+
131+
if (xCollision && yCollision) {
132+
ECS.removeComponentFromEntity(entity, "onCollisionDeleteComponent")
133+
ECS.addComponentToEntity(entity, "growEffect")
134+
ECS.addComponentToEntity(entity, "jitter")
135+
ECS.addComponentToEntity(entity, "flashColor")
136+
}
137+
}
138+
})
139+
}
140+
}
141+
76142
const growProcesssor = {
77143
name: "growProcessor",
78144
target: "growEffect",
@@ -83,6 +149,7 @@ const growProcesssor = {
83149

84150
if (entity.state.width > entity.state.maxGrowSize) {
85151
ECS.removeEntity(entity)
152+
score += 10
86153
}
87154
})
88155
}
@@ -95,6 +162,14 @@ const controlProcessor = {
95162
entities.forEach(entity => {
96163
entity.state.x += xDirecton * entity.state.moveSpeed
97164
entity.state.y += yDirection * entity.state.moveSpeed
165+
166+
if (isSpacebar && xDirecton) {
167+
entity.state.x += xDirecton * entity.state.runSpeed
168+
}
169+
170+
if (isSpacebar && yDirection) {
171+
entity.state.y += yDirection * entity.state.runSpeed
172+
}
98173
})
99174
}
100175
}
@@ -121,8 +196,10 @@ const boxRenderingProcessor = {
121196
target: "box",
122197
update(component, entities) {
123198
entities.forEach(entity => {
199+
const hasFlashColor = ECS.entityHasComponent(entity, "flashColor")
200+
124201
ctx.translate(entity.state.x, entity.state.y)
125-
ctx.fillStyle = "orange"
202+
ctx.fillStyle = hasFlashColor ? entity.state.flashColorCurrent : "orange"
126203
ctx.fillRect(0, 0, entity.state.width, entity.state.height)
127204
ctx.translate(-entity.state.x, -entity.state.y)
128205
})
@@ -142,23 +219,53 @@ const playerBoxRenderingProcessor = {
142219
}
143220
}
144221

222+
const flashColorComponent = {
223+
name: "flashColor",
224+
state: {
225+
flashColorTimer: 0,
226+
flashColorSpeed: 200,
227+
flashColor1: "white",
228+
flashColor2: "red",
229+
flashColorCurrent: "red"
230+
}
231+
}
232+
233+
const flashColorProcessor = {
234+
name: "flashColorProcessor",
235+
target: "flashColor",
236+
update(component, entities) {
237+
entities.forEach((entity) => {
238+
entity.state.flashColorTimer += 20
239+
240+
if (entity.state.flashColorTimer >= entity.state.flashColorSpeed) {
241+
entity.state.flashColorTimer = 0
242+
entity.state.flashColorCurrent = entity.state.flashColorCurrent === entity.state.flashColor1 ? entity.state.flashColor2 : entity.state.flashColor1
243+
}
244+
})
245+
}
246+
}
247+
145248
ECS.addComponent(positionComponent)
146249
ECS.addComponent(boxComponent)
147250
ECS.addComponent(jitterComponent)
148251
ECS.addComponent(controlComponent)
149252
ECS.addComponent(playerBox)
150253
ECS.addComponent(growComponent)
254+
ECS.addComponent(collisionComponent)
255+
ECS.addComponent(flashColorComponent)
256+
257+
ECS.addProcessor(flashColorProcessor)
258+
ECS.addProcessor(collisionProcessor)
151259
ECS.addProcessor(growProcesssor)
152-
ECS.addProcessor(playerBoxRenderingProcessor)
153260
ECS.addProcessor(jitterProcessor)
154261
ECS.addProcessor(boxRenderingProcessor)
155262
ECS.addProcessor(controlProcessor)
263+
ECS.addProcessor(playerBoxRenderingProcessor)
156264

157265

158266
const create100Boxes = () => {
159-
console.log(ECS)
160267
for (let i = 0; i < 100; i++) {
161-
const box = ECS.createEntity("Box", ["position", "box", "growEffect"])
268+
const box = ECS.createEntity("enemy", ["position", "box", "onCollisionDeleteComponent"])
162269

163270
box.state.x = Math.random() * canvas.width
164271
box.state.y = Math.random() * canvas.height
@@ -167,8 +274,17 @@ const create100Boxes = () => {
167274
}
168275
}
169276

277+
const create1EnemyBox = () => {
278+
const enemyBox = ECS.createEntity("enemy", ["position", "box", "onCollisionDeleteComponent"])
279+
280+
enemyBox.state.x = 100
281+
enemyBox.state.y = 100
282+
283+
ECS.addEntity(enemyBox)
284+
}
285+
170286
const createPlayerBox = () => {
171-
const playerBox = ECS.createEntity("Player", ["position", "playerBox", "control", "growEffect"])
287+
const playerBox = ECS.createEntity("Player", ["position", "playerBox", "control"])
172288

173289
playerBox.state.x = canvas.width / 2
174290
playerBox.state.y = canvas.height / 2
@@ -182,13 +298,83 @@ canvas.style.backgroundColor = "black"
182298

183299
const ctx = canvas.getContext("2d") as CanvasRenderingContext2D
184300

301+
let score = 0
302+
185303
const gameloop = () => {
186304
ctx.clearRect(0, 0, canvas.width, canvas.height)
187305
ECS.update()
306+
ctx.font = "30px Comis-Sans"
307+
ctx.fillText("Score:", 30, 30)
308+
ctx.fillText(String(score), 110, 32)
188309

189310
requestAnimationFrame(gameloop)
190311
}
191312

192313
create100Boxes()
314+
// create1EnemyBox()
193315
createPlayerBox()
194-
// gameloop()
316+
gameloop()
317+
318+
319+
const playerRendererCheckbox = document.getElementById("player-renderer") as HTMLInputElement
320+
const playerRendererContainer = document.getElementById("player-renderer-container")
321+
const playerRendererSize = document.getElementById("player-renderer-size") as HTMLInputElement
322+
323+
const playerControlsCheckbox = document.getElementById("player-controls") as HTMLInputElement
324+
const playerControlsContainer = document.getElementById("player-controls-container")
325+
const playerControlsSpeed = document.getElementById("player-controls-speed") as HTMLInputElement
326+
const playerControlsRunSpeed = document.getElementById("player-controls-run-speed") as HTMLInputElement
327+
328+
playerRendererCheckbox.addEventListener("change", () => {
329+
const checkbox = playerRendererCheckbox
330+
const isChecked = checkbox.checked
331+
const player = ECS.getEntity("Player")
332+
333+
if (isChecked) {
334+
playerRendererContainer.style.display = "block"
335+
ECS.addComponentToEntity(player, "playerBox")
336+
}
337+
338+
if (!isChecked) {
339+
playerRendererContainer.style.display = "none"
340+
ECS.removeComponentFromEntity(player, "playerBox")
341+
}
342+
})
343+
344+
playerRendererSize.addEventListener("change", () => {
345+
const player = ECS.getEntity("Player")
346+
player.state.height = Number(playerRendererSize.value)
347+
player.state.width = Number(playerRendererSize.value)
348+
})
349+
350+
playerControlsCheckbox.addEventListener("change", () => {
351+
const checkbox = playerControlsCheckbox
352+
const isChecked = checkbox.checked
353+
const player = ECS.getEntity("Player")
354+
355+
if (isChecked) {
356+
playerControlsContainer.style.display = "block"
357+
ECS.addComponentToEntity(player, "controls")
358+
}
359+
360+
if (!isChecked) {
361+
playerControlsContainer.style.display = "none"
362+
ECS.removeComponentFromEntity(player, "controls")
363+
}
364+
})
365+
366+
playerControlsSpeed.addEventListener("change", () => {
367+
const player = ECS.getEntity("Player")
368+
player.state.moveSpeed = Number(playerControlsSpeed.value)
369+
370+
console.log(player)
371+
})
372+
373+
playerControlsRunSpeed.addEventListener("change", () => {
374+
const player = ECS.getEntity("Player")
375+
player.state.runSpeed = Number(playerControlsRunSpeed.value)
376+
})
377+
378+
const getPlayerCodeSnippet = () => {
379+
const player = ECS.getEntity("Player")
380+
}

src/index.html

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,37 @@
33
<html>
44
<head>
55
<script type="module" src="./example.ts"></script>
6+
<link href="style.css" rel="stylesheet" />
67
</head>
78

89
<body>
9-
<canvas id="game-canvas"></canvas>
10+
<div class="container">
11+
<canvas id="game-canvas"></canvas>
12+
<div class="controls">
13+
<details>
14+
<summary>Player</summary>
15+
<div class="components">
16+
<div>
17+
<label>Renderer</label>
18+
<input checked id="player-renderer" type="checkbox" />
19+
<div id="player-renderer-container" class="detail-input">
20+
<label>size</label>
21+
<input id="player-renderer-size" type="range" min="0" max="100" value="10" />
22+
</div>
23+
</div>
24+
<div>
25+
<label>Controls</label>
26+
<input checked id="player-controls" type="checkbox" />
27+
<div id="player-controls-container" class="detail-input">
28+
<label>speed (arrow keys to move)</label>
29+
<input id="player-controls-speed" value="2" type="range" min="0" max="10" />
30+
<label>run speed (hold Spacebar)</label>
31+
<input id="player-controls-run-speed" value="5" type="range" min="0" max="10" />
32+
</div>
33+
</div>
34+
</div>
35+
</details>
36+
</div>
37+
</div>
1038
</body>
1139
</html>

src/style.css

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
html {
2+
font-family: Helvetica;
3+
}
4+
5+
.container {
6+
display: grid;
7+
grid-template-columns: 1fr 1fr;
8+
grid-gap: 10px;
9+
}
10+
11+
.controls {
12+
background-color: hsla(0, 0%, 90%, 1);
13+
padding: 20px;
14+
}
15+
16+
.components > div {
17+
margin: 20px 0;
18+
background-color: hsla(0, 0%, 93%, 1);
19+
padding: 10px;
20+
}
21+
22+
.detail-input {
23+
margin-left: 10px;
24+
margin-top : 10px;
25+
}
26+
27+
.detail-input > label {
28+
display: block;
29+
font-size: 16px;
30+
font-weight: 600;
31+
}
32+
33+
.detail-input > input {
34+
margin-bottom: 10px;
35+
}
36+
37+
@media only screen and (max-width: 700px) {
38+
.container {
39+
grid-template-columns: 1fr;
40+
grid-template-rows: 1fr 1fr;
41+
max-width: 500px;
42+
}
43+
}

0 commit comments

Comments
 (0)