Growing up, some of the most fun I've had with
video games has been with casual golf games. One day I saw someone on the Unity discord saying
they couldn't find any mini golf tutorials on youtube. So I decided to have a go at it it kind
of turned into a multi-day thing because I was having so much fun. I think there's a lot to share
about what I learned when making this prototype. In this video, we'll go over the fundamentals
of creating a system that allows players to click the ball and drag it in a direction
to add a force. Before you, go make sure to hit those like and subscribe buttons. It's
absolutely free and it helps the channel a ton. I have my project set up here with
my ball and a little enclosed area. The gameObjects that create the enclosed
area all have colliders attached to them. And on my ball, I've attached a sphere collider
and a rigid body.
On the rigidbody I've checked 'use gravity' and I've unchecked 'is kinematic'.
I've also set interpolate to interpolate so that things are a little bit smoother if we decide to
make a camera follow the ball at any point. And I've also set my collision detection to continuous
dynamic. The mass drag and angular drag will all change how the physics work with your ball so
feel free to experiment with them. The same can be said for all of the physics materials that
have been applied to each of the colliders. Under our ball game object we've also attached a line
game object which contains a line renderer. In order to get our ball to move when we click let's
create a new script. I'll go to add component, new script, and name this LineForce. Then I'll
click 'create and add'. Unity will attach that script to our game object and within our project
in the assets folder it'll place that script. We'll just drag this to our scripts folder
to maintain organization.
On our game object, I'll double click this LineForce to open this in
visual studio. Let's get started by visualizing our line so for a variable let's get a private
line renderer. We'll call it lineRenderer. And we'll want to assign it in the inspector
so we'll make a serialized field here. Then I'll make a private void DrawLine function
which will take in the parameter of a vector3 which will be the world point. So on our
line renderer we'll do a SetPositions equal to positions which we'll make in a second
and set the lineRenderer.enabled equal to true so that it shows.
Above
this we'll make a local vector3 array called positions we'll set it equal to the
current transform position of our game object and the world point that we take in so in this
particular case the transform.position will be the center of the object that we're drawing from
in this case it'll be the center of our ball and the world point will be our click location in
order to get our click location we're going to do some ray casting i'm going to copy and
paste a method we've used in a previous video i won't detail it too much here because we've
already gone over it in that video there's a link in the description below to the timestamp of
when we talk about that in case you want to learn a little bit more but as a really quick overview
of what's going on here we're measuring where our mouse click is on the near and far clip planes
of the camera then we're casting a ray between those two points to see if it hit any colliders
if it did we return the point at which it hit if it didn't we return null that's why here
we're returning a vector3 that is nullable now we'll call both of these in an update
function so i'll make a private void update and i'll make a vector3 that is nullable called
world point here which will be equal to our method then i'll call an if here if not worldpoint has
value if not then we'll return so what this is doing is saying if there's no value for world
point don't even worry about continuing into the rest of the code here and just return returns
like this are helpful so that you avoid nested if statements so if our word point has a value
then we'll call draw a line at the world point value back in unity on our ball script we need to
assign our line renderer assign it to that slot then i'll click play to play test now you
can see every frame a line is drawn from the center of my game object to where my
current mouse input intersects a collider and this works when you change your camera angle
but we don't want this line to show up all the time we only wanted to show up when we clicked on
our ball and when our ball is stationary so back in our script let's add a couple booleans i'll
add a private bull is idle and a private bull is aiming then i'll make a new private void on
mouse down or if is idle is aiming equals true what this means is that when we click on
our object which in this case is our ball we'll check if our object is idle and if it is
we'll allow ourselves to aim i'll also make a new method or process aim within this i'll
make an if statement for if we're not aiming or if we're not idle we'll return and i'll move
our functionality from update into this method then in update i'll put our process aim function
so every frame will do a check for if our ball is moving or if we haven't clicked on our ball to
enable aiming we'll just exit out of this function early now we need to set our idle state for that
we'll need our rigid body so i'll make a new private rigid body and call it rigidbody
then on awake i'll set my rigid body equal to get component rigidbody while we're here
i'll also set is aiming equal to false by default and to ensure that my line render isn't
drawing things that we don't want it to we'll set our enabled equal to false now in our update
i'll make an if statement that will check our dot rigidbody.velocity.mag to see if it is less
than our stop velocity we'll have to make a new variable for our stop velocity as well so i'll
declare a private float stop velocity and set it equal to 0.05 f and i want to see this inspector
in case we want to change it so i'll serialize this field so this will check whether our ball is
below a certain speed that we set if it is that's when we'll consider the ball stopped or idle but
we actually want to make sure that it's stopped so i'm going to call a stop method here that
we haven't created and then i will create it so i'll make a private void stop and when we call
stop we will set our red body velocity equal to vector 3 0 so we're stopping the velocity and
we'll do the same for our angular velocity and then we'll set our is idle equal to true
now when we play test we don't start with a line render but when we click on our ball
it starts to draw a line and it matches our cursor position now what we need to do is add
our force in our process a method under draw a line i'll add an if statement here for if input
get mouse button up 0 and this will check if our left mouse click has been raised if it has i'm
going to call a shoot function that will make and it will take in the same world point dot value
i'll create a private void shoot with a hector 3 world point i'll set my is aiming equal to false
i'll disable my line render and i'll make a new vector3 horizontal world point and set it equal
to a new vector 3 which will be our world point x and then we'll use our transform position y and
then our world point z because of the way that we have things set up our raycast can be in any
vertical space but what that means is that if we're clicking on something that is vertically
above our ball there will be a vertical force that gets applied so instead of our using our
world point directly we'll just use our transform this ensures that the velocity that's getting
applied is always horizontal with our game object from our horizontal world point we'll be able
to get the direction so we'll get a vector three call this direction we'll set it equal to the
difference of our our horizontal world point and our transform dot position and we'll do a
dot normalize so it has a magnitude of one and then we'll also calculate the strength which
we'll set equal to the vector three dot distance and we'll put in our transformed position and our
horizontal world point and then we'll add that to our rigidbody so we'll do a rigidbody.addforce
and i'll put in our direction we'll multiply it by our strength and then we will also multiply
it by our shot power which will be a valuable set so up at the very top i'll copy my stop
velocity because i'm going to have another variable here but instead of stop velocity i will
make this my shot power then back in unity on my ball i will assign a shot power of 150 and this
is just a number you can experiment with to get different strengths of the force that you're
adding now when i click play i can drag and shoot my ball in any direction that i want however
you'll notice that while i'm shooting i can still shoot in another direction let's fix that back
in our script after we add a force we're going to want to set our is idle equal to false because
now we're moving then our if statement here will be what sets it back to true now when we play
test we can shoot the ball and while it's moving i won't be able to aim again however there's a
sneaky bug in here so if i shoot it really slow i'll be able to shoot it while it's moving the
issue here is that we're doing everything in an update but we're also doing physics methods
like add force so to make our updates run at the same time as the physics time step instead
of using update we'll just use fixed update now when i play test you can see that i don't
get that bug and that's basically all there is to it we're going to be doing a mini series on
this mini golf prototype so if that's something you're interested in make sure to subscribe and
hit that notification bell so be alerted when the next video in the series goes live and
as always you can check the description down below for the line force script that we created
in this video and i'll see you in the next one you
