210 lines
6.0 KiB
Dart
210 lines
6.0 KiB
Dart
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<GameScreen> createState() => _GameScreenState();
|
|
}
|
|
|
|
class _GameScreenState extends State<GameScreen> with SingleTickerProviderStateMixin {
|
|
late Maze maze;
|
|
late Player player;
|
|
late AnimationController _controller;
|
|
final Set<LogicalKeyboardKey> _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),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|