// Cloned by Liam Eguia on 13 Aug 2023 from World 'Enhanced Space Pong' by Enhanced
// Cloned by Enhanced on 18 Jun 2018 from World 'Revamped Space Pong' by SinfulSalad
// Cloned by SinfulSalad on 12 Jun 2018 from World 'Space Pong' by Igor Strelkov
//-------------------------- TWEAKER'S BOX ------------------------------------
const UNIT = 1;
const MAP_WIDTH = 100;
const MAP_DEPTH = 100;
const BALL_RADIUS = 1.5;
const PADDLE_WIDTH = 17.5;
const PLAYER_COLOUR = 0x00bbff;
const OPPONENT_COLOUR = 0xdd0022;
const START_SPEED = 0.8;
const SPEED_BONUS = 0.05;
const SCORE_NEEDED = 5;
//----------------------------RENDERER SETUP-----------------------------------
AB.newDiv('app');
const container = document.getElementById('app');
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
container.appendChild(renderer.domElement);
//-----------------------------CAMERA SETUP------------------------------------
const camera = new THREE.PerspectiveCamera(
75, window.innerWidth / window.innerHeight, 0.1, 1000
);
camera.position.set(0, MAP_DEPTH*2/3, -MAP_DEPTH*2/3);
camera.lookAt(new THREE.Vector3(0, 0, -15))
//-----------------------------SCENE SETUP-------------------------------------
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x2b2b2d);
const ambientLight = new THREE.HemisphereLight(0xffffed, 0xb8b8a7, 0.25);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffed, 2.5);
directionalLight.position.set(-1, 1, -1);
directionalLight.castShadow = true;
directionalLight.shadow.mapSize.width = 1024;
directionalLight.shadow.mapSize.height = 1024;
directionalLight.shadow.darkness = 1;
scene.add(directionalLight);
//------------------------------EDGE SETUP-------------------------------------
const edgeGeometry = new THREE.BoxGeometry(1, 5, MAP_DEPTH);
const edgeMaterial = new THREE.MeshPhongMaterial({
color: 0x28282b
});
const leftEdge = new THREE.Mesh(edgeGeometry, edgeMaterial);
scene.add(leftEdge);
const rightEdge = new THREE.Mesh(edgeGeometry, edgeMaterial);
scene.add(rightEdge);
leftEdge.position.set(-MAP_WIDTH/2, 0, 0);
rightEdge.position.set(MAP_WIDTH/2, 0, 0);
const goalEdgeGeometry = new THREE.BoxGeometry(MAP_WIDTH, 5, 0.5);
const goalEdgeMaterial = new THREE.MeshBasicMaterial({
color: 0xfcff54,
transparent: true,
opacity: 0.5
});
const playerGoalEdge = new THREE.Mesh(goalEdgeGeometry, goalEdgeMaterial);
scene.add(playerGoalEdge);
const opponentGoalEdge = new THREE.Mesh(goalEdgeGeometry, goalEdgeMaterial);
scene.add(opponentGoalEdge);
playerGoalEdge.position.set(0, 0, -MAP_DEPTH/2 - UNIT);
opponentGoalEdge.position.set(0, 0, MAP_DEPTH/2 + UNIT);
//------------------------------BALL SETUP-------------------------------------
const ballGeometry = new THREE.SphereGeometry(BALL_RADIUS);
const ballMaterial = new THREE.MeshPhongMaterial({
color: 0xddf5ff
});
const ball = new THREE.Mesh(ballGeometry, ballMaterial);
scene.add(ball)
ball.tick = function() {
const newX = this.position.x + this.velocity * Math.sin(this.direction);
const newZ = this.position.z + this.velocity * Math.cos(this.direction);
console.log('Ball newX:', newX)
if (newX > MAP_WIDTH/2-UNIT || newX < UNIT-MAP_WIDTH/2) { // collision with side edge
this.direction = -this.direction;
}
else if (newZ > MAP_DEPTH/2-UNIT*2) {
this.velocity += SPEED_BONUS;
this.direction = -Math.PI-this.direction;
this.checkCollision(opponent);
}
else if (newZ < UNIT*2-MAP_DEPTH/2) {
this.velocity += SPEED_BONUS;
this.direction = Math.PI-this.direction;
this.checkCollision(player);
}
this.position.x += this.velocity * Math.sin(this.direction);
this.position.z += this.velocity * Math.cos(this.direction);
}
ball.checkCollision = function(paddle) {
console.log('Checking collision with:', paddle.name)
console.log('Paddle position:', paddle.position)
console.log('Ball position:', this.position)
if (this.position.x >= paddle.position.x + PADDLE_WIDTH*2/3 || this.position.x <= paddle.position.x - PADDLE_WIDTH*2/3) {
paddle.score += -1;
resetPoint();
}
else if (this.position.x > paddle.position.x + PADDLE_WIDTH/4) {
this.direction += Math.PI/8;
}
else if (this.position.x < paddle.position.x - PADDLE_WIDTH/4) {
this.direction -= Math.PI/8;
}
else if (this.position.x > paddle.position.x + PADDLE_WIDTH/8) {
this.direction += Math.PI/12;
}
else if (this.position.x < paddle.position.x - PADDLE_WIDTH/8) {
this.direction -= Math.PI/12;
}
else {
this.direction += (Math.random() * 2 - 1) * Math.PI/12
}
}
ball.reset = function() {
this.position.set(0, 0, 0);
this.direction = Math.PI;
this.velocity = START_SPEED;
};
ball.reset()
//---------------------------BASE PADDLE SETUP---------------------------------
const paddleGeometry = new THREE.BoxGeometry(PADDLE_WIDTH, 3, UNIT);
function tickPaddle(paddle) {
paddle.position.x += paddle.movement * UNIT;
if (paddle.position.x > MAP_WIDTH/2 - PADDLE_WIDTH/2) paddle.position.x = MAP_WIDTH/2 - PADDLE_WIDTH/2;
else if (paddle.position.x < PADDLE_WIDTH/2 - MAP_WIDTH/2) paddle.position.x = PADDLE_WIDTH/2 - MAP_WIDTH/2;
}
//-----------------------------PLAYER SETUP------------------------------------
const playerMaterial = new THREE.MeshPhongMaterial({
color: PLAYER_COLOUR
});
const player = new THREE.Mesh(paddleGeometry, playerMaterial);
player.name = 'player'
scene.add(player);
player.moveLeft = function() { player.movement = 1 }
player.moveRight = function() { player.movement = -1 }
player.stop = function() { player.movement = 0 }
player.tick = function() {
tickPaddle(this)
}
player.reset = function() {
this.position.set(0, 0, UNIT - (MAP_DEPTH/2));
this.movement = 0;
}
player.score = 0;
player.speed = 1;
player.reset()
//----------------------------OPPONENT SETUP-----------------------------------
const opponentMaterial = new THREE.MeshPhongMaterial({
color: OPPONENT_COLOUR
});
const opponent = new THREE.Mesh(paddleGeometry, opponentMaterial);
opponent.name = 'opponent'
scene.add(opponent);
opponent.moveLeft = function() { opponent.movement = 1 }
opponent.moveRight = function() { opponent.movement = -1 }
opponent.stop = function() { opponent.movement = 0 }
opponent.followBall = function() {
if (this.position.x > ball.position.x + PADDLE_WIDTH/10) this.movement = -1;
else if (this.position.x < ball.position.x - PADDLE_WIDTH/10) this.movement = 1;
else this.movement = 0;
}
opponent.tick = function() {
tickPaddle(this)
}
opponent.reset = function() {
this.position.set(0, 0, (MAP_DEPTH/2) - UNIT);
this.movement = 0;
}
opponent.score = 0;
opponent.speed = 1;
opponent.reset()
//------------------------------SETUP HUD--------------------------------------
var gameState = 'set';
var playerName;
if (AB.runloggedin) playerName = AB.myuserid;
else playerName = 'Player';
const score = document.createElement('div');
score.style.position = 'absolute';
score.style.top = '50px';
score.style.left = '50%';
score.style.transform = 'translateX(-50%)';
score.style.color = '#ffffff';
score.style.fontFamily = 'arial';
score.style.fontSize = '21px';
container.appendChild(score);
function updateScore(playerScore, opponentScore) {
score.innerHTML = `${playerName} ${-opponentScore} - ${-playerScore} Opponent`;
}
//------------------------WINDOW EVENT LISTENERS-------------------------------
window.addEventListener('keydown', function(event) {
console.log(event.key)
if (event.key === 'ArrowLeft') {
player.moveLeft();
}
if (event.key === 'ArrowRight') {
player.moveRight();
}
if (event.key === ' ' && gameState !== 'start') {
startGame()
}
});
window.addEventListener('keyup', function(event) {
if (event.key == 'ArrowLeft') {
if (player.movement > 0) player.stop();
}
if (event.key == 'ArrowRight') {
if (player.movement < 0) player.stop();
}
})
window.onresize = function() {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
}
//------------------------------GAME LOOP--------------------------------------
function resetPoint() {
player.reset()
opponent.reset();
ball.reset();
updateScore(player.score, opponent.score)
}
function startGame() {
console.log('Game started!')
player.score = 0;
opponent.score = 0;
resetPoint();
gameState = 'start';
}
function checkGameEnd() {
if (player.score == -SCORE_NEEDED || opponent.score == -SCORE_NEEDED) {
console.log('Game over!');
gameState = 'end';
if (player.score == -SCORE_NEEDED) score.innerHTML = 'YOU LOSE...'
else score.innerHTML = 'YOU WIN!'
}
}
function tick() {
requestAnimationFrame(tick)
if (gameState === 'start') {
ball.tick();
opponent.followBall();
opponent.tick();
player.tick();
checkGameEnd();
}
renderer.render(scene, camera)
}
tick();