Skip to content

Instantly share code, notes, and snippets.

@nosmirck
Last active November 30, 2023 08:27
Show Gist options
  • Save nosmirck/a6143a9af97d768d659ef41bdd051cfa to your computer and use it in GitHub Desktop.
Save nosmirck/a6143a9af97d768d659ef41bdd051cfa to your computer and use it in GitHub Desktop.
Widgetstein
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Widgetstein',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Widgetstein'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final colors = [Colors.white, Colors.blueGrey];
final map = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1],
[1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1],
[1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1],
[1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1],
[1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1],
[1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1],
[1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1],
[1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1],
[1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1],
[1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
];
final walls = <Map<String, double>>[];
double playerX = 5.5;
double playerY = 7.6;
double playerAngle = 4.2;
double resolution = 0.2;
double fov = pi / 3.0;
@override
void didChangeDependencies() {
super.didChangeDependencies();
update();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Column(
children: [
Expanded(
child: Stack(
children: [
LayoutBuilder(
builder: (context, constraints) {
final size =
min(constraints.maxHeight, constraints.maxWidth) /
map.length;
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: map
.map(
(e) => Row(
mainAxisAlignment: MainAxisAlignment.center,
children: e.map(
(e) {
return Container(
width: size,
height: size,
decoration: BoxDecoration(
color: colors[e],
border: Border.all(
color: Colors.black,
width: 0.1,
),
),
);
},
).toList(),
),
)
.toList(),
);
},
),
LayoutBuilder(
builder: (context, constraints) {
final size =
min(constraints.maxHeight, constraints.maxWidth);
final playerSize = size / map.length * 0.2;
return Center(
child: Container(
padding: EdgeInsets.zero,
margin: EdgeInsets.zero,
height: size,
width: size,
color: Colors.greenAccent.withOpacity(0.1),
alignment: Alignment(
mapNum(
playerX, 0.0, map.length.toDouble(), -1.0, 1.0),
mapNum(playerY, 0.0, map.length.toDouble(), -1.0,
1.0)),
child: SizedBox(
width: playerSize * 2,
height: playerSize * 2,
child: Transform.rotate(
angle: playerAngle,
child: Stack(
alignment: Alignment.center,
clipBehavior: Clip.none,
children: [
...walls.map(
(e) => Positioned(
left: playerSize,
child: Transform.rotate(
alignment: Alignment.centerLeft,
angle: e['angle']!,
child: SizedBox(
width:
e['distance']! * size / map.length,
height: 2,
child: Container(
color: Colors.blue,
),
),
),
),
),
Positioned(
left: playerSize,
child: SizedBox(
width: playerSize * 2,
height: 2,
child: Container(
color: Colors.green,
),
),
),
SizedBox(
width: playerSize,
height: playerSize,
child: ClipOval(
child: Container(
color: Colors.redAccent,
),
),
),
],
),
),
),
),
);
},
),
Positioned(
top: 0,
child: Column(
children: [
Text('Player Angle'),
Slider(
value: playerAngle,
onChanged: (value) {
playerAngle = value;
update();
},
min: -3 * pi,
max: 3 * pi,
),
Text('Player X'),
Slider(
value: playerX,
onChanged: (value) {
playerX = value;
update();
},
min: 0.0,
max: map.length.toDouble(),
),
Text('Player Y'),
Slider(
value: playerY,
onChanged: (value) {
playerY = value;
update();
},
min: 0.0,
max: map.length.toDouble(),
),
],
),
)
],
),
),
Expanded(
child: Stack(
children: [
Column(
children: [
Expanded(
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.lightBlue,
Colors.blue.shade900,
],
),
),
),
),
Expanded(
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
colors: [
Colors.lightGreen,
Colors.green.shade900,
],
),
),
),
),
],
),
LayoutBuilder(builder: (context, constraints) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: walls.map(
(e) {
final side = e['side']!.toInt();
final perpDistance = e['perpDistance']!;
final distance = e['distance']!;
final distance2 = min(1.0 / (distance), 1.0);
final colorBright = max(
(mapNum(distance2, 0.0, 1.0, 0.0, 255.0)) *
(side == 0 ? 1.0 : 0.75),
15)
.toInt();
final color = Color.fromARGB(
255, colorBright, colorBright, colorBright);
return Expanded(
child: Center(
child: Container(
height: constraints.maxHeight / perpDistance,
decoration: BoxDecoration(
color: color,
border: Border.all(
color: color,
width: 1,
),
),
),
),
);
},
).toList(),
);
})
],
),
),
],
),
);
}
void update() {
walls.clear();
final cols = (resolution * MediaQuery.of(context).size.width).toInt();
final fov2 = fov / 2.0;
final angleStep = fov / cols;
final maxDistance = map.length * 2.0;
for (var i = 0; i < cols; i++) {
final angle = -fov2 + angleStep * i;
final rayDirX = cos(playerAngle + angle);
final rayDirY = sin(playerAngle + angle);
final rayUnitStepSizeX =
sqrt(1 + (rayDirY / rayDirX) * (rayDirY / rayDirX));
final rayUnitStepSizeY =
sqrt(1 + (rayDirX / rayDirY) * (rayDirX / rayDirY));
var mapCheckX = playerX.toInt();
var mapCheckY = playerY.toInt();
var rayLenght1DX = (rayDirX < 0
? playerX - mapCheckX.toDouble()
: (mapCheckX + 1).toDouble() - playerX) *
rayUnitStepSizeX;
var rayLenght1DY = (rayDirY < 0
? playerY - mapCheckY.toDouble()
: (mapCheckY + 1).toDouble() - playerY) *
rayUnitStepSizeY;
var stepX = rayDirX < 0 ? -1 : 1;
var stepY = rayDirY < 0 ? -1 : 1;
var tileFound = false;
var distance = 0.0;
var side = 0.0;
if (mapCheckX >= 0 &&
mapCheckX < map.length &&
mapCheckY >= 0 &&
mapCheckY < map.length) {
if (map[mapCheckY][mapCheckX] > 0) {
tileFound = true;
}
}
while (!tileFound && distance < maxDistance) {
if (rayLenght1DX < rayLenght1DY) {
mapCheckX += stepX;
distance = rayLenght1DX;
rayLenght1DX += rayUnitStepSizeX;
side = 0.0;
} else {
mapCheckY += stepY;
distance = rayLenght1DY;
rayLenght1DY += rayUnitStepSizeY;
side = 1.0;
}
if (mapCheckX >= 0 &&
mapCheckX < map.length &&
mapCheckY >= 0 &&
mapCheckY < map.length) {
if (map[mapCheckY][mapCheckX] > 0) {
tileFound = true;
}
}
}
walls.add({
'distance': distance,
'perpDistance': cos(angle) * distance,
'angle': angle,
'side': side,
});
setState(() {});
}
}
}
double mapNum(
double x, double inMin, double inMax, double outMin, double outMax) {
return (x - inMin) * (outMax - outMin) / (inMax - inMin) + outMin;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment