Code edit (15 edits merged)
Please save this source code
User prompt
remove the random color logic in gateManager; keep only the key<=>color logic
User prompt
now make gates color depend on their key
User prompt
when updating Gate, update the relative y position of boundingBox relatively to the scale ; it should be at y=2450 when scale is 1.0
Code edit (1 edits merged)
Please save this source code
User prompt
add a boundingBox to gates with size 300x30
User prompt
in GateManager, when spawning a gate, set it's corresponding key from the song; then play this key when intersecting the ball
User prompt
set debug mode on
User prompt
in debug mode, add a button at top right when pressed play all keys sounds from 0 to 14 with 600 ms delay
User prompt
when ball touches a gate, play corresponding key sound
Code edit (1 edits merged)
Please save this source code
Code edit (3 edits merged)
Please save this source code
User prompt
I've imported a song in json format; adapt the GateManager to span gates at the same rythm as songList[0] songNotes
Code edit (3 edits merged)
Please save this source code
User prompt
set debug mode false
User prompt
Ok. One point : Cubes are disabled/hidden yet because it's too complex to make the match the background virtual "movement". We migh instead use colored sections of tores as "gates"; the player will have to roll though the gate with the corresponding color. Implement the Gate class that will spawn 3 gates and animate them exactly like the tores
Code edit (5 edits merged)
Please save this source code
User prompt
add a global borderLimitAngle = Math.PI * 0.2; limit the move angle to Math.PI-borderLimitAngle
User prompt
allow movement from anywhere, do not require a tap on the sphere
User prompt
move the ball exactly like the sphere
User prompt
tint ball with currentColor
User prompt
add a ball class; then instanciate it at 1024,2000
User prompt
Set debug false
User prompt
Add facekit ↪💡 Consider importing and using the following plugins: @upit/facekit.v1
User prompt
Set debug true
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
/***********************************************************************************/
/******************************* UTILITY FUNCTIONS *********************************/
/***********************************************************************************/
var BackgroundManager = Container.expand(function () {
var self = Container.call(this);
// Create three background instances for smoother tunnel effect
self.bg0 = self.attachAsset('background01', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 2.4,
scaleY: 2.4,
alpha: 1
});
self.bg1 = self.attachAsset('background01', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 1.1,
scaleY: 1.1,
alpha: 1
});
self.bg2 = self.attachAsset('background01', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 0.5,
scaleY: 0.5,
alpha: 1
});
self.bg3 = self.attachAsset('background01', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 0.22,
scaleY: 0.22,
alpha: 1
});
self.bg4 = self.attachAsset('background01', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 0.11,
scaleY: 0.11,
alpha: 1
});
self.tore0 = self.attachAsset('tore', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 1.2,
scaleY: 1.2,
alpha: 1
});
self.tore1 = self.attachAsset('tore', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 0.6,
scaleY: 0.6,
alpha: 1
});
self.tore2 = self.attachAsset('tore', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 0.26,
scaleY: 0.26,
alpha: 1
});
self.tore3 = self.attachAsset('tore', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 0.12,
scaleY: 0.12,
alpha: 1
});
// Apply different tints in debug mode
if (isDebug) {
self.bg1.tint = 0xFF0000; // Red tint for first background
self.tore1.tint = 0x00FF00; // Green tint for first tore
self.bg2.tint = 0x00FFFF; // Cyan tint for second background
self.tore2.tint = 0xFF00FF; // Magenta tint for second tore
self.bg3.tint = 0xfff200; // Yellow tint for third background
}
// Animation properties
self.bgAnimationSpeed = globalSpeed / 1000; //0.002;
self.bgAnimationAcceleration = 2;
// Add tore assets between backgrounds for animation
self.backgrounds = [self.bg0, self.tore0, self.bg1, self.tore1, self.bg2, self.tore2, self.bg3, self.tore3, self.bg4];
//self.backgrounds = [self.bg0, self.bg1, self.bg2, self.bg3];
// Define initial scale for each background/torus (tore: 0.26, bg: 0.22)
//self.bgInitialScales = [0.22, 0.22, 0.22, 0.22, 0.22];
//self.bgInitialScales = [0, 0, 0, 0, 0];
//self.bgInitialScales = [0, 0, 0, 0, 0, 0, 0, 0, 0];
// Animation state: single startTime for all backgrounds/torus
self.bgAnimStartTime = Date.now();
// Update method - handle background/torus scale animation
self.update = function () {
var now = Date.now();
var elapsed = now - self.bgAnimStartTime;
var resetTriggered = false;
for (var i = 0; i < self.backgrounds.length; i++) {
var bg = self.backgrounds[i];
// Make the scale speed increase as the scale increases (e.g. exponential or quadratic growth)
//var scaleMultiplier = bg.scaleX * self.bgAnimationAcceleration;
//bg.scaleX += self.bgAnimationSpeed * scaleMultiplier;
bg.scaleX += self.bgAnimationSpeed * bg.scaleX;
bg.scaleY = bg.scaleX;
if (bg.scaleX > 3.0) {
bg.scaleX = 0.12; //self.bgInitialScales[i];
bg.scaleY = bg.scaleX;
}
bg.alpha = Math.min(1, bg.scaleX + 0.66);
}
};
return self;
});
// Initialize the game;
/***********************************************************************************/
/********************************** BALL CLASS *************************************/
/***********************************************************************************/
var Ball = Container.expand(function () {
var self = Container.call(this);
// Create and attach ball asset
var ballGraphics = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
tint: currentColor
});
// Initialize ball properties
self.speedX = 0;
self.speedY = 0;
// Update method to follow sphere's position
self.update = function () {
// Make ball follow sphere's exact position
if (sphere) {
self.x = sphere.x;
self.y = sphere.y;
}
};
return self;
});
/***********************************************************************************/
/********************************** CUBE CLASS ************************************/
/***********************************************************************************/
var Cube = Container.expand(function (wRatio, hRatio, dRatio) {
var self = Container.call(this);
wRatio = wRatio || 1;
hRatio = hRatio || 1;
dRatio = dRatio || 1;
self.z = 0;
self.baseSize = 100;
// Initialize cube faces using SimpleFace class
self.frontFace = new SimpleFace({
w: self.baseSize * wRatio,
h: self.baseSize * hRatio,
d: self.baseSize,
dx: 0,
dy: 0,
dz: 1 * dRatio,
rx: 0,
ry: 0,
rz: 0,
ti: 0xFFFFFF
});
self.backFace = new SimpleFace({
w: self.baseSize * wRatio,
h: self.baseSize * hRatio,
d: self.baseSize,
dx: 0,
dy: 0,
dz: -1 * dRatio,
rx: Math.PI,
ry: 0,
rz: 0,
ti: 0xFFFFFF
});
self.leftFace = new SimpleFace({
w: self.baseSize * dRatio,
h: self.baseSize * hRatio,
d: self.baseSize,
dx: -1 * wRatio / dRatio,
dy: 0,
dz: 0,
rx: 0,
ry: Math.PI / 2,
rz: 0,
ti: 0xFFFFFF
});
self.rightFace = new SimpleFace({
w: self.baseSize * dRatio,
h: self.baseSize * hRatio,
d: self.baseSize,
dx: 1 * wRatio / dRatio,
dy: 0,
dz: 0,
rx: 0,
ry: -Math.PI * 0.5,
rz: 0,
ti: 0xFFFFFF
});
self.topFace = new SimpleFace({
w: self.baseSize * wRatio,
h: self.baseSize * dRatio,
d: self.baseSize,
dx: 0,
dy: -1 * hRatio / dRatio,
dz: 0,
rx: -Math.PI / 2,
ry: 0,
rz: 0,
ti: 0xFFFFFF
});
self.bottomFace = new SimpleFace({
w: self.baseSize * wRatio,
h: self.baseSize * dRatio,
d: self.baseSize,
dx: 0,
dy: 1 * hRatio / dRatio,
dz: 0,
rx: Math.PI / 2,
ry: 0,
rz: 0,
ti: 0xFFFFFF
});
self.faces = [self.frontFace, self.backFace, self.leftFace, self.rightFace, self.topFace, self.bottomFace];
self.faces.forEach(function (face) {
self.addChild(face);
});
self.speedX = 0;
self.speedY = 0;
self.speedZ = 0;
// Rotate cube around its axes
self.rotate3D = function (angleX, angleY, angleZ) {
log("cube rotate3D ");
self.rotation = angleZ;
var zScaleFactor = 1 + self.z / 500;
self.faces.forEach(function (face) {
face.rotate3D(angleX, angleY, angleZ, zScaleFactor);
});
};
});
/***********************************************************************************/
/********************************** CUBE MANAGER CLASS *****************************/
/***********************************************************************************/
var CubeManager = Container.expand(function () {
var self = Container.call(this);
// Array to hold all managed cubes
self.cubes = [];
// Configuration for spawning
self.maxCubes = 3; // Maximum number of cubes
// Spawn all cubes at once
self.spawnAllCubes = function () {
// Calculate spacing for equal distribution
var screenWidth = 2048;
var cubeSpacing = screenWidth / (self.maxCubes + 1); // Divide screen into equal sections
var verticalCenter = 2732 / 2; // Vertical center of the screen
// Prepare color assignment: one cube gets currentColor, others get two different neon colors (not currentColor)
var colorIndices = [];
for (var c = 0; c < neonColors.length; c++) {
if (neonColors[c] !== currentColor) {
colorIndices.push(c);
}
}
// Shuffle colorIndices to randomize which two colors are picked for the other cubes
for (var s = colorIndices.length - 1; s > 0; s--) {
var j = Math.floor(Math.random() * (s + 1));
var temp = colorIndices[s];
colorIndices[s] = colorIndices[j];
colorIndices[j] = temp;
}
var cubeColors = [currentColor, neonColors[colorIndices[0]], neonColors[colorIndices[1]]];
// Shuffle cubeColors so currentColor is not always in the same position
for (var s2 = cubeColors.length - 1; s2 > 0; s2--) {
var j2 = Math.floor(Math.random() * (s2 + 1));
var temp2 = cubeColors[s2];
cubeColors[s2] = cubeColors[j2];
cubeColors[j2] = temp2;
}
for (var i = 0; i < self.maxCubes; i++) {
// Create a new cube with equal dimensions (true cube, not rectangle)
var cube = new Cube(1, 1, 1);
// Set position - equally distributed horizontally at vertical center
cube.x = cubeSpacing * (i + 1); // Distribute equally across the screen
cube.y = verticalCenter;
cube.z = 0; // Keep all cubes at same Z position
// Set speeds to zero so cubes don't move
cube.speedX = 0;
cube.speedY = 0;
cube.speedZ = 0;
// Set random initial rotation
cube.rotate3D(Math.random() * Math.PI * 2, Math.random() * Math.PI * 2, Math.random() * Math.PI * 2);
// Assign color: one cube gets currentColor, others get two different neon colors
var assignedTint = cubeColors[i % cubeColors.length];
cube.faces.forEach(function (face) {
face.tint = assignedTint;
});
// Add cube to the manager and the game
self.cubes.push(cube);
self.addChild(cube);
}
};
// Remove a cube from management
self.removeCube = function (cube) {
var index = self.cubes.indexOf(cube);
if (index > -1) {
self.cubes.splice(index, 1);
cube.destroy();
}
};
// Update all managed cubes
self.updateCubes = function () {
for (var i = self.cubes.length - 1; i >= 0; i--) {
var cube = self.cubes[i];
// Cubes don't move, so no position updates needed
// Rotate cube
cube.z += 1; //cube.speedZ;
cube.rotate3D(Math.PI * 0.01, Math.PI * 0.01, Math.PI * 0.01);
}
};
// Initialize by spawning all cubes at once
self.spawnAllCubes();
// Update method called by the game loop
self.update = function () {
//self.updateCubes();
};
return self;
});
/***********************************************************************************/
/******************************* FACE CLASS *********************************/
/***********************************************************************************/
var Face = Container.expand(function (options) {
var self = Container.call(this);
options = options || {};
var points = Math.max(2, Math.min(100, options.points || 4)); // Ensure points are between 2 and 10
self.baseSize = 100;
self.w = options.w || self.baseSize;
self.h = options.h || self.baseSize;
self.d = options.d || self.baseSize;
self.dx = options.dx || 0;
self.dy = options.dy || 0;
self.dz = options.dz || 0;
self.rx = options.rx || 0;
self.ry = options.ry || 0;
self.rz = options.rz || 0;
self.tint = options.ti || 0xFFFFFF;
// Generate points for the face based on the number of points specified
self.baseFaceCoordinates = [];
for (var i = 0; i < points; i++) {
var angle = 2 * Math.PI * (i / points);
self.baseFaceCoordinates.push({
x: self.w / 2 * Math.cos(angle) + self.dx * self.w,
y: self.h / 2 * Math.sin(angle) + self.dy * self.h,
z: self.dz * self.d
});
}
self.baseFaceCoordinates.forEach(function (point) {
// Update z of each face point coordinates depending on dz and rx, ry
point.z += self.dz * Math.cos(self.rx) * Math.cos(self.ry);
});
// Create a polygon face using the Shape class
self.face = new Shape(self.baseFaceCoordinates, self.tint);
// Attach the face to the Face container
self.addChild(self.face);
// Rotate in 3D: X = roasting chicken / Y = whirling dervish / Z = wheel of Fortune
self.rotate3D = function (angleX, angleY, angleZ, scale) {
scale = scale || 1;
self.faceCoordinates = self.baseFaceCoordinates.map(function (coord) {
var x = coord.x - self.dx * self.w,
y = coord.y - self.dy * self.h,
z = coord.z - self.dz * self.d;
// Apply initial rotations (rx, ry, rz)
var newY = y * Math.cos(self.rx) - z * Math.sin(self.rx);
var newZ = y * Math.sin(self.rx) + z * Math.cos(self.rx);
var newX = x * Math.cos(self.ry) + newZ * Math.sin(self.ry);
newZ = -x * Math.sin(self.ry) + newZ * Math.cos(self.ry);
x = newX * Math.cos(self.rz) - newY * Math.sin(self.rz);
y = newX * Math.sin(self.rz) + newY * Math.cos(self.rz);
// Apply X-axis rotation
newY = y * Math.cos(angleX) - newZ * Math.sin(angleX);
newZ = y * Math.sin(angleX) + newZ * Math.cos(angleX);
// Apply Y-axis rotation
newX = x * Math.cos(angleY) + newZ * Math.sin(angleY);
newZ = -x * Math.sin(angleY) + newZ * Math.cos(angleY);
// Apply Z-axis rotation
x = newX * Math.cos(angleZ) - newY * Math.sin(angleZ);
y = newX * Math.sin(angleZ) + newY * Math.cos(angleZ);
return {
x: (x + self.dx * self.w) * scale,
y: (y + self.dy * self.h) * scale,
z: (newZ + self.dz * self.d) * scale
};
});
self.face.updateCoordinates(self.faceCoordinates);
};
// Initialize face in 3D space
self.rotate3D(0, 0, 0, 1);
});
/***********************************************************************************/
/********************************** GATE CLASS *************************************/
/***********************************************************************************/
var Gate = Container.expand(function () {
var self = Container.call(this);
// Create gate asset with initial properties
self.gateAsset = self.attachAsset('gate', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 0.26,
// Start at same scale as tore2
scaleY: 0.26,
alpha: 1
});
// Store the color for this gate
self.gateColor = 0xFFFFFF; // Default white, will be set by manager
// Set the tint to match the gate color
self.setColor = function (color) {
self.gateColor = color;
self.gateAsset.tint = color;
};
// Update scale to match background animation
self.updateScale = function (newScale) {
self.gateAsset.scaleX = newScale;
self.gateAsset.scaleY = newScale;
self.gateAsset.alpha = Math.min(1, newScale + 0.66);
};
return self;
});
/***********************************************************************************/
/********************************** GATE MANAGER CLASS *****************************/
/***********************************************************************************/
var GateManager = Container.expand(function () {
var self = Container.call(this);
// Array to hold gates
self.gates = [];
// Animation timing
self.gateAnimStartTime = Date.now();
self.gateAnimationSpeed = globalSpeed / 1000; // Same as background
// Spawn initial gates
self.spawnGates = function () {
// Create 3 gates with different colors
var gateColors = [];
// One gate gets currentColor
gateColors.push(currentColor);
// Other two get different neon colors
var availableColors = [];
for (var i = 0; i < neonColors.length; i++) {
if (neonColors[i] !== currentColor) {
availableColors.push(neonColors[i]);
}
}
// Randomly select 2 other colors
for (var j = 0; j < 2; j++) {
var randomIndex = Math.floor(Math.random() * availableColors.length);
gateColors.push(availableColors[randomIndex]);
availableColors.splice(randomIndex, 1);
}
// Shuffle gate colors
for (var s = gateColors.length - 1; s > 0; s--) {
var idx = Math.floor(Math.random() * (s + 1));
var temp = gateColors[s];
gateColors[s] = gateColors[idx];
gateColors[idx] = temp;
}
// Create 3 gates at different scales (matching tore positions)
var initialScales = [0.6, 0.26, 0.12]; // Match tore1, tore2, tore3
for (var k = 0; k < 3; k++) {
var gate = new Gate();
gate.setColor(gateColors[k]);
gate.updateScale(initialScales[k]);
// Store initial scale for reference
gate.initialScale = initialScales[k];
gate.colorIndex = k;
self.gates.push(gate);
self.addChild(gate);
}
};
// Update gates animation
self.update = function () {
var now = Date.now();
var elapsed = now - self.gateAnimStartTime;
// Animate each gate
for (var i = 0; i < self.gates.length; i++) {
var gate = self.gates[i];
var currentScale = gate.gateAsset.scaleX;
// Increase scale with acceleration
var newScale = currentScale + self.gateAnimationSpeed * currentScale;
// Reset when too large
if (newScale > 3.0) {
newScale = 0.12;
// When resetting, randomize colors again
var newColors = [];
newColors.push(currentColor);
var availableColors = [];
for (var c = 0; c < neonColors.length; c++) {
if (neonColors[c] !== currentColor) {
availableColors.push(neonColors[c]);
}
}
for (var j = 0; j < 2; j++) {
var randomIndex = Math.floor(Math.random() * availableColors.length);
newColors.push(availableColors[randomIndex]);
availableColors.splice(randomIndex, 1);
}
// Assign new color to this gate
gate.setColor(newColors[gate.colorIndex % newColors.length]);
}
gate.updateScale(newScale);
}
};
// Initialize gates
self.spawnGates();
return self;
});
/***********************************************************************************/
/********************************** SHAPE CLASS ************************************/
/***********************************************************************************/
var Shape = Container.expand(function (coordinates, tint) {
var self = Container.call(this);
self.polygon = drawPolygon(coordinates, tint); // Function to create a polygon from a list of coordinates
self.tint = tint;
self.attachLines = function () {
// Iterate through each line in the polygon and attach it to the shape
self.polygon.forEach(function (line) {
self.addChild(line);
});
};
self.attachLines();
self.updateCoordinates = function (newCoordinates) {
log("Shape updateCoordinates ", newCoordinates);
// Ensure newCoordinates is an array and has the same length as the current polygon
if (!Array.isArray(newCoordinates) || newCoordinates.length !== self.polygon.length) {
error("Invalid newCoordinates length");
return;
}
// Update each line in the polygon with new coordinates
self.polygon = updatePolygon(self.polygon, newCoordinates);
};
});
/***********************************************************************************/
/******************************* SIMPLE FACE CLASS *********************************/
/***********************************************************************************/
var SimpleFace = Container.expand(function (options) {
var self = Container.call(this);
log("SimpleFAce init options =", options);
self.baseSize = 100;
options = options || {};
self.w = options.w || self.baseSize;
self.h = options.h || self.baseSize;
self.d = options.d || self.baseSize;
self.dx = options.dx || 0;
self.dy = options.dy || 0;
self.dz = options.dz || 0;
self.rx = options.rx || 0;
self.ry = options.ry || 0;
self.rz = options.rz || 0;
self.tint = options.ti || 0xFFFFFF;
// Define faceCoordinates property
self.baseFaceCoordinates = [{
x: -self.w + self.dx * self.w,
y: -self.h + self.dy * self.h,
z: self.dz * self.d
},
// Top-left
{
x: self.w + self.dx * self.w,
y: -self.h + self.dy * self.h,
z: self.dz * self.d
},
// Top-right
{
x: self.w + self.dx * self.w,
y: self.h + self.dy * self.h,
z: self.dz * self.d
},
// Bottom-right
{
x: -self.w + self.dx * self.w,
y: self.h + self.dy * self.h,
z: self.dz * self.d
} // Bottom-left
];
log("SimpleFAce ready to init ...", self.baseFaceCoordinates, "DX=" + self.dx);
self.baseFaceCoordinates.forEach(function (point) {
// Update z of each face point coordinates depending on dz and rx, ry
point.z += self.dz * Math.cos(self.rx) * Math.cos(self.ry);
});
// Create a square face using the Shape class
self.face = new Shape(self.baseFaceCoordinates, self.tint);
// Attach the face to the SimpleFace container
self.addChild(self.face);
// Rotate in 3d : X = roasting chicken / Y = whirling dervish / Z = wheel of Fortune
self.rotate3D = function (angleX, angleY, angleZ, scale) {
scale = scale || 1;
log("SimpleFace rotate3D old coord=", self.faceCoordinates, Date.now());
self.faceCoordinates = self.baseFaceCoordinates.map(function (coord) {
return {
x: coord.x,
y: coord.y,
z: coord.z
};
});
// Apply rotation around X-axis
// Adjust initial rotation parameters before applying new rotations
self.faceCoordinates = self.faceCoordinates.map(function (coord) {
// Apply initial rotation around Z-axis
var xZ = coord.x * Math.cos(self.rz) - coord.y * Math.sin(self.rz);
var yZ = coord.x * Math.sin(self.rz) + coord.y * Math.cos(self.rz);
// Apply initial rotation around Y-axis
var xY = xZ * Math.cos(self.ry) + coord.z * Math.sin(self.ry);
var zY = coord.z * Math.cos(self.ry) - xZ * Math.sin(self.ry);
// Apply initial rotation around X-axis
var yX = yZ * Math.cos(self.rx) - zY * Math.sin(self.rx);
var zX = yZ * Math.sin(self.rx) + zY * Math.cos(self.rx);
return {
x: xY,
y: yX,
z: zX
};
});
// Apply new rotations
// Calculate center of the face
var centerX = self.faceCoordinates.reduce(function (acc, coord) {
return acc + coord.x;
}, 0) / self.faceCoordinates.length;
var centerY = self.faceCoordinates.reduce(function (acc, coord) {
return acc + coord.y;
}, 0) / self.faceCoordinates.length;
var centerZ = self.faceCoordinates.reduce(function (acc, coord) {
return acc + coord.z;
}, 0) / self.faceCoordinates.length;
self.faceCoordinates = self.faceCoordinates.map(function (coord) {
// Translate coordinates to rotate around the center including dy and dz adjustment
var translatedY = (coord.y + self.dy * self.h - centerY) * Math.cos(angleX) - (coord.z + self.dz * self.d - centerZ) * Math.sin(angleX);
var translatedZ = (coord.y + self.dy * self.h - centerY) * Math.sin(angleX) + (coord.z + self.dz * self.d - centerZ) * Math.cos(angleX);
return {
x: coord.x + self.dx * self.w - centerX,
// Keep X unchanged but translate to rotate around center
y: translatedY + centerY,
z: translatedZ + centerZ
};
});
self.faceCoordinates = self.faceCoordinates.map(function (coord) {
var translatedX = (coord.z - centerZ) * Math.sin(angleY) + (coord.x - centerX) * Math.cos(angleY);
var translatedZ = (coord.z - centerZ) * Math.cos(angleY) - (coord.x - centerX) * Math.sin(angleY);
return {
x: translatedX + centerX,
y: coord.y,
// Keep Y unchanged
z: translatedZ + centerZ
};
});
self.faceCoordinates = self.faceCoordinates.map(function (coord) {
return {
x: coord.x * scale,
y: coord.y * scale,
z: coord.z * scale
};
});
log("SimpleFace rotate3D new coord=", self.faceCoordinates, Date.now());
self.face.updateCoordinates(self.faceCoordinates);
};
// initialize face in 3D space
self.rotate3D(0, 0, 0, 1);
log("SimpleFace end init coord=", self.baseFaceCoordinates, Date.now());
});
/***********************************************************************************/
/********************************** SPHERE CLASS ***********************************/
/***********************************************************************************/
var Sphere = Container.expand(function () {
var self = Container.call(this);
self.z = 0;
self.radius = 100; // Sphere radius
// Initialize sphere as a collection of Face instances to simulate a 3D sphere
self.faces = [];
var segments = 5; // Number of segments to simulate the sphere
for (var i = 0; i < segments; i++) {
var angle = 2 * Math.PI / segments;
// Create a circular segment as a face of the sphere
var face = new Face({
points: 22,
w: self.radius * 2,
h: self.radius * 2,
d: self.radius * 2,
dx: 0,
dy: 0,
dz: 0,
rx: 0,
ry: i * angle,
rz: 0,
ti: currentColor // Use currentColor for sphere tint
});
self.faces.push(face);
self.addChild(face);
}
self.speedX = 0;
self.speedY = 0;
self.speedZ = 0;
// Rotate sphere around its axes
self.rotate3D = function (angleX, angleY, angleZ) {
log("sphere rotate3D ", angleX, angleY, angleZ);
self.rotation = angleZ;
var zScaleFactor = 1 + self.z / 500;
for (var i = 0; i < self.faces.length; i++) {
self.faces[i].rotate3D(angleX, angleY, angleZ, zScaleFactor);
}
};
self.rotate3D(Math.PI * 0.5, Math.PI * 0.5, 0);
});
/****
* Initialize Game
****/
// Utility function to draw a polygon using drawLine
var game = new LK.Game({
backgroundColor: 0x000050 // Initialize game with a black background
});
/****
* Game Code
****/
// Global array of 6 neon colors
var neonColors = [0x39FF14,
// Neon Green
0xFF073A,
// Neon Red
0x00FFFF,
// Neon Cyan
0xF3F315,
// Neon Yellow
0xFF61F6,
// Neon Pink
0xFF9900 // Neon Orange
];
// Global currentColor, set to a random neon color
var currentColor = neonColors[Math.floor(Math.random() * neonColors.length)];
/***********************************************************************************/
/******************************* UTILITY FUNCTIONS *********************************/
/***********************************************************************************/
function drawPolygon(coordinates, tint) {
log("drawPolygon ", coordinates);
var lines = [];
for (var i = 0; i < coordinates.length; i++) {
var startPoint = coordinates[i];
var endPoint = coordinates[(i + 1) % coordinates.length]; // Loop back to the first point
var line = drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y, tint);
lines.push(line);
}
return lines;
}
function updatePolygon(lines, newCoordinates, scale) {
log("updatePolygon ", lines, scale);
// Ensure lines and newCoordinates have the same length
if (lines.length !== newCoordinates.length) {
error("updatePolygon error: lines and newCoordinates length mismatch");
return lines;
}
// Update each line with new coordinates
for (var i = 0; i < lines.length; i++) {
var startPoint = newCoordinates[i];
var endPoint = newCoordinates[(i + 1) % newCoordinates.length]; // Loop back to the first point for the last line
updateLine(lines[i], startPoint.x, startPoint.y, endPoint.x, endPoint.y, scale);
}
return lines;
}
// Utility function to draw lines between two points
function drawLine(x1, y1, x2, y2, tint) {
log("drawLine ", x1, y1);
var line = LK.getAsset('line', {
anchorX: 0.0,
anchorY: 0.0,
x: x1,
y: y1,
tint: tint
});
line.startX = x1;
line.startY = y1;
line.endX = x2;
line.endY = y2;
// Calculate the distance between the two points
var distance = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
// Set the width of the line to the distance between the points
line.width = distance;
// Calculate the angle between the two points
var angle = Math.atan2(y2 - y1, x2 - x1);
// Correct angle calculation for all quadrants
line.rotation = angle;
return line;
}
// Utility function to draw lines between two points
function updateLine(line, newX1, newY1, newX2, newY2, scale) {
log("updateLine ", line);
scale = scale === undefined ? 1 : scale;
// Calculate midpoint of the original line
var midX = (newX1 + newX2) / 2;
var midY = (newY1 + newY2) / 2;
// Adjust start and end points based on scale
newX1 = midX + (newX1 - midX) * scale;
newY1 = midY + (newY1 - midY) * scale;
newX2 = midX + (newX2 - midX) * scale;
newY2 = midY + (newY2 - midY) * scale;
// Update line start and end coordinates after scaling
line.x = newX1;
line.y = newY1;
line.startX = newX1;
line.startY = newY1;
line.endX = newX2;
line.endY = newY2;
// Recalculate the distance between the new scaled points
var distance = Math.sqrt(Math.pow(newX2 - newX1, 2) + Math.pow(newY2 - newY1, 2));
// Update the width of the line to the new distance
line.width = distance;
// Recalculate the angle between the new points
var angle = Math.atan2(newY2 - newY1, newX2 - newX1);
// Update the rotation of the line to the new angle
line.rotation = angle;
return line;
}
function log() {
if (isDebug) {
console.log(arguments);
}
}
/***********************************************************************************/
/******************************* GAME VARIABLES*********************************/
/***********************************************************************************/
var isDebug = false;
var cube;
var sphere;
var face1;
var face2;
var face3;
var globalSpeed = 6;
var rotationSpeedX = 0;
var rotationSpeedY = 0;
var rotationSpeedZ = 0;
var currentRotationAngle = 0;
var fullLog = [];
var fpsText;
var lastTick;
var frameCount;
var debugText;
var cubeManager;
var isDraggingSphere = false;
var sphereDragOffset = 0;
var backgroundManager;
var gateManager;
var borderLimitAngle = Math.PI * 0.08;
/***********************************************************************************/
/***************************** GAME INITIALIZATION *********************************/
/***********************************************************************************/
function gameInitialize() {
// Initialize background manager first (so it's behind other elements)
backgroundManager = new BackgroundManager();
game.addChild(backgroundManager);
cube = new Cube(1, 1, 1);
cube.x = 2048 * 0.5; // Center horizontally
cube.y = 2732 / 2; // Center vertically
cube.z = 0;
cube.visible = false;
game.addChild(cube);
cube.rotate3D(Math.PI * 0.125, -Math.PI * 0.125, 0);
sphere = new Sphere();
sphere.x = 1024; // Starting position at center
sphere.y = 2000; // Starting y position (adjusted to match the goal)
sphere.z = 0;
// Set speeds to zero to stop movement
sphere.speedX = 0;
sphere.speedY = 0;
sphere.speedZ = 0;
game.addChild(sphere);
// Initialize cube manager
cubeManager = new CubeManager();
//game.addChild(cubeManager);
// Initialize gate manager
gateManager = new GateManager();
game.addChild(gateManager);
// Create and position ball
var ball = new Ball();
ball.x = 1024;
ball.y = 2000;
game.addChild(ball);
if (isDebug) {
var debugMarker = LK.getAsset('debugMarker', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 * 0.5,
y: 2732 / 2
});
game.addChild(debugMarker);
fpsText = new Text2('FPS: 0', {
size: 50,
fill: 0xFFFFFF
});
// Position FPS text at the bottom-right corner
fpsText.anchor.set(1, 1); // Anchor to the bottom-right
LK.gui.bottomRight.addChild(fpsText);
// Update FPS display every second
lastTick = Date.now();
frameCount = 0;
// Debug text to display cube information
debugText = new Text2('Debug Info', {
size: 50,
fill: 0xFFFFFF
});
debugText.anchor.set(0.5, 0); // Anchor to the bottom-right
LK.gui.top.addChild(debugText);
}
}
/***********************************************************************************/
/******************************** MAIN GAME LOOP ***********************************/
/***********************************************************************************/
game.update = function () {
// Rotate simpleFace in 3D on each game update
rotationSpeedX += globalSpeed * Math.PI * 0.006;
rotationSpeedY += 0 * Math.PI * 0.125 * 0.02;
rotationSpeedZ += 0 * Math.PI * 0.125 * 0.02;
// Original cube movement commented out - now handled by cubeManager
// cube.x += cube.speedX;
// cube.y += cube.speedY;
// cube.z += cube.speedZ;
// if (cube.x <= 100 || cube.x >= 2048 - 100) {
// cube.speedX *= -1;
// }
// if (cube.z <= -250 || cube.z >= 1000) {
// cube.speedZ *= -1;
// }
// if (cube.y <= 100 || cube.y >= 2732 - 100) {
// cube.speedY *= -1;
// }
sphere.rotate3D(rotationSpeedX, rotationSpeedY + currentRotationAngle, rotationSpeedZ);
if (isDebug) {
debugText.setText("X: " + Math.round(cube.x) + ", Y: " + Math.round(cube.y) + ", Z: " + Math.round(cube.z) + ", R: " + cube.rotation.toFixed(2));
// FPS
var now = Date.now();
frameCount++;
if (now - lastTick >= 1000) {
// Update every second
fpsText.setText('FPS: ' + frameCount);
frameCount = 0;
lastTick = now;
}
}
// Update cube manager
cubeManager.update();
// Update background manager
backgroundManager.update();
// Update gate manager
gateManager.update();
};
// Add game event handlers for sphere control
game.down = function (x, y, obj) {
// Allow dragging from anywhere on the screen
var touchX = x;
var touchY = y;
isDraggingSphere = true;
sphereDragOffset = touchX - sphere.x;
};
game.move = function (x, y, obj) {
if (isDraggingSphere) {
// Calculate normalized position (0 to 1) based on touch x position
var touchX = x - sphereDragOffset;
var normalizedX = (touchX - 100) / (1948 - 100); // Map touch position to 0-1 range
normalizedX = Math.max(0, Math.min(1, normalizedX)); // Clamp to 0-1
// Calculate angle for half-circle, limited by borderLimitAngle
// Instead of full PI to 0, use (PI - borderLimitAngle) to borderLimitAngle
var minAngle = borderLimitAngle;
var maxAngle = Math.PI - borderLimitAngle;
var angleRange = maxAngle - minAngle;
var angle = maxAngle - angleRange * normalizedX;
// Calculate position on the ellipse/half-circle
// Center of the ellipse path
var centerX = 1024;
var centerY = 1366;
// Radii for the ellipse
var radiusX = 924; // Half of (1948 - 100) to reach edges
var radiusY = 634; // Distance from center to starting position (2000 - 1366)
// Calculate new position
sphere.x = centerX + radiusX * Math.cos(angle);
sphere.y = centerY + radiusY * Math.sin(angle);
// Apply rotation to simulate 3D perspective
currentRotationAngle = (normalizedX - 0.5) * Math.PI * 0.5; // Rotate based on position
sphere.rotate3D(Math.PI * 0.5, Math.PI * 0.5 + currentRotationAngle, 0);
}
};
game.up = function (x, y, obj) {
isDraggingSphere = false;
};
gameInitialize(); ===================================================================
--- original.js
+++ change.js
@@ -843,9 +843,9 @@
}
/***********************************************************************************/
/******************************* GAME VARIABLES*********************************/
/***********************************************************************************/
-var isDebug = true;
+var isDebug = false;
var cube;
var sphere;
var face1;
var face2;
remove background
remove background
Futuristic speaker in the shape of a white orb. Face view
white video camera icon
landscape of a furturistic world by night
a white music note
white sparkles emiting from the center. back background
clean red-violet beam from above
button in the shape of a protorealistic holographic futuristc Rectangle . Front view.
above the clouds by a bright night, no visible moon Photorealistic
White Clef de sol
A 20 nodes straight metalic lock chain. High definition. In-Game asset. 2d. High contrast. No shadows
a closed metalic padlock. No visible key hole.
white menu icon
in white