Smoother keyboard movement using ticks instead of key repeat polling
Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
+67
-36
@@ -1,5 +1,6 @@
|
||||
import "dart:math" as math;
|
||||
|
||||
import "package:flutter/scheduler.dart";
|
||||
import "package:flutter/services.dart";
|
||||
import "package:flutter/widgets.dart";
|
||||
|
||||
@@ -151,19 +152,29 @@ class Player extends StatefulWidget {
|
||||
final ValueChanged<Offset> onPositionUpdated;
|
||||
final RayData rayData;
|
||||
|
||||
/// How "fast" the player moves
|
||||
/// Length of the player's direction line on the minimap.
|
||||
static const int speed = 5;
|
||||
|
||||
/// Turning speed in radians per second, applied every frame.
|
||||
static const double turnRate = 3.5;
|
||||
|
||||
/// Movement speed in pixels per second, applied every frame.
|
||||
static const double moveSpeed = 100;
|
||||
|
||||
@override
|
||||
State<Player> createState() => _PlayerState();
|
||||
}
|
||||
|
||||
class _PlayerState extends State<Player> {
|
||||
class _PlayerState extends State<Player> with SingleTickerProviderStateMixin {
|
||||
late final Ticker _ticker;
|
||||
Duration _lastElapsed = Duration.zero;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
HardwareKeyboard.instance.addHandler(keyPressHandler);
|
||||
HardwareKeyboard.instance.addHandler(_consumeKey);
|
||||
_ticker = createTicker(_tick)..start();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
widget.onRaysUpdated(
|
||||
@@ -185,8 +196,20 @@ class _PlayerState extends State<Player> {
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
HardwareKeyboard.instance.removeHandler(_consumeKey);
|
||||
_ticker.dispose();
|
||||
super.dispose();
|
||||
HardwareKeyboard.instance.removeHandler(keyPressHandler);
|
||||
}
|
||||
|
||||
bool _consumeKey(KeyEvent event) {
|
||||
switch (event.logicalKey) {
|
||||
case LogicalKeyboardKey.keyA:
|
||||
case LogicalKeyboardKey.keyD:
|
||||
case LogicalKeyboardKey.keyW:
|
||||
case LogicalKeyboardKey.keyS:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Get the map tile at a given pixel coordinate
|
||||
@@ -225,60 +248,68 @@ class _PlayerState extends State<Player> {
|
||||
return choices.first;
|
||||
}
|
||||
|
||||
bool keyPressHandler(KeyEvent event) {
|
||||
/// Advances the player once per frame based on the keys currently held.
|
||||
void _tick(Duration elapsed) {
|
||||
final double dt = math.min(
|
||||
(elapsed - _lastElapsed).inMicroseconds / Duration.microsecondsPerSecond,
|
||||
0.05,
|
||||
);
|
||||
_lastElapsed = elapsed;
|
||||
|
||||
final Set<LogicalKeyboardKey> keys =
|
||||
HardwareKeyboard.instance.logicalKeysPressed;
|
||||
|
||||
bool handled = false;
|
||||
|
||||
// Where is the player now?
|
||||
Offset playerPosition = widget.rayData.playerPosition;
|
||||
double playerAngle = widget.rayData.playerAngle;
|
||||
|
||||
bool hasMoved = false;
|
||||
|
||||
// Turn the player.
|
||||
final bool turningLeft = keys.contains(LogicalKeyboardKey.keyA);
|
||||
final bool turningRight = keys.contains(LogicalKeyboardKey.keyD);
|
||||
if (turningLeft != turningRight) {
|
||||
playerAngle += (turningRight ? 1 : -1) * Player.turnRate * dt;
|
||||
playerAngle = playerAngle.normalizeAngle;
|
||||
hasMoved = true;
|
||||
}
|
||||
|
||||
// Capture the direction *after* any turn this frame.
|
||||
final Offset direction = playerAngle.direction;
|
||||
final Offset playerCenter =
|
||||
playerPosition +
|
||||
Offset(GameMap.dimensions.width / 2, GameMap.dimensions.height / 2);
|
||||
double playerAngle = widget.rayData.playerAngle;
|
||||
|
||||
// How many pixels away from a wall to stay
|
||||
const double buffer = 2;
|
||||
const double wallDistanceBuffer = 2.5;
|
||||
|
||||
if (keys.contains(LogicalKeyboardKey.keyA)) {
|
||||
// Turn left
|
||||
playerAngle -= 0.1;
|
||||
handled = true;
|
||||
} else if (keys.contains(LogicalKeyboardKey.keyD)) {
|
||||
// Turn right
|
||||
playerAngle += 0.1;
|
||||
handled = true;
|
||||
}
|
||||
final bool movingForward = keys.contains(LogicalKeyboardKey.keyW);
|
||||
final bool movingBackward = keys.contains(LogicalKeyboardKey.keyS);
|
||||
if (movingForward != movingBackward) {
|
||||
final double travel = Player.moveSpeed * dt;
|
||||
|
||||
// Capture the direction *after* it's potentially mutated
|
||||
final Offset direction = playerAngle.direction;
|
||||
|
||||
if (keys.contains(LogicalKeyboardKey.keyW)) {
|
||||
if (safeDistanceToWall(playerCenter, direction) > Player.speed + buffer) {
|
||||
if (movingForward) {
|
||||
if (safeDistanceToWall(playerCenter, direction) >
|
||||
travel + wallDistanceBuffer) {
|
||||
// Move forward
|
||||
playerPosition += playerAngle.playerDelta;
|
||||
playerPosition += direction * travel;
|
||||
hasMoved = true;
|
||||
}
|
||||
handled = true;
|
||||
} else if (keys.contains(LogicalKeyboardKey.keyS)) {
|
||||
if (safeDistanceToWall(playerCenter, -direction) >
|
||||
Player.speed + buffer) {
|
||||
// Move backwwards
|
||||
playerPosition -= playerAngle.playerDelta;
|
||||
} else if (safeDistanceToWall(playerCenter, -direction) >
|
||||
travel + wallDistanceBuffer) {
|
||||
// Move backwards
|
||||
playerPosition -= direction * travel;
|
||||
hasMoved = true;
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
|
||||
if (handled) {
|
||||
if (!hasMoved) return;
|
||||
|
||||
widget.onRaysUpdated(
|
||||
calculateRays(playerPosition, playerAngle),
|
||||
);
|
||||
widget.onAngleUpdated(playerAngle);
|
||||
widget.onPositionUpdated(playerPosition);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
List<Ray> calculateRays(Offset offset, double playerAngle) {
|
||||
|
||||
Reference in New Issue
Block a user