`CameraComponent.setBounds` with `considerViewport: true` only works for bounds that start at (0, 0), at zoom 1

未关闭
#4,201 1 条评论 0 个 reaction 已指派 1 人 在 GitHub 查看

维护者通常 1 天内回复

@ufrshubham 已经在做这个了。

开始于 2026年11月2日。

  • #4205 来自 @ufrshubham —— 未关闭

评估

这个 Issue 还没有评估数据。

描述

bug
What happened?

camera.setBounds(bounds, considerViewport: true) should keep the whole viewport inside bounds. ViewportAwareBoundsBehavior calculates the region that the camera has to stay in, but it only gets it right for a Rectangle or RoundedRectangle whose top-left corner is at (0, 0), with the zoom at 1. In every other case the camera either gets stuck or shows the empty space outside of the bounds.

With a 320×180 fixed resolution camera:

  • Bounds centered on the origin, Rectangle.fromLTRB(-245, -146, 245, 146): the camera can only move ±37.5 × ±17 away from (0, 0), so it barely moves while the player walks off-screen.
  • Bounds away from the origin, Rectangle.fromLTWH(1600, 960, 490, 292): the region is (960, 570) to (2730, 1642), far larger than the bounds themselves. In its top-left corner, none of the level is visible at all.
  • Zoom other than 1: the viewport's size is subtracted, not the size of the world region that is visible through it. With Rectangle.fromLTWH(0, 0, 490, 292), at zoom 2 the camera stops at (160, 90) instead of (80, 45), so the corners of the level can't be seen. At zoom 0.8 it shows 40 units past the left edge and 22.5 past the top.
  • Bounds smaller than the view, Rectangle.fromLTWH(0, 0, 138, 100): the size passed to Rectangle.fromCenter is negative, which Rectangle.fromPoints flips into a regular rectangle. The camera moves around inside the room instead of staying centered on it.
  • Circles: Circle(Vector2.zero(), 376) gives a radius of 28, so the camera barely moves, and Circle(Vector2.zero(), 300) throws Radius cannot be negative: -10.0. Even with the right size, the radius is only reduced by half of the viewport's longer side, which lets the viewport's corners leave the circle.

https://github.com/user-attachments/assets/6521d5a6-5a13-46ac-b1bc-c39d60d07c4a

https://github.com/user-attachments/assets/45949372-36b3-42cb-94fa-765ba042344b

https://github.com/user-attachments/assets/7dbe69de-e4d8-499f-b209-fe765e6930af

https://github.com/user-attachments/assets/46eb7ff3-f873-43c3-abf0-90827740a69f

https://github.com/user-attachments/assets/3583f56a-02af-4d68-a177-8888f68e3b27

What do you expect?

The camera can move as far as possible while the whole visible region stays inside the bounds, wherever the bounds are and at any zoom. With the same 320×180 camera:

  • Rectangle.fromLTRB(-245, -146, 245, 146): the camera stays within (-85, -56) to (85, 56).
  • Rectangle.fromLTWH(1600, 960, 490, 292): within (1760, 1050) to (1930, 1162).
  • Rectangle.fromLTWH(0, 0, 490, 292) at zoom 2: within (80, 45) to (410, 247).
  • Bounds smaller than the view: the camera stays at the center of the bounds along that axis.
  • Circle(Vector2.zero(), 376): within a radius of about 192.4, so none of the viewport's corners leave the circle.
How can we reproduce this?
  1. Create a camera with CameraComponent.withFixedResolution(width: 320, height: 180).
  2. Call camera.setBounds(Rectangle.fromLTRB(-245, -146, 245, 146), considerViewport: true).
  3. Move the camera towards the top-left corner, for example with camera.moveTo(Vector2(-10000, -10000)).
  4. The camera stops at (-37.5, -17) instead of (-85, -56), and the corner of the bounds is off-screen.
What steps should take to fix this?

_calculateViewportAwareBounds (viewport_aware_bounds_behavior.dart#L77-L118) calculates the size of the bounds like this:

_boundsShape
    .support(
      _boundsShape.nearestPoint(
        _boundsShape.center + Vector2(1, 0),
      ),
    )
    .x,

support takes a direction, but it is given a point here, and .x of the result is the x coordinate of the right edge, not the width. The two are only the same when the bounds start at x = 0. The viewport's virtualSize is then subtracted without taking the zoom into account, and a negative result isn't clamped.

The region will be calculated from the shape's aabb and the size of the world region visible through the viewport, which is virtualSize / zoom, taking the viewfinder's angle into account. It is clamped at zero, so that it collapses to the center of the bounds when they are smaller than the view. For circles, the radius will be reduced by half of the visible region's diagonal, so that the corners of the viewport stay inside.

Do have an example of where the bug occurs?
import 'dart:ui';

import 'package:flame/camera.dart';
import 'package:flame/components.dart';
import 'package:flame/experimental.dart';
import 'package:flame/game.dart';
import 'package:flutter/material.dart' show Colors;
import 'package:flutter/widgets.dart';

void main() => runApp(GameWidget(game: ReproGame()));

class ReproGame extends FlameGame {
  ReproGame()
    : super(
        camera: CameraComponent.withFixedResolution(width: 320, height: 180),
      );

  // The other cases:
  //   Rectangle.fromLTWH(1600, 960, 490, 292)
  //   Rectangle.fromLTWH(0, 0, 490, 292), with camera.viewfinder.zoom = 2
  //   Rectangle.fromLTWH(0, 0, 138, 100)
  //   Circle(Vector2.zero(), 300)
  final Shape level = Rectangle.fromLTRB(-245, -146, 245, 146);

  @override
  Future<void> onLoad() async {
    final aabb = level.aabb;
    world.add(
      RectangleComponent(
        position: aabb.min,
        size: aabb.max - aabb.min,
        paint: Paint()..color = Colors.brown,
      ),
    );
    camera.setBounds(level, considerViewport: true);
    // Look at the top-left corner of the level.
    camera.moveTo(Vector2(-10000, -10000));
  }
}

Expected: the top-left corner of the level is at the top-left corner of the screen. Actual: the camera stops at (-37.5, -17), and the corner is off-screen. The other cases show the empty space next to the level, can't reach its corners, or throw.

Relevant log output
# With Circle(Vector2.zero(), 300), otherwise nothing is logged or thrown.
'package:flame/src/experimental/geometry/shapes/circle.dart': Failed assertion: line 20 pos 17: '_radius >= 0': Radius cannot be negative: -10.0
Execute in a terminal and put output into the code block below
[√] Flutter (Channel stable, 3.47.9, on Microsoft Windows [Version 10.0.22631.6199], locale en-US)
    • Flutter version 3.47.9 on channel stable
    • Framework revision c197ca5199 (4 days ago), 2026-10-29 10:10:20 -0400
    • Engine revision 89c39edfdf
    • Dart version 3.13.7
    • DevTools version 2.60.0

[√] Windows Version (11 Pro 64-bit, 23H2, 2009)
[√] Android toolchain - develop for Android devices (Android SDK version 36.1.0)
[√] Chrome - develop for the web
[√] Visual Studio - develop Windows apps (Visual Studio Community 2026 18.11.2)
[√] Connected device (3 available)
[√] Network resources

• No issues found!
Affected platforms

All

Are you interested in working on a PR for this?
  • I want to work on this
主要语言
Dart
星标
10.8k
派生
1.1k
平均合并
1 天 22 小时
30 天内合并 PR
54

环境准备

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

flame-engine/flame 的其他 Issue

查看 flame-engine/flame 的全部 Issue

相似的 Issue

更多 Dart Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。