Last active
December 5, 2023 18:23
-
-
Save gicolek/e71c9d47c6792dbc511a0b030f67138b to your computer and use it in GitHub Desktop.
Dodge and Shoot - basic concept of a 2D shooter made with ChatGPT
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
Plugin Name: Galaga Style Game | |
Plugin URI: https://wp-doin.com/2023/11/04/a-simple-javascript-dodge-and-shoot/ | |
Description: A Shortcode to display the HTML and CSS and JS of a Shooter Game | |
Version: 1.0.0 | |
Author: Rafał Gicgier | |
Author URI: https://wp-doin.com/ | |
Text Domain: wp-doin | |
Domain Path: /lang | |
*/ | |
add_shortcode( 'dodge_and_shoot', 'dodge_and_shoot_handler' ); | |
function dodge_and_shoot_handler() { | |
ob_start(); | |
?> | |
<style> | |
.main { | |
background-image: url('<?php echo plugins_url( 'frame.jpg', __FILE__ ) ?>'); | |
background-size: cover; | |
background-repeat: no-repeat; | |
} | |
#game-container { | |
width: 100%; | |
height: 700px; | |
background-color: #eee; | |
position: relative; | |
background-image: url('<?php echo plugins_url( 'bg.jpg', __FILE__ ) ?>'); | |
background-size: cover; | |
background-repeat: no-repeat; | |
background-position: center; | |
border: 10px solid black; | |
color: #fff; | |
overflow:hidden; | |
} | |
#player { | |
width: 60px; | |
height: 60px; | |
background-image: url('<?php echo plugins_url( 'spaceship-2.png', __FILE__ ) ?>'); | |
background-size: cover; | |
background-repeat: no-repeat; | |
position: absolute; | |
} | |
.bullet { | |
width: 5px; | |
height: 5px; | |
background-color: red; | |
position: absolute; | |
border: 1px solid black; | |
} | |
.enemy { | |
width: 50px; | |
height: 50px; | |
background-image: url('<?php echo plugins_url( 'alien-ship-small.png', __FILE__ ) ?>'); | |
background-size: cover; | |
background-repeat: no-repeat; | |
position: absolute; | |
} | |
.enemy-boss { | |
width: 100px; | |
height: 100px; | |
background-image: url('<?php echo plugins_url( 'alien-ship-boss.png', __FILE__ ) ?>'); | |
background-size: cover; | |
background-repeat: no-repeat; | |
position: absolute; | |
} | |
.enemy-boss-health { | |
width: 100px; | |
max-width: 100px; | |
height: 7px; | |
background-color: red; | |
display: block; | |
border: 1px solid black; | |
} | |
#score { | |
position: absolute; | |
bottom: 10px; | |
left: 10px; | |
font-size: 24px; | |
color: #fff; | |
} | |
#health { | |
position: absolute; | |
top: 10px; | |
left: 10px; | |
font-size: 24px; | |
color: #fff; | |
display: none; | |
} | |
#power-up { | |
position: absolute; | |
bottom: 10px; | |
right: 10px; | |
font-size: 24px; | |
color: #fff; | |
} | |
#timer { | |
position: absolute; | |
bottom: 80px; | |
right: 10px; | |
font-size: 24px; | |
color: #fff; | |
} | |
#game-over { | |
display: none; | |
position: absolute; | |
top: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
font-size: 36px; | |
color: #fff; | |
background: black; | |
padding: 10px; | |
border-radius: 10px; | |
z-index: 9999; | |
} | |
#start { | |
position: absolute; | |
top: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
font-size: 36px; | |
color: #fff; | |
background: black; | |
cursor: pointer; | |
padding: 10px; | |
border-radius: 10px; | |
line-height: 1; | |
} | |
#re-start { | |
position: absolute; | |
top: 60%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
font-size: 36px; | |
color: #fff; | |
background: black; | |
cursor: pointer; | |
display: none; | |
padding: 10px; | |
border-radius: 10px; | |
z-index: 9999; | |
} | |
.o-wrapper { | |
max-width: 90%; | |
margin: 0 auto; | |
} | |
.power-up { | |
width: 40px; | |
height: 40px; | |
background-image: url('<?php echo plugins_url( 'power-up.jpg', __FILE__ ) ?>'); | |
background-size: cover; | |
background-repeat: no-repeat; | |
position: absolute; | |
border: 2px solid gold; | |
box-shadow: 4px 4px #0000004f; | |
} | |
.wrap { | |
background: black; | |
padding: 10px; | |
border-radius: 10px; | |
} | |
#game-container #logo { | |
position: absolute; | |
top: 10px; | |
left: 10px; | |
width: 100px; | |
height: 100px; | |
background: black; | |
padding: 10px; | |
border-radius: 10px; | |
z-index: 9999; | |
} | |
@media only screen and (max-width: 1024px) { | |
#game-container { | |
height: 600px; | |
} | |
.o-wrapper { | |
max-width: 100%; | |
margin: 0 auto; | |
} | |
#timer { | |
top: 50px; | |
left: 10px; | |
right: auto; | |
bottom: auto; | |
} | |
#score, #health, #timer, #re-start, #start, #game-over, #power-up { | |
font-size: 16px; | |
padding: 5px; | |
} | |
#game-container #logo { | |
width: 50px; | |
height: 50px; | |
} | |
} | |
</style> | |
<div class="o-wrapper main"> | |
<div id="game-container"> | |
<div id="player"></div> | |
<div id="logo"> | |
<a href="https://wp-doin.com/" target="_blank"> | |
<img src="<?php echo plugins_url( 'logo.jpg', __FILE__); ?>" /> | |
</a> | |
</div> | |
<div id="score" class="wrap">Score: 0</div> | |
<div id="health" class="wrap">Health: 1</div> | |
<div id="power-up" class="wrap">Power UP: OFF</div> | |
<div id="timer" class="wrap">Time: 01:00</div> | |
<div id="game-over">GAME OVER</div> | |
<div id="re-start">RESTART GAME?</div> | |
<div id="start">START GAME? <br/>(CONTROLS: WSAD + U)</div> | |
<audio id="shootSound" src="<?php echo plugins_url( 'shoot.wav', __FILE__ ) ?>"></audio> | |
</div> | |
</div> | |
<script> | |
const gameContainer = document.getElementById('game-container'); | |
const startBTN = document.getElementById('start'); | |
const restartBTN = document.getElementById('re-start'); | |
const initialTime = 60; // 2 minutes (2 minutes * 60 seconds) | |
let remainingTime = initialTime; | |
const timerElement = document.getElementById('timer'); | |
const player = document.getElementById('player'); | |
const bullets = []; | |
const enemies = []; | |
const shootSound = document.getElementById('shootSound'); | |
const playerSpeed = 7; | |
const bulletSpeed = 10; | |
const enemySpeed = 2; | |
const enemyBossSpeed = 1; | |
let bulletCooldown = 300; // Time in milliseconds before the player can shoot again | |
let canShoot = true; | |
let score = 0; | |
let health = 1; | |
let isGameOver = false; | |
let isPowerUpActive = false; | |
let powerUpDuration = 10000; | |
// Initialize player position | |
let playerX = gameContainer.clientWidth / 2 - player.clientWidth / 2; | |
let playerY = gameContainer.clientHeight - player.clientHeight; | |
player.style.left = `${playerX}px`; | |
player.style.top = `${playerY}px`; | |
// Function to play the shoot sound | |
function playShootSound() { | |
shootSound.play(); | |
} | |
function movePlayer(dx, dy) { | |
playerX += dx; | |
playerY += dy; | |
// Ensure the player stays within the game container | |
playerX = Math.max(0, Math.min(gameContainer.clientWidth - player.clientWidth, playerX)); | |
playerY = Math.max(0, Math.min(gameContainer.clientHeight - player.clientHeight, playerY)); | |
player.style.left = `${playerX}px`; | |
player.style.top = `${playerY}px`; | |
} | |
function createBullet(x, y) { | |
const bullet = document.createElement('div'); | |
bullet.className = 'bullet item'; | |
bullet.style.left = `${x}px`; | |
bullet.style.top = `${y}px`; | |
gameContainer.appendChild(bullet); | |
bullets.push(bullet); | |
} | |
function moveBullets() { | |
bullets.forEach((bullet, index) => { | |
const bulletTop = parseFloat(bullet.style.top); | |
bullet.style.top = `${bulletTop - bulletSpeed}px`; | |
// Remove bullets that go off-screen | |
if (bulletTop < 0) { | |
bullets.splice(index, 1); | |
gameContainer.removeChild(bullet); | |
} | |
}); | |
} | |
function createEnemy(x) { | |
const enemy = document.createElement('div'); | |
enemy.className = 'enemy item'; | |
enemy.style.left = `${x}px`; | |
enemy.style.top = '0'; | |
gameContainer.appendChild(enemy); | |
enemies.push(enemy); | |
} | |
function createBossEnemy(x) { | |
const bossEnemy = document.createElement('div'); | |
bossEnemy.className = 'enemy-boss item'; // Add 'boss' class to the boss enemy | |
bossEnemy.style.left = `${x}px`; | |
bossEnemy.style.top = '0'; | |
bossEnemy.setAttribute("data-health", 5); | |
gameContainer.appendChild(bossEnemy); | |
const bossEnemyHealth = document.createElement('div'); | |
bossEnemyHealth.className = 'enemy-boss-health'; // Add 'boss' class to the boss enemy | |
bossEnemy.appendChild(bossEnemyHealth); | |
enemies.push(bossEnemy); | |
} | |
function moveEnemies() { | |
enemies.forEach((enemy, index) => { | |
if (isGameOver) | |
return; | |
const enemyTop = parseFloat(enemy.style.top); | |
if (enemy.classList.contains('enemy-boss')) { | |
enemy.style.top = `${enemyTop + enemyBossSpeed}px`; | |
} else { | |
enemy.style.top = `${enemyTop + enemySpeed}px`; | |
} | |
// Check for collisions with player's bullets | |
bullets.forEach((bullet, bulletIndex) => { | |
const bulletRect = bullet.getBoundingClientRect(); | |
const enemyRect = enemy.getBoundingClientRect(); | |
if ( | |
bulletRect.top < enemyRect.bottom && | |
bulletRect.right > enemyRect.left && | |
bulletRect.left < enemyRect.right | |
) { | |
if (enemy.classList.contains('enemy-boss')) { | |
// Reduce boss enemy health | |
const bossHealth = parseInt(enemy.getAttribute('data-health')); | |
if (bossHealth > 1) { | |
// Decrease boss health and update the attribute | |
enemy.setAttribute('data-health', bossHealth - 1); | |
enemy.querySelector('.enemy-boss-health').style.width = bossHealth * 20 - 10 + 'px'; | |
console.log(bossHealth); | |
bullets.splice(bulletIndex, 1); | |
gameContainer.removeChild(bullet); | |
} else { | |
// Remove boss enemy when its health reaches 0 | |
enemies.splice(index, 1); | |
gameContainer.removeChild(enemy); | |
score += 50; // Add 50 points for defeating the boss | |
document.getElementById('score').textContent = `Score: ${score}`; | |
} | |
} else { | |
enemies.splice(index, 1); | |
bullets.splice(bulletIndex, 1); | |
gameContainer.removeChild(enemy); | |
gameContainer.removeChild(bullet); | |
score += 10; | |
document.getElementById('score').textContent = `Score: ${score}`; | |
} | |
} | |
}); | |
// Check for collisions with the player | |
const playerRect = player.getBoundingClientRect(); | |
const enemyRect = enemy.getBoundingClientRect(); | |
if ( | |
playerRect.top < enemyRect.bottom && | |
playerRect.right > enemyRect.left && | |
playerRect.left < enemyRect.right && | |
playerRect.bottom > enemyRect.top | |
) { | |
health--; | |
document.getElementById('health').textContent = `Health: ${health}`; | |
if (health === 0) { | |
endGame(); | |
} | |
} | |
// Remove enemies that go off-screen | |
if (enemyTop > gameContainer.clientHeight) { | |
enemies.splice(index, 1); | |
gameContainer.removeChild(enemy); | |
} | |
}); | |
} | |
function shoot() { | |
if (canShoot) { | |
createBullet(playerX + player.clientWidth / 2, playerY); | |
if (isPowerUpActive) { | |
createBullet(playerX + player.clientWidth / 2 - 15, playerY); | |
createBullet(playerX + player.clientWidth / 2, playerY); | |
createBullet(playerX + player.clientWidth / 2 + 15, playerY); | |
} | |
canShoot = false; | |
setTimeout(() => { | |
canShoot = true; | |
}, bulletCooldown); | |
} | |
} | |
function endGame() { | |
isGameOver = true; | |
document.getElementById('game-over').style.display = 'block'; | |
restartBTN.style.display = 'block'; | |
} | |
document.addEventListener('keydown', (event) => { | |
if (isGameOver) | |
return; | |
switch (event.key) { | |
case 'w': | |
movePlayer(0, -playerSpeed); | |
break; | |
case 's': | |
movePlayer(0, playerSpeed); | |
break; | |
case 'a': | |
movePlayer(-playerSpeed, 0); | |
break; | |
case 'd': | |
movePlayer(playerSpeed, 0); | |
break; | |
case 'u': | |
shoot(); | |
// Play the shoot sound | |
playShootSound(); | |
break; | |
} | |
}); | |
function gameLoop() { | |
if (isGameOver) | |
return; | |
moveBullets(); | |
moveEnemies(); | |
if (Math.random() < 0.02) { | |
createEnemy(Math.random() * (gameContainer.clientWidth - 20)); | |
} | |
if (Math.random() < 0.02) { | |
createBossEnemy(Math.random() * (gameContainer.clientWidth - 20)); | |
} | |
requestAnimationFrame(gameLoop); | |
} | |
// Create and append a power-up element to the game container | |
function spawnPowerUp() { | |
if (isGameOver) | |
return; | |
const powerUpElement = document.createElement('div'); | |
powerUpElement.className = 'power-up item'; | |
// Randomly position the power-up within the game container | |
const randomX = Math.random() * (gameContainer.clientWidth - 20); | |
const randomY = Math.random() * (gameContainer.clientHeight - 20); | |
powerUpElement.style.left = `${randomX}px`; | |
powerUpElement.style.top = `${randomY}px`; | |
// Check for collisions with the player | |
powerUpElement.addEventListener('click', () => { | |
if (!isPowerUpActive) { | |
isPowerUpActive = true; | |
activatePowerUp(); | |
// Remove the power-up element | |
gameContainer.removeChild(powerUpElement); | |
} | |
}); | |
gameContainer.appendChild(powerUpElement); | |
// Check for collisions with the player | |
setInterval(() => { | |
checkPowerUpCollision(powerUpElement); | |
}, 100); // Check for collisions every 100 milliseconds | |
} | |
// Check for collision with the power-up | |
function checkPowerUpCollision(powerUpElement) { | |
if (isPowerUpActive) { | |
return; | |
} | |
const playerRect = player.getBoundingClientRect(); | |
const powerUpRect = powerUpElement.getBoundingClientRect(); | |
if ( | |
playerRect.top < powerUpRect.bottom && | |
playerRect.right > powerUpRect.left && | |
playerRect.left < powerUpRect.right | |
) { | |
isPowerUpActive = true; | |
activatePowerUp(); | |
// Remove the power-up element | |
gameContainer.removeChild(powerUpElement); | |
} | |
} | |
function activatePowerUp() { | |
// Apply the power-up effect | |
bulletCooldown = 100; // Increase shooting speed | |
// Visual feedback (optional) | |
player.style.width = '80px'; | |
player.style.height = '80px'; | |
document.getElementById('power-up').textContent = `Power UP: ON`; | |
// Set a timer to deactivate the power-up | |
setTimeout(deactivatePowerUp, powerUpDuration); | |
} | |
function deactivatePowerUp() { | |
// Revert the power-up effect | |
bulletCooldown = 300; // Reset shooting speed | |
// Reset visual feedback (optional) | |
player.style.width = '60px'; | |
player.style.height = '60px'; | |
document.getElementById('power-up').textContent = `Power UP: OFF`; | |
isPowerUpActive = false; | |
} | |
function updateTimerDisplay() { | |
const minutes = Math.floor(remainingTime / 60); | |
const seconds = remainingTime % 60; | |
// Display the time in the "00:00" format | |
timerElement.textContent = `Time: ${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; | |
// Check for the end of the game | |
if (remainingTime === 0) { | |
endGame(); | |
} | |
remainingTime--; // Decrement the remaining time | |
} | |
function start() { | |
gameLoop(); | |
// Call this function to spawn power-ups | |
setInterval(spawnPowerUp, 15000); // Spawn a power-up every 15 seconds | |
const timerInterval = setInterval(updateTimerDisplay, 1000); | |
} | |
startBTN.addEventListener('click', () => { | |
startBTN.style.display = 'none'; | |
start(); | |
}); | |
restartBTN.addEventListener('click', () => { | |
isGameOver = false; | |
health++; | |
remainingTime = initialTime; | |
score = 0; | |
playerX = gameContainer.clientWidth / 2 - player.clientWidth / 2; | |
playerY = gameContainer.clientHeight - player.clientHeight; | |
player.style.left = `${playerX}px`; | |
player.style.top = `${playerY}px`; | |
let items = document.getElementsByClassName("item"); | |
/*console.log(items); | |
items.forEach((item, index) => { | |
gameContainer.removeChild(item); | |
});*/ | |
enemies.splice(0, enemies.length); | |
bullets.splice(0, bullets.length); | |
for (var i = items.length - 1; i >= 0; --i) { | |
items[i].remove(); | |
} | |
document.getElementById('game-over').style.display = 'none'; | |
restartBTN.style.display = 'none'; | |
gameLoop(); | |
}); | |
// Add touch event listeners | |
let touchX = 0; | |
let touchY = 0; | |
gameContainer.addEventListener('touchstart', (event) => { | |
touchX = event.touches[0].clientX; | |
touchY = event.touches[0].clientY; | |
}); | |
gameContainer.addEventListener('touchmove', (event) => { | |
touchX = event.touches[0].clientX; | |
touchY = event.touches[0].clientY; | |
}); | |
gameContainer.addEventListener('touchend', (event) => { | |
// Calculate the angle between the player and the touch point | |
const angle = Math.atan2(touchY - playerY, touchX - playerX); | |
// Calculate the new player movement based on the touch | |
const dx = Math.cos(angle) * playerSpeed; | |
const dy = Math.sin(angle) * playerSpeed; | |
// Move the player | |
movePlayer(dx, dy); | |
// Shoot when the player taps | |
shoot(); | |
}); | |
</script> | |
<?php | |
return ob_get_clean(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment