Please create a mobile app based on below name and description. The app should be designed with mobile-first principles, optimized for responsive for mobile devices.
Name: Ludo 3D
Description: 🎨 Vibrant 3D cross-design (🔴🔥,🟡🌟,🟢🌿,🔵💧)
🏠 Floating 3D safe bases (glow effect)
🛣️ Holographic numbered path
🚀 3D launch pads per player
🏆 Interactive AR center
I'll create a vibrant 3D Ludo mobile app with all the features you've described. This will be a fully interactive 3D board game optimized for mobile devices.
I've created a stunning 3D Ludo mobile app with all the features you requested! Here's what I've implemented:
The app features a beautiful gradient background, smooth 3D animations, and an intuitive mobile interface. Players can start the game, roll dice, and enjoy the immersive 3D Ludo experience with all the visual effects you requested!
from ursina import * from ursina.prefabs.first_person_controller import FirstPersonController import random app = Ursina() # Game settings BOARD_SIZE = 10 PLAYER_COLORS = [color.red, color.green, color.blue, color.yellow] PLAYERS = 4 TOKENS_PER_PLAYER = 4 # Sound effects dice_roll_sound = Audio('dice_roll.wav', autoplay=False) token_move_sound = Audio('move.wav', autoplay=False, volume=0.5) win_sound = Audio('win.wav', autoplay=False) capture_sound = Audio('capture.wav', autoplay=False) home_sound = Audio('home.wav', autoplay=False) class LudoToken(Entity): def __init__(self, player_id, token_id, **kwargs): super().__init__( model='sphere', color=PLAYER_COLORS[player_id], scale=0.4, collider='sphere', **kwargs ) self.player_id = player_id self.token_id = token_id self.at_home = True self.current_position = -1 self.target_position = None self.move_speed = 0.5 self.is_moving = False self.move_particles = ParticleSystem( enabled=False, position=self.position, particles=10, color=PLAYER_COLORS[player_id], size=0.1, velocity=Vec3(0,0.5,0), lifespan=0.5 ) def update(self): if self.target_position and not self.is_moving: self.is_moving = True token_move_sound.play() self.move_particles.enabled = True self.move_particles.position = self.position self.animate_position( self.target_position, duration=self.move_speed, curve=curve.linear, interruptible=False, on_finish=self.on_move_complete ) def on_move_complete(self): self.is_moving = False self.move_particles.enabled = False class LudoGame: def __init__(self): self.create_board() self.create_players() self.current_player = 0 self.dice_value = 1 self.game_over = False self.setup_ui() self.setup_environment() def setup_environment(self): # Add ambient light AmbientLight(color=color.white * 0.7) # Add directional light DirectionalLight( color=color.white * 0.8, direction=(1, -1, 1), shadows=True ) # Add sky Sky(texture='sky_sunset') def create_board(self): # Create 3D game board self.board = Entity( model='cube', texture='white_cube', scale=(BOARD_SIZE, 0.2, BOARD_SIZE), position=(0, -0.1, 0), texture_scale=(BOARD_SIZE, BOARD_SIZE), color=color.white ) # Create paths self.create_paths() # Create safe zones self.create_safe_zones() # Create center self.center = Entity( model='cylinder', scale=2, position=(0, 0.1, 0), texture='brick', color=color.gray ) # Add board decorations self.add_decorations() def add_decorations(self): # Add corner towers for i in range(4): angle = i * 90 x = math.sin(math.radians(angle + 45)) * (BOARD_SIZE/2 - 0.5) z = math.cos(math.radians(angle + 45)) * (BOARD_SIZE/2 - 0.5) Entity( model='cube', scale=(0.8, 1.5, 0.8), position=(x, 0.8, z), texture='brick', color=PLAYER_COLORS[i] ) def create_paths(self): self.path_positions = [] path_radius = BOARD_SIZE/2 - 0.5 for i in range(52): angle = i * (360/52) x = math.sin(math.radians(angle)) * path_radius z = math.cos(math.radians(angle)) * path_radius self.path_positions.append(Vec3(x, 0.2, z)) # Path markers if i % 13 == 0: marker = Entity( model='cube', scale=(0.5, 0.1, 0.5), position=(x, 0.2, z), color=PLAYER_COLORS[i//13], texture='white_cube' ) marker.animate_scale((0.6, 0.15, 0.6), duration=1, loop=True) def create_safe_zones(self): for i in range(4): angle = i * 90 x = math.sin(math.radians(angle + 45)) * (BOARD_SIZE/2 - 2) z = math.cos(math.radians(angle + 45)) * (BOARD_SIZE/2 - 2) zone = Entity( model='cube', scale=(2, 0.1, 2), position=(x, 0.2, z), color=PLAYER_COLORS[i], texture='white_cube' ) zone.animate_color(color.light_gray, duration=2, loop=True) def create_players(self): self.players = [] for i in range(PLAYERS): player = { 'tokens': [], 'finished_tokens': 0, 'start_positions': [] } for j in range(TOKENS_PER_PLAYER): token = LudoToken( player_id=i, token_id=j, position=self.get_home_position(i, j) ) player['tokens'].append(token) self.players.append(player) def get_home_position(self, player_id, token_id): home_positions = [ [(-3, 0.2, -3), (-3, 0.2, -2), (-2, 0.2, -3), (-2, 0.2, -2)], [(3, 0.2, -3), (3, 0.2, -2), (2, 0.2, -3), (2, 0.2, -2)], [(3, 0.2, 3), (3, 0.2, 2), (2, 0.2, 3), (2, 0.2, 2)], [(-3, 0.2, 3), (-3, 0.2, 2), (-2, 0.2, 3), (-2, 0.2, 2)] ] return home_positions[player_id][token_id] def setup_ui(self): # Dice display self.dice_display = Entity( model='cube', scale=0.5, position=(-0.7, 0.5, -1), texture='white_cube' ) self.dice_text = Text( text=f'Dice: {self.dice_value}', position=(-0.8, 0.4), scale=2, color=color.black ) # Player indicator self.player_indicator = Entity( model='sphere', scale=0.3, position=(-0.9, 0.3, -1), color=PLAYER_COLORS[self.current_player] ) self.player_text = Text( text=f'Player {self.current_player + 1} turn', position=(-0.8, 0.3), scale=2, color=PLAYER_COLORS[self.current_player] ) # Roll button self.roll_button = Button( text='Roll Dice', position=(0.7, 0.4), color=color.azure, highlight_color=color.cyan, on_click=self.roll_dice ) self.roll_button.tooltip = Tooltip('Click to roll the dice') def roll_dice(self): if not self.game_over: dice_roll_sound.play() self.roll_button.disabled = True # Dice roll animation self.dice_display.animate_rotation((360, 360, 360), duration=1) # Random dice value invoke(self.set_dice_value, delay=1) def set_dice_value(self): self.dice_value = random.randint(1, 6) self.dice_text.text = f'Dice: {self.dice_value}' # Update dice display texture based on value self.dice_display.texture = f'dice_{self.dice_value}' print(f"Player {self.current_player+1} rolled: {self.dice_value}") self.roll_button.disabled = False def move_token(self, token): if token.at_home: if self.dice_value == 6: home_sound.play() token.at_home = False token.current_position = self.current_player * 13 token.target_position = self.path_positions[token.current_position] else: new_position = token.current_position + self.dice_value # Check for token capture for player in self.players: for other_token in player['tokens']: if (not other_token.at_home and other_token.current_position == new_position % 52 and other_token.player_id != token.player_id): capture_sound.play() other_token.at_home = True other_token.current_position = -1 other_token.target_position = self.get_home_position( other_token.player_id, other_token.token_id ) break if new_position < len(self.path_positions): token.current_position = new_position token.target_position = self.path_positions[new_position] # Check for winning condition if token.current_position >= 52: win_sound.play() self.players[self.current_player]['finished_tokens'] += 1 if self.players[self.current_player]['finished_tokens'] == TOKENS_PER_PLAYER: self.game_over = True self.show_winner() self.next_turn() def show_winner(self): winner_text = Text( text=f'Player {self.current_player+1} Wins!', position=(0, 0.2), scale=3, color=PLAYER_COLORS[self.current_player], background=True ) # Confetti effect for _ in range(100): Confetti( position=(random.uniform(-2, 2), random.uniform(0, 3), random.uniform(-2, 2)), color=random.choice(PLAYER_COLORS) ) def next_turn(self): if self.dice_value != 6: self.current_player = (self.current_player + 1) % PLAYERS self.player_text.color = PLAYER_COLORS[self.current_player] self.player_text.text = f'Player {self.current_player + 1} turn' self.player_indicator.color = PLAYER_COLORS[self.current_player] def input(self, key): if key == 'left mouse down' and not self.game_over and not self.roll_button.disabled: hit_info = mouse.hovered_entity if hit_info and isinstance(hit_info, LudoToken): if hit_info.player_id == self.current_player: self.move_token(hit_info) # Run the game game = LudoGame() # Enable camera controls for debugging # editor_camera = EditorCamera() app.run()
I see you've shared a Python Ursina-based 3D Ludo game! That's a great implementation with sound effects, animations, and 3D graphics. Let me enhance our React Three Fiber version to incorporate some of the excellent features from your Python code, particularly the game mechanics and visual effects.
I've enhanced the React Three Fiber Ludo 3D app with features inspired by your Python Ursina implementation:
The enhanced version now includes the core Ludo gameplay mechanics from your Python implementation while maintaining the mobile-first design and 3D visual appeal. Players can now actually play a full game of Ludo with proper rules, sound effects, and visual feedback!
using UnityEngine; using UnityEngine.UI; using System.Collections; using System.Collections.Generic;
public class Ludo3DGame : MonoBehaviour { [Header("Game Settings")] public float boardSize = 10f; // حجم اللوحة في المشهد public int playersCount = 4; public Material[] playerMaterials; // مواد لكل لاعب
[Header("Prefabs")]
public GameObject tokenPrefab;
public GameObject dicePrefab;
[Header("UI Elements")]
public Text diceResultText;
public Text currentPlayerText;
public Button rollButton;
private List<Player> players = new List<Player>();
private int currentPlayerIndex = 0;
private int diceResult = 1;
private bool isDiceRolling = false;
private GameObject currentDice;
private List<Vector3> pathPositions = new List<Vector3>();
private List<Vector3>[] safeZones = new List<Vector3>[4];
void Start()
{
CreateBoard();
CreatePath();
InitializePlayers();
UpdateUI();
}
void CreateBoard()
{
// إنشاء لوحة اللعبة الأساسية
GameObject board = GameObject.CreatePrimitive(PrimitiveType.Cube);
board.transform.localScale = new Vector3(boardSize, 0.1f, boardSize);
board.transform.position = Vector3.zero;
board.GetComponent<Renderer>().material = Resources.Load<Material>("Materials/BoardMat");
// إنشاء المناطق الآمنة
for (int i = 0; i < 4; i++)
{
safeZones[i] = new List<Vector3>();
float angle = i * 90f + 45f;
Vector3 center = new Vector3(
Mathf.Sin(angle * Mathf.Deg2Rad) * (boardSize/2f - 1.5f),
0.1f,
Mathf.Cos(angle * Mathf.Deg2Rad) * (boardSize/2f - 1.5f)
);
for (int j = 0; j < 4; j++)
{
Vector3 pos = center + new Vector3((j%2)*1f - 0.5f, 0, (j/2)*1f - 0.5f);
safeZones[i].Add(pos);
GameObject zone = GameObject.CreatePrimitive(PrimitiveType.Cube);
zone.transform.position = pos;
zone.transform.localScale = new Vector3(0.9f, 0.15f, 0.9f);
zone.GetComponent<Renderer>().material = playerMaterials[i];
}
}
}
void CreatePath()
{
float pathRadius = boardSize/2f - 1f;
// إنشاء 52 موقعاً على المسار
for (int i = 0; i < 52; i++)
{
float angle = i * (360f/52f);
Vector3 pos = new Vector3(
Mathf.Sin(angle * Mathf.Deg2Rad) * pathRadius,
0.1f,
Mathf.Cos(angle * Mathf.Deg2Rad) * pathRadius
);
pathPositions.Add(pos);
// وضع علامات للمسار
if (i % 13 == 0)
{
GameObject marker = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
marker.transform.position = pos + Vector3.up * 0.1f;
marker.transform.localScale = new Vector3(0.3f, 0.05f, 0.3f);
marker.GetComponent<Renderer>().material = playerMaterials[i/13];
}
}
}
void InitializePlayers()
{
for (int i = 0; i < playersCount; i++)
{
Player player = new Player(i, playerMaterials[i].color);
players.Add(player);
// إنشاء 4 قطع لكل لاعب
for (int j = 0; j < 4; j++)
{
Vector3 startPos = safeZones[i][j];
GameObject tokenObj = Instantiate(tokenPrefab, startPos, Quaternion.identity);
Token token = tokenObj.AddComponent<Token>();
token.Initialize(player, j, startPos);
player.tokens.Add(token);
// إعداد مظهر القطعة
tokenObj.GetComponent<Renderer>().material = playerMaterials[i];
tokenObj.transform.localScale = new Vector3(0.8f, 0.8f, 0.8f);
}
}
}
public void RollDice()
{
if (isDiceRolling) return;
StartCoroutine(RollDiceAnimation());
}
IEnumerator RollDiceAnimation()
{
isDiceRolling = true;
rollButton.interactable = false;
// إنشاء النرد
if (currentDice != null) Destroy(currentDice);
currentDice = Instantiate(dicePrefab, new Vector3(0, 3, -5), Quaternion.identity);
// تحريك النرد
float rollTime = 1.5f;
float elapsed = 0f;
Vector3 randomRotation = Random.insideUnitSphere * 1000f;
while (elapsed < rollTime)
{
currentDice.transform.Rotate(randomRotation * Time.deltaTime);
elapsed += Time.deltaTime;
yield return null;
}
// تحديد نتيجة النرد
diceResult = Random.Range(1, 7);
diceResultText.text = diceResult.ToString();
// تمكين اختيار القطع
EnableTokenSelection();
isDiceRolling = false;
}
void EnableTokenSelection()
{
Player currentPlayer = players[currentPlayerIndex];
foreach (Token token in currentPlayer.tokens)
{
token.SetSelectable(true);
}
}
public void OnTokenSelected(Token token)
{
if (token.owner != players[currentPlayerIndex]) return;
// تحريك القطعة
StartCoroutine(MoveToken(token));
}
IEnumerator MoveToken(Token token)
{
// تعطيل جميع القطع
foreach (Player p in players)
{
foreach (Token t in p.tokens)
{
t.SetSelectable(false);
}
}
// إذا كانت القطعة في القاعدة ويجب إخراجها
if (token.IsAtHome() && diceResult == 6)
{
token.MoveTo(pathPositions[token.owner.id * 13]);
yield return new WaitForSeconds(0.5f);
}
// إذا كانت القطعة في المسار
else if (!token.IsAtHome())
{
int newPosition = token.currentPathIndex + diceResult;
// إذا تجاوزت النهاية
if (newPosition >= pathPositions.Count)
{
newPosition = pathPositions.Count - 1;
}
// تحريك القطعة خطوة بخطوة
for (int i = token.currentPathIndex + 1; i <= newPosition; i++)
{
token.MoveTo(pathPositions[i]);
yield return new WaitForSeconds(0.2f);
}
// التحقق من الوصول للنهاية
if (newPosition == pathPositions.Count - 1)
{
token.ReachHome();
players[currentPlayerIndex].finishedTokens++;
if (players[currentPlayerIndex].finishedTokens == 4)
{
GameOver();
yield break;
}
}
}
NextTurn();
}
void NextTurn()
{
if (diceResult != 6)
{
currentPlayerIndex = (currentPlayerIndex + 1) % playersCount;
}
UpdateUI();
}
void UpdateUI()
{
currentPlayerText.text = "Player " + (currentPlayerIndex + 1);
currentPlayerText.color = players[currentPlayerIndex].color;
rollButton.interactable = true;
}
void GameOver()
{
Debug.Log("Player " + (currentPlayerIndex + 1) + " wins!");
// عرض شاشة النهاية
}
}
public class Player { public int id; public Color color; public List<Token> tokens = new List<Token>(); public int finishedTokens = 0;
public Player(int id, Color color)
{
this.id = id;
this.color = color;
}
}
public class Token : MonoBehaviour { public Player owner; public int tokenId; public int currentPathIndex = -1; // -1 يعني في القاعدة
private Vector3 startPosition;
private bool isSelectable = false;
public void Initialize(Player owner, int tokenId, Vector3 startPos)
{
this.owner = owner;
this.tokenId = tokenId;
this.startPosition = startPos;
}
public void SetSelectable(bool selectable)
{
isSelectable = selectable;
// تغيير المظهر عند التحديد
if (selectable)
{
GetComponent<Renderer>().material.SetColor("_EmissionColor", owner.color * 0.5f);
}
else
{
GetComponent<Renderer>().material.SetColor("_EmissionColor", Color.black);
}
}
public bool IsAtHome()
{
return currentPathIndex == -1;
}
public void MoveTo(Vector3 targetPosition)
{
StartCoroutine(SmoothMove(targetPosition));
}
IEnumerator SmoothMove(Vector3 target)
{
float duration = 0.5f;
float elapsed = 0f;
Vector3 startPos = transform.position;
while (elapsed < duration)
{
transform.position = Vector3.Lerp(startPos, target, elapsed/duration);
elapsed += Time.deltaTime;
yield return null;
}
transform.position = target;
}
public void ReachHome()
{
currentPathIndex = -2; // -2 يعني وصلت للنهاية
// إخفاء القطعة أو تغيير مظهرها
}
void OnMouseDown()
{
if (isSelectable)
{
Ludo3DGame.Instance.OnTokenSelected(this);
}
}
}