Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
1.0.0:pathfinding [2023/03/01 10:25] – [Setup] admin1.0.0:pathfinding [2023/03/21 15:34] (current) – [Click to Start Pathfinding] admin
Line 155: Line 155:
 Image1.Bitmap.Assign(FVerifyBmp); Image1.Bitmap.Assign(FVerifyBmp);
 </file> </file>
 +
 +
 +===== Click to Start Pathfinding  =====
 +
 +Many developers may struggle with the question how to combine the setup above with user interaction.
 +
 +Speaking for a classic Point & Click Adventure, a recommend way is to add a simple plane (TPlane, TGorillaPlane) for click detection.
 +
 +All other 3D elements should be set to HitTest = false, to not disturb mouse interaction with the helper plane.
 +
 +We then register a MouseUp event on the viewport, where we put all of our mouse handling.
 +
 +<file pascal>
 +var 
 +  FDestPoint : TPoint3D;
 +  
 +procedure TForm1.GorillaViewport1MouseUp(Sender: TObject; Button: TMouseButton;
 +  Shift: TShiftState; X, Y: Single);
 +var LRayPos,
 +    LRayDir : TPoint3D;
 +    LHitPos,
 +    LNormal,
 +    LScreenPos : TPoint3D;
 +begin
 +  // 1) Get 3D position of click, by converting screen coordinates into 3D coordinates
 +  LScreenPos := GorillaViewport1.ScreenToWorld(PointF(X, Y));
 +
 +  // 2) Cast a ray from camera to the clicking position, to retrieve the real
 +  //    3D destination coordinate
 +  LRayPos := GorillaCamera1.AbsolutePosition;
 +  LRayDir := (LScreenPos - LRayPos).Normalize();
 +  if GorillaPlane1.RayCastIntersect(LRayPos, LRayDir, LHitPos, LNormal) then
 +  begin
 +    // If raycasting was successfully - we can start store the point hit by the ray
 +    FDestPoint := LHitPos;
 +    FDestPoint.Y := 0;
 +    
 +    // Aftwards we start pathfinding
 +    UpdatePathfinding();
 +  end;
 +end;
 +
 +procedure TForm1.UpdatePathfinding();
 +var LAgentPos : TPoint3D;
 +begin
 +  // Get the agent position (pathfinding moving object)
 +  LAgentPos := GorillaModel1.AbsolutePosition;
 +  
 +  // Compute the path from 3D position - this will generate our new walking path
 +  FPathFinder.FindPath(LAgentPos, FDestPoint);
 +
 +  // Stop the current animation
 +  FPathAnim.Stop();
 +  FPathAnim.Enabled := false;
 +
 +  // Apply computed path data to our existing TGorillaPath3D instance, instead for recreating each time
 +  FPathFinder.ApplyToPath3D(FPath);
 +
 +  // Reset the used path data in our path animation
 +  FPathAnim.Duration := FPath.Path.GetLength;
 +  FPathAnim.Path := FPath.Path;
 +
 +  // For correct rotation of our character, we need the polygon to detect the
 +  // next node to adjust to - 
 +  // NOTICE: READ THE SECTION BELOW
 +  FPath.Path.FlattenToPolygon(FPolygon);
 +
 +  // Restart movement on path
 +  FPathAnim.Enabled := true;
 +  FPathAnim.Start();
 +end;
 +</file>
 +
 +
 +===== Auto-Rotate an animated character =====
 +
 +<file pascal>
 +procedure TForm1.FormCreate(Sender: TObject);
 +begin
 +[...]
 +
 +  // Register callback events
 +  FPathAnim.OnProcess  := DoOnPathAnimProcess;
 +  FPathAnim.OnFinish   := DoOnPathAnimFinished;
 +  
 +  // For correct rotation of our character, we need the polygon to detect the
 +  // next node to adjust to
 +  // NOTICE: If the path changes, this has to be called again!
 +  FPath.Path.FlattenToPolygon(FPolygon);
 +end;
 +
 +procedure TForm1.DoOnPathAnimProcess(ASender : TObject);
 +
 +  function GetKeyValues(APolygon : TGorillaPolygon3D; const T : Single;
 +  out P1, P2 : TPoint3D) : Single;
 +  var LDeltaPos : Single;
 +      LSglEnt : Integer;
 +  begin
 +    if System.Length(APolygon) <= 0 then
 +    begin
 +      P1 := TPoint3D.Zero;
 +      P2 := TPoint3D.Zero;
 +      Exit;
 +    end;
 +
 +    // get current key position
 +    LDeltaPos := T * High(APolygon);
 +    // key value
 +    LSglEnt := Floor(LDeltaPos);
 +    // key offset
 +    Result := LDeltaPos - LSglEnt;
 +
 +    // key coordinate
 +    if (LSglEnt <= High(APolygon)) then
 +      P1 := APolygon[LSglEnt]
 +    else
 +      P1 := APolygon[High(APolygon)];
 +
 +    inc(LSglEnt);
 +    if (LSglEnt <= High(APolygon)) then
 +      P2 := APolygon[LSglEnt]
 +    else
 +      P2 := APolygon[High(APolygon)];
 +  end;
 +
 +var dx, dz  : Single;
 +    LAngleY : Single;
 +    P1, P2  : TPoint3D;
 +begin
 +  // Get the currently relevant point to compute rotation
 +  GetKeyValues(FPolygon, FPathAnim.NormalizedTime, P1, P2);
 +
 +  dx := P2.X - P1.X;
 +  dz := P1.Z - P2.Z;
 +  LAngleY := arctan2(dx, dz);
 +
 +  GorillaModel1.RotationAngle.Y := RadToDeg(LAngleY);
 +  GorillaModel1.AnimationManager.PlayAnimation('run-forward.dae');
 +end;
 +
 +procedure TForm1.DoOnPathAnimFinished(ASender : TObject);
 +begin
 +  GorillaModel1.AnimationManager.PlayAnimation('idle.dae');
 +end;
 +</file>
 +
  
 Next: [[renderpass|Render Pass]] Next: [[renderpass|Render Pass]]