Initial commit: labirynt 3D pseudo-raycasting game

This commit is contained in:
lukasz@orzechowski.eu
2026-02-07 10:20:50 +01:00
commit a7f14c010f
132 changed files with 5319 additions and 0 deletions

155
lib/raycaster.dart Normal file
View File

@@ -0,0 +1,155 @@
import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'maze.dart';
import 'player.dart';
class RaycasterPainter extends CustomPainter {
final Maze maze;
final Player player;
static const double fov = pi / 3;
static const int maxDepth = 20;
RaycasterPainter({required this.maze, required this.player});
@override
void paint(Canvas canvas, Size size) {
_drawSky(canvas, size);
_drawFloor(canvas, size);
_drawWalls(canvas, size);
_drawMinimap(canvas, size);
}
void _drawSky(Canvas canvas, Size size) {
final paint = Paint()
..shader = const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.center,
colors: [Color(0xFF1a1a2e), Color(0xFF16213e)],
).createShader(Rect.fromLTWH(0, 0, size.width, size.height / 2));
canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height / 2), paint);
}
void _drawFloor(Canvas canvas, Size size) {
final paint = Paint()
..shader = const LinearGradient(
begin: Alignment.center,
end: Alignment.bottomCenter,
colors: [Color(0xFF2d4059), Color(0xFF1a1a2e)],
).createShader(Rect.fromLTWH(0, size.height / 2, size.width, size.height / 2));
canvas.drawRect(Rect.fromLTWH(0, size.height / 2, size.width, size.height / 2), paint);
}
void _drawWalls(Canvas canvas, Size size) {
final numRays = size.width.toInt();
for (int i = 0; i < numRays; i++) {
final rayAngle = player.angle - fov / 2 + (i / numRays) * fov;
final result = _castRay(rayAngle);
final distance = result.$1 * cos(rayAngle - player.angle);
if (distance <= 0) continue;
final wallHeight = size.height / distance;
final wallTop = (size.height - wallHeight) / 2;
final brightness = (1.0 - (distance / maxDepth)).clamp(0.1, 1.0);
final isVertical = result.$2;
final r = isVertical ? (40 * brightness).toInt() : (60 * brightness).toInt();
final g = isVertical ? (80 * brightness).toInt() : (100 * brightness).toInt();
final b = isVertical ? (120 * brightness).toInt() : (160 * brightness).toInt();
final paint = Paint()
..color = Color.fromARGB(255, r, g, b)
..strokeWidth = 1.5;
canvas.drawLine(
Offset(i.toDouble(), wallTop),
Offset(i.toDouble(), wallTop + wallHeight),
paint,
);
}
}
(double, bool) _castRay(double angle) {
final sinA = sin(angle);
final cosA = cos(angle);
double dist = 0;
bool vertical = false;
for (double d = 0.01; d < maxDepth; d += 0.02) {
final testX = player.x + cosA * d;
final testY = player.y + sinA * d;
if (maze.isWall(testX.toInt(), testY.toInt())) {
dist = d;
final fracX = testX - testX.floorToDouble();
final fracY = testY - testY.floorToDouble();
vertical = fracX < 0.05 || fracX > 0.95;
if (!vertical) vertical = !(fracY < 0.05 || fracY > 0.95);
break;
}
}
return (dist, vertical);
}
void _drawMinimap(Canvas canvas, Size size) {
const cellSize = 6.0;
const offsetX = 10.0;
const offsetY = 10.0;
final mapWidth = maze.width * cellSize;
final mapHeight = maze.height * cellSize;
// Background
canvas.drawRect(
Rect.fromLTWH(offsetX - 2, offsetY - 2, mapWidth + 4, mapHeight + 4),
Paint()..color = const Color(0x88000000),
);
// Walls
final wallPaint = Paint()..color = const Color(0xFF4a6fa5);
for (int y = 0; y < maze.height; y++) {
for (int x = 0; x < maze.width; x++) {
if (maze.grid[y][x] == 1) {
canvas.drawRect(
Rect.fromLTWH(offsetX + x * cellSize, offsetY + y * cellSize, cellSize, cellSize),
wallPaint,
);
}
}
}
// Goal
canvas.drawCircle(
Offset(offsetX + (maze.width - 2) * cellSize + cellSize / 2,
offsetY + (maze.height - 2) * cellSize + cellSize / 2),
cellSize / 2,
Paint()..color = const Color(0xFF00ff00),
);
// Player
canvas.drawCircle(
Offset(offsetX + player.x * cellSize, offsetY + player.y * cellSize),
cellSize / 2,
Paint()..color = const Color(0xFFff4444),
);
// Direction
canvas.drawLine(
Offset(offsetX + player.x * cellSize, offsetY + player.y * cellSize),
Offset(
offsetX + (player.x + cos(player.angle) * 2) * cellSize,
offsetY + (player.y + sin(player.angle) * 2) * cellSize,
),
Paint()
..color = const Color(0xFFff4444)
..strokeWidth = 2,
);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}