Spaces:
Running
Running
KaiShin1885
commited on
Update index.html
Browse files- index.html +235 -22
index.html
CHANGED
@@ -17,7 +17,6 @@
|
|
17 |
height: 100px;
|
18 |
position: absolute;
|
19 |
bottom: 0;
|
20 |
-
transition: height 0.2s;
|
21 |
background-size: contain;
|
22 |
background-repeat: no-repeat;
|
23 |
background-position: center;
|
@@ -100,16 +99,6 @@
|
|
100 |
animation: counterAnim 0.4s linear;
|
101 |
}
|
102 |
|
103 |
-
#status {
|
104 |
-
position: fixed;
|
105 |
-
left: 20px;
|
106 |
-
bottom: 20px;
|
107 |
-
padding: 10px;
|
108 |
-
background: rgba(0,0,0,0.8);
|
109 |
-
color: white;
|
110 |
-
font-family: monospace;
|
111 |
-
}
|
112 |
-
|
113 |
#instructions {
|
114 |
position: fixed;
|
115 |
right: 20px;
|
@@ -152,7 +141,6 @@
|
|
152 |
</div>
|
153 |
<div id="player" class="character"></div>
|
154 |
<div id="enemy" class="character"></div>
|
155 |
-
<div id="status"></div>
|
156 |
<div id="instructions">
|
157 |
Controls:<br><br>
|
158 |
W - Jump<br>
|
@@ -167,28 +155,38 @@
|
|
167 |
</div>
|
168 |
|
169 |
<script>
|
170 |
-
|
171 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
172 |
|
173 |
const SPRITES = {
|
174 |
-
stand: [
|
175 |
-
SPRITE_BASE64, // kstand1
|
176 |
-
SPRITE_BASE64, // kstand2 - 실제로는 다른 이미지
|
177 |
-
SPRITE_BASE64 // kstand3 - 실제로는 다른 이미지
|
178 |
-
]
|
179 |
};
|
180 |
|
181 |
class Character {
|
182 |
constructor(element, isPlayer = true) {
|
183 |
this.element = element;
|
184 |
this.isPlayer = isPlayer;
|
185 |
-
this.health =
|
186 |
this.pos = { x: isPlayer ? 100 : 650, y: 0 };
|
187 |
this.vel = { x: 0, y: 0 };
|
188 |
this.direction = isPlayer ? 'right' : 'left';
|
189 |
this.isMoving = false;
|
190 |
this.isAttacking = false;
|
191 |
this.isJumping = false;
|
|
|
192 |
this.currentFrame = 0;
|
193 |
this.lastAnimationUpdate = 0;
|
194 |
|
@@ -199,7 +197,7 @@
|
|
199 |
|
200 |
updateAnimation(timestamp) {
|
201 |
if (this.isPlayer && !this.isMoving && !this.isAttacking) {
|
202 |
-
if (timestamp - this.lastAnimationUpdate >=
|
203 |
this.currentFrame = (this.currentFrame + 1) % SPRITES.stand.length;
|
204 |
this.element.style.backgroundImage =
|
205 |
`url(${SPRITES.stand[this.currentFrame]})`;
|
@@ -209,7 +207,222 @@
|
|
209 |
}
|
210 |
}
|
211 |
|
212 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
213 |
</script>
|
214 |
</body>
|
215 |
</html>
|
|
|
17 |
height: 100px;
|
18 |
position: absolute;
|
19 |
bottom: 0;
|
|
|
20 |
background-size: contain;
|
21 |
background-repeat: no-repeat;
|
22 |
background-position: center;
|
|
|
99 |
animation: counterAnim 0.4s linear;
|
100 |
}
|
101 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
102 |
#instructions {
|
103 |
position: fixed;
|
104 |
right: 20px;
|
|
|
141 |
</div>
|
142 |
<div id="player" class="character"></div>
|
143 |
<div id="enemy" class="character"></div>
|
|
|
144 |
<div id="instructions">
|
145 |
Controls:<br><br>
|
146 |
W - Jump<br>
|
|
|
155 |
</div>
|
156 |
|
157 |
<script>
|
158 |
+
const SETTINGS = {
|
159 |
+
FPS: 60,
|
160 |
+
FRAME_TIME: 1000 / 60,
|
161 |
+
MOVE_SPEED: 5,
|
162 |
+
JUMP_FORCE: 15,
|
163 |
+
GRAVITY: 0.8,
|
164 |
+
INITIAL_HEALTH: 1000,
|
165 |
+
DAMAGE: 10,
|
166 |
+
SPECIAL_DAMAGE: 300,
|
167 |
+
ATTACK_DELAY: 200,
|
168 |
+
COUNTER_WINDOW: 400,
|
169 |
+
COUNTER_COOLDOWN: 5000,
|
170 |
+
SPECIAL_COOLDOWN: 30000,
|
171 |
+
ANIMATION_INTERVAL: 500
|
172 |
+
};
|
173 |
|
174 |
const SPRITES = {
|
175 |
+
stand: ['kstand1.png', 'kstand2.png', 'kstand3.png']
|
|
|
|
|
|
|
|
|
176 |
};
|
177 |
|
178 |
class Character {
|
179 |
constructor(element, isPlayer = true) {
|
180 |
this.element = element;
|
181 |
this.isPlayer = isPlayer;
|
182 |
+
this.health = SETTINGS.INITIAL_HEALTH;
|
183 |
this.pos = { x: isPlayer ? 100 : 650, y: 0 };
|
184 |
this.vel = { x: 0, y: 0 };
|
185 |
this.direction = isPlayer ? 'right' : 'left';
|
186 |
this.isMoving = false;
|
187 |
this.isAttacking = false;
|
188 |
this.isJumping = false;
|
189 |
+
this.isBlocking = false;
|
190 |
this.currentFrame = 0;
|
191 |
this.lastAnimationUpdate = 0;
|
192 |
|
|
|
197 |
|
198 |
updateAnimation(timestamp) {
|
199 |
if (this.isPlayer && !this.isMoving && !this.isAttacking) {
|
200 |
+
if (timestamp - this.lastAnimationUpdate >= SETTINGS.ANIMATION_INTERVAL) {
|
201 |
this.currentFrame = (this.currentFrame + 1) % SPRITES.stand.length;
|
202 |
this.element.style.backgroundImage =
|
203 |
`url(${SPRITES.stand[this.currentFrame]})`;
|
|
|
207 |
}
|
208 |
}
|
209 |
|
210 |
+
class Game {
|
211 |
+
constructor() {
|
212 |
+
this.lastFrameTime = 0;
|
213 |
+
this.gameTime = 60;
|
214 |
+
this.specialCooldown = Date.now() + SETTINGS.SPECIAL_COOLDOWN;
|
215 |
+
this.counterCooldown = 0;
|
216 |
+
this.canCounter = false;
|
217 |
+
this.lastHit = null;
|
218 |
+
this.isGameOver = false;
|
219 |
+
|
220 |
+
this.player = new Character(document.getElementById('player'), true);
|
221 |
+
this.enemy = new Character(document.getElementById('enemy'), false);
|
222 |
+
|
223 |
+
this.keys = {};
|
224 |
+
this.setupControls();
|
225 |
+
this.startGame();
|
226 |
+
}
|
227 |
+
|
228 |
+
setupControls() {
|
229 |
+
document.addEventListener('keydown', (e) => {
|
230 |
+
if (this.isGameOver) return;
|
231 |
+
this.keys[e.key.toLowerCase()] = true;
|
232 |
+
|
233 |
+
switch(e.key.toLowerCase()) {
|
234 |
+
case 'j':
|
235 |
+
case 'k':
|
236 |
+
this.startAttack(this.player, this.enemy,
|
237 |
+
e.key === 'k' ? 'high' : 'mid');
|
238 |
+
break;
|
239 |
+
case 'q':
|
240 |
+
if (Date.now() >= this.specialCooldown) {
|
241 |
+
this.useUltimate(this.player, this.enemy);
|
242 |
+
}
|
243 |
+
break;
|
244 |
+
case 'l':
|
245 |
+
this.tryCounter();
|
246 |
+
break;
|
247 |
+
case 's':
|
248 |
+
this.player.element.classList.add('crouch');
|
249 |
+
break;
|
250 |
+
}
|
251 |
+
});
|
252 |
+
|
253 |
+
document.addEventListener('keyup', (e) => {
|
254 |
+
this.keys[e.key.toLowerCase()] = false;
|
255 |
+
if (e.key.toLowerCase() === 's') {
|
256 |
+
this.player.element.classList.remove('crouch');
|
257 |
+
}
|
258 |
+
});
|
259 |
+
}
|
260 |
+
|
261 |
+
startAttack(attacker, defender, type) {
|
262 |
+
attacker.isAttacking = true;
|
263 |
+
const attackEl = document.createElement('div');
|
264 |
+
attackEl.className = `attack ${type}Attack`;
|
265 |
+
|
266 |
+
const xOffset = attacker.direction === 'right' ? 45 : -30;
|
267 |
+
const yOffset = type === 'high' ? 70 : 35;
|
268 |
+
|
269 |
+
attackEl.style.left = `${attacker.pos.x + xOffset}px`;
|
270 |
+
attackEl.style.bottom = `${yOffset}px`;
|
271 |
+
document.getElementById('gameArea').appendChild(attackEl);
|
272 |
+
|
273 |
+
setTimeout(() => {
|
274 |
+
attackEl.remove();
|
275 |
+
if (!defender.isBlocking) {
|
276 |
+
defender.health -= SETTINGS.DAMAGE;
|
277 |
+
this.updateHealthBars();
|
278 |
+
}
|
279 |
+
}, SETTINGS.ATTACK_DELAY);
|
280 |
+
|
281 |
+
setTimeout(() => {
|
282 |
+
attacker.isAttacking = false;
|
283 |
+
}, SETTINGS.ATTACK_DELAY + 100);
|
284 |
+
}
|
285 |
+
|
286 |
+
useUltimate(attacker, defender) {
|
287 |
+
const ultimateEl = document.createElement('div');
|
288 |
+
ultimateEl.className = 'attack ultimate';
|
289 |
+
|
290 |
+
const xOffset = attacker.direction === 'right' ? 45 : -300;
|
291 |
+
ultimateEl.style.left = `${attacker.pos.x + xOffset}px`;
|
292 |
+
ultimateEl.style.bottom = '0px';
|
293 |
+
|
294 |
+
document.getElementById('gameArea').appendChild(ultimateEl);
|
295 |
+
|
296 |
+
setTimeout(() => {
|
297 |
+
if (!defender.isBlocking) {
|
298 |
+
defender.health -= SETTINGS.SPECIAL_DAMAGE;
|
299 |
+
this.updateHealthBars();
|
300 |
+
}
|
301 |
+
ultimateEl.remove();
|
302 |
+
}, 300);
|
303 |
+
|
304 |
+
this.specialCooldown = Date.now() + SETTINGS.SPECIAL_COOLDOWN;
|
305 |
+
}
|
306 |
+
|
307 |
+
tryCounter() {
|
308 |
+
if (Date.now() < this.counterCooldown) return;
|
309 |
+
|
310 |
+
const counterEl = document.createElement('div');
|
311 |
+
counterEl.className = 'counter-effect';
|
312 |
+
counterEl.style.left = `${this.player.pos.x}px`;
|
313 |
+
counterEl.style.bottom = '0px';
|
314 |
+
document.getElementById('gameArea').appendChild(counterEl);
|
315 |
+
|
316 |
+
setTimeout(() => counterEl.remove(), 400);
|
317 |
+
|
318 |
+
if (this.lastHit && Date.now() - this.lastHit.time <= SETTINGS.COUNTER_WINDOW) {
|
319 |
+
this.player.isBlocking = true;
|
320 |
+
setTimeout(() => {
|
321 |
+
this.player.isBlocking = false;
|
322 |
+
}, 400);
|
323 |
+
} else {
|
324 |
+
this.counterCooldown = Date.now() + SETTINGS.COUNTER_COOLDOWN;
|
325 |
+
}
|
326 |
+
}
|
327 |
+
|
328 |
+
updateAI() {
|
329 |
+
if (Date.now() - this.enemy.lastAction < 300) return;
|
330 |
+
|
331 |
+
const distance = Math.abs(this.player.pos.x - this.enemy.pos.x);
|
332 |
+
const healthRatio = this.enemy.health / this.player.health;
|
333 |
+
|
334 |
+
if (healthRatio < 0.7 || distance < 80) {
|
335 |
+
this.enemy.vel.x = -SETTINGS.MOVE_SPEED;
|
336 |
+
} else if (distance > 150) {
|
337 |
+
this.enemy.vel.x = SETTINGS.MOVE_SPEED;
|
338 |
+
} else if (Math.random() < 0.05) {
|
339 |
+
this.startAttack(this.enemy, this.player,
|
340 |
+
Math.random() > 0.5 ? 'high' : 'mid');
|
341 |
+
}
|
342 |
+
|
343 |
+
this.enemy.direction = this.player.pos.x > this.enemy.pos.x ?
|
344 |
+
'right' : 'left';
|
345 |
+
}
|
346 |
+
|
347 |
+
update(timestamp) {
|
348 |
+
if (timestamp - this.lastFrameTime >= SETTINGS.FRAME_TIME) {
|
349 |
+
if (this.keys['w'] && !this.player.isJumping) {
|
350 |
+
this.player.isJumping = true;
|
351 |
+
this.player.vel.y = -SETTINGS.JUMP_FORCE;
|
352 |
+
}
|
353 |
+
if (this.keys['a']) {
|
354 |
+
this.player.vel.x = -SETTINGS.MOVE_SPEED;
|
355 |
+
this.player.direction = 'left';
|
356 |
+
this.player.isMoving = true;
|
357 |
+
this.player.element.classList.add('facing-left');
|
358 |
+
} else if (this.keys['d']) {
|
359 |
+
this.player.vel.x = SETTINGS.MOVE_SPEED;
|
360 |
+
this.player.direction = 'right';
|
361 |
+
this.player.isMoving = true;
|
362 |
+
this.player.element.classList.remove('facing-left');
|
363 |
+
} else {
|
364 |
+
this.player.isMoving = false;
|
365 |
+
}
|
366 |
+
|
367 |
+
[this.player, this.enemy].forEach(char => {
|
368 |
+
if (char.isJumping) {
|
369 |
+
char.vel.y += SETTINGS.GRAVITY;
|
370 |
+
char.pos.y = Math.max(0, char.pos.y - char.vel.y);
|
371 |
+
if (char.pos.y === 0) {
|
372 |
+
char.isJumping = false;
|
373 |
+
char.vel.y = 0;
|
374 |
+
}
|
375 |
+
}
|
376 |
+
|
377 |
+
char.pos.x += char.vel.x;
|
378 |
+
char.pos.x = Math.max(0, Math.min(755, char.pos.x));
|
379 |
+
char.vel.x *= 0.8;
|
380 |
+
|
381 |
+
char.element.style.left = `${char.pos.x}px`;
|
382 |
+
char.element.style.bottom = `${char.pos.y}px`;
|
383 |
+
|
384 |
+
char.updateAnimation(timestamp);
|
385 |
+
});
|
386 |
+
|
387 |
+
this.updateAI();
|
388 |
+
this.lastFrameTime = timestamp;
|
389 |
+
}
|
390 |
+
|
391 |
+
if (!this.isGameOver) {
|
392 |
+
requestAnimationFrame(this.update.bind(this));
|
393 |
+
}
|
394 |
+
}
|
395 |
+
|
396 |
+
updateHealthBars() {
|
397 |
+
document.getElementById('playerHealthFill').style.width =
|
398 |
+
`${(this.player.health/SETTINGS.INITIAL_HEALTH)*100}%`;
|
399 |
+
document.getElementById('enemyHealthFill').style.width =
|
400 |
+
`${(this.enemy.health/SETTINGS.INITIAL_HEALTH)*100}%`;
|
401 |
+
}
|
402 |
+
|
403 |
+
startGame() {
|
404 |
+
this.updateHealthBars();
|
405 |
+
requestAnimationFrame(this.update.bind(this));
|
406 |
+
|
407 |
+
const timer = setInterval(() => {
|
408 |
+
this.gameTime--;
|
409 |
+
document.getElementById('timer').textContent = this.gameTime;
|
410 |
+
|
411 |
+
if (this.gameTime <= 0) {
|
412 |
+
clearInterval(timer);
|
413 |
+
this.endGame();
|
414 |
+
}
|
415 |
+
}, 1000);
|
416 |
+
}
|
417 |
+
|
418 |
+
endGame() {
|
419 |
+
this.isGameOver = true;
|
420 |
+
const winner = this.player.health > this.enemy.health ? 'Player' : 'Enemy';
|
421 |
+
alert(`Game Over! ${winner} wins!`);
|
422 |
+
}
|
423 |
+
}
|
424 |
+
|
425 |
+
new Game();
|
426 |
</script>
|
427 |
</body>
|
428 |
</html>
|