Skip to content

Instantly share code, notes, and snippets.

@erbolamm
Created January 20, 2026 05:57
Show Gist options
  • Select an option

  • Save erbolamm/0bfb7a4b7f27d8749ffe2fc2f7e7b3d6 to your computer and use it in GitHub Desktop.

Select an option

Save erbolamm/0bfb7a4b7f27d8749ffe2fc2f7e7b3d6 to your computer and use it in GitHub Desktop.
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:math';
void main() {
runApp(const ApliArtePacApp());
}
class ApliArtePacApp extends StatelessWidget {
const ApliArtePacApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: PacManGame(),
);
}
}
// --- COLORES CORPORATIVOS APLIARTE ---
class ApliArteColors {
static const Color darkBlue = Color(0xFF164885); // Azul oscuro del logo
static const Color lightBlue = Color(0xFF69B6E8); // Azul claro del logo
static const Color darkGrey = Color(0xFF2F2F2F); // Gris oscuro/negro del logo
static const Color background = Colors.black; // Fondo juego
}
// Estados del juego (Incluye Intro)
enum GameState { intro, menu, playing, gameOver, win }
// Direcciones
enum Direction { right, down, left, up }
class PacManGame extends StatefulWidget {
const PacManGame({super.key});
@override
State<PacManGame> createState() => _PacManGameState();
}
class _PacManGameState extends State<PacManGame> with TickerProviderStateMixin {
// Constantes del juego
static const int mazeWidth = 19;
static const int mazeHeight = 21;
// Estados del juego
GameState gameState = GameState.intro; // Empieza con la intro
int score = 0;
int lives = 3;
int highScore = 0;
double gridSize = 0;
// Power pellet
bool powerPelletActive = false;
Timer? powerPelletTimer;
int ghostsEaten = 0;
// Laberinto
List<List<int>> maze = [];
final List<List<int>> originalMaze = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1],
[1, 3, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 3, 1],
[1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1],
[1, 2, 1, 1, 1, 2, 1, 2, 1, 1, 1, 2, 1, 2, 1, 1, 1, 2, 1],
[1, 2, 2, 2, 2, 2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 2, 2, 2, 1],
[1, 1, 1, 1, 1, 2, 1, 1, 0, 1, 0, 1, 1, 2, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 2, 1, 0, 1, 0, 1, 0, 1, 2, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 2, 0, 0, 1, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 2, 1, 0, 1, 1, 1, 0, 1, 2, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 2, 1, 1, 0, 1, 0, 1, 1, 2, 1, 1, 1, 1, 1],
[1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1],
[1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1],
[1, 3, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 3, 1],
[1, 1, 1, 2, 1, 2, 1, 2, 1, 1, 1, 2, 1, 2, 1, 2, 1, 1, 1],
[1, 2, 2, 2, 2, 2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 2, 2, 2, 1],
[1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1],
[1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
];
Player player = Player();
List<Ghost> ghosts = [];
late AnimationController mouthController;
late AnimationController introController; // Animación para la intro
Timer? gameTimer;
@override
void initState() {
super.initState();
mouthController = AnimationController(
duration: const Duration(milliseconds: 200),
vsync: this,
)..repeat(reverse: true);
introController = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
)..forward();
// Iniciar la secuencia de Intro
startIntroSequence();
}
void startIntroSequence() async {
// Esperar 3.5 segundos en la intro antes de ir al menú
await Future.delayed(const Duration(milliseconds: 3500));
if (mounted) {
setState(() {
gameState = GameState.menu;
});
loadHighScore();
}
}
void initializeGame() {
maze = originalMaze.map((row) => List<int>.from(row)).toList();
player = Player();
ghosts = [
Ghost(x: 9.0, y: 9.0, color: Colors.red, direction: Direction.up),
Ghost(x: 8.0, y: 9.0, color: Colors.pinkAccent, direction: Direction.down),
Ghost(x: 10.0, y: 9.0, color: Colors.cyanAccent, direction: Direction.left),
Ghost(x: 9.0, y: 8.0, color: Colors.orange, direction: Direction.right),
];
powerPelletActive = false;
powerPelletTimer?.cancel();
ghostsEaten = 0;
}
void loadHighScore() async {
setState(() {
highScore = 0;
});
}
void saveHighScore() async {
if (score > highScore) {
highScore = score;
}
}
void startGame() {
setState(() {
gameState = GameState.playing;
score = 0;
lives = 3;
});
initializeGame();
startGameLoop();
}
void startGameLoop() {
gameTimer = Timer.periodic(const Duration(milliseconds: 100), (timer) {
if (gameState == GameState.playing) {
updateGame();
}
});
}
void updateGame() {
movePlayer();
moveGhosts();
checkCollisions();
collectDots();
checkWinCondition();
setState(() {});
}
void movePlayer() {
double newX = player.x;
double newY = player.y;
switch (player.nextDirection) {
case Direction.right: newX += 0.2; break;
case Direction.down: newY += 0.2; break;
case Direction.left: newX -= 0.2; break;
case Direction.up: newY -= 0.2; break;
}
if (!isWall(newX, newY)) {
player.direction = player.nextDirection;
player.x = newX;
player.y = newY;
} else {
newX = player.x;
newY = player.y;
switch (player.direction) {
case Direction.right: newX += 0.2; break;
case Direction.down: newY += 0.2; break;
case Direction.left: newX -= 0.2; break;
case Direction.up: newY -= 0.2; break;
}
if (!isWall(newX, newY)) {
player.x = newX;
player.y = newY;
}
}
if (player.x < 0) player.x = mazeWidth - 1;
if (player.x >= mazeWidth) player.x = 0;
}
void moveGhosts() {
for (var ghost in ghosts) {
if (ghost.isScared) {
ghost.moveAway(maze, mazeWidth, mazeHeight, player.x, player.y);
} else {
ghost.move(maze, mazeWidth, mazeHeight);
}
}
}
bool isWall(double x, double y) {
int gridX = x.round();
int gridY = y.round();
if (gridX < 0 || gridX >= mazeWidth || gridY < 0 || gridY >= mazeHeight) return true;
return maze[gridY][gridX] == 1;
}
void collectDots() {
int gridX = player.x.round();
int gridY = player.y.round();
if (gridX >= 0 && gridX < mazeWidth && gridY >= 0 && gridY < mazeHeight) {
int cell = maze[gridY][gridX];
if (cell == 2) {
maze[gridY][gridX] = 0;
score += 10;
} else if (cell == 3) {
maze[gridY][gridX] = 0;
score += 50;
activatePowerPellet();
}
if (score > highScore) highScore = score;
}
}
void activatePowerPellet() {
powerPelletActive = true;
ghostsEaten = 0;
for (var ghost in ghosts) {
ghost.isVulnerable = true;
ghost.isScared = true;
}
powerPelletTimer?.cancel();
powerPelletTimer = Timer(const Duration(seconds: 8), () {
powerPelletActive = false;
for (var ghost in ghosts) {
ghost.isVulnerable = false;
ghost.isScared = false;
}
if (mounted) setState(() {});
});
setState(() {});
}
void checkCollisions() {
for (var ghost in ghosts) {
double distance = sqrt(pow(player.x - ghost.x, 2) + pow(player.y - ghost.y, 2));
if (distance < 0.8) {
if (ghost.isVulnerable) {
int points = 100 * pow(2, ghostsEaten).toInt();
score += points;
ghostsEaten++;
ghost.x = 9.0; ghost.y = 9.0;
ghost.isVulnerable = false; ghost.isScared = false;
if (score > highScore) highScore = score;
} else {
lives--;
if (lives <= 0) {
gameOver();
} else {
player.x = 9.0; player.y = 15.0;
player.direction = Direction.right;
player.nextDirection = Direction.right;
}
break;
}
}
}
}
void checkWinCondition() {
int remainingDots = 0;
for (var row in maze) {
for (var cell in row) {
if (cell == 2 || cell == 3) remainingDots++;
}
}
if (remainingDots == 0) win();
}
void gameOver() {
setState(() { gameState = GameState.gameOver; });
gameTimer?.cancel();
saveHighScore();
}
void win() {
setState(() { gameState = GameState.win; });
gameTimer?.cancel();
saveHighScore();
}
void resetGame() {
gameTimer?.cancel();
setState(() { gameState = GameState.menu; });
}
void setPlayerDirection(Direction direction) {
if (gameState == GameState.playing) {
player.nextDirection = direction;
}
}
@override
void dispose() {
mouthController.dispose();
introController.dispose();
gameTimer?.cancel();
powerPelletTimer?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: ApliArteColors.background,
body: SafeArea(
child: buildGameScreen(),
),
);
}
Widget buildGameScreen() {
switch (gameState) {
case GameState.intro:
return buildIntroScreen();
case GameState.menu:
return buildMenuScreen();
case GameState.playing:
return buildPlayingScreen();
case GameState.gameOver:
return buildGameOverScreen();
case GameState.win:
return buildWinScreen();
}
}
// --- PANTALLA INTRO APLIARTE ---
Widget buildIntroScreen() {
return FadeTransition(
opacity: introController,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Simulación del logo con texto y colores
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.keyboard_double_arrow_up, size: 80, color: ApliArteColors.darkBlue),
const SizedBox(width: 10),
Icon(Icons.keyboard_double_arrow_up, size: 80, color: ApliArteColors.lightBlue),
],
),
const SizedBox(height: 20),
RichText(
text: TextSpan(
style: const TextStyle(fontSize: 40, fontWeight: FontWeight.bold, fontFamily: 'monospace'),
children: [
TextSpan(text: 'Apli', style: TextStyle(color: ApliArteColors.lightBlue)),
TextSpan(text: 'Arte', style: TextStyle(color: ApliArteColors.darkGrey)),
],
),
),
const SizedBox(height: 10),
const Text(
'GAMES',
style: TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
letterSpacing: 8.0,
color: ApliArteColors.darkBlue,
fontFamily: 'monospace',
),
),
],
),
),
);
}
Widget buildMenuScreen() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'PAC-MAN',
style: TextStyle(
fontSize: 48,
fontWeight: FontWeight.bold,
color: ApliArteColors.lightBlue, // Pacman azul ApliArte
fontFamily: 'monospace',
),
),
const SizedBox(height: 16),
Text(
'EDICIÓN APLIARTE',
style: TextStyle(
fontSize: 20,
color: ApliArteColors.darkBlue,
fontFamily: 'monospace',
),
),
const SizedBox(height: 32),
Text(
'Récord: $highScore',
style: const TextStyle(
fontSize: 18,
color: Colors.white,
fontFamily: 'monospace',
),
),
const SizedBox(height: 32),
ElevatedButton(
onPressed: startGame,
style: ElevatedButton.styleFrom(
backgroundColor: ApliArteColors.lightBlue,
foregroundColor: ApliArteColors.darkGrey,
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16),
textStyle: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
child: const Text('JUGAR'),
),
const SizedBox(height: 16),
const Text(
'Usa los controles para moverte',
style: TextStyle(fontSize: 14, color: Colors.grey, fontFamily: 'monospace'),
),
],
),
);
}
Widget buildPlayingScreen() {
return Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
icon: const Icon(Icons.arrow_back, color: ApliArteColors.lightBlue),
onPressed: () {
setState(() => gameState = GameState.menu);
},
),
Column(
children: [
Text('PUNTOS', style: TextStyle(fontSize: 12, color: ApliArteColors.lightBlue, fontFamily: 'monospace')),
Text('$score', style: const TextStyle(fontSize: 20, color: Colors.white, fontFamily: 'monospace')),
],
),
Column(
children: [
Text('RÉCORD', style: TextStyle(fontSize: 12, color: ApliArteColors.darkBlue, fontFamily: 'monospace')),
Text('$highScore', style: const TextStyle(fontSize: 20, color: Colors.white, fontFamily: 'monospace')),
],
),
Row(
children: List.generate(lives, (index) => const Icon(Icons.favorite, color: Colors.red, size: 20)),
),
],
),
),
Expanded(
child: Center(
child: LayoutBuilder(
builder: (context, constraints) {
double maxWidth = constraints.maxWidth;
double maxHeight = constraints.maxHeight;
gridSize = min(maxWidth / mazeWidth, maxHeight / mazeHeight);
return Container(
width: gridSize * mazeWidth,
height: gridSize * mazeHeight,
decoration: BoxDecoration(
border: Border.all(color: ApliArteColors.darkBlue, width: 2),
),
child: CustomPaint(
painter: GamePainter(
maze: maze,
player: player,
ghosts: ghosts,
mouthAnimation: mouthController,
gridSize: gridSize,
),
size: Size(gridSize * mazeWidth, gridSize * mazeHeight),
),
);
},
),
),
),
buildControls(),
],
);
}
Widget buildControls() {
return Container(
padding: const EdgeInsets.all(16),
child: Column(
children: [
ElevatedButton(
onPressed: () => setPlayerDirection(Direction.up),
style: ElevatedButton.styleFrom(
backgroundColor: ApliArteColors.darkGrey,
shape: const CircleBorder(),
padding: const EdgeInsets.all(20),
),
child: const Icon(Icons.keyboard_arrow_up, color: ApliArteColors.lightBlue, size: 30),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: () => setPlayerDirection(Direction.left),
style: ElevatedButton.styleFrom(
backgroundColor: ApliArteColors.darkGrey,
shape: const CircleBorder(),
padding: const EdgeInsets.all(20),
),
child: const Icon(Icons.keyboard_arrow_left, color: ApliArteColors.lightBlue, size: 30),
),
const SizedBox(width: 40),
ElevatedButton(
onPressed: () => setPlayerDirection(Direction.right),
style: ElevatedButton.styleFrom(
backgroundColor: ApliArteColors.darkGrey,
shape: const CircleBorder(),
padding: const EdgeInsets.all(20),
),
child: const Icon(Icons.keyboard_arrow_right, color: ApliArteColors.lightBlue, size: 30),
),
],
),
ElevatedButton(
onPressed: () => setPlayerDirection(Direction.down),
style: ElevatedButton.styleFrom(
backgroundColor: ApliArteColors.darkGrey,
shape: const CircleBorder(),
padding: const EdgeInsets.all(20),
),
child: const Icon(Icons.keyboard_arrow_down, color: ApliArteColors.lightBlue, size: 30),
),
],
),
);
}
Widget buildGameOverScreen() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'FIN DEL JUEGO',
style: TextStyle(fontSize: 36, fontWeight: FontWeight.bold, color: Colors.red, fontFamily: 'monospace'),
),
const SizedBox(height: 16),
Text(
'Puntaje Final: $score',
style: TextStyle(fontSize: 24, color: ApliArteColors.lightBlue, fontFamily: 'monospace'),
),
const SizedBox(height: 32),
ElevatedButton(
onPressed: resetGame,
style: ElevatedButton.styleFrom(backgroundColor: Colors.red, padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16)),
child: const Text('REINTENTAR', style: TextStyle(fontSize: 18, color: Colors.white)),
),
],
),
);
}
Widget buildWinScreen() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'¡GANASTE!',
style: TextStyle(fontSize: 36, fontWeight: FontWeight.bold, color: ApliArteColors.lightBlue, fontFamily: 'monospace'),
),
const SizedBox(height: 16),
Text(
'Puntaje Final: $score',
style: const TextStyle(fontSize: 24, color: Colors.white, fontFamily: 'monospace'),
),
const SizedBox(height: 32),
ElevatedButton(
onPressed: resetGame,
style: ElevatedButton.styleFrom(backgroundColor: ApliArteColors.lightBlue, padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16)),
child: Text('JUGAR DE NUEVO', style: TextStyle(fontSize: 18, color: ApliArteColors.darkGrey)),
),
],
),
);
}
}
class Player {
double x = 9.0;
double y = 15.0;
Direction direction = Direction.right;
Direction nextDirection = Direction.right;
}
class Ghost {
double x;
double y;
Color color;
Direction direction;
Random random = Random();
bool isVulnerable = false;
bool isScared = false;
Ghost({required this.x, required this.y, required this.color, required this.direction});
void move(List<List<int>> maze, int mazeWidth, int mazeHeight) {
List<Direction> possibleDirections = [];
if (x > 0 && maze[y.round()][(x - 0.3).round()] != 1) possibleDirections.add(Direction.left);
if (x < mazeWidth - 1 && maze[y.round()][(x + 0.3).round()] != 1) possibleDirections.add(Direction.right);
if (y > 0 && maze[(y - 0.3).round()][x.round()] != 1) possibleDirections.add(Direction.up);
if (y < mazeHeight - 1 && maze[(y + 0.3).round()][x.round()] != 1) possibleDirections.add(Direction.down);
if (!possibleDirections.contains(direction) || random.nextInt(20) == 0) {
if (possibleDirections.isNotEmpty) direction = possibleDirections[random.nextInt(possibleDirections.length)];
}
switch (direction) {
case Direction.right: if (possibleDirections.contains(Direction.right)) x += 0.15; break;
case Direction.down: if (possibleDirections.contains(Direction.down)) y += 0.15; break;
case Direction.left: if (possibleDirections.contains(Direction.left)) x -= 0.15; break;
case Direction.up: if (possibleDirections.contains(Direction.up)) y -= 0.15; break;
}
if (x < 0) x = mazeWidth - 1;
if (x >= mazeWidth) x = 0;
}
void moveAway(List<List<int>> maze, int mazeWidth, int mazeHeight, double playerX, double playerY) {
List<Direction> possibleDirections = [];
if (x > 0 && maze[y.round()][(x - 0.3).round()] != 1) possibleDirections.add(Direction.left);
if (x < mazeWidth - 1 && maze[y.round()][(x + 0.3).round()] != 1) possibleDirections.add(Direction.right);
if (y > 0 && maze[(y - 0.3).round()][x.round()] != 1) possibleDirections.add(Direction.up);
if (y < mazeHeight - 1 && maze[(y + 0.3).round()][x.round()] != 1) possibleDirections.add(Direction.down);
if (possibleDirections.isEmpty) return;
Direction bestDirection = possibleDirections[0];
double maxDistance = 0;
for (var dir in possibleDirections) {
double testX = x, testY = y;
switch (dir) {
case Direction.left: testX -= 0.15; break;
case Direction.right: testX += 0.15; break;
case Direction.up: testY -= 0.15; break;
case Direction.down: testY += 0.15; break;
}
double distance = sqrt(pow(testX - playerX, 2) + pow(testY - playerY, 2));
if (distance > maxDistance) {
maxDistance = distance;
bestDirection = dir;
}
}
direction = bestDirection;
switch (direction) {
case Direction.right: x += 0.15; break;
case Direction.down: y += 0.15; break;
case Direction.left: x -= 0.15; break;
case Direction.up: y -= 0.15; break;
}
if (x < 0) x = mazeWidth - 1;
if (x >= mazeWidth) x = 0;
}
}
class GamePainter extends CustomPainter {
final List<List<int>> maze;
final Player player;
final List<Ghost> ghosts;
final AnimationController mouthAnimation;
final double gridSize;
GamePainter({required this.maze, required this.player, required this.ghosts, required this.mouthAnimation, required this.gridSize});
@override
void paint(Canvas canvas, Size size) {
drawMaze(canvas);
drawGhosts(canvas);
drawPlayer(canvas);
}
void drawMaze(Canvas canvas) {
for (int y = 0; y < maze.length; y++) {
for (int x = 0; x < maze[y].length; x++) {
double pixelX = x * gridSize;
double pixelY = y * gridSize;
switch (maze[y][x]) {
case 1: // Pared (USANDO AZUL OSCURO APLIARTE)
Paint wallPaint = Paint()..color = ApliArteColors.darkBlue;
canvas.drawRect(Rect.fromLTWH(pixelX, pixelY, gridSize, gridSize), wallPaint);
break;
case 2: // Punto pequeño
Paint dotPaint = Paint()..color = Colors.white.withOpacity(0.5);
canvas.drawCircle(Offset(pixelX + gridSize / 2, pixelY + gridSize / 2), gridSize * 0.12, dotPaint);
break;
case 3: // Power pellet
Paint pelletPaint = Paint()..color = Colors.white;
canvas.drawCircle(Offset(pixelX + gridSize / 2, pixelY + gridSize / 2), gridSize * 0.32, pelletPaint);
break;
}
}
}
}
void drawPlayer(Canvas canvas) {
double pixelX = player.x * gridSize + gridSize / 2;
double pixelY = player.y * gridSize + gridSize / 2;
// PACMAN COLOR APLIARTE (Azul Claro)
Paint playerPaint = Paint()..color = ApliArteColors.lightBlue;
canvas.drawCircle(Offset(pixelX, pixelY), gridSize / 2 - 2, playerPaint);
Paint mouthPaint = Paint()..color = Colors.black;
double mouthAngle = mouthAnimation.value * 60;
double startAngle = 0;
switch (player.direction) {
case Direction.right: startAngle = -mouthAngle / 2 * (pi / 180); break;
case Direction.down: startAngle = (90 - mouthAngle / 2) * (pi / 180); break;
case Direction.left: startAngle = (180 - mouthAngle / 2) * (pi / 180); break;
case Direction.up: startAngle = (270 - mouthAngle / 2) * (pi / 180); break;
}
canvas.drawArc(Rect.fromCircle(center: Offset(pixelX, pixelY), radius: gridSize / 2 - 2), startAngle, mouthAngle * (pi / 180), true, mouthPaint);
}
void drawGhosts(Canvas canvas) {
for (var ghost in ghosts) {
double pixelX = ghost.x * gridSize;
double pixelY = ghost.y * gridSize;
Color ghostColor = ghost.isVulnerable ? ApliArteColors.darkBlue.withOpacity(0.7) : ghost.color; // Fantasmas vulnerables se ponen azul oscuro
Paint ghostPaint = Paint()..color = ghostColor;
Path ghostPath = Path();
ghostPath.addRRect(RRect.fromRectAndCorners(Rect.fromLTWH(pixelX + 2, pixelY + 2, gridSize - 4, gridSize - 4), topLeft: Radius.circular(gridSize / 2), topRight: Radius.circular(gridSize / 2)));
canvas.drawPath(ghostPath, ghostPaint);
Paint eyePaint = Paint()..color = Colors.white;
Paint pupilPaint = Paint()..color = Colors.black;
canvas.drawCircle(Offset(pixelX + gridSize * 0.32, pixelY + gridSize * 0.32), gridSize * 0.12, eyePaint);
canvas.drawCircle(Offset(pixelX + gridSize * 0.32, pixelY + gridSize * 0.32), gridSize * 0.06, pupilPaint);
canvas.drawCircle(Offset(pixelX + gridSize * 0.68, pixelY + gridSize * 0.32), gridSize * 0.12, eyePaint);
canvas.drawCircle(Offset(pixelX + gridSize * 0.68, pixelY + gridSize * 0.32), gridSize * 0.06, pupilPaint);
}
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment