Unity 2D Shooter - Arm Rotation - unity3d

I'm making a 2D platformer game where the player has an arm that points towards the mouse cursor. This mechanism is used to aim the player's weapon. However, I am experiencing some glitches when rotating the arm.
I know the reason why I am getting these errors: The arm (in the hierarchy) is a child of the player, and what I'm doing to the player is flipping (inverting) its transform.localScale.x value whenever it moves in a specified direction, so that the player sprite looks in the given direction. Since the arm is a child of the player, its own transform is flipped as well, making it look in the same direction as the player, which is what I want. However, this causes some errors with the arm rotation. Since the arm's transform.localScale.x value is flipped, the rotation is all messed up, and I can't figure out how to solve it.
Here are some example scripts for moving the player and arm. They are simplified so that you can read it better.
// Update is called once per frame
void Update () {
var input = Input.GetAxis("Horizontal");
if(input > 0) {
if(!facingRight) {
ChangeOrientation(gameObject, new Vector3(-1,1,1));
facingRight = !facingRight;
}
}
if(input < 0) {
if(facingRight) {
ChangeOrientation(gameObject, new Vector3(-1,1,1));
facingRight = !facingRight;
}
}
rbody.velocity = new Vector2(speed * input, rbody.velocity.y);
}
public static void ChangeOrientation (GameObject g, Vector3 changeBy) {
var newTransform = g.transform.localScale;
newTransform.x *= changeBy.x;
newTransform.y *= changeBy.y;
newTransform.z *= changeBy.x;
g.transform.localScale = newTransform;
}
And my arm rotation script:
void Update () {
//rotation
Vector3 mousePos = Input.mousePosition;
mousePos.z = 0;
Vector3 objectPos = Camera.main.WorldToScreenPoint (transform.position);
mousePos.x = mousePos.x - objectPos.x;
mousePos.y = mousePos.y - objectPos.y;
float angle = Mathf.Atan2(mousePos.y, mousePos.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(new Vector3(0, 0,
(PlayerRunMovementController.facingRight ? angle : angle )));
}
Thanks for any help you can offer.

Related

How can I make a 2D gun shoot towards the Direction of the mouse rather than the Position of the mouse?

Currently, my gun is able to instantiate new bullets that work fine (they have velocity and shoot towards the mouse's position). However, if the mouse is close to the player, I noticed the velocity is drastically lessened because it is targeting the mouse's position instead of the general direction.
Is there a way to get the angle from the player to the mouse, as well as apply velocity to the bullet, without it going exactly to the mouse's position?
public GameObject bullet;
public GameObject player;
// All the following is within an update function
//Gets the mouse position, and assigns direction from gun to mouse
Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector2 direction = mousePosition - transform.position;
float angle = Vector2.SignedAngle(Vector2.right, direction);
Vector2 PlayerDimensions = player.transform.lossyScale;
Vector2 PlayerLocation = player.transform.position;
//If the Gun weapon type is selected
if (weaponType == 2)
{
// Is used to flip the player sprite based on if the gun is facing left or right
if (PlayerLocation.x > mousePosition.x)
{
PlayerDimensions.x = -1;
player.transform.localScale = new Vector2(PlayerDimensions.x, PlayerDimensions.y);
}
else if (PlayerLocation.x < mousePosition.x)
{
PlayerDimensions.x = 1;
player.transform.localScale = new Vector2(PlayerDimensions.x, PlayerDimensions.y);
}
// Aims the gun based on if player is facing left or right
if (player.transform.lossyScale.x > 0)
{
transform.eulerAngles = new Vector3(0, 0, angle);
}
else if (player.transform.lossyScale.x < 0)
{
transform.eulerAngles = new Vector3(0, 0, 180 + angle);
}
}
if (Input.GetMouseButtonDown(0) && weaponType == 2)
{
Debug.Log("Shoot");
Vector3 myPos = new Vector3(transform.position.x, transform.position.y);
GameObject Bullet = Instantiate(bullet, myPos, Quaternion.identity);
Physics2D.IgnoreCollision(Bullet.GetComponent<BoxCollider2D>(), player.GetComponent<BoxCollider2D>(), GameObject.Find("blue_bullet_medium(Clone)"));
Bullet.GetComponent<Rigidbody2D>().velocity = new Vector2(direction.x, direction.y);
}
You can use normalized vector and speed instead of just direction to get constant bullet speed. In code it will be like this:
Vector2 direction = mousePosition - transform.position;
direction.Normalize();
Vector2 bulletVelocity = direction * _speed; // assume _speed can be tuned
The normalized vector will always have length 1, so the speed will not depend on the distance from the player to the cursor position

Unity Mouse Input without Ray/Raycast and Colliders

First of all thank you for your time. I'm quite new in this so I'm struggling a bit. I'm trying to make a drag and release shooter which doesn't really depend on colliders or raycasts but solely depends on mouse delta and camera position. The way I'm trying to have it done is I'm mapping mouse position (x,y) to velocity direction such as new Vector3(mouseX, 0f, mouseY) then rotating it about Y axis to match the visual drag on the screen.
void OnMouseUp()
{
releasePos = Input.mousePosition;
Vector2 m_direction = releasePos - initialPos;
Vector3 direction = new Vector3(m_direction.x, 0, m_direction.y);
float userDrag = Mathf.Clamp(direction.magnitude, 0f, maxVelocity);
direction = Translation(direction);
direction = Vector3.Normalize(direction);
rigidbody.velocity = (direction * userDrag) * force;
}
private Vector3 Translation(Vector3 direction)
{
Vector3 camPos = Camera.main.transform.position;
camPos.y = 0;
float angle = Vector3.Angle(new Vector3(0f, 0f, 1f), camPos);
Quaternion translatedAngle = Quaternion.AngleAxis(angle, Vector3.up);
direction = translatedAngle * direction;
But as the angle changes it kinda fails to deliver what I'm asking for. Is there a way I can avoid bunch of if, else statements for the angle value or a shorter way of doing this?
Example
OnMouseUp is fundamentally a collider method.
Meaning, the mouse MUST be over the GameObject's collider for the GameObject this code is attached to. If you want to move away from using colliders, then you need to move away from this method.
The generic works-anywhere way of saying "is the mouse button up or down?" is the static methods available in the Input class:
Input.GetMouseButtonDown() True on the single frame after the mouse button is depressed
Input.GetMouseButtonUp() True on the single frame after the mouse button is released
Input.GetMouseButton() True any frame the mouse button is depressed (otherwise false)
So I think I have a solution finally and I thought I'd share it for people who might be interested.
The challenge was to rotate mouse delta's direction according to camera position so it gives a natural vibe to the player when dragging and releasing the ball for putting. I did some tests to see where my code had problems rotating delta direction properly(so it matches the screen) and realized when I'm "looking from" (0,0,1) for comparison while the camera's x position is positive it works fine. But when x is negative it doesn't because Vector3.Angle doesn't return a negative value as far as I'm concerned so I just multiplied the result with minus. Seems to work.
Here is the final code:
private void Start()
{
rigidbody = GetComponent<Rigidbody>();
}
void OnMouseDown()
{
ballPos = Input.mousePosition;
}
void OnMouseUp()
{
Vector2 m_direction = Input.mousePosition - ballPos;
Vector3 direction = new Vector3(m_direction.x, 0, m_direction.y);
float userDrag = Mathf.Clamp(direction.magnitude, 0f, maxVelocity);
direction = Translation(direction);
direction = Vector3.Normalize(direction);
rigidbody.velocity = (direction * userDrag) * force;
}
private Vector3 Translation(Vector3 direction)
{
float angle = GetAngle();
Quaternion translatedAngle = Quaternion.AngleAxis(angle, Vector3.up);
direction = translatedAngle * direction;
return direction;
}
private float GetAngle()
{
Vector3 camPos = Camera.main.transform.position;
camPos.y = 0;
if (camPos.x > 0)
{
angle = Vector3.Angle(Vector3.forward, camPos);
}
else if (camPos.x <0)
{ angle = -Vector3.Angle(Vector3.forward, camPos); }
return angle;
}
please do share if you have suggestions regarding the code.

Box Collider 2D overlapping in Unity

I have two GameObjects with a box collider. One is the player, moved with a fixed velocity towards the other. The collision stops the player (the Poo character ;D)
But they overlap, you can see it here:
I don't know why this is happening. Colliding with the top or bottom works just fine...The same effect happens from the left side. The green block has just a collider, no RigidBody.
Gif:
Another Gif, with MovePosition() ... Colliding fromt the top works, but "reentering" stops the character. Why?!:
GIF, moving up and down is okay, left and right at the top of the blocks slows him down. Weird...
Movement Script:
public class PlayerController : MonoBehaviour
{
public float Speed = 10f;
private Rigidbody2D rb2D;
private Vector2 DirectionLeft;
private Vector2 DirectionRight;
private Vector2 DirectionUp;
private Vector2 DirectionDown;
private Vector2 CurrentDirection;
// Use this for initialization
void Start()
{
rb2D = GetComponent<Rigidbody2D>();
DirectionLeft = new Vector2(Speed*-1, 0);
DirectionRight = new Vector2(Speed, 0);
DirectionUp = new Vector2(0, Speed * -1);
DirectionDown = new Vector2(0, Speed);
CurrentDirection = DirectionLeft;
}
void SetAnimationDirection()
{
Vector3 scale = transform.localScale;
if (CurrentDirection == DirectionLeft)
scale.x = 1;
else
scale.x = -1;
transform.localScale = scale;
}
void FixedUpdate()
{
float moveHorizontal = Input.GetAxis("Horizontal");
float moveVertical = Input.GetAxis("Vertical");
if (moveHorizontal > 0)
CurrentDirection = DirectionRight;
if (moveHorizontal < 0)
CurrentDirection = DirectionLeft;
if (moveVertical < 0)
CurrentDirection = DirectionUp;
if (moveVertical > 0)
CurrentDirection = DirectionDown;
Debug.Log(CurrentDirection);
SetAnimationDirection();
rb2D.velocity = CurrentDirection;
}
The solution to the first problem (overlapping):
I used transform.localScale = scale to change the direction of the sprite on every FixedUpdate() and also forgot to update the factor of the scale, since I added a new extension, which resized my object by 1 more Unity unit.
The other problem (getting stuck at the edge) is solved adding setting the Edge Radius of the Collider 2D to something below 1.0f (e.g 0.09f) and also resize the bounding box. This will prevent the object from getting stuck at the edges, because the bounding box edges are now rounded.

Unity3d - Moving camera literally cuts FPS in half?

I'm having an issue with the cameras in Unity. When the camera moves through any means it seems to cut my FPS in half if not more. It's not really noticeable on PC, unless I'm looking at the from from 800fps to about 150fps, however on mobile it'll cut the smooth 60fps I'm getting to 20fps on a Nexus 4. It's absolutely devastating.
Here's the properties for the camera I'm using & the script HOWEVER this issue still happens without ANY of these components and a completely reset camera component:
public class ViewDrag : MonoBehaviour
{
public Vector3 hit_position = Vector3.zero;
public Vector3 current_position = Vector3.zero;
public Vector3 camera_position = Vector3.zero;
public Vector2 min_position;
public Vector2 max_position;
float z = 0.0f;
MouseHolder holder;
hider sidebarHide;
GameObject gameStructure;
// Use this for initialization
void Start()
{
gameStructure = GameObject.FindGameObjectWithTag("GameStructure");
holder = gameStructure.GetComponent<MouseHolder>();
sidebarHide = GameObject.FindGameObjectWithTag("SidebarBG").GetComponent<hider>();
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
hit_position = Input.mousePosition;
camera_position = transform.position;
}
if (Input.GetMouseButton(0))
{
current_position = Input.mousePosition;
LeftMouseDrag();
}
if (!sidebarHide.isHidden)
{
//GetComponent<Camera2DFollow>().enabled = true;
}
if(gameStructure.GetComponent<ExecuteMovement2>().isExecuted)
{
//GetComponent<Camera2DFollow>().enabled = true;
}
}
void LeftMouseDrag()
{
// From the Unity3D docs: "The z position is in world units from the camera." In my case I'm using the y-axis as height
// with my camera facing back down the y-axis. You can ignore this when the camera is orthograhic.
//current_position.z = hit_position.z = camera_position.y;
// Get direction of movement. (Note: Don't normalize, the magnitude of change is going to be Vector3.Distance(current_position-hit_position)
// anyways.
Vector3 direction = Camera.main.ScreenToWorldPoint(current_position) - Camera.main.ScreenToWorldPoint(hit_position);
// Invert direction to that terrain appears to move with the mouse.
direction = direction * -1;
Vector3 position = camera_position + direction;
if (position.x < max_position.x && position.x > min_position.x && position.y < max_position.y && position.y > min_position.y)
{
if (!EventSystem.current.IsPointerOverGameObject() && holder.fromSlot == null )
{
if (sidebarHide.isHidden)
{
if (!gameStructure.GetComponent<ExecuteMovement2>().isExecuted)
{
//GetComponent<Camera2DFollow>().enabled = false;
transform.position = position;
}
}
}
}
}
}
Has anyone got an idea why this happens and how I can work around it if not fix it?
Through closer inspection I think it has something to do with the Canvas being screen space.. But it's kind of needed that way. Again, any workaround?
Check comments for profiler screenshot.
Problem Solved:
In the profiler I found that Canvas.SendWillRenderCanvases() was causing huge spikes. I solved the issue completely by turning off Pixel Perfect in the Canvas. Smooth as butter now.

Make the character face the direction he is moving?

I know I should have posted this question on Unity's questions page but I actually did and haven't received an answer for 3 days.
So I'm trying to make the character face the direction it is moving but I can't make it work. I'm working on a 3d game with a diablo-style camera but without depending on the mouse, and I'm using the script from 'Survival Shooter' as a reference.
Here's the movement script:
public float speed = 6f; // The speed that the player will move at.
public float rotateSpeed = 2f;
public Camera playerCam;
Vector3 movement; // The vector to store the direction of the player's movement.
Animator anim; // Reference to the animator component.
Rigidbody playerRigidbody; // Reference to the player's rigidbody.
int floorMask; // A layer mask so that a ray can be cast just at gameobjects on the floor layer.
float camRayLength = 100f; // The length of the ray from the camera into the scene.
Vector3 rotationY;
void Awake ()
{
// Create a layer mask for the floor layer.
floorMask = LayerMask.GetMask ("Floor");
// Set up references.
anim = GetComponent <Animator> ();
playerRigidbody = GetComponent <Rigidbody> ();
}
void FixedUpdate ()
{
// Store the input axes.
float h = Input.GetAxisRaw ("Horizontal");
float v = Input.GetAxisRaw ("Vertical");
// Move the player around the scene.
Move (h, v);
// Turn the player to face the mouse cursor.
Turning (h, v);
}
void Move (float h, float v)
{
// Move the player to it's current position plus the movement.
Vector3 targetDirection = new Vector3(h, 0f, v);
targetDirection = Camera.main.transform.TransformDirection(targetDirection);
targetDirection = targetDirection.normalized * speed * Time.deltaTime;
targetDirection.y = 0.0f;
playerRigidbody.MovePosition (transform.position + targetDirection);
}
void Turning(float y, float x)
{
if (y != 0) {
rotationY.Set (0f, y, 0f);
//rotationY = rotationY.normalized * (5 * rotateSpeed);
Quaternion deltaRotation = Quaternion.Euler (rotationY);
playerRigidbody.MoveRotation (deltaRotation);
}
}
However when I move the character it always faces the bottom-left side of the screen. How can I fix that?