feat: initial Star Taxi APK test prototype

This commit is contained in:
2026-02-23 15:11:14 +01:00
commit e295eb5195
130 changed files with 5157 additions and 0 deletions

304
lib/main.dart Normal file
View File

@@ -0,0 +1,304 @@
import 'dart:async';
import 'dart:math' as math;
import 'package:flutter/material.dart';
void main() {
runApp(const StarTaxiApp());
}
class StarTaxiApp extends StatelessWidget {
const StarTaxiApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Star Taxi APK Test',
theme: ThemeData.dark(useMaterial3: true),
home: const StarTaxiDemo(),
);
}
}
class StarTaxiDemo extends StatefulWidget {
const StarTaxiDemo({super.key});
@override
State<StarTaxiDemo> createState() => _StarTaxiDemoState();
}
class _StarTaxiDemoState extends State<StarTaxiDemo> {
static const int lanes = 3;
static const double playerY = 0.86;
final _rng = math.Random();
final List<_Obstacle> _obstacles = [];
Timer? _loop;
double _lane = 1;
double _speed = 0.010;
int _score = 0;
int _best = 0;
bool _running = true;
@override
void initState() {
super.initState();
_spawnInitial();
_loop = Timer.periodic(const Duration(milliseconds: 16), (_) => _tick());
}
@override
void dispose() {
_loop?.cancel();
super.dispose();
}
void _spawnInitial() {
_obstacles.clear();
for (var i = 0; i < 5; i++) {
_obstacles.add(_Obstacle(
lane: _rng.nextInt(lanes).toDouble(),
y: -0.8 - i * 0.45,
));
}
}
void _tick() {
if (!_running) return;
setState(() {
_speed = (_speed + 0.00001).clamp(0.010, 0.03);
_score += 1;
for (final o in _obstacles) {
o.y += _speed;
if (o.y > 1.15) {
o.y = -0.9 - _rng.nextDouble() * 0.8;
o.lane = _rng.nextInt(lanes).toDouble();
}
}
for (final o in _obstacles) {
if ((o.y - playerY).abs() < 0.085 && (o.lane - _lane).abs() < 0.1) {
_running = false;
_best = math.max(_best, _score);
break;
}
}
});
}
void _move(int dir) {
if (!_running) return;
setState(() {
_lane = (_lane + dir).clamp(0, lanes - 1).toDouble();
});
}
void _restart() {
setState(() {
_score = 0;
_speed = 0.010;
_lane = 1;
_running = true;
_spawnInitial();
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(12),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Score: $_score', style: const TextStyle(fontSize: 20)),
Text('Best: $_best', style: const TextStyle(fontSize: 18)),
],
),
),
Expanded(
child: AspectRatio(
aspectRatio: 9 / 16,
child: Container(
margin: const EdgeInsets.all(10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
gradient: const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Color(0xFF0B1021), Color(0xFF1B2348)],
),
),
child: CustomPaint(
painter: _RoadPainter(
lane: _lane,
obstacles: _obstacles,
),
child: !_running
? Center(
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.65),
borderRadius: BorderRadius.circular(12),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text('Crash! 💥',
style: TextStyle(fontSize: 26)),
const SizedBox(height: 8),
ElevatedButton(
onPressed: _restart,
child: const Text('Nochmal'),
),
],
),
),
)
: null,
),
),
),
),
Padding(
padding: const EdgeInsets.fromLTRB(20, 8, 20, 20),
child: Row(
children: [
Expanded(
child: FilledButton.icon(
onPressed: () => _move(-1),
icon: const Icon(Icons.arrow_left),
label: const Text('Links'),
),
),
const SizedBox(width: 12),
Expanded(
child: FilledButton.icon(
onPressed: () => _move(1),
icon: const Icon(Icons.arrow_right),
label: const Text('Rechts'),
),
),
],
),
),
const Padding(
padding: EdgeInsets.only(bottom: 10),
child: Text('Star Taxi kleines 3D-Arcade APK Testbuild'),
),
],
),
),
);
}
}
class _RoadPainter extends CustomPainter {
final double lane;
final List<_Obstacle> obstacles;
const _RoadPainter({required this.lane, required this.obstacles});
@override
void paint(Canvas canvas, Size size) {
final road = Paint()..color = const Color(0xFF2A2F40);
final line = Paint()
..color = const Color(0xAAFFFFFF)
..strokeWidth = 2;
final topWidth = size.width * 0.35;
final bottomWidth = size.width * 0.95;
final roadPath = Path()
..moveTo((size.width - topWidth) / 2, 0)
..lineTo((size.width + topWidth) / 2, 0)
..lineTo((size.width + bottomWidth) / 2, size.height)
..lineTo((size.width - bottomWidth) / 2, size.height)
..close();
canvas.drawPath(roadPath, road);
for (int i = 1; i < 3; i++) {
final t = i / 3;
final xTop = size.width / 2 + (t - 0.5) * topWidth;
final xBottom = size.width / 2 + (t - 0.5) * bottomWidth;
canvas.drawLine(Offset(xTop, 0), Offset(xBottom, size.height), line);
}
for (double y = 0; y < 1.1; y += 0.12) {
final py = y * size.height;
final w = _roadWidthAtY(size, py, topWidth, bottomWidth);
canvas.drawLine(
Offset(size.width / 2 - w * 0.04, py),
Offset(size.width / 2 + w * 0.04, py),
Paint()
..color = Colors.white.withValues(alpha: 0.35)
..strokeWidth = 2,
);
}
for (final o in obstacles) {
_drawCar(
canvas,
size,
lane: o.lane,
yNorm: o.y,
color: const Color(0xFFE26D5A),
);
}
_drawCar(
canvas,
size,
lane: lane,
yNorm: _StarTaxiDemoState.playerY,
color: const Color(0xFF3DDC97),
);
}
void _drawCar(Canvas canvas, Size size,
{required double lane, required double yNorm, required Color color}) {
final y = yNorm * size.height;
final wRoad = _roadWidthAtY(size, y, size.width * 0.35, size.width * 0.95);
final laneWidth = wRoad / 3;
final left = size.width / 2 - wRoad / 2 + lane * laneWidth + laneWidth * 0.15;
final width = laneWidth * 0.7;
final height = width * 0.9;
final rect = RRect.fromRectAndRadius(
Rect.fromLTWH(left, y - height / 2, width, height),
const Radius.circular(8),
);
canvas.drawRRect(rect, Paint()..color = color);
canvas.drawRRect(
rect,
Paint()
..style = PaintingStyle.stroke
..strokeWidth = 1.2
..color = Colors.white.withValues(alpha: 0.65),
);
}
double _roadWidthAtY(Size size, double y, double topWidth, double bottomWidth) {
final t = (y / size.height).clamp(0.0, 1.0);
return topWidth + (bottomWidth - topWidth) * t;
}
@override
bool shouldRepaint(covariant _RoadPainter oldDelegate) => true;
}
class _Obstacle {
_Obstacle({required this.lane, required this.y});
double lane;
double y;
}