So I need help with starting something. I have a ragdoll for a 2D platformer that I've got moving and he's able to stand on his feet.
So now I have made a gun as a child of his forearm, and I want to have the character aim with his gun wielding hand using mouse input (x, y movement of the mouse). Where the player aims and shoots, that's where the projectile will travel. I currently don't know how to start with that.
My character movement mainly runs through physics, and I want it like that cause I want the recoil of my weapon to add force to the character so he can fly.
I haven't really done much besides, creating the empty GameObject for where my projectiles will spawn. Also I have created a script to read player input, then instantiate projectiles.
Alright, so you want to:
There're indeed many methods to implement this, so let me show you how I'd do it my way. I assume you haven't done it yet, so I'll try to explain as clear as I can.
1. Create a player
I assume you have already implemented this logic, so that's how my player looks like
Playerhas aRigidbody2DBody,ArmandWeaponall have aBoxCollider2DHandleis anempty GameObject, which has aWeaponinside of it. Changing theHandle'sZrotation, rotates theWeaponas you want2. Rotate the weapon in the mouse direction
The
α(alpha) represents theZrotation of ourhandleindegrees, that's the value we need to get.First, we get the
mouse positionand convert it fromscreentoworldspace._mainCamerawill be ourCamera.main, assigned in theStartmethod.To ensure we don't have issues with performance, we store the previous mouse position as
_prevMousePosand check if our current isn't the same. This way we don't make any useless calculations, which affect the performance.As I drew on the image above, we first have to calculate the
offsetbetween themouseandhandlepositions. This gives us thexandysides of the imaginary triangle.Now, according to the tangent ratio, our formula looks like this, where
xisoffset.xandyisoffset.y.tan^{-1}is calledarc-tangent, and there are 2 similar methods to do this. The explanations are taken from this methods' summaries.I'd suggest using
Atan2. It returns an angle inradians, so we have to convert it todegrees, as this is what Unity's rotation eulers use. We can useMathf.Rad2Deg(Unity's radians-to-degrees conversion constant).Now, we can create a method which returns the distance angle in
degrees.We rotate the
weaponjust around theZaxis, so we have to assign just it. This isn't possible settingtransform.rotation.zdirectly, unless you know how to translate it (which you don't really need to know).We use
Quaternion.Eulermethod withVector3.forwardmultiplied by the angle, which is the same asnew Vector3(0f, 0f, angle), but more readable, as for me. Let's create a method out of it.We can clamp the
anglewe get between themininumandmaximumvalues serialized in theInspectorasweaponRotMinandweaponRotMax.And so our current logic, which rotates the
weaponin the direction of themouse, looks like this.3. Shoot the bullets from the weapon
3.1 Instantiate a bullet
We have to shoot, which means spawning the bullet, when the player presses the
left mouse button.This can be also implemented with other keys like
Space, for instance.We have to serialize the
bulletandbullet holder, where thebulletsare spawned, in theInspector.We spawn the
bulletprefab usingInstantiatemethod, the 2nd parameter is theparentthebulletis spawned in.Then we have to get an
offset, as shown on the image above.We get the
angle, which is theZposition of thebulletand translate it fromdegreestoradians, as we'll have to pass it intosinandcos.Note that the it's not
rotation.z, for the reason I've mentioned above, in the 1st header.The
diagonal, namedPCon the image, is the half of theweaponandbulletlossyScales, which, unlike thelocalScale, is inglobalcoordinates.Now, according to the
cosineandsine ratio, out formulas look like this.And as we multiply them both by the
diagonal, ouroffsetis gotten this way.In order to get position of the instantiated
bullet, we have to add theoffsetto thepositionof theweapon, as shown on the image.Let us create a method for the
offset.And now assign the new
positionof thebulletto theweapon'spositionadded to the method's result.Now, in order to make a
bulletthat's not a perfect circle be shot correctly, we have to rotate it as shown on the image.The
rotationis the same as that of theweapon, and we can use previously createdRotateTransformmethod.Let's create a full method of out it, which takes the
rotationof theweaponas the parameter.And now the full
bullet spawnlogic looks like this.3.2 Make the bullet fly
I assume, you already have a
bulletGameObjectand there's nothing hard in its creation. Just make sure not to assign aRigidbody2Dwith the gravity enabled.We have to create a script for the
bullet, which can be calledBulletController.Let's assign its
speedin theInspector.Now, there are lots of ways to make it fly, so let's just use
transform.Translatemethod inUpdate.The
bulletwill fly e.g. to the right, which we multiply with thespeedandTime.deltaTime, to make the bullet fly with the samespeed, regardless of theframes per second.It doesn't really matter, but we can add a
lifetimefield to destroy thebulletafter the certain amount of seconds, but I am sure you can implement the desired logic yourself.With the added
BulletControllerscript, thebulletflies as soon as it is spawned in thePlayerControllerscript. So an example of thebulletscript might look like this.4. Add a recoil force from the shot
The
recoil forceusually happens in the opposite direction of the shot.The
forceis usually applied withRigidbody2D.AddForce(Vector2 force)method, so we have to assign theRigidbody2Din theStartmethod first.If we have an
angleof45degrees, bothxandysides of thetriangleareequal, which should give us aVectorof(.5, .5). This way, theforceapplied to thexandyaxes is the same.If we have an
angleof90degrees, whichtriangleis basically a straight line (although it's not even a triangle), theVectorof(0, 1)is returned, giving us theforcefully on theyaxis, which makes theplayerjump upward (downward, in our case, as the direction is opposite)Now, I have great news for you. We have already calculated the
directionof theforcewith this line in theShootmethod.Although there's a problem: the
offsetmay be both:(10, 20), with a ratio of10 / 20 = .5(100, 200), with a ratio of100 / 200 = .5And even though the
ratiosare the same, the appliedforcewill be 10 times greater in the second variant.That's why we use
Vector2.normalized, which returns theVectorwith the sameratio, but the maximum value of1(meaning 100%).So in both variants the same
Vectorof(.5, 1)is returned. This allows us to apply the same force to the player, regardless of theoffset.Let's serialize the
recoilForcefield in theInspector, for us to easily adjust it.And create an
AddRecoilForcemethod, which adds thenormalized forcemultipled byrecoilForceand10, to not makerecoilForcetoo big.The method should be called directly in
Shootmethod with the previously assignedoffsetparameter, so it'll look like this.Full code
Now, the full code, with some
regionsfor readability, looks like this.The
PlayerControlleris assigned to thePlayer, andBulletControllerto theBulletprefab.