Minion rush 3D source code
A downloadable Modding code
Here is minion rush 3d remake source modding code for the game
// ==========================================
// 1. GAME STATE & MODIFIABLE CONFIGURATION
// ==========================================
let audioEnabled = true;
let synth = null;
let scene, camera, renderer;
let minionGroup, bodyMesh, goggleMesh, eyeMesh, pupilMesh, overallMesh, accessoryMesh;
let trackTiles = [];
let obstacles = [];
let bananas = [];
// Saved Cloud Data Slots
window.cloudSavesList = [
{ id: 'save_default', name: 'Agent Dave Backup', bananas: 1250, tokens: 45, highscore: 3420, gameVersion: 'v2.8.0', savedAt: new Date().toLocaleDateString() }
];
// Outfit Data - Modify colors, pricing, or currency types
const costumeData = [
{ id: 'classic', name: 'Classic Dave', color: 0xffd200, price: 0, currency: 'banana', desc: 'Standard yellow Minion' },
{ id: 'tourist', name: 'Tourist Carl', color: 0x38bdf8, price: 500, currency: 'banana', desc: 'Beach vibes & cool outfit' },
{ id: 'disco', name: 'Disco Stuart', color: 0xa855f7, price: 1000, currency: 'banana', desc: 'Groovy dance style' },
{ id: 'evil', name: 'Evil Minion', color: 0x9333ea, price: 20, currency: 'token', desc: 'Wild purple skin & monster look' }
];
// Power-up Upgrade Configurations
const upgradeData = [
{ id: 'magnet', name: 'Banana Magnet', icon: 'fa-magnet', color: 'text-amber-400', level: 1, maxLevel: 5, desc: 'Attracts nearby bananas automatically' },
{ id: 'shield', name: 'Gru Shield', icon: 'fa-shield-halved', color: 'text-cyan-400', level: 1, maxLevel: 5, desc: 'Protects from 1 fatal crash' }
];
// Main Runtime State Variables
const gameState = {
bananas: 1250,
tokens: 45,
level: 1,
xp: 60,
highscore: 3420,
version: 'v2.8.0',
currentCostume: 'classic',
unlockedCostumes: ['classic'],
controlMode: 'pc',
inGame: false,
isPaused: false,
gameMode: 'classic', // Modes: 'classic' | 'challenge'
activeChallenge: null,
challengeProgress: 0,
challengeTimer: 0,
challengeDodged: 0,
score: 0,
distance: 0,
currentLane: 0, // -1: Left Lane, 0: Center, 1: Right Lane
targetX: 0,
minionY: 0,
minionVelocityY: 0,
isJumping: false,
isSliding: false,
slideTimer: 0,
speed: 0.22, // Modify game runner speed here
hasShield: false,
dailyRewardsClaimed: [1]
};
// Moddable Challenge Mission Objectives
const challengeData = [
{
id: 'c1',
title: 'Banana Blitz',
desc: 'Collect 30 Bananas in under 35 seconds!',
icon: 'fa-bananas',
targetType: 'bananas',
targetVal: 30,
timeLimit: 35,
rewardBananas: 500,
rewardTokens: 10,
speedMult: 1.0
},
{
id: 'c2',
title: 'Speed Demon',
desc: 'Run 400 meters at 1.8x Super Speed!',
icon: 'fa-gauge-high',
targetType: 'distance',
targetVal: 400,
timeLimit: 0,
rewardBananas: 800,
rewardTokens: 20,
speedMult: 1.8
},
{
id: 'c3',
title: 'Laser Dodger',
desc: 'Dodge 15 obstacles without crashing!',
icon: 'fa-shield-halved',
targetType: 'dodged',
targetVal: 15,
timeLimit: 0,
rewardBananas: 1000,
rewardTokens: 25,
speedMult: 1.2
}
];
// ==========================================
// 2. THREE.JS 3D SCENE & ENGINE INITIALIZATION
// ==========================================
function initThree() {
const container = document.getElementById('canvas-container');
if (!container) return;
scene = new THREE.Scene();
scene.background = new THREE.Color(0x0f172a);
scene.fog = new THREE.FogExp2(0x0f172a, 0.025);
camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 3.5, 6.5);
camera.lookAt(0, 1, 0);
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
container.appendChild(renderer.domElement);
const ambientLight = new THREE.AmbientLight(0xffffff, 0.8);
scene.add(ambientLight);
const dirLight = new THREE.DirectionalLight(0xffffff, 0.9);
dirLight.position.set(5, 12, 8);
dirLight.castShadow = true;
scene.add(dirLight);
// Build 3D Character Geometry Group
minionGroup = new THREE.Group();
const bodyGeo = new THREE.CylinderGeometry(0.5, 0.5, 1.2, 32);
const bodyMat = new THREE.MeshStandardMaterial({ color: 0xffd200, roughness: 0.3 });
bodyMesh = new THREE.Mesh(bodyGeo, bodyMat);
bodyMesh.position.y = 0.8;
bodyMesh.castShadow = true;
minionGroup.add(bodyMesh);
const overallGeo = new THREE.CylinderGeometry(0.52, 0.52, 0.5, 32);
const overallMat = new THREE.MeshStandardMaterial({ color: 0x0055b8, roughness: 0.5 });
overallMesh = new THREE.Mesh(overallGeo, overallMat);
overallMesh.position.y = 0.45;
minionGroup.add(overallMesh);
const goggleGeo = new THREE.TorusGeometry(0.22, 0.06, 16, 32);
const goggleMat = new THREE.MeshStandardMaterial({ color: 0x94a3b8, metalness: 0.8, roughness: 0.2 });
goggleMesh = new THREE.Mesh(goggleGeo, goggleMat);
goggleMesh.position.set(0, 1.05, 0.48);
minionGroup.add(goggleMesh);
const eyeGeo = new THREE.SphereGeometry(0.15, 16, 16);
const eyeMat = new THREE.MeshBasicMaterial({ color: 0xffffff });
eyeMesh = new THREE.Mesh(eyeGeo, eyeMat);
eyeMesh.position.set(0, 1.05, 0.45);
minionGroup.add(eyeMesh);
const pupilGeo = new THREE.SphereGeometry(0.06, 16, 16);
const pupilMat = new THREE.MeshBasicMaterial({ color: 0x3b1c08 });
pupilMesh = new THREE.Mesh(pupilGeo, pupilMat);
pupilMesh.position.set(0, 1.05, 0.58);
minionGroup.add(pupilMesh);
scene.add(minionGroup);
// Construct Initial Track Tiles
for (let i = 0; i < 12; i++) {
const tile = createTrackTile();
tile.position.z = -i * 15;
scene.add(tile);
trackTiles.push(tile);
if (i > 1) spawnTileContents(tile.position.z);
}
window.addEventListener('resize', onWindowResize, false);
setupInputListeners();
animate();
}
function createTrackTile() {
const tileGroup = new THREE.Group();
const floorGeo = new THREE.BoxGeometry(7.5, 0.2, 15);
const floorMat = new THREE.MeshStandardMaterial({ color: 0x1e293b, roughness: 0.8 });
const floor = new THREE.Mesh(floorGeo, floorMat);
floor.position.y = -0.1;
floor.receiveShadow = true;
tileGroup.add(floor);
// Lane Lines
[-1.25, 1.25].forEach(x => {
const lineGeo = new THREE.BoxGeometry(0.1, 0.02, 15);
const lineMat = new THREE.MeshBasicMaterial({ color: 0x3b82f6 });
const line = new THREE.Mesh(lineGeo, lineMat);
line.position.set(x, 0.01, 0);
tileGroup.add(line);
});
// Side Guard Rails
[-3.8, 3.8].forEach(x => {
const borderGeo = new THREE.BoxGeometry(0.3, 0.5, 15);
const borderMat = new THREE.MeshStandardMaterial({ color: 0xffd200 });
const border = new THREE.Mesh(borderGeo, borderMat);
border.position.set(x, 0.25, 0);
tileGroup.add(border);
});
return tileGroup;
}
// Spawns Obstacles & Bananas dynamically
function spawnTileContents(zPos) {
const lanePositions = [-2, 0, 2];
if (Math.random() > 0.3) {
const lane = Math.floor(Math.random() * 3);
const type = Math.random() > 0.5 ? 'low' : 'high';
const obsGroup = new THREE.Group();
if (type === 'low') {
// Low Hurdle (Requires Jumping)
const geo = new THREE.BoxGeometry(1.6, 0.7, 0.6);
const mat = new THREE.MeshStandardMaterial({ color: 0xef4444, roughness: 0.4 });
const mesh = new THREE.Mesh(geo, mat);
mesh.position.y = 0.35;
mesh.castShadow = true;
obsGroup.add(mesh);
} else {
// High Bar (Requires Sliding)
const postGeo = new THREE.CylinderGeometry(0.08, 0.08, 2, 16);
const postMat = new THREE.MeshStandardMaterial({ color: 0x64748b });
const p1 = new THREE.Mesh(postGeo, postMat);
p1.position.set(-0.8, 1, 0);
const p2 = new THREE.Mesh(postGeo, postMat);
p2.position.set(0.8, 1, 0);
const barGeo = new THREE.CylinderGeometry(0.06, 0.06, 1.8, 16);
const barMat = new THREE.MeshBasicMaterial({ color: 0x06b6d4 });
const bar = new THREE.Mesh(barGeo, barMat);
bar.rotation.z = Math.PI / 2;
bar.position.set(0, 1.4, 0);
obsGroup.add(p1);
obsGroup.add(p2);
obsGroup.add(bar);
}
obsGroup.position.set(lanePositions[lane], 0, zPos);
obsGroup.userData = { lane: lanePositions[lane], type: type, passed: false };
scene.add(obsGroup);
obstacles.push(obsGroup);
}
// Spawn Pickups
const bananaLane = Math.floor(Math.random() * 3);
for (let b = 0; b < 3; b++) {
const bananaGeo = new THREE.TorusGeometry(0.18, 0.07, 8, 16, Math.PI);
const bananaMat = new THREE.MeshStandardMaterial({ color: 0xfacc15, roughness: 0.2 });
const bananaMesh = new THREE.Mesh(bananaGeo, bananaMat);
bananaMesh.position.set(lanePositions[bananaLane], 0.6, zPos + (b * 1.5));
bananaMesh.rotation.x = Math.PI / 2;
scene.add(bananaMesh);
bananas.push(bananaMesh);
}
}
// Tone.js Sound Synthesizer Audio Handler
function playSound(type) {
if (!audioEnabled) return;
try {
if (!synth && typeof Tone !== 'undefined') {
synth = new Tone.Synth().toDestination();
}
if (synth) {
if (type === 'banana') synth.triggerAttackRelease('C6', '16n');
else if (type === 'jump') synth.triggerAttackRelease('G4', '8n');
else if (type === 'slide') synth.triggerAttackRelease('E3', '8n');
else if (type === 'hit') synth.triggerAttackRelease('C2', '4n');
else if (type === 'click') synth.triggerAttackRelease('E5', '16n');
else if (type === 'taunt') synth.triggerAttackRelease('A5', '8n');
}
} catch(e) {}
}
function toggleAudio() {
audioEnabled = !audioEnabled;
const icon = document.getElementById('audio-icon');
if (icon) {
icon.className = audioEnabled ? "fa-solid fa-volume-high text-base sm:text-lg" : "fa-solid fa-volume-xmark text-base sm:text-lg text-red-400";
}
}
// ==========================================
// 3. CONTROLS & PLAYER INPUT LOGIC
// ==========================================
function setupInputListeners() {
window.addEventListener('keydown', (e) => {
if (!gameState.inGame) return;
if (e.key === 'ArrowLeft' || e.key === 'a' || e.key === 'A') triggerLaneLeft();
if (e.key === 'ArrowRight' || e.key === 'd' || e.key === 'D') triggerLaneRight();
if (e.key === 'ArrowUp' || e.key === 'w' || e.key === 'W' || e.key === ' ') triggerJump();
if (e.key === 'ArrowDown' || e.key === 's' || e.key === 'S') triggerSlide();
if (e.key === 'p' || e.key === 'P' || e.key === 'Escape') {
if (gameState.isPaused) resumeGame();
else pauseGame();
}
});
let touchStartX = 0, touchStartY = 0;
window.addEventListener('touchstart', (e) => {
if (e.touches.length > 0) {
touchStartX = e.touches[0].clientX;
touchStartY = e.touches[0].clientY;
}
});
window.addEventListener('touchend', (e) => {
if (!gameState.inGame || e.changedTouches.length === 0) return;
const touchEndX = e.changedTouches[0].clientX;
const touchEndY = e.changedTouches[0].clientY;
const dx = touchEndX - touchStartX;
const dy = touchEndY - touchStartY;
if (Math.abs(dx) > Math.abs(dy)) {
if (dx > 30) triggerLaneRight();
else if (dx < -30) triggerLaneLeft();
} else {
if (dy < -30) triggerJump();
else if (dy > 30) triggerSlide();
}
});
}
function triggerLaneLeft() {
if (gameState.currentLane > -1) {
gameState.currentLane--;
gameState.targetX = gameState.currentLane * 2;
}
}
function triggerLaneRight() {
if (gameState.currentLane < 1) {
gameState.currentLane++;
gameState.targetX = gameState.currentLane * 2;
}
}
function triggerJump() {
if (!gameState.isJumping) {
gameState.isJumping = true;
gameState.minionVelocityY = 0.22;
playSound('jump');
}
}
function triggerSlide() {
if (!gameState.isSliding) {
gameState.isSliding = true;
gameState.slideTimer = 25;
if (minionGroup) minionGroup.scale.set(1, 0.5, 1);
playSound('slide');
}
}
// ==========================================
// 4. MAIN GAME LOOP & PHYSICS TICK
// ==========================================
function animate() {
requestAnimationFrame(animate);
if (gameState.inGame && !gameState.isPaused) {
// Smooth Lane Interpolation
minionGroup.position.x += (gameState.targetX - minionGroup.position.x) * 0.2;
// Gravity & Jump Physics
if (gameState.isJumping) {
gameState.minionY += gameState.minionVelocityY;
gameState.minionVelocityY -= 0.012;
if (gameState.minionY <= 0) {
gameState.minionY = 0;
gameState.isJumping = false;
}
}
minionGroup.position.y = gameState.minionY;
// Slide State Reset
if (gameState.isSliding) {
gameState.slideTimer--;
if (gameState.slideTimer <= 0) {
gameState.isSliding = false;
if (minionGroup) minionGroup.scale.set(1, 1, 1);
}
}
// Idle Bounce Oscillation
if (!gameState.isJumping && !gameState.isSliding) {
minionGroup.rotation.z = Math.sin(Date.now() * 0.01) * 0.05;
}
let currentSpeed = gameState.speed;
if (gameState.gameMode === 'challenge' && gameState.activeChallenge) {
currentSpeed *= gameState.activeChallenge.speedMult;
}
// Track Movement & Infinite Recycling
trackTiles.forEach(tile => {
tile.position.z += currentSpeed * 2.5;
if (tile.position.z > 15) {
tile.position.z -= 12 * 15;
spawnTileContents(tile.position.z);
}
});
// Update Distance Tracking
gameState.distance += currentSpeed * 0.8;
document.getElementById('hud-distance').innerText = Math.floor(gameState.distance);
// Update Obstacles & Hit Collisions
for (let i = obstacles.length - 1; i >= 0; i--) {
const obs = obstacles[i];
obs.position.z += currentSpeed * 2.5;
if (!obs.userData.passed && obs.position.z > 0) {
obs.userData.passed = true;
gameState.challengeDodged++;
checkChallengeProgress();
}
// Hit Detection Bounds
if (Math.abs(obs.position.z) < 0.8 && Math.abs(obs.position.x - minionGroup.position.x) < 0.8) {
let hit = false;
if (obs.userData.type === 'low' && gameState.minionY < 0.5) hit = true;
if (obs.userData.type === 'high' && !gameState.isSliding) hit = true;
if (hit) {
if (gameState.hasShield) {
gameState.hasShield = false;
playSound('hit');
scene.remove(obs);
obstacles.splice(i, 1);
continue;
} else {
triggerGameOver();
return;
}
}
}
if (obs.position.z > 10) {
scene.remove(obs);
obstacles.splice(i, 1);
}
}
// Pickups & Banana Collisions
for (let i = bananas.length - 1; i >= 0; i--) {
const b = bananas[i];
b.position.z += currentSpeed * 2.5;
b.rotation.z += 0.05;
if (Math.abs(b.position.z) < 0.9 && Math.abs(b.position.x - minionGroup.position.x) < 0.9 && gameState.minionY < 1.2) {
gameState.bananas++;
gameState.score += 10;
document.getElementById('hud-banana').innerText = gameState.bananas;
document.getElementById('banana-count').innerText = gameState.bananas.toLocaleString();
playSound('banana');
checkChallengeProgress();
scene.remove(b);
bananas.splice(i, 1);
continue;
}
if (b.position.z > 10) {
scene.remove(b);
bananas.splice(i, 1);
}
}
// Challenge Mode Timer Check
if (gameState.gameMode === 'challenge' && gameState.activeChallenge && gameState.activeChallenge.timeLimit > 0) {
gameState.challengeTimer -= 1 / 60;
document.getElementById('challenge-timer-hud').innerText = Math.max(0, Math.ceil(gameState.challengeTimer)) + 's';
if (gameState.challengeTimer <= 0) {
triggerGameOver();
return;
}
}
} else if (!gameState.inGame) {
if (minionGroup) {
minionGroup.rotation.y += 0.01;
}
}
renderer.render(scene, camera);
}
// ==========================================
// 5. GAME FLOW & MODAL MANAGEMENT
// ==========================================
function startGame(mode) {
gameState.gameMode = mode;
gameState.inGame = true;
gameState.isPaused = false;
gameState.score = 0;
gameState.distance = 0;
gameState.currentLane = 0;
gameState.targetX = 0;
gameState.minionY = 0;
gameState.isJumping = false;
gameState.isSliding = false;
gameState.speed = 0.22;
gameState.challengeDodged = 0;
obstacles.forEach(o => scene.remove(o));
bananas.forEach(b => scene.remove(b));
obstacles = [];
bananas = [];
document.getElementById('main-menu').classList.add('hidden');
document.getElementById('game-hud').classList.remove('hidden');
document.getElementById('game-over-modal').classList.add('hidden');
document.getElementById('challenge-modal').classList.add('hidden');
if (mode === 'challenge' && gameState.activeChallenge) {
gameState.challengeProgress = 0;
gameState.challengeTimer = gameState.activeChallenge.timeLimit;
document.getElementById('challenge-status-bar').classList.remove('hidden');
document.getElementById('challenge-title-hud').innerText = gameState.activeChallenge.title;
checkChallengeProgress();
} else {
document.getElementById('challenge-status-bar').classList.add('hidden');
}
minionGroup.position.set(0, 0, 0);
camera.position.set(0, 3.5, 6.5);
camera.lookAt(0, 1, 0);
}
function pauseGame() {
if (!gameState.inGame) return;
gameState.isPaused = true;
document.getElementById('pause-modal')?.classList.remove('hidden');
}
function resumeGame() {
gameState.isPaused = false;
document.getElementById('pause-modal')?.classList.add('hidden');
}
function triggerGameOver() {
gameState.inGame = false;
playSound('hit');
if (gameState.distance > gameState.highscore) {
gameState.highscore = Math.floor(gameState.distance);
}
document.getElementById('go-distance').innerText = Math.floor(gameState.distance) + ' m';
document.getElementById('go-bananas').innerText = '๐ +' + gameState.score;
document.getElementById('go-highscore').innerText = gameState.highscore + ' m';
| Updated | 3 days ago |
| Published | 8 days ago |
| Status | Released |
| Author | profilethings |
| Tags | minion-rush-3d-remake, modding, Open Source, sourcecode |
| Content | No generative AI was used |
Leave a comment
Log in with itch.io to leave a comment.