Skip to content

Instantly share code, notes, and snippets.

@gicolek
Last active December 5, 2023 18:23
Show Gist options
  • Save gicolek/e71c9d47c6792dbc511a0b030f67138b to your computer and use it in GitHub Desktop.
Save gicolek/e71c9d47c6792dbc511a0b030f67138b to your computer and use it in GitHub Desktop.
Dodge and Shoot - basic concept of a 2D shooter made with ChatGPT
<?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