import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'maze.dart'; import 'player.dart'; import 'raycaster.dart'; void main() { runApp(const LabiryntApp()); } class LabiryntApp extends StatelessWidget { const LabiryntApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Labirynt 3D', theme: ThemeData.dark(), home: const GameScreen(), ); } } class GameScreen extends StatefulWidget { const GameScreen({super.key}); @override State createState() => _GameScreenState(); } class _GameScreenState extends State with SingleTickerProviderStateMixin { late Maze maze; late Player player; late AnimationController _controller; final Set _pressedKeys = {}; bool _won = false; int _mazeSize = 15; @override void initState() { super.initState(); _initGame(); _controller = AnimationController( vsync: this, duration: const Duration(seconds: 1), )..addListener(_gameLoop); _controller.repeat(); } void _initGame() { if (_mazeSize.isEven) _mazeSize++; maze = Maze(_mazeSize, _mazeSize); player = Player(x: 1.5, y: 1.5); _won = false; } void _gameLoop() { if (_won) return; setState(() { if (_pressedKeys.contains(LogicalKeyboardKey.keyW) || _pressedKeys.contains(LogicalKeyboardKey.arrowUp)) { player.moveForward(maze); } if (_pressedKeys.contains(LogicalKeyboardKey.keyS) || _pressedKeys.contains(LogicalKeyboardKey.arrowDown)) { player.moveBackward(maze); } if (_pressedKeys.contains(LogicalKeyboardKey.keyA)) { player.strafeLeft(maze); } if (_pressedKeys.contains(LogicalKeyboardKey.keyD)) { player.strafeRight(maze); } if (_pressedKeys.contains(LogicalKeyboardKey.arrowLeft)) { player.rotateLeft(); } if (_pressedKeys.contains(LogicalKeyboardKey.arrowRight)) { player.rotateRight(); } if (maze.isGoal(player.x, player.y)) { _won = true; } }); } void _newGame() { setState(() { _mazeSize += 2; if (_mazeSize > 31) _mazeSize = 15; _initGame(); }); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( body: KeyboardListener( focusNode: FocusNode()..requestFocus(), autofocus: true, onKeyEvent: (event) { if (event is KeyDownEvent) { _pressedKeys.add(event.logicalKey); } else if (event is KeyUpEvent) { _pressedKeys.remove(event.logicalKey); } }, child: Stack( children: [ CustomPaint( painter: RaycasterPainter(maze: maze, player: player), size: Size.infinite, ), Positioned( bottom: 16, left: 16, child: Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.black54, borderRadius: BorderRadius.circular(8), ), child: const Text( 'WASD / Strzalki — ruch\nCel: zielony punkt na mapie', style: TextStyle(color: Colors.white70, fontSize: 14), ), ), ), Positioned( bottom: 16, right: 16, child: Column( children: [ _controlButton(Icons.arrow_upward, () => player.moveForward(maze)), Row( mainAxisSize: MainAxisSize.min, children: [ _controlButton(Icons.rotate_left, () => player.rotateLeft()), const SizedBox(width: 8), _controlButton(Icons.rotate_right, () => player.rotateRight()), ], ), _controlButton(Icons.arrow_downward, () => player.moveBackward(maze)), ], ), ), if (_won) Container( color: Colors.black87, child: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ const Text( 'WYGRANA!', style: TextStyle( color: Colors.greenAccent, fontSize: 64, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 24), Text( 'Labirynt ${_mazeSize}x$_mazeSize ukonczony', style: const TextStyle(color: Colors.white70, fontSize: 24), ), const SizedBox(height: 32), ElevatedButton( onPressed: _newGame, style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16), ), child: const Text('Nastepny poziom', style: TextStyle(fontSize: 20)), ), ], ), ), ), ], ), ), ); } Widget _controlButton(IconData icon, VoidCallback onPressed) { return Padding( padding: const EdgeInsets.all(2), child: GestureDetector( onTapDown: (_) => setState(onPressed), child: Container( width: 56, height: 56, decoration: BoxDecoration( color: Colors.white24, borderRadius: BorderRadius.circular(12), ), child: Icon(icon, color: Colors.white70, size: 28), ), ), ); } }