Unity2D
How to make game object to face opposite from the position of another game object?
I have a fish that is going always forward and randomly rotating to make semi random movement,
and i want that in range of player(shark) the fish change direction opposite to shark and start moving faster(trying to escape). I have speed increase already but i doknt know how to make the opposite direction.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FishMove : MonoBehaviour
{
Rigidbody2D rb;
int rotZ = 0;
[SerializeField]
Transform target;
float speed;
bool move=true;
bool rotace = true;
[SerializeField]
int smerRotace;
[SerializeField]
float casRotace;
float nula;
[SerializeField]
Transform shark;
float fearRange=4;
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
void FixedUpdate()
{
if (rotace)
{
rotace = false;
smerRotace = Random.Range(-1, 2);
if (smerRotace == 0) nula = 0.2f;
Invoke("Rotace", Random.Range(3.0f*nula, 9.0f*nula));
nula = 1;
}
rotZ += 2 * smerRotace;
float distance = Vector2.Distance(transform.position, shark.position);
if (distance < fearRange)
{
speed = 0.1f;
}
else
{
transform.rotation = Quaternion.Euler(0, 0, rotZ);
}
transform.position = Vector2.MoveTowards(transform.position, target.position, speed);
speed = 0.05f;
}
void Rotace()
{
rotace = true;
}
}
Notice: at the part when I am saying to add/subtract 90 (you’ll know when you get there), try adding 90, if that doesn’t work, try subtracting 90 to get the right result.
To make it move fully the opposite direction, you should do a few things. First, I will tell you about Mathf.Atan2(float y, float x). Atan2 is arctangent. Arctangent takes a position, and finds the amount of rotations to rotate at that object, in radians. Then, you would want to multiply by Mathf.Rad2Deg, or radians to degrees. It converts radians to degrees, which unity uses. Then, you would add some degrees to face the opposite direction.
Here is a way to make an object look away from the the mouse:
using UnityEngine;
using System;
using System.Collections.Generic;
public class LookAtMouse : MonoBehaviour
{
public GameObject obj;
Vector3 offset;
Vector3 mousePosition;
float rotation;
void Update()
{
mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
offset = mousePosition - obj.transform.position;
rotation = Mathf.Atan2(offset.y, offset.x) * Mathf.Rad2Deg - 90f;
obj.transform.rotation = Quaternion.Euler(0f, 0f, rotation);
}
}
Basically, the script gets the mouse position, and stores it in mousePosition. Then, it gets the offset of the mouse relative to obj. The rotation is the product of the arctangent of the offset and the amount of degrees in a radian (unity uses degrees, not radians), minus 90. Arctangent is 90 degrees off of the target, so people usually add 90 to get it to look at the target. We are looking away from the target, so we subtract 90 to be 180 degrees away, or half way.
We would do the same thing for your script; here it is:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FishMove : MonoBehaviour
{
Rigidbody2D rb;
int rotZ = 0;
[SerializeField] Transform target;
float speed;
bool move=true;
bool rotace = true;
[SerializeField] int smerRotace;
[SerializeField] float casRotace;
float nula;
[SerializeField] Transform shark;
float fearRange=4;
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
Vector3 offset;
float rotation;
void FixedUpdate()
{
if (rotace)
{
rotace = false;
smerRotace = Random.Range(-1, 2);
if (smerRotace == 0) nula = 0.2f;
Invoke("Rotace", Random.Range(3.0f*nula, 9.0f*nula));
nula = 1;
}
rotZ += 2 * smerRotace;
float distance = Vector2.Distance(transform.position, shark.position);
if (distance < fearRange)
{
speed = 0.1f;
offset = transform.position - shark.transform.position;
rotation = Mathf.Atan2(offset.y, offset.x) * Mathf.Rad2Deg - 90f;
transform.rotation = Quaternion.Euler(0f, 0f, rotation);
}
else
{
transform.rotation = Quaternion.Euler(0, 0, rotZ);
}
transform.position = Vector2.MoveTowards(transform.position, target.position, speed);
speed = 0.05f;
}
void Rotace()
{
rotace = true;
}
}
Tell me in the comments if it works or if it doesn't work.
Related
My question might sound confusing but I want to make my code so when my character doesn't move, he looks directly towards the closest enemy. The problem is that when I stop moving my character, it only does the command once and doesn't redirect to the enemy's new position. I tested out many things like making it so when i move, it looks at the enemy and it seems to work but whenever I set it to not move, it doesn't work.
using System.Collections.Generic;
using UnityEngine;
namespace work.working.worked
{
public class Movement : MonoBehaviour
{
public float moveSpeed;
public float rotationSpeed;
public static bool ismoving;
public Rigidbody2D rb;
Vector2 movement;
// Update is called once per frame
void FixedUpdate()
{
rb.MovePosition(rb.position + movement * moveSpeed * Time.fixedDeltaTime);
}
void Update()
{
movement.x = Input.GetAxisRaw("Horizontal");
movement.y = Input.GetAxisRaw("Vertical");
if (Input.anyKey)
{
ismoving = true;
}
else
{
ismoving = false;
}
if (ismoving == true)
{
Vector2 direction = new Vector2(movement.x, movement.y);
direction.Normalize();
if (direction != Vector2.zero)
{
Quaternion toRotation = Quaternion.LookRotation(Vector3.forward, direction);
transform.rotation = Quaternion.RotateTowards(transform.rotation, toRotation, rotationSpeed * Time.deltaTime);
}
}
if (ismoving == false)
{
Vector3 direction = FindClosest.closestEnemy.position - transform.position;
direction.Normalize();
float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg - 85f;
rb.rotation = angle;
}
}
}
}
Instead of Input.anyKey you could check that the magnitude of movement vector to see if character is moving.
ismoving = movement.magnitude > 0.0f;
using System.Collections.Generic;
using UnityEngine;
namespace work.working.worked
{
public class Movement : MonoBehaviour
{
public float moveSpeed;
public float rotationSpeed;
public bool ismoving;
public Rigidbody2D rb;
Vector2 movement;
void FixedUpdate()
{
rb.MovePosition(rb.position + movement * moveSpeed * Time.fixedDeltaTime);
}
void Update()
{
movement.x = Input.GetAxisRaw("Horizontal");
movement.y = Input.GetAxisRaw("Vertical");
ismoving = movement.magnitude > 0.0f;
if (ismoving)
{
Vector2 direction = new Vector2(movement.x, movement.y);
direction.Normalize();
if (direction != Vector2.zero)
{
Quaternion toRotation = Quaternion.LookRotation(Vector3.forward, direction);
transform.rotation = Quaternion.RotateTowards(transform.rotation, toRotation, rotationSpeed * Time.deltaTime);
}
}
else
{
Vector3 direction = FindClosest.closestEnemy.position - transform.position;
direction.Normalize();
float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg - 85f;
rb.rotation = angle;
}
}
}
Also don't make ismoving variable static or it gets set by every active Movement component in loaded scenes which can lead to unexpected behavior down the line. If its public, you can see it's state in Unity inspector by selecting the GameObject with Movement component.
I have this script below and I want my player to always move towards the rotation angle, which doesn't happen. It only changes direction when I click.
My purpose is for the player to move at all times and towards the rotation which should be controlled by mouse position/mouse x axis (kind of like auto-run, but always change rotation based on mouse, not just move right or left).
I've tried about 10 different methods, nothing worked so far...
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MovementController : MonoBehaviour
{
public float speed = 4;
public float rot = 0f;
public float rotSpeed = 80;
public float gravity = 8;
private Camera cam;
Vector3 moveDir = Vector3.zero;
CharacterController controller;
Animator anim;
// Start is called before the first frame update
void Start()
{
controller = GetComponent<CharacterController> ();
anim = GetComponent<Animator>();
}
// Update is called once per frame
void Update()
{
float horizontalSpeed = 8.0f;
//Get the mouse delta. This is not in the range -1...1
float h = horizontalSpeed * Input.GetAxis("Mouse X");
float z = horizontalSpeed * Input.GetAxis("Mouse Y");
transform.Rotate(0, h, 0);
//Move Input
if(controller.isGrounded){
if(Input.GetMouseButtonUp(1))
{
anim.SetInteger ("condition", 1);
moveDir = new Vector3 (0,0,1) * speed;
// moveDir *= speed;
moveDir = transform.TransformDirection(moveDir);
}
if(Input.GetMouseButtonDown(1))
{
anim.SetInteger("condition", 0);
moveDir = Vector3.zero;
}
}
moveDir.y -= gravity * Time.deltaTime;
controller.Move(moveDir * Time.deltaTime);
}
}
Transform.LookAt
Just get a cursor position and that's it
Vector3 direction = target.position - player.transform.position;
Quaternion finalPlayerRotation = Quaternion.LookRotation(direction);
player.transform.rotation = finalPlayerRotation;
This also works in some cases:
Vector3 direction = target.position - player.transform.position;
player.transform.right /*You may need to change the Right to upper, -upper, -Right depend on the player rotation and the target position*/ = direction;
I want to create effect like the object is looking around.
Like it's inspecting around. In this he is looking at a window so the idea is to make like he is looking the view outside.
This is a screenshot of the Navi looking at the window :
The camera is positioned out of the window looking forward on the Navi face:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ItemAction : MonoBehaviour
{
public float xAngle, yAngle, zAngle;
public float speed;
public camMouseLook mouselook;
public GameObject lockedRoomCamera;
public Camera playerCamera;
public GameObject navi;
private bool torotate = false;
public void Init()
{
navi.transform.parent = null;
navi.transform.localScale = new Vector3(0.5f, 0.5f, 0.5f);
navi.transform.Rotate(new Vector3(0, 180, 0));
PlayerController.disablePlayerController = true;
mouselook.enabled = false;
playerCamera.enabled = false;
lockedRoomCamera.SetActive(true);
torotate = true;
}
private void Update()
{
if(torotate == true)
{
navi.transform.Rotate(xAngle, Random.Range(90, 270) * speed * Time.deltaTime, zAngle, Space.Self);
}
}
}
I want to rotate the object only on the y axis randomly between 90 degrees and 270 degrees. So it will looks like the object is looking to the sides left and right.
But now the object is just spinning nonstop on one direction to the left.
One way is to use a coroutine to generate random rotations every so often and then lerp to them over time:
IEnumerator DoLookAround()
{
float lookPeriod = 5f; // change look every 5 seconds
float maxRotationSpeed = 90f; // turn no faster than 90 degrees per second
Vector3 neutralForward = transform.forward;
while(true)
{
float timeToNextLook = lookPeriod;
while (timeToNextLook > 0) {
// Get random offset from forward
float targetYRotation = Random.Range(-90f, 90f);
// calculate target rotation
Quaternion targetRotation = Quaternion.LookRotation(neutralForward, transform.up)
* Quaternion.AngleAxis(targetYRotation, Vector3.up);
// rotate towards target limited by speed
Quaternion newRotation = Quaternion.RotateTowards(transform.rotation, targetRotation, maxRotationSpeed * Time.deltaTime);
timeToNextLook -= Time.deltaTime;
yield return null;
}
}
}
Then you can call it with:
StartCoroutine("DoLookAround");
and stop it with
StopCoroutine("DoLookAround");
I made a simple racing game. The camera follows the car (player) and its position and rotation is based on car's Y rotation. I want to smooth the camera rotation, but when it crosses 0 degree point, it rotates 360 degrees.
Here's the code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Camera : MonoBehaviour {
public Transform camera, player;
void Update() {
Vector3 cameraPosition = camera.transform.position;
float cameraRotation = camera.eulerAngles.y;
float playerRotation = player.eulerAngles.y;
Vector3 playerPosition = player.transform.position;
cameraPosition.x = (Mathf.Sin((playerRotation / 180) * Mathf.PI) * -6 + player.position.x);
cameraPosition.y = playerPosition.y + 2.5f;
cameraPosition.z = (Mathf.Cos((playerRotation / 180) * Mathf.PI) * -6 + player.position.z);
camera.transform.position = cameraPosition;
cameraRotation = cameraRotation + (playerRotation-cameraRotation)/2;
camera.localRotation = Quaternion.Euler(20f, cameraRotation, 0f);
}
}
I figured out that this rotation is caused by smoothing script:
cameraRotation = cameraRotation + (playerRotation-cameraRotation)/2;
How to prevent this unwanted rotation?
This is how I would do it:
public int smoothSpeed = 1f; // Change accordingly to increase/decrease smooth speed.
Then in update:
Vector3 directionToCar = player.position - camera.position;
Quaternion desiredRotation = Quaternion.LookRotation(directionToCar);
camera.rotation = Quaternion.Slerp(camera.rotation, desiredRotation, Time.deltaTime * smoothSpeed);
On a side node, if this camera script is attached to the camera, you don't have to make a field referencing the camera's transform. You can simply do transform.position instead of camera.transform.position.
A simpler solution to using the Slerp is to use Mathf.SmoothDamp, which has a variant called Mathf.SmoothDampAngle which handles the looping correctly. I've also had success doing the wraparound by hand but its not a very rewarding experience
using UnityEngine;
public class RotFollow : MonoBehaviour
{
[SerializeField] Transform carTransform;
[SerializeField] float smoothTime = .4f;
float currentYAngle;
float targetYAngle;
float angleVel;
void Update()
{
targetYAngle = carTransform.rotation.eulerAngles.y;
currentYAngle = Mathf.SmoothDampAngle(currentYAngle, targetYAngle, ref angleVel, smoothTime);
transform.rotation = Quaternion.Euler(
transform.rotation.eulerAngles.x,
currentYAngle,
transform.rotation.eulerAngles.z);
}
}
For the entire day, I been trying to find a good solution to completely stop the player from going offscreen without hard coding.
I have this script called player controller and all it does so far is allow the player to move along the x-axis. It also has an additional function that clamps the player's movement in the x-axis. Here it is.
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public const float MAX_SPEED = 5.0f;
// Update is called once per frame
void Update()
{
transform.Translate(Input.GetAxis("Horizontal") * MAX_SPEED * Time.deltaTime, 0.0f, 0.0f);
clampPlayerMovement();
}
void clampPlayerMovement()
{
Vector3 pos = Camera.main.WorldToViewportPoint(transform.position);
pos.x = Mathf.Clamp01(pos.x);
transform.position = Camera.main.ViewportToWorldPoint(pos);
}
}
The problem with this script is that it doesn't completely stops the player from going offscreen(half of the player's body still goes offscreen).
So this is what I tried next.
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public const float MAX_SPEED = 5.0f;
private float xLeft;
private float xRight;
void Start()
{
float pivotX = GetComponent<SpriteRenderer>().sprite.pivot.x;
float pixelsPerunit = GetComponent<SpriteRenderer>().sprite.pixelsPerUnit;
float textureWidth = GetComponent<SpriteRenderer>().sprite.texture.width;
//Units on the left from the sprite's pivot.
xLeft = pivotX / pixelsPerunit;
//Units on the right from the sprite's pivot.
xRight = (textureWidth - pivotX) / pixelsPerunit;
}
// Update is called once per frame
void Update()
{
transform.Translate(Input.GetAxis("Horizontal") * MAX_SPEED * Time.deltaTime, 0.0f, 0.0f);
clampPlayersMovement();
}
void clampPlayersMovement()
{
Vector3 pos = transform.position;
Vector3 posMin = transform.position;
Vector3 posMax = transform.position;
posMin.x = posMin.x - xLeft;
posMax.x = posMax.x + xRight;
pos = Camera.main.WorldToViewportPoint(pos);
posMin = Camera.main.WorldToViewportPoint(posMin);
posMax = Camera.main.WorldToViewportPoint(posMax);
pos.x = Mathf.Clamp(pos.x, posMin.x, posMax.x);
transform.position = Camera.main.ViewportToWorldPoint(pos);
}
}
Unfortunately, this code is no good. In fact, it is even worse because it does not stop the player from going offscreen at all.
So at this point I'm stuck between a rock and a hard place. Any suggestions would be vastly appreciated.
After long searching I finally found an answer.
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public const float MAX_SPEED = 5.0f;
private float halfPlayerSizeX;
void Start()
{
halfPlayerSizeX = GetComponent<SpriteRenderer>().bounds.size.x / 2;
}
// Update is called once per frame
void Update()
{
transform.Translate(Input.GetAxis("Horizontal") * MAX_SPEED * Time.deltaTime, 0.0f, 0.0f);
clampPlayerMovement();
}
void clampPlayerMovement()
{
Vector3 position = transform.position;
float distance = transform.position.z - Camera.main.transform.position.z;
float leftBorder = Camera.main.ViewportToWorldPoint(new Vector3(0, 0, distance)).x + halfPlayerSizeX;
float rightBorder = Camera.main.ViewportToWorldPoint(new Vector3(1, 0, distance)).x - halfPlayerSizeX;
position.x = Mathf.Clamp(position.x, leftBorder, rightBorder);
transform.position = position;
}
}
The only thing that I don't get is why do I need to subtract the z position from both the gameobject and the camera? why not the x position?