Last active
September 23, 2020 15:33
-
-
Save FantasyCheese/04142b1e7777543aa4f753a096733ca1 to your computer and use it in GitHub Desktop.
Pacman in Flutter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
import 'dart:async'; | |
import 'dart:math'; | |
const originalMap = [ | |
[1,1,1,1,1,1,1,1,1,1,1,], | |
[1,0,0,0,0,0,0,0,0,0,1,], | |
[1,0,1,0,1,0,1,0,1,0,1,], | |
[1,0,1,0,1,1,1,0,1,0,1,], | |
[1,0,1,0,0,0,0,0,1,0,1,], | |
[1,0,1,0,1,0,1,0,1,0,1,], | |
[1,0,0,0,1,0,1,0,0,0,1,], | |
[1,1,1,1,1,0,1,1,1,1,1,], | |
[0,0,0,0,0,0,0,0,0,0,0,], | |
[1,1,1,1,1,0,1,1,1,1,1,], | |
[1,0,0,0,1,0,1,0,0,0,1,], | |
[1,0,1,0,1,0,1,0,1,0,1,], | |
[1,0,1,0,0,0,0,0,1,0,1,], | |
[1,0,1,0,1,1,1,0,1,0,1,], | |
[1,0,1,0,1,0,1,0,1,0,1,], | |
[1,0,0,0,0,0,0,0,0,0,1,], | |
[1,1,1,1,1,1,1,1,1,1,1,], | |
]; | |
enum Direction { LEFT, UP, DOWN, RIGHT } | |
class Character { | |
Character(this.position); | |
Point position; | |
static Point _nextPosition(Point position, Direction direction) { | |
Point newPosition = position; | |
switch (direction) { | |
case Direction.UP: newPosition += Point(0, -1); break; | |
case Direction.DOWN: newPosition += Point(0, 1); break; | |
case Direction.LEFT: newPosition += Point(-1, 0); break; | |
case Direction.RIGHT: newPosition += Point(1, 0); break; | |
} | |
return newPosition; | |
} | |
bool tryMove(List<List<int>> map, Direction direction) { | |
final nextPosition = _nextPosition(position, direction); | |
if (map[nextPosition.y][nextPosition.x] == 1) | |
return false; | |
position = nextPosition; | |
return true; | |
} | |
} | |
class Pacman extends Character { | |
Pacman(Point position) : super(position); | |
Direction facing = Direction.RIGHT; | |
@override | |
bool tryMove(List<List<int>> map, Direction direction) { | |
final moved = super.tryMove(map, direction); | |
if (moved) { | |
facing = direction; | |
map[position.y][position.x] = -1; | |
} | |
return moved; | |
} | |
} | |
class Ghost extends Character { | |
Ghost(Point position) : super(position); | |
Direction heading = Direction.UP; | |
void move(List<List<int>> map) { | |
while (!tryMove(map, heading)) { | |
heading = Direction.values[Random().nextInt(3)]; | |
} | |
} | |
} | |
void main() { | |
runApp(MaterialApp( | |
home: PacManScreen(), | |
)); | |
} | |
class PacManScreen extends StatefulWidget { | |
@override | |
_PacManScreenState createState() => _PacManScreenState(); | |
} | |
class _PacManScreenState extends State<PacManScreen> { | |
Pacman pacman; | |
List<Ghost> ghosts; | |
List<List<int>> currentMap; | |
Timer timer; | |
bool gameOver; | |
@override | |
void initState() { | |
super.initState(); | |
_startGame(); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
backgroundColor: Colors.white, | |
body: Column( | |
children: [ | |
Expanded(child: Container( | |
color: Colors.black, | |
child: _buildGameDisplay(), | |
)), | |
_buildGameInfo(), | |
_buildGameControl(), | |
], | |
), | |
); | |
} | |
void _startGame() { | |
setState(() { | |
gameOver = false; | |
pacman = Pacman(Point(1, 1)); | |
ghosts = [ | |
Ghost(Point(7,1)), | |
Ghost(Point(1, 15)), | |
Ghost(Point(9, 13)), | |
]; | |
currentMap = List.generate(originalMap.length, (i) => List.from(originalMap[i])); | |
timer?.cancel(); | |
timer = Timer.periodic(Duration(seconds: 1), (timer) { | |
setState(() { | |
ghosts.forEach((it) => it.move(currentMap)); | |
_checkGameOver(); | |
}); | |
}); | |
}); | |
} | |
void _checkGameOver() { | |
gameOver = ghosts.any((it) => it.position == pacman.position); | |
if (gameOver) timer.cancel(); | |
} | |
Row _buildGameInfo() { | |
final score = currentMap.map((row) => row.where((it) => it == -1).length).fold(0, (a, b) => a+b); | |
return Row( | |
mainAxisAlignment: MainAxisAlignment.spaceEvenly, | |
children: [ | |
FlatButton( | |
child: Text("START"), | |
onPressed: () => _startGame(), | |
color: Colors.green, | |
), | |
Text("SCORE: $score"), | |
], | |
); | |
} | |
Row _buildGameControl() { | |
return Row( | |
mainAxisAlignment: MainAxisAlignment.spaceEvenly, | |
children: Direction.values.map(_buildKey).toList(), | |
); | |
} | |
Widget _buildKey(Direction direction) { | |
IconData icon; | |
switch(direction) { | |
case Direction.RIGHT: icon = Icons.arrow_forward; break; | |
case Direction.LEFT: icon = Icons.arrow_back; break; | |
case Direction.UP: icon = Icons.arrow_upward; break; | |
case Direction.DOWN: icon = Icons.arrow_downward; break; | |
} | |
return GestureDetector( | |
onTap: () { | |
if (gameOver) return; | |
setState(() { | |
pacman.tryMove(currentMap, direction); | |
_checkGameOver(); | |
}); | |
}, | |
child: Padding( | |
padding: EdgeInsets.all(8), | |
child: Icon(icon), | |
), | |
); | |
} | |
Widget _buildGameDisplay() { | |
if (gameOver) { | |
return Center(child: Text("GAME OVER!", style: TextStyle(color: Colors.red))); | |
} | |
final rows = currentMap.length; | |
final columns = currentMap[0].length; | |
return GridView.count( | |
crossAxisCount: columns, | |
mainAxisSpacing: 2, | |
crossAxisSpacing: 2, | |
childAspectRatio: 1.1, | |
children: List.generate(rows * columns, (index) { | |
final position = Point(index % columns, index ~/ columns); | |
final cellValue = currentMap[position.y][position.x]; | |
return _buildCell(position, cellValue); | |
}), | |
); | |
} | |
Widget _buildCell(Point<int> position, int cell) { | |
if (ghosts.any((ghost) => ghost.position == position)) { | |
return Image.network("https://i.imgur.com/1uneU2Q.png"); | |
} | |
if (pacman.position == position) { | |
return RotatedBox( | |
quarterTurns: [Direction.RIGHT, Direction.DOWN, Direction.LEFT, Direction.UP,].indexOf(pacman.facing), | |
child: Image.network("https://i.imgur.com/8foCiWg.png"), | |
); | |
} | |
switch (cell) { | |
case -1: return Container(); | |
case 0: return Padding( | |
padding: const EdgeInsets.all(12.0), | |
child: Container( | |
decoration: BoxDecoration( | |
color: Colors.yellow, | |
borderRadius: BorderRadius.circular(16), | |
), | |
), | |
); | |
case 1: return Container( | |
decoration: BoxDecoration( | |
color: Colors.blue, | |
borderRadius: BorderRadius.circular(8) | |
), | |
); | |
} | |
return Container(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment