# Unity - How to move a ship realistically to a point in a 2D top-down game

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

i'd be glad if someone could help me with this issue thank

• As your image seems to depict sails: Should wind be taken into account? Some maneuvers are impossible to do with the wrong wind or lack thereof. – Nobody Dec 8 '15 at 17:43
• More to the point, realistic-looking sailing ship movement really requires taking wind into account; ignoring it would be almost like ignoring gravity when implementing jumping. You don't necessarily need a particularly detailed wind model, but you do need to keep in mind that your ships are being pushed around by the wind in their sails (and the water against their keel and rudder). In particular, ships can't sail directly upwind; they'll need to beat their way there instead. – Ilmari Karonen Dec 8 '15 at 19:20
• Usualy a "goto point" can be split into rotation phase and forward movement phase. Take the same aproach but impose to the rotation a forward movement. Example each x rad of rotation move the boat forward of y meters – dnk drone.vs.drones Dec 8 '15 at 21:23

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

• This answer does not really look into the physics of ships. I also find it problematic that it is basically a link to and a lengthy excerpt from another website (I am unsure about the legality). – Nobody Dec 8 '15 at 17:47
• The last time I provided an existing resource that was a solution to the question being asked, I was asked to include the content of the link in the event that the target site ceased to be. Now I'm being asked to not include the content. Make up your minds. – Draco18s Dec 8 '15 at 17:59
• @Draco18s: What you should do is summarize the essential points of the linked material in your own words. (Or, better yet, answer the question based on your own experience, and only use links as supporting material or for further reading.) Short quotes are generally OK, especially in situations where they can't be really avoided (e.g. quoting somebody's exact words to demonstrate that they really said something), but quoting a substantial portion of an article is really going beyond fair use. – Ilmari Karonen Dec 8 '15 at 19:05
• If the point is within the circle you can go out a bit and come back. – user253751 Dec 8 '15 at 19:08
• (Ps. See also these two questions on meta.SE.) – Ilmari Karonen Dec 8 '15 at 19:08

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.

• This is a nice answer, but it may or may not be realistic enough for the OP. Unlike, say, cars, ships don't really have a "turning radius": most self (engine/human) powered ships can essentially turn on a dime, while sailing ships depend on the wind, and can actually have a negative effective turning radius when tacking, in the sense that turning left into the wind can cause the ship to drift to the right. What ships do have is inertia (and drag): they cannot turn or move instantly, and once moving or turning, take some time and force to stop. Still, have a +1. – Ilmari Karonen Dec 9 '15 at 13:45
• Thank you very much for your answer!!! :) you sir are my hero! – DavidT Dec 10 '15 at 17:08
• @DavidT Then consider marking his/her answer as the accepted one, if it was able to solve your problem satisfactorily. :) – MAnd Dec 10 '15 at 23:38

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)

Unity manual Page on setting up a nav mesh

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

• I find Draco18s's answer lacking in that regard as well. However, yours is not a real answer and more of a comment. – Nobody Dec 8 '15 at 17:41
• This is a good suggestion but, in order to be a good answer, it needs support and information on implementation. Please add some information regarding configuring nav meshes to make this a good answer. I think that's what the above commenters are trying to say :) – sirdank Dec 8 '15 at 17:49