restrict players and ball from traveling outside
This commit is contained in:
parent
c82e73843b
commit
794387fa46
@ -1,108 +1,11 @@
|
|||||||
|
#scoreboard {
|
||||||
body {
|
position: absolute;
|
||||||
font-family: 'Roboto', sans-serif;
|
top: 10px;
|
||||||
background-color: #F8F9FA;
|
left: 50%;
|
||||||
color: #212529;
|
transform: translateX(-50%);
|
||||||
}
|
font-size: 40px;
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6, .navbar-brand {
|
|
||||||
font-family: 'Poppins', sans-serif;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar {
|
|
||||||
transition: background-color 0.3s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero {
|
|
||||||
background: linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), url('https://picsum.photos/seed/soccerhero/1600/900') no-repeat center center;
|
|
||||||
background-size: cover;
|
|
||||||
color: white;
|
color: white;
|
||||||
padding: 10rem 0;
|
font-family: sans-serif;
|
||||||
text-align: center;
|
font-weight: bold;
|
||||||
}
|
text-shadow: 2px 2px 4px #000000;
|
||||||
|
|
||||||
.hero h1 {
|
|
||||||
font-size: 4rem;
|
|
||||||
font-weight: 700;
|
|
||||||
text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero p {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary {
|
|
||||||
background-color: #2ECC71;
|
|
||||||
border-color: #2ECC71;
|
|
||||||
font-weight: 700;
|
|
||||||
padding: 0.75rem 1.5rem;
|
|
||||||
transition: background-color 0.2s, border-color 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary:hover {
|
|
||||||
background-color: #27ae60;
|
|
||||||
border-color: #27ae60;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-secondary {
|
|
||||||
background-color: #3498DB;
|
|
||||||
border-color: #3498DB;
|
|
||||||
font-weight: 700;
|
|
||||||
padding: 0.75rem 1.5rem;
|
|
||||||
transition: background-color 0.2s, border-color 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-secondary:hover {
|
|
||||||
background-color: #2980b9;
|
|
||||||
border-color: #2980b9;
|
|
||||||
}
|
|
||||||
|
|
||||||
section {
|
|
||||||
padding: 5rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-title {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 3rem;
|
|
||||||
font-size: 2.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
border: none;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
box-shadow: 0 4px 15px rgba(0,0,0,0.08);
|
|
||||||
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card:hover {
|
|
||||||
transform: translateY(-5px);
|
|
||||||
box-shadow: 0 8px 25px rgba(0,0,0,0.12);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-title {
|
|
||||||
color: #2ECC71;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-group-item {
|
|
||||||
border: none;
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.control-item {
|
|
||||||
background-color: #e9ecef;
|
|
||||||
padding: 1rem;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.control-item strong {
|
|
||||||
color: #3498DB;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer {
|
|
||||||
background-color: #212529;
|
|
||||||
color: white;
|
|
||||||
padding: 2rem 0;
|
|
||||||
}
|
}
|
||||||
@ -32,12 +32,24 @@ export class Ball {
|
|||||||
offset = direction.multiplyScalar(ballOffset);
|
offset = direction.multiplyScalar(ballOffset);
|
||||||
}
|
}
|
||||||
this.mesh.position.copy(this.mesh.possessedBy.position).add(offset);
|
this.mesh.position.copy(this.mesh.possessedBy.position).add(offset);
|
||||||
this.mesh.position.y = 0.8; // Keep it on the ground
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Update ball position based on velocity (when not possessed)
|
// Update ball position based on velocity (when not possessed)
|
||||||
this.mesh.position.add(this.mesh.velocity);
|
this.mesh.position.add(this.mesh.velocity);
|
||||||
this.mesh.velocity.multiplyScalar(0.97); // Friction
|
this.mesh.velocity.multiplyScalar(0.97); // Friction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Field Boundaries ---
|
||||||
|
const fieldWidth = 60;
|
||||||
|
const fieldLength = 100;
|
||||||
|
const ballRadius = 0.8;
|
||||||
|
|
||||||
|
this.mesh.position.x = Math.max(-fieldWidth / 2 + ballRadius, Math.min(fieldWidth / 2 - ballRadius, this.mesh.position.x));
|
||||||
|
this.mesh.position.z = Math.max(-fieldLength / 2 + ballRadius, Math.min(fieldLength / 2 - ballRadius, this.mesh.position.z));
|
||||||
|
|
||||||
|
|
||||||
|
// --- Force 2D movement on the XZ plane ---
|
||||||
|
this.mesh.position.y = 0.8;
|
||||||
|
this.mesh.velocity.y = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,25 +13,38 @@ export class Team {
|
|||||||
|
|
||||||
createTeam() {
|
createTeam() {
|
||||||
for (let i = 0; i < this.numberOfPlayers; i++) {
|
for (let i = 0; i < this.numberOfPlayers; i++) {
|
||||||
let player;
|
const position = this.generatePosition(i);
|
||||||
if (this.isPlayerTeam) {
|
const player = new Player(this.scene, this.color, position, this.isPlayerTeam ? 'player' : 'bot');
|
||||||
if (i === 0) {
|
|
||||||
player = new Player(this.scene, this.color, { x: -15, y: 1.5, z: 0 }, 'player');
|
|
||||||
} else {
|
|
||||||
player = new Player(this.scene, this.color, {
|
|
||||||
x: -Math.random() * 25 - 5, // Left side
|
|
||||||
y: 1.5,
|
|
||||||
z: (Math.random() - 0.5) * 80
|
|
||||||
}, 'player');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
player = new Player(this.scene, this.color, {
|
|
||||||
x: Math.random() * 25 + 5, // Right side
|
|
||||||
y: 1.5,
|
|
||||||
z: (Math.random() - 0.5) * 80
|
|
||||||
}, 'bot');
|
|
||||||
}
|
|
||||||
this.players.push(player);
|
this.players.push(player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetPositions() {
|
||||||
|
for (let i = 0; i < this.players.length; i++) {
|
||||||
|
const player = this.players[i];
|
||||||
|
const position = this.generatePosition(i);
|
||||||
|
player.mesh.position.set(position.x, position.y, position.z);
|
||||||
|
player.mesh.velocity.set(0, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
generatePosition(i) {
|
||||||
|
if (this.isPlayerTeam) {
|
||||||
|
if (i === 0) {
|
||||||
|
return { x: -15, y: 1.5, z: 0 };
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
x: -Math.random() * 25 - 5, // Left side
|
||||||
|
y: 1.5,
|
||||||
|
z: (Math.random() - 0.5) * 80
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
x: Math.random() * 25 + 5, // Right side
|
||||||
|
y: 1.5,
|
||||||
|
z: (Math.random() - 0.5) * 80
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,6 +32,10 @@ class Game {
|
|||||||
this.player = this.playerTeam.players[0];
|
this.player = this.playerTeam.players[0];
|
||||||
|
|
||||||
this.initControls();
|
this.initControls();
|
||||||
|
this.score = { red: 0, blue: 0 };
|
||||||
|
this.redScoreEl = document.getElementById('red-score');
|
||||||
|
this.blueScoreEl = document.getElementById('blue-score');
|
||||||
|
this.isGoalScored = false;
|
||||||
this.animate();
|
this.animate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,6 +82,12 @@ class Game {
|
|||||||
|
|
||||||
const newPosition = this.player.mesh.position.clone().add(velocity);
|
const newPosition = this.player.mesh.position.clone().add(velocity);
|
||||||
|
|
||||||
|
// --- Field Boundaries ---
|
||||||
|
const fieldWidth = 60;
|
||||||
|
const fieldLength = 100;
|
||||||
|
newPosition.x = Math.max(-fieldWidth / 2 + playerRadius, Math.min(fieldWidth / 2 - playerRadius, newPosition.x));
|
||||||
|
newPosition.z = Math.max(-fieldLength / 2 + playerRadius, Math.min(fieldLength / 2 - playerRadius, newPosition.z));
|
||||||
|
|
||||||
// Collision detection with other players
|
// Collision detection with other players
|
||||||
let collision = false;
|
let collision = false;
|
||||||
for (const otherPlayer of this.allPlayers) {
|
for (const otherPlayer of this.allPlayers) {
|
||||||
@ -93,6 +103,9 @@ class Game {
|
|||||||
this.player.mesh.position.copy(newPosition);
|
this.player.mesh.position.copy(newPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Force 2D movement on the XZ plane ---
|
||||||
|
this.player.mesh.position.y = 1;
|
||||||
|
|
||||||
// Shooting
|
// Shooting
|
||||||
if (this.keyboardState['KeyD']) {
|
if (this.keyboardState['KeyD']) {
|
||||||
this.player.shoot(this.ball);
|
this.player.shoot(this.ball);
|
||||||
@ -130,6 +143,12 @@ class Game {
|
|||||||
|
|
||||||
const newPosition = aiPlayer.mesh.position.clone().add(velocity);
|
const newPosition = aiPlayer.mesh.position.clone().add(velocity);
|
||||||
|
|
||||||
|
// --- Field Boundaries ---
|
||||||
|
const fieldWidth = 60;
|
||||||
|
const fieldLength = 100;
|
||||||
|
newPosition.x = Math.max(-fieldWidth / 2 + playerRadius, Math.min(fieldWidth / 2 - playerRadius, newPosition.x));
|
||||||
|
newPosition.z = Math.max(-fieldLength / 2 + playerRadius, Math.min(fieldLength / 2 - playerRadius, newPosition.z));
|
||||||
|
|
||||||
// Basic collision avoidance with other players
|
// Basic collision avoidance with other players
|
||||||
let collision = false;
|
let collision = false;
|
||||||
for (const otherPlayer of this.allPlayers) {
|
for (const otherPlayer of this.allPlayers) {
|
||||||
@ -145,6 +164,9 @@ class Game {
|
|||||||
aiPlayer.mesh.position.copy(newPosition);
|
aiPlayer.mesh.position.copy(newPosition);
|
||||||
aiPlayer.mesh.lastVelocity.copy(velocity);
|
aiPlayer.mesh.lastVelocity.copy(velocity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Force 2D movement on the XZ plane ---
|
||||||
|
aiPlayer.mesh.position.y = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,12 +236,107 @@ class Game {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateScoreboard() {
|
||||||
|
this.redScoreEl.textContent = this.score.red;
|
||||||
|
this.blueScoreEl.textContent = this.score.blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
resetPositions(teamToGetPossession) {
|
||||||
|
this.ball.mesh.position.set(0, 0.8, 0);
|
||||||
|
this.ball.mesh.velocity.set(0, 0, 0);
|
||||||
|
this.ball.mesh.possessedBy = null;
|
||||||
|
|
||||||
|
// Reset player positions
|
||||||
|
this.playerTeam.resetPositions();
|
||||||
|
this.botTeam.resetPositions();
|
||||||
|
|
||||||
|
// Give possession to the team that was scored on
|
||||||
|
if (teamToGetPossession) {
|
||||||
|
// Give ball to the player closest to the center
|
||||||
|
let closestPlayer = null;
|
||||||
|
let minDistance = Infinity;
|
||||||
|
|
||||||
|
const team = teamToGetPossession === 'red' ? this.playerTeam : this.botTeam;
|
||||||
|
|
||||||
|
for (const player of team.players) {
|
||||||
|
const distance = player.mesh.position.length();
|
||||||
|
if (distance < minDistance) {
|
||||||
|
minDistance = distance;
|
||||||
|
closestPlayer = player;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(closestPlayer){
|
||||||
|
this.ball.mesh.possessedBy = closestPlayer.mesh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleGoal(scoringTeam) {
|
||||||
|
if (this.isGoalScored) return;
|
||||||
|
this.isGoalScored = true;
|
||||||
|
|
||||||
|
if (scoringTeam === 'red') {
|
||||||
|
this.score.blue++;
|
||||||
|
} else {
|
||||||
|
this.score.red++;
|
||||||
|
}
|
||||||
|
this.updateScoreboard();
|
||||||
|
|
||||||
|
const losingTeam = scoringTeam === 'red' ? 'blue' : 'red';
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.resetPositions(losingTeam);
|
||||||
|
this.isGoalScored = false;
|
||||||
|
}, 1000); // 1-second delay
|
||||||
|
}
|
||||||
|
|
||||||
|
checkCollisions() {
|
||||||
|
if (this.isGoalScored) return;
|
||||||
|
|
||||||
|
const ballPos = this.ball.mesh.position;
|
||||||
|
const ballRadius = 0.8; // Corrected from this.ball.radius
|
||||||
|
const fieldWidth = 60;
|
||||||
|
const fieldLength = 100;
|
||||||
|
const goalWidth = 20;
|
||||||
|
|
||||||
|
// Sideline collision (bounce)
|
||||||
|
const hasHitSideline = (Math.abs(ballPos.x) + ballRadius) > (fieldWidth / 2);
|
||||||
|
if (hasHitSideline) {
|
||||||
|
this.ball.mesh.velocity.x *= -1;
|
||||||
|
ballPos.x = (fieldWidth / 2 - ballRadius) * Math.sign(ballPos.x);
|
||||||
|
this.ball.mesh.velocity.y = 0; // Keep ball on the ground
|
||||||
|
ballPos.y = 0.8; // Reset y position
|
||||||
|
}
|
||||||
|
|
||||||
|
const goalLine = fieldLength / 2;
|
||||||
|
const isWithinGoalPosts = Math.abs(ballPos.x) < goalWidth / 2;
|
||||||
|
|
||||||
|
// Goal detection or back wall bounce
|
||||||
|
if (Math.abs(ballPos.z) + ballRadius > goalLine) {
|
||||||
|
if (isWithinGoalPosts) {
|
||||||
|
// Goal
|
||||||
|
if (ballPos.z > 0) {
|
||||||
|
this.handleGoal('blue'); // Blue team scored
|
||||||
|
} else {
|
||||||
|
this.handleGoal('red'); // Red team scored
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Bounce off back wall
|
||||||
|
this.ball.mesh.velocity.z *= -1;
|
||||||
|
ballPos.z = (goalLine - ballRadius) * Math.sign(ballPos.z);
|
||||||
|
this.ball.mesh.velocity.y = 0; // Keep ball on the ground
|
||||||
|
ballPos.y = 0.8; // Reset y position
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
animate() {
|
animate() {
|
||||||
requestAnimationFrame(() => this.animate());
|
requestAnimationFrame(() => this.animate());
|
||||||
|
|
||||||
this.updatePlayerPosition();
|
this.updatePlayerPosition();
|
||||||
this.updateAIPlayers();
|
this.updateAIPlayers();
|
||||||
this.updateBall();
|
this.updateBall();
|
||||||
|
this.checkCollisions();
|
||||||
|
|
||||||
// Camera follows player from the sideline
|
// Camera follows player from the sideline
|
||||||
this.camera.position.z = this.player.mesh.position.z;
|
this.camera.position.z = this.player.mesh.position.z;
|
||||||
|
|||||||
6
game.php
6
game.php
@ -4,12 +4,16 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Pocket 5 Soccer</title>
|
<title>Pocket 5 Soccer</title>
|
||||||
|
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||||
<style>
|
<style>
|
||||||
body { margin: 0; overflow: hidden; }
|
body { margin: 0; overflow: hidden; }
|
||||||
canvas { display: block; }
|
canvas { display: block; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<div id="scoreboard">
|
||||||
|
<span id="red-score">0</span> - <span id="blue-score">0</span>
|
||||||
|
</div>
|
||||||
<script type="importmap">
|
<script type="importmap">
|
||||||
{
|
{
|
||||||
"imports": {
|
"imports": {
|
||||||
@ -18,6 +22,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<script type="module" src="assets/js/game.js?v=<?php echo time(); ?>"></script>
|
<script type="module" src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user