If anyone is interested in helping me with coding some of the game, please reach out. It's something I've been working on in between @enginewitty and the PIMP backend work, and my own PeakeCoin Projects. This is the code as far as I have come. It can be a lot of work, but it does come with its perks.
I'll work on getting this uploaded so it's playable as I work on it.
```javascript
//game.js
// Babylon.js 2D top-down setup const canvas = document.getElementById("gameCanvas"); const engine = new BABYLON.Engine(canvas, true); const scene = new BABYLON.Scene(engine); scene.clearColor = new BABYLON.Color3(0.95, 0.95, 0.95);
// Camera for side-view 2.5D (like Mario) const camera = new BABYLON.FreeCamera("camera1", new BABYLON.Vector3(0, 50, 150), scene); camera.setTarget(new BABYLON.Vector3(0, 50, 0)); camera.mode = BABYLON.Camera.ORTHOGRAPHIC_CAMERA; camera.orthoLeft = -canvas.width / 2; camera.orthoRight = canvas.width / 2; camera.orthoTop = canvas.height / 2; camera.orthoBottom = -canvas.height / 2;
// Add lighting (essential for Babylon.js materials) const light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 1, 0), scene); light.intensity = 1.0;
// Simple tile map (10x10 for demo) const TILE_SIZE = 48; const MAP_W = 10; const MAP_H = 10; const map = [ [0,0,0,0,0,0,0,0,0,0], [0,1,1,0,0,0,1,1,1,0], [0,0,0,0,1,0,0,0,1,0], [0,1,0,0,1,0,1,0,0,0], [0,1,0,0,0,0,1,0,1,0], [0,0,0,1,1,0,0,0,1,0], [0,1,0,0,0,0,1,0,0,0], [0,1,1,1,0,1,1,0,1,0], [0,0,0,0,0,0,0,0,1,0], [0,0,1,1,1,0,0,0,0,0], ];
// Create side-view ground platforms (like Mario levels)
for (let x = 0; x < MAP_W * 2; x++) {
// Ground platform
const ground = BABYLON.MeshBuilder.CreateBox(ground_${x}
, {width: TILE_SIZE, height: TILE_SIZE/2, depth: TILE_SIZE}, scene);
ground.position.x = (x - MAP_W) * TILE_SIZE;
ground.position.y = 0; // Ground level
ground.position.z = 0;
const groundMat = new BABYLON.StandardMaterial(groundMat_${x}
, scene);
groundMat.diffuseColor = new BABYLON.Color3(0.4, 0.3, 0.2); // Brown ground
ground.material = groundMat;
// Occasional platforms at different heights
if (x % 4 === 0 && x > 2) {
const platform = BABYLON.MeshBuilder.CreateBox(platform_${x}
, {width: TILE_SIZE * 2, height: TILE_SIZE/3, depth: TILE_SIZE}, scene);
platform.position.x = (x - MAP_W) * TILE_SIZE;
platform.position.y = 60 + Math.sin(x * 0.5) * 30; // Varying platform heights
platform.position.z = 0;
const platMat = new BABYLON.StandardMaterial(platMat_${x}
, scene);
platMat.diffuseColor = new BABYLON.Color3(0.2, 0.6, 0.2); // Green platforms
platform.material = platMat;
}
}
// Player as a colored circle
// Player as a simple human figure (top-down)
const playerGroup = new BABYLON.TransformNode("playerGroup", scene);
// Create super smooth character with joint hierarchy const charSize = 20;
// Create materials with bright outline effect const createOutlineMaterial = (color, outlineColor = new BABYLON.Color3(0, 1, 1)) => { const mat = new BABYLON.StandardMaterial("material", scene); mat.diffuseColor = color; mat.emissiveColor = color.scale(0.1); // Slight glow
// Create outline effect using edge rendering mat.backFaceCulling = false; return mat; };
// Head with smooth sphere const head = BABYLON.MeshBuilder.CreateSphere("head", {diameter: charSize, segments: 32}, scene); const headMat = createOutlineMaterial(new BABYLON.Color3(1, 0.85, 0.7)); head.material = headMat; head.position.y = 55; head.position.z = 0; head.parent = playerGroup;
// Neck connector (smooth joint) const neck = BABYLON.MeshBuilder.CreateCylinder("neck", {height: 8, diameter: 6, tessellation: 16}, scene); neck.material = headMat; neck.position.y = 46; neck.parent = playerGroup;
// Torso (smooth rounded rectangle) const torso = BABYLON.MeshBuilder.CreateCylinder("torso", {height: charSize * 1.4, diameter: charSize * 0.9, tessellation: 8}, scene); const torsoMat = createOutlineMaterial(new BABYLON.Color3(0.9, 0.3, 0.3)); torso.material = torsoMat; torso.position.y = 35; torso.parent = playerGroup;
// Shoulder joints (smooth connection points) const leftShoulder = BABYLON.MeshBuilder.CreateSphere("leftShoulder", {diameter: 6, segments: 16}, scene); leftShoulder.material = headMat; leftShoulder.position.x = -charSize * 0.4; leftShoulder.position.y = 42; leftShoulder.parent = playerGroup;
const rightShoulder = BABYLON.MeshBuilder.CreateSphere("rightShoulder", {diameter: 6, segments: 16}, scene); rightShoulder.material = headMat; rightShoulder.position.x = charSize * 0.4; rightShoulder.position.y = 42; rightShoulder.parent = playerGroup;
// Upper arms (smooth cylinders) const leftUpperArm = BABYLON.MeshBuilder.CreateCylinder("leftUpperArm", {height: charSize * 0.7, diameter: 5, tessellation: 12}, scene); const armMat = createOutlineMaterial(new BABYLON.Color3(1, 0.8, 0.6)); leftUpperArm.material = armMat; leftUpperArm.position.x = -charSize * 0.4; leftUpperArm.position.y = 35; leftUpperArm.parent = playerGroup;
const rightUpperArm = BABYLON.MeshBuilder.CreateCylinder("rightUpperArm", {height: charSize * 0.7, diameter: 5, tessellation: 12}, scene); rightUpperArm.material = armMat; rightUpperArm.position.x = charSize * 0.4; rightUpperArm.position.y = 35; rightUpperArm.parent = playerGroup;
// Elbow joints const leftElbow = BABYLON.MeshBuilder.CreateSphere("leftElbow", {diameter: 4, segments: 12}, scene); leftElbow.material = armMat; leftElbow.position.x = -charSize * 0.4; leftElbow.position.y = 28; leftElbow.parent = playerGroup;
const rightElbow = BABYLON.MeshBuilder.CreateSphere("rightElbow", {diameter: 4, segments: 12}, scene); rightElbow.material = armMat; rightElbow.position.x = charSize * 0.4; rightElbow.position.y = 28; rightElbow.parent = playerGroup;
// Forearms const leftForearm = BABYLON.MeshBuilder.CreateCylinder("leftForearm", {height: charSize * 0.6, diameter: 4, tessellation: 12}, scene); leftForearm.material = armMat; leftForearm.position.x = -charSize * 0.4; leftForearm.position.y = 22; leftForearm.parent = playerGroup;
const rightForearm = BABYLON.MeshBuilder.CreateCylinder("rightForearm", {height: charSize * 0.6, diameter: 4, tessellation: 12}, scene); rightForearm.material = armMat; rightForearm.position.x = charSize * 0.4; rightForearm.position.y = 22; rightForearm.parent = playerGroup;
// Hip joints const leftHip = BABYLON.MeshBuilder.CreateSphere("leftHip", {diameter: 6, segments: 16}, scene); const legMat = createOutlineMaterial(new BABYLON.Color3(0.2, 0.2, 0.9)); leftHip.material = legMat; leftHip.position.x = -charSize * 0.2; leftHip.position.y = 25; leftHip.parent = playerGroup;
const rightHip = BABYLON.MeshBuilder.CreateSphere("rightHip", {diameter: 6, segments: 16}, scene); rightHip.material = legMat; rightHip.position.x = charSize * 0.2; rightHip.position.y = 25; rightHip.parent = playerGroup;
// Thighs const leftThigh = BABYLON.MeshBuilder.CreateCylinder("leftThigh", {height: charSize * 0.8, diameter: 6, tessellation: 12}, scene); leftThigh.material = legMat; leftThigh.position.x = -charSize * 0.2; leftThigh.position.y = 18; leftThigh.parent = playerGroup;
const rightThigh = BABYLON.MeshBuilder.CreateCylinder("rightThigh", {height: charSize * 0.8, diameter: 6, tessellation: 12}, scene); rightThigh.material = legMat; rightThigh.position.x = charSize * 0.2; rightThigh.position.y = 18; rightThigh.parent = playerGroup;
// Knee joints const leftKnee = BABYLON.MeshBuilder.CreateSphere("leftKnee", {diameter: 5, segments: 12}, scene); leftKnee.material = legMat; leftKnee.position.x = -charSize * 0.2; leftKnee.position.y = 12; leftKnee.parent = playerGroup;
const rightKnee = BABYLON.MeshBuilder.CreateSphere("rightKnee", {diameter: 5, segments: 12}, scene); rightKnee.material = legMat; rightKnee.position.x = charSize * 0.2; rightKnee.position.y = 12; rightKnee.parent = playerGroup;
// Calves const leftCalf = BABYLON.MeshBuilder.CreateCylinder("leftCalf", {height: charSize * 0.7, diameter: 5, tessellation: 12}, scene); leftCalf.material = legMat; leftCalf.position.x = -charSize * 0.2; leftCalf.position.y = 7; leftCalf.parent = playerGroup;
const rightCalf = BABYLON.MeshBuilder.CreateCylinder("rightCalf", {height: charSize * 0.7, diameter: 5, tessellation: 12}, scene); rightCalf.material = legMat; rightCalf.position.x = charSize * 0.2; rightCalf.position.y = 7; rightCalf.parent = playerGroup;
// Feet for complete walking cycle const leftFoot = BABYLON.MeshBuilder.CreateBox("leftFoot", {width: 8, height: 3, depth: 12}, scene); const footMat = createOutlineMaterial(new BABYLON.Color3(0.3, 0.2, 0.1)); leftFoot.material = footMat; leftFoot.position.x = -charSize * 0.2; leftFoot.position.y = 2; leftFoot.parent = playerGroup;
const rightFoot = BABYLON.MeshBuilder.CreateBox("rightFoot", {width: 8, height: 3, depth: 12}, scene); rightFoot.material = footMat; rightFoot.position.x = charSize * 0.2; rightFoot.position.y = 2; rightFoot.parent = playerGroup;
// Add bright outline effect to entire character playerGroup.getChildMeshes().forEach(mesh => { if (mesh.material) { mesh.renderOutline = true; mesh.outlineWidth = 0.5; mesh.outlineColor = new BABYLON.Color3(0, 1, 1); // Bright cyan outline } });
// Store body parts for smooth animation const bodyParts = { head, neck, torso, leftShoulder, rightShoulder, leftUpperArm, rightUpperArm, leftElbow, rightElbow, leftForearm, rightForearm, leftHip, rightHip, leftThigh, rightThigh, leftKnee, rightKnee, leftCalf, rightCalf, leftFoot, rightFoot };
// Initial position (standing on ground) playerGroup.position.x = 0; playerGroup.position.y = 25; // Standing on ground level playerGroup.position.z = 0;
// Add a simple test box to verify rendering const testBox = BABYLON.MeshBuilder.CreateBox("testBox", {size: 20}, scene); const testMat = new BABYLON.StandardMaterial("testMat", scene); testMat.diffuseColor = new BABYLON.Color3(1, 0, 0); // bright red testBox.material = testMat; testBox.position.x = 100; testBox.position.y = 100; testBox.position.z = 2;
console.log("Player created at position:", playerGroup.position); console.log("Camera position:", camera.position); console.log("Camera ortho bounds:", camera.orthoLeft, camera.orthoRight, camera.orthoTop, camera.orthoBottom); console.log("Test box created at:", testBox.position);
// Player movement with Mario-style physics let playerX = 0; let playerVelocityY = 0; // For jumping/gravity let isMoving = false; let walkCycle = 0; const speed = 3; const keys = {};
// Super smooth joint animation with fluid movement function animateWalk() { if (!isMoving) { // Smoothly return to neutral pose with interpolation bodyParts.leftUpperArm.rotation.z = BABYLON.Scalar.Lerp(bodyParts.leftUpperArm.rotation.z, 0, 0.15); bodyParts.rightUpperArm.rotation.z = BABYLON.Scalar.Lerp(bodyParts.rightUpperArm.rotation.z, 0, 0.15); bodyParts.leftForearm.rotation.z = BABYLON.Scalar.Lerp(bodyParts.leftForearm.rotation.z, 0, 0.15); bodyParts.rightForearm.rotation.z = BABYLON.Scalar.Lerp(bodyParts.rightForearm.rotation.z, 0, 0.15); bodyParts.leftThigh.rotation.z = BABYLON.Scalar.Lerp(bodyParts.leftThigh.rotation.z, 0, 0.15); bodyParts.rightThigh.rotation.z = BABYLON.Scalar.Lerp(bodyParts.rightThigh.rotation.z, 0, 0.15); bodyParts.leftCalf.rotation.z = BABYLON.Scalar.Lerp(bodyParts.leftCalf.rotation.z, 0, 0.15); bodyParts.rightCalf.rotation.z = BABYLON.Scalar.Lerp(bodyParts.rightCalf.rotation.z, 0, 0.15); bodyParts.torso.position.y = BABYLON.Scalar.Lerp(bodyParts.torso.position.y, 35, 0.15); bodyParts.head.position.y = BABYLON.Scalar.Lerp(bodyParts.head.position.y, 55, 0.15); return; }
walkCycle += 0.2; // Smooth cycle speed
// Calculate fluid movement phases const armPhase = Math.sin(walkCycle) * 0.5; const legPhase = Math.sin(walkCycle) * 0.7; const kneePhase = Math.sin(walkCycle + Math.PI/3) * 0.4; const elbowPhase = Math.sin(walkCycle + Math.PI/4) * 0.3; const bounce = Math.abs(Math.sin(walkCycle * 2)) * 2; const sway = Math.sin(walkCycle * 0.5) * 0.05;
// Super smooth arm movement with natural joint articulation bodyParts.leftUpperArm.rotation.z = armPhase; bodyParts.rightUpperArm.rotation.z = -armPhase;
// Forearms follow with natural elbow bend bodyParts.leftForearm.rotation.z = armPhase * 0.6 + elbowPhase; bodyParts.rightForearm.rotation.z = -armPhase * 0.6 - elbowPhase;
// Biomechanically correct leg movement bodyParts.leftThigh.rotation.z = legPhase; bodyParts.rightThigh.rotation.z = -legPhase;
// Correct knee bending: knees bend when leg swings forward (lifting), straighten when pushing back const leftKneeBend = legPhase > 0 ? Math.abs(legPhase) * 0.8 : 0; // Bend when swinging forward const rightKneeBend = -legPhase > 0 ? Math.abs(-legPhase) * 0.8 : 0; // Bend when swinging forward
bodyParts.leftCalf.rotation.z = -leftKneeBend; // Negative rotation bends knee forward bodyParts.rightCalf.rotation.z = -rightKneeBend;
// Smooth body movement bodyParts.torso.position.y = 35 + bounce; bodyParts.head.position.y = 55 + bounce; bodyParts.neck.position.y = 46 + bounce;
// Natural body sway and rotation bodyParts.torso.rotation.z = sway; bodyParts.head.rotation.z = sway * 0.5;
// Shoulder movement follows arm swing bodyParts.leftShoulder.position.y = 42 + bounce + Math.sin(walkCycle + armPhase) * 0.5; bodyParts.rightShoulder.position.y = 42 + bounce + Math.sin(walkCycle - armPhase) * 0.5;
// Hip movement follows leg swing bodyParts.leftHip.rotation.z = legPhase * 0.3; bodyParts.rightHip.rotation.z = -legPhase * 0.3;
// Joint connections move smoothly bodyParts.leftElbow.position.y = 28 + bounce + Math.sin(walkCycle + elbowPhase) * 0.3; bodyParts.rightElbow.position.y = 28 + bounce + Math.sin(walkCycle - elbowPhase) * 0.3;
// Knee position follows thigh movement but also lifts during bending const leftKneeHeight = 12 + bounce * 0.5 + (leftKneeBend * 2); // Knee lifts when bending const rightKneeHeight = 12 + bounce * 0.5 + (rightKneeBend * 2);
bodyParts.leftKnee.position.y = leftKneeHeight; bodyParts.rightKnee.position.y = rightKneeHeight;
// Calf position should follow knee movement bodyParts.leftCalf.position.y = 7 + bounce * 0.3 + (leftKneeBend * 1.5); bodyParts.rightCalf.position.y = 7 + bounce * 0.3 + (rightKneeBend * 1.5);
// Feet follow the walking cycle - lift when leg swings forward, plant when stepping const leftFootLift = leftKneeBend * 3; // Foot lifts more dramatically const rightFootLift = rightKneeBend * 3;
bodyParts.leftFoot.position.y = 2 + leftFootLift; bodyParts.rightFoot.position.y = 2 + rightFootLift;
// Foot angle changes during walk cycle bodyParts.leftFoot.rotation.z = leftKneeBend * 0.3; // Foot tilts up when lifting bodyParts.rightFoot.rotation.z = rightKneeBend * 0.3; }
// Handle keyboard input window.addEventListener("keydown", (e) => { keys[e.key.toLowerCase()] = true; });
window.addEventListener("keyup", (e) => { keys[e.key.toLowerCase()] = false; });
// Side-scrolling movement (Mario-style) function updateMovement() { let moving = false; let deltaX = 0;
// Horizontal movement only (side-scrolling) if (keys['d'] || keys['arrowright']) { deltaX += speed; moving = true; // Face right playerGroup.rotation.y = 0; } if (keys['a'] || keys['arrowleft']) { deltaX -= speed; moving = true; // Face left playerGroup.rotation.y = Math.PI; }
// Jump (Mario-style) if (keys['w'] || keys['arrowup'] || keys[' ']) { if (Math.abs(playerGroup.position.y - 25) < 5) { // Only jump if on ground playerVelocityY = 8; // Jump force } }
// Apply horizontal movement playerX += deltaX; playerGroup.position.x = playerX;
// Apply gravity and vertical movement playerVelocityY -= 0.3; // Gravity playerGroup.position.y += playerVelocityY;
// Ground collision if (playerGroup.position.y <= 25) { playerGroup.position.y = 25; playerVelocityY = 0; }
// Update animation state isMoving = moving; animateWalk(); }
engine.runRenderLoop(() => { updateMovement(); scene.render(); });