User prompt
durağı yolun karşısına al bizim konumu daha yukarı koy yolu biraz daha genişlet
Code edit (1 edits merged)
Please save this source code
User prompt
Sling Street: Zıpla ve Geç!
Initial prompt
Frogger and Crossy Road oyunları gibi karşıdan karşıya geçmeye çalıştığımız bir oyun ama sapanla geri çek fırlat mekaniği var aşşağıdan topu çekip bırakıyoruz ortada duran durağa denk getirmeye çalışıyoruz yokuş yukarı fırlattığımız için geri düşebiliyor top. 4 şeritli yol yoldan geçen farklı araçlar olacak normal araç uzun kamyon ve zıpzıplı araba yap zıpzıplı arabanın üstüne topumuz düşerse geri yukarı zıplatsın
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Ball (player) class var Ball = Container.expand(function () { var self = Container.call(this); var ballGfx = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.7, scaleY: 1.7 }); self.radius = ballGfx.width / 2; self.vx = 0; self.vy = 0; self.launched = false; self.resting = true; // true if waiting for launch self.bounceCooldown = 0; // prevent double bounces self.update = function () { if (self.launched) { // Gravity self.vy += 0.1344; // Move self.x += self.vx; self.y += self.vy; // Friction (air) self.vx *= 0.995; // Clamp to game area if (self.x < self.radius) self.x = self.radius; if (self.x > 2048 - self.radius) self.x = 2048 - self.radius; // If falls below screen, game over if (self.y > 2732 + 200) { LK.effects.flashScreen(0xff0000, 800); LK.showGameOver(); } } }; // Reset ball to start self.reset = function () { self.x = 2048 / 2; self.y = typeof ballStartY !== "undefined" ? ballStartY : 2732 - 180; self.vx = 0; self.vy = 0; self.launched = false; self.resting = true; self.bounceCooldown = 0; }; // Bounce up (from bouncy car) self.bounce = function (strength) { self.vy = -Math.abs(strength || 38 + Math.random() * 8); self.launched = true; self.resting = false; self.bounceCooldown = 10; }; return self; }); // BouncyCar (bouncy vehicle) class var BouncyCar = Container.expand(function () { var self = Container.call(this); var bouncyGfx = self.attachAsset('bouncycar', { anchorX: 0.5, anchorY: 0.5 }); self.scale.set(1.2, 1.2); self.width = bouncyGfx.width * 1.2; self.height = bouncyGfx.height * 1.2; self.speed = 0; self.lane = 0; self.type = 'bouncycar'; self.bouncePhase = Math.random() * Math.PI * 2; self.update = function () { self.x += self.speed; // Bouncy up-down self.y0 = self.y0 || self.y; self.y = self.y0 + Math.sin(LK.ticks / 12 + self.bouncePhase) * 38; if (self.speed > 0 && self.x > 2048 + 200) self.destroyed = true; if (self.speed < 0 && self.x < -200) self.destroyed = true; }; return self; }); // Car (normal) class var Car = Container.expand(function () { var self = Container.call(this); var carGfx = self.attachAsset('car', { anchorX: 0.5, anchorY: 0.5 }); self.scale.set(1.2, 1.2); self.width = carGfx.width * 1.2; self.height = carGfx.height * 1.2; self.speed = 0; self.lane = 0; self.type = 'car'; self.update = function () { self.x += self.speed; // Remove if out of screen if (self.speed > 0 && self.x > 2048 + 200) self.destroyed = true; if (self.speed < 0 && self.x < -200) self.destroyed = true; }; return self; }); // Truck (long vehicle) class var Truck = Container.expand(function () { var self = Container.call(this); var truckGfx = self.attachAsset('truck', { anchorX: 0.5, anchorY: 0.5 }); self.scale.set(1.2, 1.2); self.width = truckGfx.width * 1.2; self.height = truckGfx.height * 1.2; self.speed = 0; self.lane = 0; self.type = 'truck'; self.update = function () { self.x += self.speed; if (self.speed > 0 && self.x > 2048 + 300) self.destroyed = true; if (self.speed < 0 && self.x < -300) self.destroyed = true; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x222222 }); /**** * Game Code ****/ // --- Game variables --- // Ball (player) // Stop (target) // Road background // Lane divider // Car // Truck // Bouncy car // Slingshot band var lanes = [0, 1, 2, 3, 4, 5]; var laneYs = []; var laneCount = 6; var verticalOffset = -200; // Move everything up by 200px var roadTop = 600 + verticalOffset; var roadHeight = 1536; var laneGap = roadHeight / laneCount; for (var i = 0; i < laneCount; ++i) { laneYs[i] = roadTop + laneGap / 2 + i * laneGap; } // Place stop (target) at the top of the road, centered horizontally var stopY = roadTop - 100; var stopX = 2048 / 2; // Update stop trigger area to match new stop (target) width var stopRadius = { x: 158, // half of stop image width (316/2), matches texture y: 90 }; // x: horizontal radius, y: vertical radius var level = 1; var vehicles = []; var ball = null; var stop = null; var slingshotBand = null; var isDragging = false; // Place the ball higher up (not at the very bottom) var ballStartY = roadTop + roadHeight + 120; var dragStart = { x: 0, y: 0 }; var dragCurrent = { x: 0, y: 0 }; var launchPower = 0; var launchAngle = 0; var canLaunch = true; var scoreTxt = null; var levelTxt = null; var winTimeout = null; // --- Draw road and lanes --- var road = LK.getAsset('road', { x: 0, y: roadTop, width: 2048, height: roadHeight }); game.addChild(road); // Lane dividers for (var i = 1; i < laneCount; ++i) { var divider = LK.getAsset('divider', { x: 0, y: roadTop + i * laneGap - 12, width: 2048, height: 24 }); game.addChild(divider); } // --- Stop (target) --- stop = LK.getAsset('stop', { anchorX: 0.5, anchorY: 0.5, x: stopX, y: stopY, scaleX: 1.2, scaleY: 1.2 }); game.addChild(stop); // --- Ball (player) --- ball = new Ball(); ball.scale.set(1.2, 1.2); game.addChild(ball); ball.reset(); // --- Slingshot band (visual) --- slingshotBand = LK.getAsset('band', { anchorX: 0.5, anchorY: 1, x: ball.x, y: ball.y }); slingshotBand.visible = false; game.addChild(slingshotBand); // --- GUI: Score and Level --- scoreTxt = new Text2('0', { size: 110, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); levelTxt = new Text2('1', { size: 70, fill: "#fff" }); levelTxt.anchor.set(0.5, 0); LK.gui.topRight.addChild(levelTxt); // --- Game state --- function resetGameState() { // Remove vehicles for (var i = 0; i < vehicles.length; ++i) { vehicles[i].destroy(); } vehicles = []; // Reset ball ball.reset(); // Reset slingshot slingshotBand.visible = false; isDragging = false; canLaunch = true; // Reset score/level LK.setScore(0); scoreTxt.setText('0'); level = 1; levelTxt.setText('1'); } resetGameState(); // --- Vehicle spawner --- function spawnVehicle() { // Randomly pick lane var lane = Math.floor(Math.random() * laneCount); var y = laneYs[lane]; // Adjusted spawn probabilities: All vehicle types reduced by 5% // Old: Car 60%, Truck 25%, BouncyCar 15% // New: Car 55%, Truck 20%, BouncyCar 10% var t = Math.random(); var v, dir, speed; if (t < 0.55) { v = new Car(); speed = (12 + level * 1.2 + Math.random() * 2) * 0.5; } else if (t < 0.75) { v = new Truck(); speed = (8 + level * 1.1 + Math.random() * 2) * 0.5 * 0.9; // Truck speed reduced by 10% } else { v = new BouncyCar(); speed = (10 + level * 1.3 + Math.random() * 2) * 0.5 * 1.1; // BouncyCar speed increased by 10% } // Direction: even lanes left->right, odd right->left dir = lane % 2 === 0 ? 1 : -1; v.lane = lane; v.y = y; v.y0 = y; v.speed = speed * dir; if (dir > 0) { v.x = -200; } else { v.x = 2048 + 200; // Flip the vehicle horizontally if moving right-to-left if (v.children && v.children.length > 0 && v.children[0]) { v.children[0].scale.x = -1; } } vehicles.push(v); game.addChild(v); } // --- Level progression --- function nextLevel() { level += 1; levelTxt.setText(level + ''); // Remove all vehicles for (var i = 0; i < vehicles.length; ++i) vehicles[i].destroy(); vehicles = []; // Reset ball ball.reset(); canLaunch = true; slingshotBand.visible = false; } // --- Game update --- game.update = function () { // Ball update ball.update(); // Vehicles update // --- Make vehicles follow the speed of the vehicle in front if they are too close in the same lane --- for (var i = vehicles.length - 1; i >= 0; --i) { var v = vehicles[i]; // Find the closest vehicle ahead in the same lane and direction var minDist = Infinity; var frontVehicle = null; for (var j = 0; j < vehicles.length; ++j) { if (i === j) continue; var v2 = vehicles[j]; if (v2.lane !== v.lane) continue; // Only consider vehicles in the same direction if (v.speed > 0 && v2.x > v.x || v.speed < 0 && v2.x < v.x) { var dist = Math.abs(v2.x - v.x); if (dist < minDist) { minDist = dist; frontVehicle = v2; } } } // If too close, match speed to the front vehicle if (frontVehicle) { // Use the larger of the two widths for minimum gap var minGap = Math.max(v.width, frontVehicle.width) * 0.85; if (minDist < minGap) { v.speed = frontVehicle.speed; // Snap position to avoid overlap if (v.speed > 0) { v.x = Math.min(v.x, frontVehicle.x - minGap + 1); } else { v.x = Math.max(v.x, frontVehicle.x + minGap - 1); } } } v.update(); // Remove destroyed if (v.destroyed) { v.destroy(); vehicles.splice(i, 1); continue; } } // Ball-vehicle collision if (ball.launched && ball.bounceCooldown <= 0) { for (var i = 0; i < vehicles.length; ++i) { var v = vehicles[i]; // AABB collision var dx = Math.abs(ball.x - v.x); var dy = Math.abs(ball.y - v.y); var overlapX = dx < ball.radius + v.width / 2 - 10; var overlapY = dy < ball.radius + v.height / 2 - 10; if (overlapX && overlapY) { if (v.type === 'bouncycar') { // Calculate the incoming angle and speed var incomingAngle = Math.atan2(ball.vy, ball.vx); var speed = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy); // Reverse the angle (go in the exact opposite direction) var baseBounceAngle = incomingAngle + Math.PI; // Add up to ±10% randomness to the angle var randomAngle = (Math.random() - 0.5) * (Math.PI * 0.2); // ±0.1π ≈ ±18° var bounceAngle = baseBounceAngle + randomAngle; // Bounce speed is 120% of the incoming speed var bounceSpeed = speed * 1.2; // Set new velocity ball.vx = Math.cos(bounceAngle) * bounceSpeed; ball.vy = Math.sin(bounceAngle) * bounceSpeed; ball.launched = true; ball.resting = false; ball.bounceCooldown = 12; LK.effects.flashObject(v, 0xffff00, 400); // Do NOT randomize X anymore, keep physics consistent // After bounce, check if ball is immediately on stop (target) var dxStop = ball.x - stop.x; var dyStop = ball.y - stop.y; // Use ellipse collision: (dx/a)^2 + (dy/b)^2 < 1 if (dxStop * dxStop / ((stopRadius.x + ball.radius - 10) * (stopRadius.x + ball.radius - 10)) + dyStop * dyStop / ((stopRadius.y + ball.radius - 10) * (stopRadius.y + ball.radius - 10)) < 1 && Math.abs(ball.vy) < 18) { // Win! LK.setScore(LK.getScore() + 1); scoreTxt.setText(LK.getScore() + ''); LK.effects.flashObject(stop, 0x00ff00, 600); canLaunch = false; ball.launched = false; ball.resting = true; // Randomize stop X position (avoid edges) var stopMargin = 180 + stopRadius.x; stop.x = stopMargin + Math.random() * (2048 - 2 * stopMargin); // Next level after short delay if (winTimeout) LK.clearTimeout(winTimeout); winTimeout = LK.setTimeout(function () { nextLevel(); }, 1200); return; } } else { // Crash: game over LK.effects.flashScreen(0xff0000, 800); LK.showGameOver(); return; } } } } if (ball.bounceCooldown > 0) ball.bounceCooldown--; // Ball-stop (target) collision if (ball.launched && !ball.resting) { var dx = ball.x - stop.x; var dy = ball.y - stop.y; // Use ellipse collision: (dx/a)^2 + (dy/b)^2 < 1 if (dx * dx / ((stopRadius.x + ball.radius - 10) * (stopRadius.x + ball.radius - 10)) + dy * dy / ((stopRadius.y + ball.radius - 10) * (stopRadius.y + ball.radius - 10)) < 1 && Math.abs(ball.vy) < 18) { // Win! LK.setScore(LK.getScore() + 1); scoreTxt.setText(LK.getScore() + ''); LK.effects.flashObject(stop, 0x00ff00, 600); canLaunch = false; ball.launched = false; ball.resting = true; // Randomize stop X position (avoid edges) var margin = 180 + stopRadius.x; stop.x = margin + Math.random() * (2048 - 2 * margin); // Next level after short delay if (winTimeout) LK.clearTimeout(winTimeout); winTimeout = LK.setTimeout(function () { nextLevel(); }, 1200); } } // Vehicle spawn logic if (LK.ticks % Math.max(38 - Math.min(level * 2, 30), 10) === 0) { spawnVehicle(); } }; // --- Slingshot controls --- function getDragVector() { var dx = dragCurrent.x - dragStart.x; var dy = dragCurrent.y - dragStart.y; return { dx: dx, dy: dy }; } function updateSlingshotBand() { if (!isDragging) { slingshotBand.visible = false; return; } var vec = getDragVector(); var len = Math.sqrt(vec.dx * vec.dx + vec.dy * vec.dy); var maxLen = 420; if (len > maxLen) { vec.dx *= maxLen / len; vec.dy *= maxLen / len; len = maxLen; } // Band position: from ball center to drag point slingshotBand.visible = true; slingshotBand.x = ball.x; slingshotBand.y = ball.y; slingshotBand.height = len; slingshotBand.rotation = Math.atan2(vec.dy, vec.dx) + Math.PI / 2; } // --- Input handlers --- game.down = function (x, y, obj) { // Only allow drag if ball is at rest and not in win state if (!canLaunch || !ball.resting) return; // Only allow drag if touch is near ball var dx = x - ball.x; var dy = y - ball.y; if (dx * dx + dy * dy < ball.radius * ball.radius * 2.2) { isDragging = true; dragStart.x = ball.x; dragStart.y = ball.y; dragCurrent.x = x; dragCurrent.y = y; updateSlingshotBand(); } }; game.move = function (x, y, obj) { if (!isDragging) return; dragCurrent.x = x; dragCurrent.y = y; updateSlingshotBand(); }; game.up = function (x, y, obj) { if (!isDragging) return; isDragging = false; slingshotBand.visible = false; // Launch! var vec = getDragVector(); var len = Math.sqrt(vec.dx * vec.dx + vec.dy * vec.dy); if (len < 60) return; // too short var maxLen = 420; if (len > maxLen) { vec.dx *= maxLen / len; vec.dy *= maxLen / len; len = maxLen; } // Set velocity (scaled) ball.vx = -vec.dx * 0.055; ball.vy = -vec.dy * 0.055; ball.launched = true; ball.resting = false; canLaunch = false; }; // --- Game over/win handling --- LK.on('gameover', function () { resetGameState(); }); LK.on('youwin', function () { resetGameState(); });
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Ball (player) class
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballGfx = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.7,
scaleY: 1.7
});
self.radius = ballGfx.width / 2;
self.vx = 0;
self.vy = 0;
self.launched = false;
self.resting = true; // true if waiting for launch
self.bounceCooldown = 0; // prevent double bounces
self.update = function () {
if (self.launched) {
// Gravity
self.vy += 0.1344;
// Move
self.x += self.vx;
self.y += self.vy;
// Friction (air)
self.vx *= 0.995;
// Clamp to game area
if (self.x < self.radius) self.x = self.radius;
if (self.x > 2048 - self.radius) self.x = 2048 - self.radius;
// If falls below screen, game over
if (self.y > 2732 + 200) {
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
}
}
};
// Reset ball to start
self.reset = function () {
self.x = 2048 / 2;
self.y = typeof ballStartY !== "undefined" ? ballStartY : 2732 - 180;
self.vx = 0;
self.vy = 0;
self.launched = false;
self.resting = true;
self.bounceCooldown = 0;
};
// Bounce up (from bouncy car)
self.bounce = function (strength) {
self.vy = -Math.abs(strength || 38 + Math.random() * 8);
self.launched = true;
self.resting = false;
self.bounceCooldown = 10;
};
return self;
});
// BouncyCar (bouncy vehicle) class
var BouncyCar = Container.expand(function () {
var self = Container.call(this);
var bouncyGfx = self.attachAsset('bouncycar', {
anchorX: 0.5,
anchorY: 0.5
});
self.scale.set(1.2, 1.2);
self.width = bouncyGfx.width * 1.2;
self.height = bouncyGfx.height * 1.2;
self.speed = 0;
self.lane = 0;
self.type = 'bouncycar';
self.bouncePhase = Math.random() * Math.PI * 2;
self.update = function () {
self.x += self.speed;
// Bouncy up-down
self.y0 = self.y0 || self.y;
self.y = self.y0 + Math.sin(LK.ticks / 12 + self.bouncePhase) * 38;
if (self.speed > 0 && self.x > 2048 + 200) self.destroyed = true;
if (self.speed < 0 && self.x < -200) self.destroyed = true;
};
return self;
});
// Car (normal) class
var Car = Container.expand(function () {
var self = Container.call(this);
var carGfx = self.attachAsset('car', {
anchorX: 0.5,
anchorY: 0.5
});
self.scale.set(1.2, 1.2);
self.width = carGfx.width * 1.2;
self.height = carGfx.height * 1.2;
self.speed = 0;
self.lane = 0;
self.type = 'car';
self.update = function () {
self.x += self.speed;
// Remove if out of screen
if (self.speed > 0 && self.x > 2048 + 200) self.destroyed = true;
if (self.speed < 0 && self.x < -200) self.destroyed = true;
};
return self;
});
// Truck (long vehicle) class
var Truck = Container.expand(function () {
var self = Container.call(this);
var truckGfx = self.attachAsset('truck', {
anchorX: 0.5,
anchorY: 0.5
});
self.scale.set(1.2, 1.2);
self.width = truckGfx.width * 1.2;
self.height = truckGfx.height * 1.2;
self.speed = 0;
self.lane = 0;
self.type = 'truck';
self.update = function () {
self.x += self.speed;
if (self.speed > 0 && self.x > 2048 + 300) self.destroyed = true;
if (self.speed < 0 && self.x < -300) self.destroyed = true;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// --- Game variables ---
// Ball (player)
// Stop (target)
// Road background
// Lane divider
// Car
// Truck
// Bouncy car
// Slingshot band
var lanes = [0, 1, 2, 3, 4, 5];
var laneYs = [];
var laneCount = 6;
var verticalOffset = -200; // Move everything up by 200px
var roadTop = 600 + verticalOffset;
var roadHeight = 1536;
var laneGap = roadHeight / laneCount;
for (var i = 0; i < laneCount; ++i) {
laneYs[i] = roadTop + laneGap / 2 + i * laneGap;
}
// Place stop (target) at the top of the road, centered horizontally
var stopY = roadTop - 100;
var stopX = 2048 / 2;
// Update stop trigger area to match new stop (target) width
var stopRadius = {
x: 158,
// half of stop image width (316/2), matches texture
y: 90
}; // x: horizontal radius, y: vertical radius
var level = 1;
var vehicles = [];
var ball = null;
var stop = null;
var slingshotBand = null;
var isDragging = false;
// Place the ball higher up (not at the very bottom)
var ballStartY = roadTop + roadHeight + 120;
var dragStart = {
x: 0,
y: 0
};
var dragCurrent = {
x: 0,
y: 0
};
var launchPower = 0;
var launchAngle = 0;
var canLaunch = true;
var scoreTxt = null;
var levelTxt = null;
var winTimeout = null;
// --- Draw road and lanes ---
var road = LK.getAsset('road', {
x: 0,
y: roadTop,
width: 2048,
height: roadHeight
});
game.addChild(road);
// Lane dividers
for (var i = 1; i < laneCount; ++i) {
var divider = LK.getAsset('divider', {
x: 0,
y: roadTop + i * laneGap - 12,
width: 2048,
height: 24
});
game.addChild(divider);
}
// --- Stop (target) ---
stop = LK.getAsset('stop', {
anchorX: 0.5,
anchorY: 0.5,
x: stopX,
y: stopY,
scaleX: 1.2,
scaleY: 1.2
});
game.addChild(stop);
// --- Ball (player) ---
ball = new Ball();
ball.scale.set(1.2, 1.2);
game.addChild(ball);
ball.reset();
// --- Slingshot band (visual) ---
slingshotBand = LK.getAsset('band', {
anchorX: 0.5,
anchorY: 1,
x: ball.x,
y: ball.y
});
slingshotBand.visible = false;
game.addChild(slingshotBand);
// --- GUI: Score and Level ---
scoreTxt = new Text2('0', {
size: 110,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
levelTxt = new Text2('1', {
size: 70,
fill: "#fff"
});
levelTxt.anchor.set(0.5, 0);
LK.gui.topRight.addChild(levelTxt);
// --- Game state ---
function resetGameState() {
// Remove vehicles
for (var i = 0; i < vehicles.length; ++i) {
vehicles[i].destroy();
}
vehicles = [];
// Reset ball
ball.reset();
// Reset slingshot
slingshotBand.visible = false;
isDragging = false;
canLaunch = true;
// Reset score/level
LK.setScore(0);
scoreTxt.setText('0');
level = 1;
levelTxt.setText('1');
}
resetGameState();
// --- Vehicle spawner ---
function spawnVehicle() {
// Randomly pick lane
var lane = Math.floor(Math.random() * laneCount);
var y = laneYs[lane];
// Adjusted spawn probabilities: All vehicle types reduced by 5%
// Old: Car 60%, Truck 25%, BouncyCar 15%
// New: Car 55%, Truck 20%, BouncyCar 10%
var t = Math.random();
var v, dir, speed;
if (t < 0.55) {
v = new Car();
speed = (12 + level * 1.2 + Math.random() * 2) * 0.5;
} else if (t < 0.75) {
v = new Truck();
speed = (8 + level * 1.1 + Math.random() * 2) * 0.5 * 0.9; // Truck speed reduced by 10%
} else {
v = new BouncyCar();
speed = (10 + level * 1.3 + Math.random() * 2) * 0.5 * 1.1; // BouncyCar speed increased by 10%
}
// Direction: even lanes left->right, odd right->left
dir = lane % 2 === 0 ? 1 : -1;
v.lane = lane;
v.y = y;
v.y0 = y;
v.speed = speed * dir;
if (dir > 0) {
v.x = -200;
} else {
v.x = 2048 + 200;
// Flip the vehicle horizontally if moving right-to-left
if (v.children && v.children.length > 0 && v.children[0]) {
v.children[0].scale.x = -1;
}
}
vehicles.push(v);
game.addChild(v);
}
// --- Level progression ---
function nextLevel() {
level += 1;
levelTxt.setText(level + '');
// Remove all vehicles
for (var i = 0; i < vehicles.length; ++i) vehicles[i].destroy();
vehicles = [];
// Reset ball
ball.reset();
canLaunch = true;
slingshotBand.visible = false;
}
// --- Game update ---
game.update = function () {
// Ball update
ball.update();
// Vehicles update
// --- Make vehicles follow the speed of the vehicle in front if they are too close in the same lane ---
for (var i = vehicles.length - 1; i >= 0; --i) {
var v = vehicles[i];
// Find the closest vehicle ahead in the same lane and direction
var minDist = Infinity;
var frontVehicle = null;
for (var j = 0; j < vehicles.length; ++j) {
if (i === j) continue;
var v2 = vehicles[j];
if (v2.lane !== v.lane) continue;
// Only consider vehicles in the same direction
if (v.speed > 0 && v2.x > v.x || v.speed < 0 && v2.x < v.x) {
var dist = Math.abs(v2.x - v.x);
if (dist < minDist) {
minDist = dist;
frontVehicle = v2;
}
}
}
// If too close, match speed to the front vehicle
if (frontVehicle) {
// Use the larger of the two widths for minimum gap
var minGap = Math.max(v.width, frontVehicle.width) * 0.85;
if (minDist < minGap) {
v.speed = frontVehicle.speed;
// Snap position to avoid overlap
if (v.speed > 0) {
v.x = Math.min(v.x, frontVehicle.x - minGap + 1);
} else {
v.x = Math.max(v.x, frontVehicle.x + minGap - 1);
}
}
}
v.update();
// Remove destroyed
if (v.destroyed) {
v.destroy();
vehicles.splice(i, 1);
continue;
}
}
// Ball-vehicle collision
if (ball.launched && ball.bounceCooldown <= 0) {
for (var i = 0; i < vehicles.length; ++i) {
var v = vehicles[i];
// AABB collision
var dx = Math.abs(ball.x - v.x);
var dy = Math.abs(ball.y - v.y);
var overlapX = dx < ball.radius + v.width / 2 - 10;
var overlapY = dy < ball.radius + v.height / 2 - 10;
if (overlapX && overlapY) {
if (v.type === 'bouncycar') {
// Calculate the incoming angle and speed
var incomingAngle = Math.atan2(ball.vy, ball.vx);
var speed = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy);
// Reverse the angle (go in the exact opposite direction)
var baseBounceAngle = incomingAngle + Math.PI;
// Add up to ±10% randomness to the angle
var randomAngle = (Math.random() - 0.5) * (Math.PI * 0.2); // ±0.1π ≈ ±18°
var bounceAngle = baseBounceAngle + randomAngle;
// Bounce speed is 120% of the incoming speed
var bounceSpeed = speed * 1.2;
// Set new velocity
ball.vx = Math.cos(bounceAngle) * bounceSpeed;
ball.vy = Math.sin(bounceAngle) * bounceSpeed;
ball.launched = true;
ball.resting = false;
ball.bounceCooldown = 12;
LK.effects.flashObject(v, 0xffff00, 400);
// Do NOT randomize X anymore, keep physics consistent
// After bounce, check if ball is immediately on stop (target)
var dxStop = ball.x - stop.x;
var dyStop = ball.y - stop.y;
// Use ellipse collision: (dx/a)^2 + (dy/b)^2 < 1
if (dxStop * dxStop / ((stopRadius.x + ball.radius - 10) * (stopRadius.x + ball.radius - 10)) + dyStop * dyStop / ((stopRadius.y + ball.radius - 10) * (stopRadius.y + ball.radius - 10)) < 1 && Math.abs(ball.vy) < 18) {
// Win!
LK.setScore(LK.getScore() + 1);
scoreTxt.setText(LK.getScore() + '');
LK.effects.flashObject(stop, 0x00ff00, 600);
canLaunch = false;
ball.launched = false;
ball.resting = true;
// Randomize stop X position (avoid edges)
var stopMargin = 180 + stopRadius.x;
stop.x = stopMargin + Math.random() * (2048 - 2 * stopMargin);
// Next level after short delay
if (winTimeout) LK.clearTimeout(winTimeout);
winTimeout = LK.setTimeout(function () {
nextLevel();
}, 1200);
return;
}
} else {
// Crash: game over
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
}
}
}
}
if (ball.bounceCooldown > 0) ball.bounceCooldown--;
// Ball-stop (target) collision
if (ball.launched && !ball.resting) {
var dx = ball.x - stop.x;
var dy = ball.y - stop.y;
// Use ellipse collision: (dx/a)^2 + (dy/b)^2 < 1
if (dx * dx / ((stopRadius.x + ball.radius - 10) * (stopRadius.x + ball.radius - 10)) + dy * dy / ((stopRadius.y + ball.radius - 10) * (stopRadius.y + ball.radius - 10)) < 1 && Math.abs(ball.vy) < 18) {
// Win!
LK.setScore(LK.getScore() + 1);
scoreTxt.setText(LK.getScore() + '');
LK.effects.flashObject(stop, 0x00ff00, 600);
canLaunch = false;
ball.launched = false;
ball.resting = true;
// Randomize stop X position (avoid edges)
var margin = 180 + stopRadius.x;
stop.x = margin + Math.random() * (2048 - 2 * margin);
// Next level after short delay
if (winTimeout) LK.clearTimeout(winTimeout);
winTimeout = LK.setTimeout(function () {
nextLevel();
}, 1200);
}
}
// Vehicle spawn logic
if (LK.ticks % Math.max(38 - Math.min(level * 2, 30), 10) === 0) {
spawnVehicle();
}
};
// --- Slingshot controls ---
function getDragVector() {
var dx = dragCurrent.x - dragStart.x;
var dy = dragCurrent.y - dragStart.y;
return {
dx: dx,
dy: dy
};
}
function updateSlingshotBand() {
if (!isDragging) {
slingshotBand.visible = false;
return;
}
var vec = getDragVector();
var len = Math.sqrt(vec.dx * vec.dx + vec.dy * vec.dy);
var maxLen = 420;
if (len > maxLen) {
vec.dx *= maxLen / len;
vec.dy *= maxLen / len;
len = maxLen;
}
// Band position: from ball center to drag point
slingshotBand.visible = true;
slingshotBand.x = ball.x;
slingshotBand.y = ball.y;
slingshotBand.height = len;
slingshotBand.rotation = Math.atan2(vec.dy, vec.dx) + Math.PI / 2;
}
// --- Input handlers ---
game.down = function (x, y, obj) {
// Only allow drag if ball is at rest and not in win state
if (!canLaunch || !ball.resting) return;
// Only allow drag if touch is near ball
var dx = x - ball.x;
var dy = y - ball.y;
if (dx * dx + dy * dy < ball.radius * ball.radius * 2.2) {
isDragging = true;
dragStart.x = ball.x;
dragStart.y = ball.y;
dragCurrent.x = x;
dragCurrent.y = y;
updateSlingshotBand();
}
};
game.move = function (x, y, obj) {
if (!isDragging) return;
dragCurrent.x = x;
dragCurrent.y = y;
updateSlingshotBand();
};
game.up = function (x, y, obj) {
if (!isDragging) return;
isDragging = false;
slingshotBand.visible = false;
// Launch!
var vec = getDragVector();
var len = Math.sqrt(vec.dx * vec.dx + vec.dy * vec.dy);
if (len < 60) return; // too short
var maxLen = 420;
if (len > maxLen) {
vec.dx *= maxLen / len;
vec.dy *= maxLen / len;
len = maxLen;
}
// Set velocity (scaled)
ball.vx = -vec.dx * 0.055;
ball.vy = -vec.dy * 0.055;
ball.launched = true;
ball.resting = false;
canLaunch = false;
};
// --- Game over/win handling ---
LK.on('gameover', function () {
resetGameState();
});
LK.on('youwin', function () {
resetGameState();
});
bir palyanço kafasının arka üsten görünümü çizgi film stili. In-Game asset. 2d. High contrast. No shadows
ileriyi gösteren . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
zıpzıp taşıyan kamyonet üsten görünüm. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
lüks sarı araba üsten görünüş. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
birebir aynı tıs tekerlerin düzeltilmesi gerek sadece
otobuş durağı. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat