Reputation: 67
I am trying to make a basketball shoot game. My issue is with my projected trajectory line. It seems to be pretty accurate when the initial velocity isn't above ~140. I have tried adjusting my formula a ton, I mostly just followed this tutorial: https://www.iforce2d.net/b2dtut/projected-trajectory
Here is the code; I am not sure if something is wrong with my formula or there is something I am missing in regards to Box2D within Flutter Flame (what I'm using to develop this)
import 'dart:async';
import 'package:bball_blast/Background.dart';
import 'package:bball_blast/entities/Hoop.dart';
import 'package:bball_blast/entities/Wall.dart';
import 'package:bball_blast/entities/ball.dart';
import 'package:flame/components.dart';
import 'package:flame/events.dart';
import 'package:flame/extensions.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/material.dart';
import 'package:bball_blast/config.dart';
class BBallBlast extends Forge2DGame with PanDetector, HasGameRef<BBallBlast>{
BBallBlast(): super(
gravity: Vector2(0,gravity),
camera: CameraComponent.withFixedResolution(width: gameWidth, height: gameHeight),
);
//vars we need to be visible thoughout entire file
late Ball ball;
double linearImpulseStrengthMult = 12.5;
late Vector2 impulse;
//positional vars
double startPosX = 00;
double startPosY = 00;
late Vector2 startPos;
//Vars for determining how ball should be thrown
late Offset startOfDrag;
late Offset currentDragPos;
late Offset dragBehindBall = Offset.zero;
bool isDragging = false;
bool isShot = false;
@override
FutureOr<void> onLoad() async {
//set startPos of ball
startPos = Vector2(startPosX, startPosY);
//load images into cache
await images.loadAllImages();
//make ballSprite and ball
Sprite ballImg = await loadSprite('ball.png');
ball = Ball(this, Vector2(startPosX, startPosY), ballImg);
//add leftWall and rightWall, and ceiling
Wall wallLeft = Wall(Vector2(camera.visibleWorldRect.topLeft.dx-1, camera.visibleWorldRect.topLeft.dy), 1.0, gameHeight);
Wall wallRight = Wall(Vector2(camera.visibleWorldRect.topRight.dx+1, camera.visibleWorldRect.topRight.dy), 1.0, gameHeight);
Wall ceiling = Wall(Vector2(camera.visibleWorldRect.topLeft.dx-1, camera.visibleWorldRect.topRight.dy-1), gameWidth, 1.0);
//create hoopimg, hoop, and add it
Sprite hoopImg = await loadSprite('hoop.png');
Hoop hoop = Hoop(this, true, hoopImg);
//add components to game
world.addAll([Background(), ball, wallLeft, wallRight, ceiling, hoop]);
debugMode=true;
super.onLoad();
}
//-----------------------INPUT HANDLING (DRAGS)-----------------------
@override
void onPanStart(DragStartInfo info) {
//when user drags screen, store whre it happened and let program know dragging=true
startOfDrag = Offset(info.eventPosition.global.x, info.eventPosition.global.y);
isDragging = true;
isShot = false;
}
@override
void onPanUpdate(DragUpdateInfo info) {
//we get the dragPos, then we get the distance of the drag relative to starting point
//then apply it to our "dragBehindBall" which is given to trajectory drawing
currentDragPos = Offset(info.eventPosition.global.x, info.eventPosition.global.y);
double relX = startOfDrag.dx - currentDragPos.dx;
double relY = startOfDrag.dy - currentDragPos.dy;
dragBehindBall = Offset((relX), (relY));
}
@override
void onPanEnd(DragEndInfo info) {
//make ball move when thrown
ball.body.setType(BodyType.dynamic);
impulse = Vector2(dragBehindBall.dx, dragBehindBall.dy) * linearImpulseStrengthMult;
ball.body.applyLinearImpulse(impulse);
print("SHOT STRENGTH: ${Vector2(dragBehindBall.dx, dragBehindBall.dy) * linearImpulseStrengthMult}");
print("VEL: ${ball.body.linearVelocity}");
print("BALL MASS: ${ball.body.mass}");
//reset necessary vars
isDragging=false;
isShot = true;
dragBehindBall = Offset(startPosX, startPosY);
}
int count = 1;
@override
void update(double dt) {
super.update(dt);
//print("TIMESTEP: $count VEL: ${ball.body.linearVelocity} \n POS: ${ball.getSuperPosition()}");
}
}
import 'dart:async';
import 'dart:ui';
import 'package:bball_blast/BBallBlast.dart';
import 'package:bball_blast/config.dart';
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/material.dart';
class Ball extends BodyComponent {
@override
final BBallBlast game;
@override
final Vector2 position;
//points to draw for trajectory
late List<Vector2> points;
//TODO: CHANGE WHEN YOU CHANGE BODY
late double velocityRatio = 1/28.274333882308138;
int steps = 180;
Ball(this.game, this.position, Sprite sprite) : super (
renderBody: false,
//start body as static then set as dynamic when it is shot
bodyDef: BodyDef()
..position = position
..type = BodyType.static
..linearDamping = 0,
fixtureDefs: [
FixtureDef(CircleShape()..radius = 6/2)
..restitution = 0.3
..density = 1
//..friction = 0.5
],
//add our sprite
children: [
SpriteComponent(
sprite: sprite,
size: Vector2 (7, 7),
anchor: Anchor.center,
)
]
);
@override
Future<void> onLoad() async {
super.onLoad();
}
@override
void render(Canvas canvas) {
super.render(canvas);
//draw projected trajectory with info given by drags
if (game.isDragging) {
//we multiply the input by that number as it's the ratio that converts pixel to velocity
Vector2 initialVelocity = Vector2(game.dragBehindBall.dx, game.dragBehindBall.dy) * game.linearImpulseStrengthMult * velocityRatio;
//initialVelocity = _checkVelMax(initialVelocity);
//print(initialVelocity);
//get points to draw projected trajectory
points = trajectoryPoints(initialVelocity, Vector2(game.startPosX, game.startPosY), steps, (1/60)); //60 fps so our dt is 1/60
Paint paint = Paint()
..color = const Color.fromRGBO(244, 67, 54, 1)
..strokeWidth = 0.5
..style = PaintingStyle.stroke;
for (int i = 0; i < points.length - 1; i++) {
canvas.drawLine(
Offset(points[i].x, points[i].y),
Offset(points[i + 1].x, points[i + 1].y),
paint,
);
}
}
}
List<Vector2> trajectoryPoints(Vector2 initialVelocity, Vector2 startPos, int steps, double timeStep) {
List<Vector2> points = [];
for(int i=0; i<steps+1; i++) {
//get the timestep for a certain time in place
double t = i * timeStep;
//multiply velocity by timestep to get appropriate velocity and grav at ceratain time
Vector2 stepVel = initialVelocity * t;
Vector2 stepGrav = Vector2(0.0, 0.5* gravity *(t*t));
//calculate displacement based on stepVel and stepGrav in relation to starting position
double xDisplacement = startPos.x + stepVel.x;
double yDisplacement = startPos.y + stepVel.y + stepGrav.y;
//form our vectors
Vector2 pos = Vector2(xDisplacement, yDisplacement);
points.add(pos);
}
return points;
}
Vector2 getSuperPosition(){
return super.position;
}
}
Thank you for any help someone may provide.
Upvotes: 0
Views: 101
Reputation: 11582
You are hitting the Box2D max velocity, that is why Forge2DGame
is zoomed to 10.0 by default, but that zoom level disappears when you use CameraComponent.withFixedResolution
.
Upvotes: 1