Slide doors open

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
2026-03-13 17:24:35 +01:00
parent de643b7cdc
commit 630f027b68
2 changed files with 78 additions and 21 deletions

View File

@@ -11,6 +11,7 @@ class RaycasterPainter extends CustomPainter {
final LinearCoordinates player;
final double playerAngle;
final double fov;
final Map<String, double> doorOffsets;
RaycasterPainter({
required this.map,
@@ -18,6 +19,7 @@ class RaycasterPainter extends CustomPainter {
required this.player,
required this.playerAngle,
required this.fov,
required this.doorOffsets,
});
@override
@@ -61,6 +63,11 @@ class RaycasterPainter extends CustomPainter {
bool hitOutOfBounds = false;
int side = 0;
int hitWallId = 0;
double doorOffset =
0.0; // Track the slide amount to pass to the texture drawer
Set<String> ignoredDoors =
{}; // Prevent the ray from checking the same door gap twice
if (rayDirX < 0) {
stepX = -1;
@@ -94,8 +101,33 @@ class RaycasterPainter extends CustomPainter {
mapX < 0 ||
mapX >= map[0].length) {
hit = true;
hitOutOfBounds = true; // Safely abort if we clip out of bounds
hitOutOfBounds = true;
} else if (map[mapY][mapX] > 0) {
// NEW: DOOR PASS-THROUGH LOGIC
String doorKey = '$mapX,$mapY';
if (map[mapY][mapX] >= 90 && !ignoredDoors.contains(doorKey)) {
double currentOffset = doorOffsets[doorKey] ?? 0.0;
if (currentOffset > 0.0) {
// Calculate exactly where the ray hit the block
double perpWallDistTemp = (side == 0)
? (sideDistX - deltaDistX)
: (sideDistY - deltaDistY);
double wallXTemp = (side == 0)
? player.y + perpWallDistTemp * rayDirY
: player.x + perpWallDistTemp * rayDirX;
wallXTemp -= wallXTemp.floor();
// If the hit coordinate is less than the open amount, the ray passes through!
if (wallXTemp < currentOffset) {
ignoredDoors.add(doorKey);
continue; // Skip the rest of this loop iteration and keep raycasting!
}
}
doorOffset =
currentOffset; // Save the offset so we can slide the texture
}
hit = true;
hitWallId = map[mapY][mapX];
}
@@ -127,6 +159,7 @@ class RaycasterPainter extends CustomPainter {
size,
hitWallId,
textures,
doorOffset,
);
}
}
@@ -140,28 +173,29 @@ class RaycasterPainter extends CustomPainter {
Size size,
int hitWallId,
List<Matrix<int>> textures,
double doorOffset,
) {
if (distance <= 0.01) distance = 0.01;
double wallHeight = size.height / distance;
int drawStart = ((size.height / 2) - (wallHeight / 2)).toInt();
// 1. PERFECT TEXTURE MAPPING
// TEXTURE MAPPING
// Wolf3D stores textures in pairs. Even = N/S (Light), Odd = E/W (Dark).
int texNum = ((hitWallId - 1) * 2).clamp(0, textures.length - 2);
int texX = (wallX * 64).toInt().clamp(0, 63);
// INTERCEPT DOORS
if (hitWallId >= 90) {
// Safely clamp the door texture index so it never crashes the paint loop!
texNum = 98.clamp(0, textures.length - 1);
texX = ((wallX - doorOffset) * 64).toInt().clamp(0, 63);
} else {
// Standard wall texture pairing
texNum = ((hitWallId - 1) * 2).clamp(0, textures.length - 2);
if (side == 1) texNum += 1;
}
int texX = (wallX * 64).toInt().clamp(0, 63);
if (side == 0 && math.cos(playerAngle) > 0) texX = 63 - texX;
if (side == 1 && math.sin(playerAngle) < 0) texX = 63 - texX;

View File

@@ -30,6 +30,11 @@ class _WolfRendererState extends State<WolfRenderer>
bool _isLoading = true;
bool _spaceWasPressed = false;
// Track door animations
// Key is "X,Y" coordinate. Value is how far open it is (0.0 to 1.0)
Map<String, double> doorOffsets = {};
Map<String, int> doorStates = {}; // 1 = opening, 2 = fully open
@override
void initState() {
super.initState();
@@ -131,16 +136,38 @@ class _WolfRendererState extends State<WolfRenderer>
}
}
bool _isWalkable(int x, int y) {
if (currentLevel[y][x] == 0) return true; // Empty space
if (currentLevel[y][x] >= 90) {
String key = '$x,$y';
// Allow the player to walk through if the door is > 70% open
if (doorOffsets[key] != null && doorOffsets[key]! > 0.7) {
return true;
}
}
return false;
}
void _tick(Duration elapsed) {
const double moveSpeed = 0.12;
const double turnSpeed = 0.08;
// 1. ANIMATE DOORS
doorStates.forEach((key, state) {
if (state == 1) {
// If opening
doorOffsets[key] = (doorOffsets[key] ?? 0.0) + 0.02; // Slide speed
if (doorOffsets[key]! >= 1.0) {
doorOffsets[key] = 1.0;
doorStates[key] = 2; // Mark as fully open
}
}
});
double moveStepX = 0;
double moveStepY = 0;
final pressedKeys = HardwareKeyboard.instance.logicalKeysPressed;
// 1. Calculate intended movement amounts
if (pressedKeys.contains(LogicalKeyboardKey.keyW)) {
moveStepX += math.cos(playerAngle) * moveSpeed;
moveStepY += math.sin(playerAngle) * moveSpeed;
@@ -149,8 +176,6 @@ class _WolfRendererState extends State<WolfRenderer>
moveStepX -= math.cos(playerAngle) * moveSpeed;
moveStepY -= math.sin(playerAngle) * moveSpeed;
}
// 2. Handle Turning
if (pressedKeys.contains(LogicalKeyboardKey.keyA)) {
playerAngle -= turnSpeed;
}
@@ -160,18 +185,14 @@ class _WolfRendererState extends State<WolfRenderer>
if (playerAngle < 0) playerAngle += 2 * math.pi;
if (playerAngle > 2 * math.pi) playerAngle -= 2 * math.pi;
// 3. Wall Sliding Collision (with Hitbox Margin!)
// A 0.3 margin keeps the camera plane safely out of the walls
// 2. UPDATED WALL COLLISION
const double margin = 0.3;
double newX = player.x + moveStepX;
// Check the edge of our hitbox, not just the center point
int checkX = (moveStepX > 0)
? (newX + margin).toInt()
: (newX - margin).toInt();
// Try to move along the X axis
if (currentLevel[player.y.toInt()][checkX] == 0) {
if (_isWalkable(checkX, player.y.toInt())) {
player = (x: newX, y: player.y);
}
@@ -180,12 +201,11 @@ class _WolfRendererState extends State<WolfRenderer>
? (newY + margin).toInt()
: (newY - margin).toInt();
// Try to move along the Y axis
if (currentLevel[checkY][player.x.toInt()] == 0) {
if (_isWalkable(player.x.toInt(), checkY)) {
player = (x: player.x, y: newY);
}
// 4. DOOR INTERACTION LOGIC
// 3. UPDATED DOOR INTERACTION
bool isSpacePressed = pressedKeys.contains(LogicalKeyboardKey.space);
if (isSpacePressed && !_spaceWasPressed) {
@@ -196,10 +216,12 @@ class _WolfRendererState extends State<WolfRenderer>
targetY < currentLevel.length &&
targetX > 0 &&
targetX < currentLevel[0].length) {
int targetBlock = currentLevel[targetY][targetX];
if (targetBlock >= 90) {
currentLevel[targetY][targetX] = 0;
if (currentLevel[targetY][targetX] >= 90) {
String key = '$targetX,$targetY';
// Start the animation if it isn't already opening!
if (!doorStates.containsKey(key) || doorStates[key] == 0) {
doorStates[key] = 1;
}
}
}
}
@@ -230,6 +252,7 @@ class _WolfRendererState extends State<WolfRenderer>
player: player,
playerAngle: playerAngle,
fov: fov,
doorOffsets: doorOffsets,
),
);
},