[SOLVED] Need help with my custom throw system

I try to create a throw system.
I use bezier.js for this.

What I do in the update:

  1. Set point 1 of the bezier at the player position with offset.
  2. Set point 3 of the bezier to the mouse hit position.
  3. Set point 2 of the bezier in the middle between point 1 and point 3.
  4. Render a line between the points using the divisions of the bezier.
  5. Do a raycast between the points using the divisions of the bezier.
  6. Set point 3 to the raycast hit if there is an obstacle in the path.

The problem:

The position of point 3 keeps switching between step 2 and step 6.

Result step 2:

Result step 6:

Do you do the raycast in order (going from the player to the target point of mouse click)? Do you stop doing raycasts when it hits something?

I do the raycast from point 1 to point 3. Then I set point 3 to the hit point position. With every mouse move point 3 is going back to the mouse hit position.

I would consider not reusing point 3 for the intersection point and use a new point or point 4. That way the bezier curve won’t change.

I understand. I think I have to use a second bezier for the raycast (always from the player to the mouse)?

Second bezier? What would that be needed for? :thinking:

Because I can’t create a line and a raycast with the same bezier if the last point is not the same.

That’s why I recommended using a 4th vector to store the intersection point from the raycasts and keep points 1-3 just for the bezier path.

So do I have to expand the current bezier with a 4th point or just a single vector?

I must be missing something from this. This is what I’m understanding:

Point 1 is the position of the player.
Point 3 is the position of the mouse intersecting with the world (this case the floor)

Create a bezier curve based off these points and point 2 which is in between these 2.

Do raycasts along the bezier curve until it hits something, store this in a separate pc.Vec3.

Unless the mouse moves or the player moves, the bezier curve should not change.

But I need to set point 3 to that vector to update the (blue) bezier line, otherwise the player can’t see where his throwing object is ended.

Render the original bezier curve up to the intersection point. If you move point 3, you could change the shape of the curve.

I think I do that already:

var Throw = pc.createScript('throw');
var throwPoint;

// initialize code called once per entity
Throw.prototype.initialize = function() {
    this.divisions = 30;
    this.pathRoot = this.entity.findByName("ThrowPath");
  
    this.point1 = this.entity.findByName("Point1");
    this.point2 = this.entity.findByName("Point2");
    this.point3 = this.entity.findByName("Point3"); 
};

// update code called every frame
Throw.prototype.update = function(dt) {
    // Point 1 is the throw start point, the same player entity position with an offset
    
    // Point 3 is the throw end point, the mouse hit position until an obstacle blocks the path
    // ThrowPoint is the mouse hit point in another script
    this.point3.setPosition(throwPoint);
    
    // Point 2 is the middle point, between point 1 and point 3
    var middle = this.point3.localPosition.z / 2;
    this.point2.setLocalPosition(new pc.Vec3(this.point2.getLocalPosition().x, this.point2.getLocalPosition().y, middle));

    // Create the division between the three points
    var children = this.pathRoot.children;
        for (var p = 0; i < children.length; p++) {
        var position = children[p].getPosition();
        points.push({x:position.x, y:position.y, z:position.z});
        this.positions.push(position);
    }
    
    if (children.length !== 3) {
        console.error('You need to create three child entities of the Bezier entity that are control points.');
    }
    
    var p1 = children[0].getPosition(); // this.point1
    var p2 = children[1].getPosition(); // this.point2
    var p3 = children[2].getPosition(); // this.point3
    
    this.curve = new Bezier(p1.x, p1.y, p1.z, p2.x, p2.y+1.5, p2.z, p3.x, p3.y, p3.z);
    
    this.from = new pc.Vec3();
    this.to = new pc.Vec3();
    
    var divisions = Math.floor(this.divisions);
    
    // Render the curve itself
    var lut = this.curve.getLUT(divisions);
    for (var i = 0; i < lut.length-1; i++) {
        this.from.x = lut[i].x;
        this.from.y = lut[i].y;
        this.from.z = lut[i].z;
        this.to.x = lut[i+1].x;
        this.to.y = lut[i+1].y;
        this.to.z = lut[i+1].z;
        
        // Render the line
        this.app.renderLine(this.from, this.to, colorBlue);
        
        // Raycast
        var obstacle = this.app.systems.rigidbody.raycastFirst(this.from, this.to);
        if (obstacle) {
            // If there is an obstacle that block the path then set point 3 to the hit point
            this.point3.setPosition(obstacle.point);
            this.app.root.findByName("DebugPoint").setPosition(obstacle.point);
        }
    }
};

So now I create a second bezier. One for the raycast and one for the line. Sometimes it works and sometimes it doesn’t. I feel like the raycast is missing the obstacle. This is also one of the situations where I would like to use sphereCast.

Something is a little funky as the curve shouldn’t change that much between the two frames at 0:05:


It looks like the curve changes when it intersects with something when it shoudn’t.

Just to sanity check, what happens when you remove this line:

this.point3.setPosition(obstacle.point);

If I remove that line there is just a curved line between the player and the mouse (that goes through objects).

That is precisely the intention. So the second image (at 0:06) is the correct result.

That feels wrong to me as when an object is thrown at the same angle and power, it’s going to follow the same path no matter what in the path.

With the line of code removed, the curved line is more stable, it doesn’t change regardless what it intersects with which is what I would expect as a player.

If you want that curve to change if it intersects with something (which it sounds like you do), I would do a second bezier curve just to render the line with a new set of points and not change the value of point3.

So that’s what I want to avoid doing the raycast and update the path (and line) before the player actually throws it.

That is not the intention because I want to help the player see where his thrown object will end.

I have already tried that, but it is not stable either. The first video I sent was done that way.

But otherwise I will do it so that the line is only updated when the player clicks. And if the player double-clicks then actually throw.

Not working. I think the raycast fails and don’t always detect the collider.