User prompt
Increase the size of the dice thrown on the table by 2 times ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Add assets to D10 dice
User prompt
Dice models are the same as in real life, d4 dice have 4 sides, d6 dice have 6 sides. D10 dice have 10 sides, d12 dice have 12 sides, d20 dice have 20 sides.
User prompt
Let the number of surfaces in the dice images above be the same and write on each surface. When we throw the dice, let's see the result on the surface of the dice. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Slide the dice to be selected above and add the clear button to the bottom left corner
User prompt
Each dice should look like what it is. It should be centered on the right side of the screen, top to bottom. Make the clear button a little bigger and add the text clear.
User prompt
Add dice history tab top right of screen.
User prompt
Need dice history tab with small windows.
User prompt
Need screen clear button.
User prompt
I can't see any dice
Code edit (1 edits merged)
Please save this source code
User prompt
Dice Table - 3D Billiard Dice Simulator
Initial prompt
Develop a mobile dice-rolling simulation game inspired by a billiard table. The player sees a realistic green felt table as the background. On the right side of the screen, there is a panel showing different dice types: D4, D6, D8, D10, D12, and D20. Each dice type has a 3D preview or icon. When the player double-taps a dice on the panel, it spawns in the middle of the table. The player can hold and drag to rotate the dice in 3D before releasing it. Once released, the dice drops naturally onto the table surface using realistic physics and shows the result of the top face. There should be soft shadow lighting, gentle bounce sounds when dice land, and a minimalistic, polished user interface suitable for Android and iOS. The game supports different dice types with correct numbering on each face. Add a UI text panel showing the result after the dice comes to rest. Make the design clean, modern, and visually appealing with intuitive touch controls.
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Dice = Container.expand(function (diceType, value) {
var self = Container.call(this);
self.diceType = diceType;
self.value = value || 1;
self.isRolling = false;
self.isDragging = false;
self.rotationSpeed = 0;
self.velocity = {
x: 0,
y: 0
};
self.lastPosition = {
x: 0,
y: 0
};
// Create shadow
var shadow = self.attachAsset('shadow', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3
});
shadow.y = 10;
// Create dice graphic
var diceGraphic = self.attachAsset(diceType, {
anchorX: 0.5,
anchorY: 0.5
});
self.startRoll = function (forceX, forceY) {
self.isRolling = true;
self.velocity.x = forceX || (Math.random() - 0.5) * 10;
self.velocity.y = forceY || (Math.random() - 0.5) * 10;
self.rotationSpeed = (Math.random() - 0.5) * 0.3;
// Play bounce sound
LK.getSound('bounce').play();
};
self.stopRoll = function () {
self.isRolling = false;
self.velocity.x = 0;
self.velocity.y = 0;
self.rotationSpeed = 0;
// Generate final result
var maxValue = parseInt(diceType.substring(1));
self.value = Math.floor(Math.random() * maxValue) + 1;
// Show result
showDiceResult(self.value, diceType);
};
self.update = function () {
if (self.isRolling) {
// Apply physics
self.x += self.velocity.x;
self.y += self.velocity.y;
diceGraphic.rotation += self.rotationSpeed;
// Apply friction
self.velocity.x *= 0.95;
self.velocity.y *= 0.95;
self.rotationSpeed *= 0.95;
// Bounce off table edges
if (self.x < tableBounds.left + 40) {
self.x = tableBounds.left + 40;
self.velocity.x = -self.velocity.x * 0.7;
LK.getSound('bounce').play();
}
if (self.x > tableBounds.right - 40) {
self.x = tableBounds.right - 40;
self.velocity.x = -self.velocity.x * 0.7;
LK.getSound('bounce').play();
}
if (self.y < tableBounds.top + 40) {
self.y = tableBounds.top + 40;
self.velocity.y = -self.velocity.y * 0.7;
LK.getSound('bounce').play();
}
if (self.y > tableBounds.bottom - 40) {
self.y = tableBounds.bottom - 40;
self.velocity.y = -self.velocity.y * 0.7;
LK.getSound('bounce').play();
}
// Stop rolling when velocity is low
if (Math.abs(self.velocity.x) < 0.1 && Math.abs(self.velocity.y) < 0.1) {
self.stopRoll();
}
}
// Update shadow position
shadow.x = diceGraphic.x * 0.1;
shadow.y = diceGraphic.y * 0.1 + 15;
};
self.down = function (x, y, obj) {
if (!self.isRolling) {
self.isDragging = true;
self.lastPosition.x = x;
self.lastPosition.y = y;
}
};
self.up = function (x, y, obj) {
if (self.isDragging) {
self.isDragging = false;
// Calculate force based on drag distance
var forceX = (x - self.lastPosition.x) * 0.3;
var forceY = (y - self.lastPosition.y) * 0.3;
self.startRoll(forceX, forceY);
}
};
return self;
});
var DiceSelector = Container.expand(function (diceType, yPos) {
var self = Container.call(this);
self.diceType = diceType;
var tapCount = 0;
var lastTapTime = 0;
// Create dice preview
var preview = self.attachAsset(diceType, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
// Create label
var label = new Text2(diceType.toUpperCase(), {
size: 24,
fill: 0xFFFFFF
});
label.anchor.set(0.5, 0.5);
label.y = 50;
self.addChild(label);
self.y = yPos;
self.down = function (x, y, obj) {
var currentTime = Date.now();
if (currentTime - lastTapTime < 300) {
// Double tap detected
spawnDice(self.diceType);
tapCount = 0;
} else {
tapCount = 1;
}
lastTapTime = currentTime;
// Visual feedback
tween(preview, {
scaleX: 0.6,
scaleY: 0.6
}, {
duration: 100
});
};
self.up = function (x, y, obj) {
tween(preview, {
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 100
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a4d3a
});
/****
* Game Code
****/
var activeDice = [];
var tableBounds = {};
var resultPanel = null;
var diceHistory = [];
var historyPanel = null;
var showingHistory = false;
// Create table
var tableEdge = game.attachAsset('tableEdge', {
anchorX: 0.5,
anchorY: 0.5,
x: 824,
y: 1366
});
var table = game.attachAsset('table', {
anchorX: 0.5,
anchorY: 0.5,
x: 824,
y: 1366
});
// Set table bounds
tableBounds = {
left: 824 - 900,
right: 824 + 900,
top: 1366 - 600,
bottom: 1366 + 600
};
// Create side panel
var sidePanel = game.attachAsset('sidePanel', {
anchorX: 0,
anchorY: 0.5,
x: 1848,
y: 1366
});
// Create dice selectors
var diceTypes = ['d4', 'd6', 'd8', 'd10', 'd12', 'd20'];
var selectors = [];
for (var i = 0; i < diceTypes.length; i++) {
var selector = new DiceSelector(diceTypes[i], 200 + i * 180);
selector.x = 100;
sidePanel.addChild(selector);
selectors.push(selector);
}
// Instructions text
var instructionText = new Text2('Double-tap\ndice to spawn', {
size: 32,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 0.5);
LK.gui.top.addChild(instructionText);
instructionText.y = 100;
// Create clear button
var clearButton = new Container();
var clearBg = LK.getAsset('sidePanel', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.2,
alpha: 0.8
});
clearButton.addChild(clearBg);
var clearText = new Text2('CLEAR', {
size: 36,
fill: 0xFFFFFF
});
clearText.anchor.set(0.5, 0.5);
clearButton.addChild(clearText);
clearButton.x = 100;
clearButton.y = 1400;
sidePanel.addChild(clearButton);
// Create history button in top right
var historyButton = new Container();
var historyBg = LK.getAsset('sidePanel', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.15,
alpha: 0.8
});
historyButton.addChild(historyBg);
var historyText = new Text2('HISTORY', {
size: 28,
fill: 0xFFFFFF
});
historyText.anchor.set(0.5, 0.5);
historyButton.addChild(historyText);
LK.gui.topRight.addChild(historyButton);
historyButton.x = -120;
historyButton.y = 80;
clearButton.down = function (x, y, obj) {
// Visual feedback
tween(clearBg, {
scaleX: 0.6,
scaleY: 0.15
}, {
duration: 100
});
};
clearButton.up = function (x, y, obj) {
// Reset button scale
tween(clearBg, {
scaleX: 0.8,
scaleY: 0.2
}, {
duration: 100
});
// Clear all dice
for (var i = activeDice.length - 1; i >= 0; i--) {
var dice = activeDice[i];
dice.destroy();
activeDice.splice(i, 1);
}
// Clear result panel if showing
if (resultPanel) {
resultPanel.destroy();
resultPanel = null;
}
// Clear history
diceHistory = [];
// Hide history panel if showing
if (showingHistory) {
hideHistoryPanel();
}
};
function spawnDice(diceType) {
var dice = new Dice(diceType);
dice.x = 824 + (Math.random() - 0.5) * 200;
dice.y = 1366 + (Math.random() - 0.5) * 200;
activeDice.push(dice);
game.addChild(dice);
// Start with a small initial roll
dice.startRoll((Math.random() - 0.5) * 3, (Math.random() - 0.5) * 3);
}
function showDiceResult(value, diceType) {
// Save to history
diceHistory.push({
type: diceType,
value: value,
timestamp: Date.now()
});
// Keep only last 50 entries
if (diceHistory.length > 50) {
diceHistory.shift();
}
// Remove existing result panel
if (resultPanel) {
resultPanel.destroy();
}
// Create result panel
resultPanel = new Container();
var resultBg = LK.getAsset('sidePanel', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.3,
alpha: 0.9
});
resultPanel.addChild(resultBg);
var resultText = new Text2(diceType.toUpperCase() + ': ' + value, {
size: 48,
fill: 0xFFFFFF
});
resultText.anchor.set(0.5, 0.5);
resultPanel.addChild(resultText);
resultPanel.x = 824;
resultPanel.y = 800;
game.addChild(resultPanel);
// Auto-hide after 3 seconds
LK.setTimeout(function () {
if (resultPanel) {
tween(resultPanel, {
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
if (resultPanel) {
resultPanel.destroy();
resultPanel = null;
}
}
});
}
}, 3000);
}
historyButton.down = function (x, y, obj) {
// Visual feedback
tween(historyBg, {
scaleX: 0.6,
scaleY: 0.15
}, {
duration: 100
});
};
historyButton.up = function (x, y, obj) {
// Reset button scale
tween(historyBg, {
scaleX: 0.8,
scaleY: 0.2
}, {
duration: 100
});
// Toggle history panel
toggleHistoryPanel();
};
function toggleHistoryPanel() {
if (showingHistory) {
hideHistoryPanel();
} else {
showHistoryPanel();
}
}
function showHistoryPanel() {
if (historyPanel) {
historyPanel.destroy();
}
showingHistory = true;
historyPanel = new Container();
// Create background
var historyBg = LK.getAsset('sidePanel', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 4,
alpha: 0.95
});
historyPanel.addChild(historyBg);
// Title
var titleText = new Text2('DICE HISTORY', {
size: 36,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.y = -400;
historyPanel.addChild(titleText);
// History entries in small windows
var maxEntries = Math.min(diceHistory.length, 12);
var cols = 3;
var rows = Math.ceil(maxEntries / cols);
for (var i = 0; i < maxEntries; i++) {
var entry = diceHistory[diceHistory.length - 1 - i];
var col = i % cols;
var row = Math.floor(i / cols);
var entryContainer = new Container();
// Small window background
var entryBg = LK.getAsset('tableEdge', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.15,
scaleY: 0.15,
alpha: 0.8
});
entryContainer.addChild(entryBg);
// Dice type and value
var entryText = new Text2(entry.type.toUpperCase() + '\n' + entry.value, {
size: 20,
fill: 0xFFFFFF
});
entryText.anchor.set(0.5, 0.5);
entryContainer.addChild(entryText);
entryContainer.x = (col - 1) * 180;
entryContainer.y = -200 + row * 120;
historyPanel.addChild(entryContainer);
}
// Close button
var closeButton = new Container();
var closeBg = LK.getAsset('sidePanel', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.15,
alpha: 0.8
});
closeButton.addChild(closeBg);
var closeText = new Text2('CLOSE', {
size: 28,
fill: 0xFFFFFF
});
closeText.anchor.set(0.5, 0.5);
closeButton.addChild(closeText);
closeButton.y = 350;
historyPanel.addChild(closeButton);
closeButton.down = function (x, y, obj) {
tween(closeBg, {
scaleX: 0.6,
scaleY: 0.12
}, {
duration: 100
});
};
closeButton.up = function (x, y, obj) {
tween(closeBg, {
scaleX: 0.8,
scaleY: 0.15
}, {
duration: 100
});
hideHistoryPanel();
};
historyPanel.x = 1024;
historyPanel.y = 1366;
game.addChild(historyPanel);
}
function hideHistoryPanel() {
if (historyPanel) {
historyPanel.destroy();
historyPanel = null;
}
showingHistory = false;
}
// Drag handling for dice rotation
var draggedDice = null;
game.move = function (x, y, obj) {
if (draggedDice && draggedDice.isDragging) {
// Rotate dice based on drag movement
var deltaX = x - draggedDice.lastPosition.x;
var deltaY = y - draggedDice.lastPosition.y;
// Only rotate if not rolling
if (!draggedDice.isRolling) {
draggedDice.rotation += deltaX * 0.01;
}
draggedDice.lastPosition.x = x;
draggedDice.lastPosition.y = y;
}
};
game.down = function (x, y, obj) {
// Check if clicking on a dice
for (var i = 0; i < activeDice.length; i++) {
var dice = activeDice[i];
if (dice.intersects({
x: x,
y: y,
width: 1,
height: 1
})) {
draggedDice = dice;
break;
}
}
};
game.up = function (x, y, obj) {
draggedDice = null;
};
game.update = function () {
// Clean up dice that are no longer needed
for (var i = activeDice.length - 1; i >= 0; i--) {
var dice = activeDice[i];
// Remove dice if they fall off the table (shouldn't happen but safety check)
if (dice.x < 0 || dice.x > 2048 || dice.y < 0 || dice.y > 2732) {
dice.destroy();
activeDice.splice(i, 1);
}
}
}; ===================================================================
--- original.js
+++ change.js
@@ -238,27 +238,27 @@
clearButton.addChild(clearText);
clearButton.x = 100;
clearButton.y = 1400;
sidePanel.addChild(clearButton);
-// Create history button
+// Create history button in top right
var historyButton = new Container();
var historyBg = LK.getAsset('sidePanel', {
anchorX: 0.5,
anchorY: 0.5,
- scaleX: 0.8,
- scaleY: 0.2,
+ scaleX: 0.6,
+ scaleY: 0.15,
alpha: 0.8
});
historyButton.addChild(historyBg);
var historyText = new Text2('HISTORY', {
- size: 32,
+ size: 28,
fill: 0xFFFFFF
});
historyText.anchor.set(0.5, 0.5);
historyButton.addChild(historyText);
-historyButton.x = 100;
-historyButton.y = 1550;
-sidePanel.addChild(historyButton);
+LK.gui.topRight.addChild(historyButton);
+historyButton.x = -120;
+historyButton.y = 80;
clearButton.down = function (x, y, obj) {
// Visual feedback
tween(clearBg, {
scaleX: 0.6,