I am making top-down shooter on Unity and I need to implement shotgun, which will release 5 rounds at a time, each next will have rotation 10 degrees less than previous (from 20 to -20 in total). Instantiating the bullet on firepoint, the velocity is applied to it by bullet script, attached to bullet prefab. I apply the rotation in instantiating method too, but the bullet just rotates around itself, not to direction it flying.
Shooting code:
Vector2 mouseScreenPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector2 direction = (mouseScreenPosition - (Vector2) transform.position).normalized;
transform.up = direction;
GameObject newBullet1 = Instantiate(bullet, firePoint.position, Quaternion.Euler(0f, 0f, 20f)) as GameObject;
newBullet1.GetComponent<Bullet>().direction = direction;
GameObject newBullet2 = Instantiate(bullet, firePoint.position, Quaternion.Euler(0f, 0f, 10f)) as GameObject;
newBullet2.GetComponent<Bullet>().direction = direction;
Bullet code:
public Vector3 direction;
public float bulletSpeed;
void Update () {
GetComponent<Rigidbody2D>().velocity = new Vector3 (direction.x, direction.y, transform.position.z) * bulletSpeed;
}
You need to apply the rotation to the direction vector for each bullet. Formula for the rotation of a vector is...
x1 = x0*cos(angle) - y0*sin(angle);
y1 = x0*sin(angle) + y0*cos(angle);
Vector2 direction10 = new Vector2(direction.x*Mathf.cos(10*Mathf.Deg2Rad) - direction.y*Mathf.sin(10*Mathf.Deg2Rad), direction.x*Mathf.sin(10*Mathf.Deg2Rad) + direction.y*Mathf.cos(10*Mathf.Deg2Rad));
Vector2 direction20 = new Vector2(direction.x*Mathf.cos(20*Mathf.Deg2Rad) - direction.y*Mathf.sin(20*Mathf.Deg2Rad), direction.x*Mathf.sin(20*Mathf.Deg2Rad) + direction.y*Mathf.cos(20*Mathf.Deg2Rad));
newBullet2.GetComponent<Bullet>().direction = direction10;
newBullet3.GetComponent<Bullet>().direction = direction20;
... //etc
Can probably do this cleaner with a loop.
Related
New to C# and Unity, trying to have a pointer that follows a player around in 2D space but it is bound within a circle of radius 4. My code below produces some odd behaviour, the cursor seems to move farther away from the player, and is not bound by the radius.
void Update()
{
Vector3 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
mousePos.z = Camera.main.transform.position.z + Camera.main.nearClipPlane;
Vector3 centerScr = Camera.main.ViewportToWorldPoint(new Vector3(0.5f, 0.5f, Camera.main.nearClipPlane));
Vector3 direction = mousePos - centerScr;
direction = Vector3.ClampMagnitude(direction, 4f);
transform.position = mousePos + direction;
}
}
So I have script which I got from Blackthornprods ranged combat youtube video since im a beginner. Im trying to get my weapon to rotate around my sphere gameobject but for some reason it doesnt rotate to where the mouse position is, but when I jump it rotates around weirdly not really to the mouse but randomly (im assuming randomly). My game is 3D and his tutorial was for 2d. I would really appreciate any attempt for a solution. Here is my code:
void Update()
{
Vector3 difference = Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position;
float rotZ = Mathf.Atan2(difference.y, difference.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(0f, 0f, rotZ + offset);
}
The weapon is still to rotate around the Z axis.
First, Camera.main calls FindGameObjectsWithTag, which is an expensive operation, so (as the documentation says), you should call it as few times as possible and cache the result:
Camera mainCam;
void Awake()
{
mainCam = Camera.main;
}
Second, you're using ScreenToWorldPoint incorrectly. If this is really a 3d game as the question describes, you should provide a depth from the camera as the z component of the argument. You can use vector math to do this, and the result is a world position you can tread the cursor as being at.:
Camera mainCam;
void Awake()
{
mainCam = Camera.main;
}
void Update()
{
float objectDepthFromCamera = Vector3.Dot(
transform.position - mainCam.transform.position,
mainCam.transform.forward);
Vector3 cursorWorldPosition = mainCam.ScreenToWorldPoint(Input.mousePosition
+ Vector3.forward * objectDepthFromCamera);
Then assuming the "front" of the object points out of the local up direction, you can use the optional second parameter of Quaternion.SetRotation to set the rotation so that is pointing toward the cursor:
Camera mainCam;
void Awake()
{
mainCam = Camera.main;
}
void Update()
{
float objectDepthFromCamera = Vector3.Dot(
transform.position - mainCam.transform.position,
mainCam.transform.forward);
Vector3 cursorWorldPosition = mainCam.ScreenToWorldPoint(Input.mousePosition
+ Vector3.forward * objectDepthFromCamera);
transform.rotation = Quaternion.LookRotation(Vector3.forward,
cursorWorldPosition - transform.position);
}
If the front of the object points out the local right direction, you can use Vector3.Cross to determine what direction the local up should be pointing:
Camera mainCam;
void Awake()
{
mainCam = Camera.main;
}
void Update()
{
float objectDepthFromCamera = Vector3.Dot(
transform.position - mainCam.transform.position,
mainCam.transform.forward);
Vector3 cursorWorldPosition = mainCam.ScreenToWorldPoint(Input.mousePosition
+ Vector3.forward * objectDepthFromCamera);
Vector3 localUpNeeded = Vector3.Cross(Vector3.forward,
cursorWorldPosition - transform.position);
transform.rotation = Quaternion.LookRotation(Vector3.forward, localUpNeeded);
}
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;
}
}
I Have a gun that spawn a projectile that bounces of colliders (a Ricochet). It is supposed to be shooting to the direction of where the gun is facing but what I am getting is the projectile always shoots 45 degrees upwards to the right I know this is because of my constant declared vector 2.
I tried using Vector2.up but it prevents the projectile to do the ricochet effect because it always wants to go upwards.
How should I implement those things? I just want the projectile to shoot to the direction where my gun is facing and bounces of on colliders. This is a 2D game btw. I have my codes attached below so you can see. Thanks!
Projectile Script:
private Rigidbody2D rb;
public static bool canMove = true;
void Start()
{
rb = GetComponent<Rigidbody2D>();
rb.velocity = new Vector2(10f, 10f);
}
void Update()
{
//transform.Translate(Vector2.up * speed * Time.deltaTime);
if (canMove)
{
rb.isKinematic = false;
}
else if (!canMove)
{
rb.isKinematic = true;
}
}
Gun Script:
float offset = -90f;
public GameObject projectile;
public Transform shotPoint;
public GameObject child;
void Start()
{
}
void Update()
{
Vector3 diff = Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position;
float rotZ = Mathf.Atan2(diff.y, diff.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(0f, 0f, rotZ + offset);
if (Input.GetMouseButtonDown(0))
{
Instantiate(projectile, shotPoint.position, transform.rotation);
Projectile.canMove = true;
}
}
The Rigodbody.velocity is in World-Space coordinates.
When you pass in
rb.velocity = new Vector2(10f, 10f);
it will go in world space 10 in X and 10 in Y direction.
In order to pass it in as local coordinates in general you can not always rely on Tramsform.InverseTransformDirection as suggested here since the Transform component. In this specific case it might work but in general you set velocities in FixedUpdate and in that moment the Transform component might not be updated yet!
But the Rigidbody2D component is so in general you can use Rigidbody2D.GetRelativeVector in order to convert a local vector relative to the Rigidbody into world coordinates:
// Might also be Vector.up depending on your setup
rb.velocity = rb.GetRelativeVector(Vector2.right * speed);
Note: it would be better you make
[SerializeField] private Rigidbody2D rb;
and already reference it via the Inspector. Then you can get rid of the expensive GetComponent call.
Because you are telling it to do so.
rb.velocity = new Vector2(10f, 10f);
10 to the right, and 10 upwards.
Unless your projectile has a constant force applied to it, like a missile, get rid of everything related to forces or velocity in the projectile script. It will do you no good.
Then, on the gun script:
//...
if (Input.GetMouseButtonDown(0)) {
var projectileInstance = Instantiate(projectile, shotPoint.position, transform.rotation);
var rigidbody = projectileInstance.GetComponent<Rigidbody2D>();
rigidbody.velocity = transform.TransformDirection(yourDirectionVector);
Projectile.canMove = true;
}
Where Transform.TransformDirection is what makes yourDirectionVector, which is a direction relative to the gun, be transformed into one relative to world-space.
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).