i'm trying to move a sailing ship to the point where i clicked with the mouse. this movement should be realistic (oar at the back where the ship moves around) so if the mouse click is left and in front of the ship the ship should afterwards move there with a curvy path in order to have the right rotation
See this page
Adding Realistic Turns
The next step is to add realistic curved turns for our units, so that they don't appear to change direction abruptly every time they need to turn. A simple solution involves using a spline to smooth the abrupt corners into turns. While this solves some of the aesthetic concerns, it still results in physically very unrealistic movement for most units. For example, it might change an abrupt cornering of a tank into a tight curve, but the curved turn would still be much tighter than the tank could actually perform.
For a better solution, the first thing we need to know is the turning radius for our unit. Turning radius is a fairly simple concept: if you're in a big parking lot in your car, and turn the wheel to the left as far as it will go and proceed to drive in a circle, the radius of that circle is your turning radius. The turning radius of a Volkswagen Beetle will be substantially smaller than that of a big SUV, and the turning radius of a person will be substantially less than that of a large, lumbering bear.
Let's say you're at some point (origin) and pointed in a certain direction, and you need to get to some other point (destination), as illustrated in Figure 5. The shortest path is found either by turning left as far as you can, going in a circle until you are directly pointed at the destination, and then proceeding forward, or by turning right and doing the same thing.
In Figure 5 the shortest route is clearly the green line at the bottom. This path turns out to be fairly straightforward to calculate due to some geometric relationships, illustrated in Figure 6.
First we calculate the location of point P, which is the center of our turning circle, and is always radius r away from the starting point. If we are turning right from our initial direction, that means P is at an angle of (initial_direction - 90) from the origin, so:
angleToP = initial_direction - 90 P.x = Origin.x + r * cos(angleToP) P.y = Origin.y + r * sin(angleToP)
Now that we know the location of the center point P, we can calculate the distance from P to the destination, shown as h on the diagram:
dx = Destination.x - P.x dy = Destination.y - P.y h = sqrt(dx*dx + dy*dy)
At this point we also want to check that the destination is not within the circle, because if it were, we could never reach it:
if (h < r) return false
Now we can calculate the length of segment d, since we already know the lengths of the other two sides of the right triangle, namely h and r. We can also determine angle from the right-triangle relationship:
d = sqrt(h*h - r*r) theta = arccos(r / h)
Finally, to figure out the point Q at which to leave the circle and start on the straight line, we need to know the total angle + , and is easily determined as the angle from P to the destination:
phi = arctan(dy / dx) [offset to the correct quadrant] Q.x = P.x + r * cos(phi + theta) Q.y = P.y + r * sin(phi + theta)
The above calculations represent the right-turning path. The left-hand path can be calculated in exactly the same way, except that we add 90 to initial_direction for calculating angleToP, and later we use - instead of + . After calculating both, we simply see which path is shorter and use that one.
In our implementation of this algorithm and the ones that follow, we utilize a data structure which stores up to four distinct "line segments," each one being either straight or curved. For the curved paths described here, there are only two segments used: an arc followed by a straight line. The data structure contains members which specify whether the segment is an arc or a straight line, the length of the segment, and its starting position. If the segment is a straight line, the data structure also specifies the angle; for arcs, it specifies the center of the circle, the starting angle on the circle, and the total radians covered by the arc.
Once we have calculated the curved path necessary to get between two points, we can easily calculate our position and direction at any given instant in time, as shown in Listing 2.
LISTING 2. Calculating the position and orientation at a particular time.
distance = unit_speed * elapsed_time loop i = 0 to 3: if (distance < LineSegment[i].length) // Unit is somewhere on this line segment if LineSegment[i] is an arc //determine current angle on arc (theta) by adding or //subtracting (distance / r) to the starting angle //depending on whether turning to the left or right position.x = LineSegment[i].center.x + r*cos(theta) position.y = LineSegment[i].center.y + r*sin(theta) //determine current direction (direction) by adding or //subtracting 90 to theta, depending on left/right else position.x = LineSegment[i].start.x + distance * cos(LineSegment[i].line_angle) position.y = LineSegment[i].start.y + distance * sin(LineSegment[i].line_angle) direction = theta break out of loop else distance = distance - LineSegment[i].length
As simple solution , as already I said in a comment, you can try this aproach:
consider a phase where you point the ship in the target direction, in that phase you apply a rotation to the sip but also a forward movement. When the ship is already facing target you can apply a full forward speed. I arranged a test in love2d , here follow the ship update method .
turnAngSpeed = 0.4 --direction changing speed ForwordSpeed = 40 -- full forward speed turnForwordSpeed = ForwordSpeed *0.6 -- forward speed while turning function ent:update(dt) dir = getVec2(self.tx-self.x,self.ty-self.y) -- ship --> target direction (vec2) dir = dir.normalize(dir) --normalized a= dir:angle() - self.forward:angle() --angle between target direction e current forward ship vector if (a<0) then a=a+math.pi *2 -- some workaround to have all positive values end if a > 0.05 then -- if angle difference if a < math.pi then --turn right self.forward = vec2.rotate(self.forward,getVec2(0,0),turnAngSpeed * dt) else --turn left self.forward = vec2.rotate(self.forward,getVec2(0,0),-turnAngSpeed * dt) end --apply turnForwordSpeed self.x = self.x+ self.forward.x * turnForwordSpeed * dt self.y = self.y+ self.forward.y * turnForwordSpeed * dt else --applly ForwordSpeed self.x = self.x+ self.forward.x * ForwordSpeed * dt self.y = self.y+ self.forward.y * ForwordSpeed * dt end end
The example animation shows (the final loop) a case where the ship can't reach the target , as the combination of turning and forward speed defines a turning radius too big, in this cases can bee usefull reducing the "
turnForwordSpeed" or better make it dependent on angle distance (
a) and target distance.
Unity Nav mesh system, it would likely do what you want with a little playing around with the nav agent values.
Nav Meshes are pretty simple to use. And only usable in the top down setup (or at least only available to x/z movement)
Basically you can use any shape mesh to bake a navigation area, and add nav agents to your objects and have them find their paths around a navigation mesh