gpt4 book ai didi

flutter 火焰 : How to calculate the relect of a raycast?

转载 作者:行者123 更新时间:2023-12-05 03:33:05 27 4
gpt4 key购买 nike

感谢您的阅读。我的英语不好,我会尽可能地解释这个。我需要在 Canvas 中生成反射线,但效果不佳。

enter image description here

我认为我的问题是“计算反射矢量的公式”。

之前学的数学和canvas不一样,所以很迷茫

这是我的代码(错误),你可以运行这个(第一行是正确的,但反射是错误的):

import 'package:flame/game.dart';
import 'package:flame/input.dart';
import 'package:flame_forge2d/body_component.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flame_forge2d/forge2d_game.dart';
import 'package:flutter/material.dart';

import 'component/fixture/fixture_data.dart';

void main() {
runApp(
GameWidget(
game: TestGame(),
),
);
}

class TestGame extends Forge2DGame with MultiTouchDragDetector {
List<Offset> points = [Offset.zero, Offset.zero, Offset.zero];
late var boundaries;

@override
Future<void> onLoad() async {
await super.onLoad();

boundaries = createBoundaries(this);
boundaries.forEach(add);

points[0] = (camera.canvasSize / 2).toOffset();
}

@override
void update(double dt) {
super.update(dt);
}

@override
void render(Canvas canvas) {
super.render(canvas);

canvas.drawLine(points[0], points[1], Paint()..color = Colors.white);

canvas.drawLine(points[1], points[2], Paint()..color = Colors.white);

canvas.drawCircle(points[2], 30, Paint()..color = Colors.white);
}

/// @param [pointStart] start point of line
/// @param [point2] second point of line
/// @param [x] the position x of point need to calculate
/// @return Offset of the point on line
Offset calculateOffsetByX(Offset pointStart, Offset point2, double x) {
//y = ax + b
final a = (pointStart.dy - point2.dy) / (pointStart.dx - point2.dx);
final b = pointStart.dy - a * pointStart.dx;
return Offset(x, a * x + b);
}

/// @param [pointStart] start point of line
/// @param [point2] second point of line
/// @param [y] the position y of point need to calculate
/// @return Offset of the point on line
Offset calculateOffsetByY(Offset pointStart, Offset point2, double y) {
//y = ax + b
final a = (pointStart.dy - point2.dy) / (pointStart.dx - point2.dx);
final b = pointStart.dy - a * pointStart.dx;
return Offset((y - b) / a, y);
}

@override
void onDragUpdate(int pointerId, DragUpdateInfo info) {
var callback = MyRayCastCallBack(this);

var finalPos = getFinalPos(screenToWorld(camera.viewport.effectiveSize) / 2,
info.eventPosition.game);

world.raycast(
callback, screenToWorld(camera.viewport.effectiveSize) / 2, finalPos);

var n = callback.normal;
var i = info.eventPosition.game;
var r = i + (n * (2 * i.dot(n)));

var callback2 = MyRayCastCallBack2(this);
world.raycast(callback2, callback.point, r);
}

Vector2 getFinalPos(Vector2 startPos, Vector2 touchPos) {
return Vector2(
calculateOffsetByY(startPos.toOffset(), touchPos.toOffset(), 0).dx,
calculateOffsetByY(startPos.toOffset(), touchPos.toOffset(), 0).dy);
}
}

class MyRayCastCallBack extends RayCastCallback {
TestGame game;
late Vector2 normal;
late Vector2 point;

MyRayCastCallBack(this.game);

@override
double reportFixture(
Fixture fixture, Vector2 point, Vector2 normal, double fraction) {
game.points[1] = game.worldToScreen(point).toOffset();
this.normal = normal;
this.point = point;
return 0;
}
}

class MyRayCastCallBack2 extends RayCastCallback {
TestGame game;
late Vector2 normal;
late Vector2 point;

MyRayCastCallBack2(this.game);

@override
double reportFixture(
Fixture fixture, Vector2 point, Vector2 normal, double fraction) {
game.points[2] = game.worldToScreen(point).toOffset();
this.normal = normal;
this.point = point;
return 0;
}
}

List<Wall> createBoundaries(Forge2DGame game) {
/*final topLeft = Vector2.zero();
final bottomRight = game.screenToWorld(game.camera.viewport.effectiveSize);
final topRight = Vector2(bottomRight.x, topLeft.y);
final bottomLeft = Vector2(topLeft.x, bottomRight.y);*/
final bottomRight =
game.screenToWorld(game.camera.viewport.effectiveSize) / 8 * 7;

final topLeft = game.screenToWorld(game.camera.viewport.effectiveSize) / 8;
final topRight = Vector2(bottomRight.x, topLeft.y);
final bottomLeft = Vector2(topLeft.x, bottomRight.y);

return [
Wall(topLeft, topRight, FixtureKey.wallTop),
Wall(topRight, bottomRight, FixtureKey.wallRight),
Wall(bottomRight, bottomLeft, FixtureKey.wallBottom),
Wall(bottomLeft, topLeft, FixtureKey.wallLeft),
];
}

class Wall extends BodyComponent {
final Vector2 start;
final Vector2 end;
final FixtureKey fixtureKey;

Wall(this.start, this.end, this.fixtureKey);

@override
Body createBody() {
final shape = EdgeShape()..set(start, end);

final fixtureDef = FixtureDef(shape)
..restitution = 0
..density = 1.0
..friction = 0
..userData = FixtureData(type: FixtureType.wall, key: fixtureKey);
;

final bodyDef = BodyDef()
..userData = this // To be able to determine object in collision
..position = Vector2.zero()
..type = BodyType.static;

return world.createBody(bodyDef)..createFixture(fixtureDef);
}
}


最佳答案

您的代码中有几处错误。但是您的猜测是正确的,反射(reflect)关于法线的光线转换的数学是错误的。更准确地说,您用作第一个光线转换的矢量似乎是错误的。

代替,

var i = info.eventPosition.game;

应该是这样的

var i = callback.point - (screenToWorld(camera.viewport.effectiveSize) / 2);

基本上,它应该是一个从您的原始起点开始并指向命中点的相对向量。希望这能解决您的问题。

这里还有一些在您的代码中看起来不对的地方

  • 多个回调类:您无需为捕获多个光线转换定义多个回调类。您可以使用同一类的多个对象。如果你真的想推送它,你甚至可以根据你的用例一次又一次地使用同一个对象。

  • 返回自 reportFixture() : 我看到你从这个方法返回 0。在您的简单情况下,这可能会正常工作,但返回 0 意味着您希望光线转换在它遇到的第一个固定装置处停止。不幸的是, world.raycast does not report fixtures in any fixed order .所以如果沿射线有多个固定装置,reportFixture可能会要求最远的固定装置。如果你真的想得到最近的灯具,你应该返回 fraction参数作为返回值。

我在这里发布了一个示例代码,它可以根据 nBounces 向您报告多次点击。参数。

void newRaycast(Vector2 start, Vector2 end, int nBounces) {
if (nBounces > 0) {
final callback = NearestRayCastCallback();
world.raycast(callback, start, end);

// Make sure that we got a hit.
if (callback.nearestPoint != null && callback.normalAtInter != null) {
// Store the hit location for rendering later on.
points.add(worldToScreen(callback.nearestPoint!.clone()));

// Figure out the current ray direction and then reflect it
// about the collision normal.
Vector2 originalDirection =
(callback.nearestPoint! - start).normalized();
final dotProduct = callback.normalAtInter!.dot(originalDirection);
final newDirection =
originalDirection - callback.normalAtInter!.scaled(2 * dotProduct);

// Call newRayCast with new start and end points. New end is just a point 500
// units along the new direction.
newRaycast(
callback.nearestPoint!.clone(), newDirection.scaled(500), --nBounces);
}
}
}

这是我的回调类的定义。

class NearestRayCastCallback extends RayCastCallback {
Vector2? nearestPoint;
Vector2? normalAtInter;

@override
double reportFixture(
Fixture fixture, Vector2 point, Vector2 normal, double fraction) {
nearestPoint = point;
normalAtInter = normal;
return fraction;
}
}

在上面的代码中,pointsfinal points = List<Vector2>.empty(growable: true);这就是我从这些点渲染线条的方式:

for (int i = 0; i < points.length - 1; ++i) {
canvas.drawLine(points[i].toOffset(), points[i + 1].toOffset(),
Paint()..color = Colors.black..strokeWidth = 2);
}

关于 flutter 火焰 : How to calculate the relect of a raycast?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70381597/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com