User prompt
increase more little speed
User prompt
make speed a little fast of launcher
User prompt
make changes because the ball is not launching
Code edit (1 edits merged)
Please save this source code
User prompt
Bubble Blast: Cosmic Bubble Shooter
Initial prompt
Develop Bubble Blast, a professional 2D bubble shooter game using Unity or Godot, targeting PC and mobile platforms. Create addictive match-3 gameplay with smooth aiming, shooting, and popping mechanics, incorporating standard and special bubbles (bomb, rainbow, lightning). Design a vibrant cosmic-themed art style with modern 2D visuals, parallax backgrounds, and dynamic animations. Implement 100+ campaign levels, an endless mode, daily challenges, and a shop system for power-ups and cosmetics. Add global leaderboards, cloud saves, and monetization via in-app purchases and opt-in ads. Optimize for 60 FPS performance on low-to-mid-end devices, ensuring intuitive controls and engaging audio. Deliver a polished, addictive puzzle game with high replayability and regular content updates
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Bubble = Container.expand(function (color, type) {
var self = Container.call(this);
self.color = color || 'red';
self.type = type || 'normal';
self.gridX = -1;
self.gridY = -1;
self.isMoving = false;
self.velocityX = 0;
self.velocityY = 0;
self.isMarkedForRemoval = false;
var assetName = 'bubble_' + self.color;
if (self.type === 'bomb') assetName = 'bubble_bomb';
if (self.type === 'rainbow') assetName = 'bubble_rainbow';
var bubbleGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
if (self.isMoving) {
self.x += self.velocityX;
self.y += self.velocityY;
// Bounce off walls
if (self.x <= 40 && self.velocityX < 0) {
self.velocityX = -self.velocityX;
}
if (self.x >= 2008 && self.velocityX > 0) {
self.velocityX = -self.velocityX;
}
}
};
return self;
});
var BubbleGrid = Container.expand(function () {
var self = Container.call(this);
self.grid = [];
self.gridWidth = 13;
self.gridHeight = 20;
self.bubbleSize = 80;
self.offsetX = 200;
self.offsetY = 200;
self.initializeGrid = function () {
for (var y = 0; y < self.gridHeight; y++) {
self.grid[y] = [];
for (var x = 0; x < self.gridWidth; x++) {
self.grid[y][x] = null;
}
}
// Create initial bubble pattern
var colors = ['red', 'blue', 'green', 'yellow', 'purple', 'orange'];
for (var y = 0; y < 8; y++) {
for (var x = 0; x < self.gridWidth; x++) {
if (y % 2 === 1 && x === self.gridWidth - 1) continue; // Offset rows
var randomColor = colors[Math.floor(Math.random() * colors.length)];
var bubble = new Bubble(randomColor);
self.addBubbleToGrid(bubble, x, y);
}
}
};
self.addBubbleToGrid = function (bubble, gridX, gridY) {
if (gridY < 0 || gridY >= self.gridHeight || gridX < 0 || gridX >= self.gridWidth) return false;
if (self.grid[gridY][gridX] !== null) return false;
bubble.gridX = gridX;
bubble.gridY = gridY;
self.grid[gridY][gridX] = bubble;
var offsetForRow = gridY % 2 * (self.bubbleSize / 2);
bubble.x = self.offsetX + gridX * self.bubbleSize + offsetForRow;
bubble.y = self.offsetY + gridY * self.bubbleSize * 0.85;
self.addChild(bubble);
return true;
};
self.getGridPosition = function (worldX, worldY) {
var localX = worldX - self.offsetX;
var localY = worldY - self.offsetY;
var gridY = Math.round(localY / (self.bubbleSize * 0.85));
var offsetForRow = gridY % 2 * (self.bubbleSize / 2);
var gridX = Math.round((localX - offsetForRow) / self.bubbleSize);
return {
x: gridX,
y: gridY
};
};
self.findMatchingBubbles = function (startX, startY, targetColor) {
var visited = [];
var matches = [];
function floodFill(x, y) {
if (y < 0 || y >= self.gridHeight || x < 0 || x >= self.gridWidth) return;
if (visited[y * self.gridWidth + x]) return;
if (!self.grid[y][x]) return;
if (self.grid[y][x].color !== targetColor && self.grid[y][x].type !== 'rainbow') return;
visited[y * self.gridWidth + x] = true;
matches.push({
x: x,
y: y,
bubble: self.grid[y][x]
});
// Check neighbors (hexagonal grid)
var neighbors = self.getNeighbors(x, y);
for (var i = 0; i < neighbors.length; i++) {
floodFill(neighbors[i].x, neighbors[i].y);
}
}
floodFill(startX, startY);
return matches;
};
self.getNeighbors = function (x, y) {
var neighbors = [];
var isOddRow = y % 2 === 1;
// Standard neighbors
if (x > 0) neighbors.push({
x: x - 1,
y: y
});
if (x < self.gridWidth - 1) neighbors.push({
x: x + 1,
y: y
});
// Upper neighbors
if (y > 0) {
neighbors.push({
x: x,
y: y - 1
});
if (isOddRow) {
if (x < self.gridWidth - 1) neighbors.push({
x: x + 1,
y: y - 1
});
} else {
if (x > 0) neighbors.push({
x: x - 1,
y: y - 1
});
}
}
// Lower neighbors
if (y < self.gridHeight - 1) {
neighbors.push({
x: x,
y: y + 1
});
if (isOddRow) {
if (x < self.gridWidth - 1) neighbors.push({
x: x + 1,
y: y + 1
});
} else {
if (x > 0) neighbors.push({
x: x - 1,
y: y + 1
});
}
}
return neighbors;
};
self.removeBubbles = function (bubblesToRemove) {
for (var i = 0; i < bubblesToRemove.length; i++) {
var bubble = bubblesToRemove[i].bubble;
self.grid[bubble.gridY][bubble.gridX] = null;
tween(bubble, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 200,
onFinish: function onFinish() {
if (bubble.parent) {
bubble.parent.removeChild(bubble);
}
}
});
}
LK.getSound('bubble_pop').play();
};
self.findFloatingBubbles = function () {
var connected = [];
var floating = [];
// Mark all bubbles connected to top row
function markConnected(x, y) {
if (y < 0 || y >= self.gridHeight || x < 0 || x >= self.gridWidth) return;
if (connected[y * self.gridWidth + x]) return;
if (!self.grid[y][x]) return;
connected[y * self.gridWidth + x] = true;
var neighbors = self.getNeighbors(x, y);
for (var i = 0; i < neighbors.length; i++) {
markConnected(neighbors[i].x, neighbors[i].y);
}
}
// Start from top row
for (var x = 0; x < self.gridWidth; x++) {
if (self.grid[0][x]) {
markConnected(x, 0);
}
}
// Find floating bubbles
for (var y = 0; y < self.gridHeight; y++) {
for (var x = 0; x < self.gridWidth; x++) {
if (self.grid[y][x] && !connected[y * self.gridWidth + x]) {
floating.push({
x: x,
y: y,
bubble: self.grid[y][x]
});
}
}
}
return floating;
};
self.checkLevelComplete = function () {
for (var y = 0; y < self.gridHeight; y++) {
for (var x = 0; x < self.gridWidth; x++) {
if (self.grid[y][x]) return false;
}
}
return true;
};
return self;
});
var Launcher = Container.expand(function () {
var self = Container.call(this);
self.currentBubble = null;
self.nextBubble = null;
self.aimAngle = -Math.PI / 2;
self.trajectoryDots = [];
self.isAiming = false;
var launcherGraphics = self.attachAsset('launcher', {
anchorX: 0.5,
anchorY: 0.5
});
self.generateNextBubble = function () {
var colors = ['red', 'blue', 'green', 'yellow', 'purple', 'orange'];
var randomColor = colors[Math.floor(Math.random() * colors.length)];
return new Bubble(randomColor);
};
self.loadNextBubble = function () {
if (self.currentBubble) {
self.removeChild(self.currentBubble);
}
self.currentBubble = self.nextBubble || self.generateNextBubble();
self.nextBubble = self.generateNextBubble();
if (self.currentBubble) {
self.addChild(self.currentBubble);
self.currentBubble.x = 0;
self.currentBubble.y = -40;
}
};
self.createTrajectoryDots = function () {
for (var i = 0; i < 20; i++) {
var dot = LK.getAsset('trajectory_dot', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
self.trajectoryDots.push(dot);
self.addChild(dot);
}
};
self.updateTrajectory = function () {
if (!self.isAiming || !self.currentBubble) {
for (var i = 0; i < self.trajectoryDots.length; i++) {
self.trajectoryDots[i].alpha = 0;
}
return;
}
var startX = self.x;
var startY = self.y - 40;
var velocityX = Math.cos(self.aimAngle) * 20;
var velocityY = Math.sin(self.aimAngle) * 20;
var simX = startX;
var simY = startY;
var simVelX = velocityX;
var simVelY = velocityY;
for (var i = 0; i < self.trajectoryDots.length; i++) {
simX += simVelX;
simY += simVelY;
// Bounce off walls
if (simX <= 40 && simVelX < 0) {
simVelX = -simVelX;
}
if (simX >= 2008 && simVelX > 0) {
simVelX = -simVelX;
}
if (simY < 200) break;
self.trajectoryDots[i].x = simX - self.x;
self.trajectoryDots[i].y = simY - self.y;
self.trajectoryDots[i].alpha = 1 - i / self.trajectoryDots.length;
}
};
self.shoot = function () {
if (!self.currentBubble) return null;
var shootingBubble = self.currentBubble;
self.removeChild(shootingBubble);
shootingBubble.isMoving = true;
shootingBubble.velocityX = Math.cos(self.aimAngle) * 20;
shootingBubble.velocityY = Math.sin(self.aimAngle) * 20;
self.currentBubble = null;
self.isAiming = false;
LK.getSound('bubble_shoot').play();
return shootingBubble;
};
return self;
});
var StarField = Container.expand(function () {
var self = Container.call(this);
self.stars = [];
self.numStars = 100;
self.createStars = function () {
for (var i = 0; i < self.numStars; i++) {
var star = LK.getAsset('star', {
anchorX: 0.5,
anchorY: 0.5,
alpha: Math.random() * 0.8 + 0.2
});
star.x = Math.random() * 2048;
star.y = Math.random() * 2732;
star.speed = Math.random() * 2 + 0.5;
self.addChild(star);
self.stars.push(star);
}
};
self.update = function () {
for (var i = 0; i < self.stars.length; i++) {
var star = self.stars[i];
star.y += star.speed;
if (star.y > 2732) {
star.y = -10;
star.x = Math.random() * 2048;
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a0d2e
});
/****
* Game Code
****/
var starField = game.addChild(new StarField());
starField.createStars();
var bubbleGrid = game.addChild(new BubbleGrid());
bubbleGrid.initializeGrid();
var launcher = game.addChild(new Launcher());
launcher.x = 1024;
launcher.y = 2600;
launcher.createTrajectoryDots();
launcher.loadNextBubble();
var currentShot = null;
var gameState = 'playing'; // 'playing', 'gameOver', 'levelComplete'
var scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var levelTxt = new Text2('Level 1', {
size: 50,
fill: 0xFFFFFF
});
levelTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(levelTxt);
function handleAiming(x, y) {
if (gameState !== 'playing' || currentShot) return;
var dx = x - launcher.x;
var dy = y - launcher.y;
if (dy < 0) {
launcher.aimAngle = Math.atan2(dy, dx);
launcher.isAiming = true;
launcher.updateTrajectory();
}
}
function handleShoot() {
if (gameState !== 'playing' || currentShot || !launcher.currentBubble) return;
if (launcher.isAiming) {
currentShot = launcher.shoot();
if (currentShot) {
currentShot.x = launcher.x;
currentShot.y = launcher.y - 40;
game.addChild(currentShot);
}
}
}
game.move = function (x, y, obj) {
handleAiming(x, y);
};
game.down = function (x, y, obj) {
handleAiming(x, y);
};
game.up = function (x, y, obj) {
handleShoot();
};
game.update = function () {
starField.update();
if (currentShot) {
// Check collision with existing bubbles
var shotPos = bubbleGrid.getGridPosition(currentShot.x, currentShot.y);
// Check if shot reaches top boundary
if (currentShot.y <= bubbleGrid.offsetY) {
var validPos = false;
// Find nearest valid grid position
for (var dy = 0; dy < 3; dy++) {
for (var dx = -1; dx <= 1; dx++) {
var testX = shotPos.x + dx;
var testY = shotPos.y + dy;
if (bubbleGrid.addBubbleToGrid(currentShot, testX, testY)) {
validPos = true;
break;
}
}
if (validPos) break;
}
if (validPos) {
currentShot.isMoving = false;
// Check for matches
var matches = bubbleGrid.findMatchingBubbles(currentShot.gridX, currentShot.gridY, currentShot.color);
if (matches.length >= 3) {
bubbleGrid.removeBubbles(matches);
LK.setScore(LK.getScore() + matches.length * 10);
// Check for floating bubbles
var floating = bubbleGrid.findFloatingBubbles();
if (floating.length > 0) {
bubbleGrid.removeBubbles(floating);
LK.setScore(LK.getScore() + floating.length * 5);
}
}
// Check level complete
if (bubbleGrid.checkLevelComplete()) {
gameState = 'levelComplete';
LK.getSound('level_complete').play();
LK.showYouWin();
}
scoreTxt.setText('Score: ' + LK.getScore());
} else {
// Game over - couldn't place bubble
LK.showGameOver();
}
currentShot = null;
launcher.loadNextBubble();
} else {
// Check collision with grid bubbles
for (var y = 0; y < bubbleGrid.gridHeight; y++) {
for (var x = 0; x < bubbleGrid.gridWidth; x++) {
var gridBubble = bubbleGrid.grid[y][x];
if (gridBubble) {
var dx = currentShot.x - gridBubble.x;
var dy = currentShot.y - gridBubble.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 70) {
// Find nearest empty spot
var neighbors = bubbleGrid.getNeighbors(x, y);
var placed = false;
for (var i = 0; i < neighbors.length; i++) {
var neighbor = neighbors[i];
if (bubbleGrid.grid[neighbor.y] && bubbleGrid.grid[neighbor.y][neighbor.x] === null) {
if (bubbleGrid.addBubbleToGrid(currentShot, neighbor.x, neighbor.y)) {
placed = true;
break;
}
}
}
if (placed) {
currentShot.isMoving = false;
// Check for matches
var matches = bubbleGrid.findMatchingBubbles(currentShot.gridX, currentShot.gridY, currentShot.color);
if (matches.length >= 3) {
bubbleGrid.removeBubbles(matches);
LK.setScore(LK.getScore() + matches.length * 10);
// Check for floating bubbles
var floating = bubbleGrid.findFloatingBubbles();
if (floating.length > 0) {
bubbleGrid.removeBubbles(floating);
LK.setScore(LK.getScore() + floating.length * 5);
}
}
// Check level complete
if (bubbleGrid.checkLevelComplete()) {
gameState = 'levelComplete';
LK.getSound('level_complete').play();
LK.showYouWin();
}
scoreTxt.setText('Score: ' + LK.getScore());
currentShot = null;
launcher.loadNextBubble();
return;
}
}
}
}
}
}
}
launcher.updateTrajectory();
}; ===================================================================
--- original.js
+++ change.js
@@ -270,10 +270,10 @@
return;
}
var startX = self.x;
var startY = self.y - 40;
- var velocityX = Math.cos(self.aimAngle) * 15;
- var velocityY = Math.sin(self.aimAngle) * 15;
+ var velocityX = Math.cos(self.aimAngle) * 20;
+ var velocityY = Math.sin(self.aimAngle) * 20;
var simX = startX;
var simY = startY;
var simVelX = velocityX;
var simVelY = velocityY;
@@ -297,10 +297,10 @@
if (!self.currentBubble) return null;
var shootingBubble = self.currentBubble;
self.removeChild(shootingBubble);
shootingBubble.isMoving = true;
- shootingBubble.velocityX = Math.cos(self.aimAngle) * 15;
- shootingBubble.velocityY = Math.sin(self.aimAngle) * 15;
+ shootingBubble.velocityX = Math.cos(self.aimAngle) * 20;
+ shootingBubble.velocityY = Math.sin(self.aimAngle) * 20;
self.currentBubble = null;
self.isAiming = false;
LK.getSound('bubble_shoot').play();
return shootingBubble;