Unity Launch Rigidbody at Angle - c#

I wish to launch my Arrow GameObject at an angle of 30˚ and a velocity of 30 m/s. In a script I add a rigidbody to this Arrow. However, I am also trying to launch this Arrow in the direction of the player (away from the enemy) in a 3D scene. I cannot figure out how to plug in these variables to get the Vector3 for the "arrowRigidbody.velocity"
//THE VARIABLES REFERENCED ABOVE APPEAR LIKE SO:
Rigidbody arrowRigidbody;
Transform playerTransform;
Transform enemyTransform;
float angle = 30f;
float velocity = 30f;
//How do I use these variables in order to shoot the projectile at a speed
//of 30 m/s and an angle of 30˚ in the direction of the player in 3D scene
arrowRigidbody.velocity = /*????*/;
Thank you for your time and patience :)

Assuming you only shoot 'forward' you can use the simplified:
var targetDirn = transform.forward;
var elevationAxis = transform.right;
var releaseAngle = 30f;
var releaseSpeed = 30f;
var releaseVector = Quaternion.AngleAxis(releaseAngle, elevationAxis) * targetDirn;
arrowRigidbody.velocity = releaseVector * releaseSpeed;
If you need to shoot 'off-axis', you can replace the first two lines:
var targetDirn = (target.transform.position - transform.position).normalized;
var elevationAxis = Vector3.Cross(targetDirn, Vector3.up);

Using some geometry, knowing that vector will have a magnitude (m) of 1, the y-component will be m/2 and the x-component will be m*(3^.5)/2. Which will make your final value:
arrowRigidbody.velocity = new Vector2(Mathf.Pow(3, .5f)/2, 1/2) * velocity;
For a changing angle, you know that the x component will be m * cos(angle) and the y component will be m * sin(angle), leaving you with:
float velx = velocity * Mathf.Cos(angle * Mathf.Deg2Rad);
float vely = velocity * Mathf.Sin(angle * Mathf.Deg2Rad);
arrowRigidbody.velocity = new Vector2(velx, vely);

Related

Unity2D how do i calculate jump height using Velocity & Gravity

i would like to calculate the jump height. but i don't know how
Example:
RightBody2D rb = this.GetComponent<RightBody2D>();
// Do jump
rb.velocity = new Vector2(rb.velocity.x, jump);
// Here is the question. how do i calculate jump height
float jumpHeight = CalculateJumpHeight(jump);
indicator.transform.position = new Vector2(transform.position.x, jumpHeight);
You can calculate this as follows, however, it's not going to be perfectly accurate in every instance because Unity's physics system isn't deterministic.
float timeToReachApexOfJump = Mathf.Abs(rb.velocity.y / Physics.gravity.y);
float heightOfJump = (0.5f * Physics.gravity.y * timeToReachApexOfJump) + (rb.velocity.y * timeToReachApexOfJump);
Debug.Log(timeToReachApexOfJump);
Debug.Log(heightOfJump);

Restrict gameobject inside a radius around my player

I need to restrict my Players hand inside a radius around the character.
The game is 2D and the hand movement is basically exactly the same as: Madness Interactive (https://www.newgrounds.com/portal/view/118826)
[SerializeField] private float speed = 2f;
[SerializeField] private GameObject radiusCenter;
[SerializeField] private float radius = 0.5f;
void Start()
{
Cursor.visible = false;
}
void Update()
{
Vector3 playerPos = radiusCenter.transform.position;
Vector3 playerToHand = this.transform.position - playerPos;
var moveDirection = new Vector3(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y"), 0);
float DistanceToRadius = Vector3.Distance(playerPos, playerToHand);
transform.position += moveDirection * speed * Time.deltaTime;
float angle = Mathf.Atan2(playerToHand.y, playerToHand.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.AngleAxis(angle, Vector3.forward);
}
I cant figure out the way to keep my players hand inside a set radius around my player, see pictures below for further explanation:
I thought maybe I could get a float value from playerPos and playerToHand and create a IF-statement that restricts the hand from going outside the set radius float, but I didnt get it to work...
EDIT 1: The hand is a child of my player. I use a gameObject in the center of my player as radiusCenter which my hand rotates around.
One very simple way to check if the hand is too far away is to use this line of code:
<movement code here>
if (Vector3.distance(playerPos, transform.position) > radius)
{
transform.localPosition = transform.localPosition.normalized * radius;
{
This will make it so if the hand will be outside the circle after the move, it will just put the hand on the circle.

Tilt first person camera's z axis while turning

I am working on controls for a bipedal tank, that has a boost/dash feature.
When the boost/dash mode is activated by holding a button the tank changes from a traditional WASD strafing movement to something like a jet fighter but on ground.
Now when the player tries to turn while boosting the camera needs to tilt with the turning direction to make it feel smoother.
Here is a video clip showing my problem
This is the script added to the main camera
public class PlayerCameraTilt : MonoBehaviour
{
private Camera viewCam;
private CharacterController characterController;
public float tiltAmount = 10f;
private void Start()
{
viewCam = GetComponent<Camera>();
characterController = GetComponentInParent<CharacterController>();
}
private void Update()
{
if (characterController.GetComponent<PlayerController>().movementState == MovementModes.boost)
{
float inputValue = Input.GetAxis("MoveHorizontal") * tiltAmount;
Vector3 euler = transform.localEulerAngles;
euler.z = Mathf.Lerp(euler.z, -inputValue, 5f * Time.deltaTime);
transform.localEulerAngles = euler;
}
else
{
Vector3 euler = transform.localEulerAngles;
euler.z = Mathf.Lerp(euler.z, 0f, 5f * Time.deltaTime);
transform.localEulerAngles = euler;
}
}
}
It kind of works, BUT only in one rotation direction, namely the positive one. If i turn the other way the angle becomes negative and does a whole 360 degree rotation until it ends up at 0 degrees.
I tried around with the built in Quaternion methods like Quaternion.Rotate() or Quaternion.AngleAxis()
but they didn't work because Quaternion.Rotate() doesn't tilt the Camera but completely rotates it. Quaternion.AngleAxis() works with the whole tilt angle limit but also does something to the other axis which i don't want to modify.
Is there a way to prevent the camera from doing a complete 360° rotation when the axis input becomes negative and just tilt in a slight angle?
Wraparound issues aside, the z component is "applied" first among the euler components, so I wouldn't modify it directly like that in this application.
Instead, I would convert the input to an angle to lean, rotate the direction of the local up before localRotation is applied (Quaternion.Inverse(transform.localRotation) * transform.up) by that angle, then use Quaternion.LookRotation to find a rotation with the current forward and that up, then use Quaternion.Slerp to slerp toward that rotation:
private void Update()
{
if (characterController.GetComponent<PlayerController>().movementState == MovementModes.boost)
{
float inputValue = Input.GetAxis("MoveHorizontal") * tiltAmount;
Vector3 upOfParent = Quaternion.Inverse(transform.localRotation) * transform.up;
Vector3 upDir = Quaternion.AngleAxis(-inputValue, transform.forward) * upOfParent;
Quaternion goalRot = Quaternion.LookRotation(transform.forward, upDir);
transform.rotation = Quaternion.Slerp(transform.rotation, goalRot, 5f * Time.deltaTime);
}
else
{
float inputValue = Input.GetAxis("MoveHorizontal") * tiltAmount;
Vector3 upOfParent = Quaternion.Inverse(transform.localRotation) * transform.up;
Quaternion goalRot = Quaternion.LookRotation(transform.forward, upOfParent);
transform.rotation = Quaternion.Slerp(transform.rotation, goalRot, 5f * Time.deltaTime);
}
}
Just keep in mind that using t = 5f * Time.deltaTime for lerp/slerp operations does not always guarantee that the output will ever equal the to parameter.

How to change the orientation for an overlapbox and a gizmo?

after a good amount of searching I haven't been able to find out how to change the rotation of my overlap boxes set up and the gizmo used to visualize them.
//draw a hitbox in front of the character to see which objects it collides with
Vector3 boxPosition = transform.position + (Vector3.up * lastAttack.collHeight)
+ Vector3.right * lastAttack.collDistance;
Vector3 boxSize = new Vector3 (lastAttack.CollSize/2, lastAttack.CollSize/2, hitZRange/2);
Collider[] hitColliders = Physics.OverlapBox(boxPosition, boxSize, Quaternion.identity,
HitLayerMask);
I'm using it for damage calculation. I want the OverlapBox to take the same rotation of the player and always be in front of the player.
void OnDrawGizmos(){
if (lastAttack != null && (Time.time - lastAttackTime) < lastAttack.duration) {
Gizmos.color = Color.red;
Vector3 boxPosition = transform.position + (Vector3.up * lastAttack.collHeight)
+ Vector3.right * ((int)lastAttackDirection * lastAttack.collDistance);
Vector3 boxSize = new Vector3 (lastAttack.CollSize, lastAttack.CollSize, hitZRange);
Gizmos.DrawWireCube (boxPosition, boxSize);
}
}
Draw the Box Overlap as a gizmo to show where it currently is testing
To have the overlap box inherit the transform's rotation, you can use transform.rotation instead of Quaternion.identity for the overlap box's rotation.
For the gizmo, it's a little more complex. One way to fix this is to change the Gizmo matrix to be the local transform matrix with Gizmos.matrix = transform.localToWorldMatrix, which will make the gizmo inherit the player's rotation. But, it will also make the gizmo's position be relative to the player's local position. So, you'll need to convert the world position boxPosition to local position before you draw the gizmo. You can use transform.InverseTransformPoint to do this.
You may want to restore the gizmo settings to what they were previously or it could result in unexpected behaviour in other places where Gizmos is used.
Altogether:
//draw a hitbox in front of the character to see which objects it collides with
Vector3 boxPosition = transform.position + (Vector3.up * lastAttack.collHeight)
+ Vector3.right * lastAttack.collDistance;
Vector3 boxSize = new Vector3 (lastAttack.CollSize/2, lastAttack.CollSize/2, hitZRange/2);
Collider[] hitColliders = Physics.OverlapBox(boxPosition, boxSize,
transform.rotation, HitLayerMask);
...
void OnDrawGizmos(){
if (lastAttack != null && (Time.time - lastAttackTime) < lastAttack.duration) {
// cache previous Gizmos settings
Color prevColor = Gizmos.color;
Matrix4x4 prevMatrix = Gismos.matrix;
Gizmos.color = Color.red;
Gizmos.matrix = transform.localToWorldMatrix;
Vector3 boxPosition = transform.position + (Vector3.up * lastAttack.collHeight)
+ Vector3.right * ((int)lastAttackDirection * lastAttack.collDistance);
// convert from world position to local position
boxPosition = transform.InverseTransformPoint(boxPosition);
Vector3 boxSize = new Vector3 (lastAttack.CollSize, lastAttack.CollSize, hitZRange);
Gizmos.DrawWireCube (boxPosition, boxSize);
// restore previous Gizmos settings
Gizmos.color = prevColor;
Gizmos.matrix = prevMatrix;
}
}

Instantiate Object on Raycast2D hit and rotate Instance

I want to move an instance of a gameObject along the outline of another gameobject. Its a 2D Project in Unity.
My current Code:
Vector3 mousePosition = m_camera.ScreenToWorldPoint(Input.mousePosition);
RaycastHit2D hit = Physics2D.Raycast(new Vector2(mousePosition.x, mousePosition.y), new Vector2(player.transform.position.x, player.transform.position.y));
if (hit.collider != null && hit.collider.gameObject.tag == "Player") {
if (!pointerInstance) {
pointerInstance = Instantiate(ghostPointer, new Vector3(hit.point.x, hit.point.y, -1.1f), Quaternion.identity);
} else if(pointerInstance) {
pointerInstance.gameObject.transform.position = new Vector3(hit.point.x, hit.point.y, -1.1f);
pointerInstance.gameObject.transform.eulerAngles = new Vector3(0f, 0f, hit.normal.x);
}
}
Unfortunately, the gameObject doesn't rotate towards the mouse and the position on the left side of the playerObject is also sometimes off. I tried to use Instantiate() with Quaternion.LookRotation(hit.normal), but no luck either.
Here a rough sketch of what I want to achieve:
Any help is appreciated. Thanks!
it's better to use Mathematical way instead of physical way(Raycasting),because in raycasting you have to throw ray several time for checking hit point and rotate your object,it makes lag in your game.
Attach this script to your instantiated object:
using UnityEngine;
using System.Collections;
public class Example : MonoBehaviour
{
public Transform Player;
void Update()
{
//Rotating Around Circle(circular movement depend on mouse position)
Vector3 targetScreenPos = Camera.main.WorldToScreenPoint(Player.position);
targetScreenPos.z = 0;//filtering target axis
Vector3 targetToMouseDir = Input.mousePosition - targetScreenPos;
Vector3 targetToMe = transform.position - Player.position;
targetToMe.z = 0;//filtering targetToMe axis
Vector3 newTargetToMe = Vector3.RotateTowards(targetToMe, targetToMouseDir, /* max radians to turn this frame */ 2, 0);
transform.position = Player.position + /*distance from target center to stay at*/newTargetToMe.normalized;
//Look At Mouse position
var objectPos = Camera.main.WorldToScreenPoint(transform.position);
var dir = Input.mousePosition - objectPos;
transform.rotation = Quaternion.Euler(0, 0, Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg);
}
}
Useful explanations
Atan2:
atan2(y,x) gives you the angle between the x-axis and the vector (x,y), usually in radians and signed such that for positive y you get an angle between 0 and π, and for negative y the result is between −π and 0.
https://math.stackexchange.com/questions/67026/how-to-use-atan2
Returns the angle in radians whose Tan is y/x.
Return value is the angle between the x-axis and a 2D vector starting at zero and terminating at (x,y).
https://docs.unity3d.com/ScriptReference/Mathf.Atan2.html
Mathf.Rad2Deg:
Radians-to-degrees conversion constant (Read Only).
This is equal to 360 / (PI * 2).

Categories

Resources