Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
1.0.0:pathfinding [2023/03/01 10:25] – [Setup] admin | 1.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); | ||
</ | </ | ||
+ | |||
+ | |||
+ | ===== 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: | ||
+ | Shift: TShiftState; | ||
+ | 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, | ||
+ | |||
+ | // 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, | ||
+ | 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, | ||
+ | |||
+ | // 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; | ||
+ | </ | ||
+ | |||
+ | |||
+ | ===== Auto-Rotate an animated character ===== | ||
+ | |||
+ | <file pascal> | ||
+ | procedure TForm1.FormCreate(Sender: | ||
+ | begin | ||
+ | [...] | ||
+ | |||
+ | // Register callback events | ||
+ | FPathAnim.OnProcess | ||
+ | FPathAnim.OnFinish | ||
+ | | ||
+ | // 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; | ||
+ | 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, | ||
+ | |||
+ | dx := P2.X - P1.X; | ||
+ | dz := P1.Z - P2.Z; | ||
+ | LAngleY := arctan2(dx, dz); | ||
+ | |||
+ | GorillaModel1.RotationAngle.Y := RadToDeg(LAngleY); | ||
+ | GorillaModel1.AnimationManager.PlayAnimation(' | ||
+ | end; | ||
+ | |||
+ | procedure TForm1.DoOnPathAnimFinished(ASender : TObject); | ||
+ | begin | ||
+ | GorillaModel1.AnimationManager.PlayAnimation(' | ||
+ | end; | ||
+ | </ | ||
+ | |||
Next: [[renderpass|Render Pass]] | Next: [[renderpass|Render Pass]] |