I'm working on a little 2d game where you control a planet to dodge incoming asteroids. I'm implementing gravity in the following manner:
public class Gravity : MonoBehaviour
{
Rigidbody2D rb;
Vector2 lookDirection;
float lookAngle;
[Header ("Gravity")]
// Distance where gravity works
[Range(0.0f, 1000.0f)]
public float maxGravDist = 150.0f;
// Gravity force
[Range(0.0f, 1000.0f)]
public float maxGravity = 150.0f;
// Your planet
public GameObject planet;
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
void Update()
{
// Distance to the planet
float dist = Vector3.Distance(planet.transform.position, transform.position);
// Gravity
Vector3 v = planet.transform.position - transform.position;
rb.AddForce(v.normalized * (1.0f - dist / maxGravDist) * maxGravity);
// Rotating to the planet
lookDirection = planet.transform.position - transform.position;
lookAngle = Mathf.Atan2(lookDirection.y, lookDirection.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(0f, 0f, lookAngle);
}
}
The problem is that the asteroids are attracted to the initial spawn point of the planet (0,0), it doesn't update in real time with the movement of the planet. So if I move the planet to the corner of the screen, the asteroids are still attracted to the centre of it.
Is there a way to solve this?
Thank you very much and excuse any flagrant errors!
There are 2 ways to get to an object with speed:
get the object to the player and in each update just use the planet.transform.position
using look at first to rotate to the plant and then using vector3.forword as the direction of the movement.
The first solution doesn't work for you so you might want to try the second one.
any way, if your lookAt part doesn't work too you can use
Vector3 dir = target.position - transform.position;
float angle = Mathf.Atan2(dir.y,dir.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.AngleAxis(angle, Vector3.forward);
or
Vector3 targetPos = target.transform.position;
Vector3 targetPosFlattened = new Vector3(targetPos.x, targetPos.y, 0);
transform.LookAt(targetPosFlattened);
Related
I'm making top down shooting game. I wrote the code where enemies spawn randomly on map and they're trying to catch you. I made them do that and also I wrote a code to make them look at you. Basically rotate towards you only on Z axis. But problem is that when they are spawned on players' right, enemy is moving away from player. but if I rotate and start to move they are trying to fix themselves. Here's my script:
void FixedUpdate () {
Vector3 difference = player.position - transform.position;
float rotationZ = Mathf.Atan2(difference.y, difference.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(0.0f, 0.0f, rotationZ);
Vector2 toTarget = player.transform.position - transform.position;
float speed = 1.5f;
transform.Translate(toTarget * speed * Time.deltaTime);
}
Consider that Translate is a relative modifier. For this reason, when you specify the direction in the Translate itself, the movement becomes confused. Use Vector3.MoveTowards to solve the problem. If your game is 2D, you can also use Vector2 like below:
Vector2.MoveTowards(currentPosition, targetPosition, step);
Preferably you can fix this code like this and set the return value of MoveTowards equal to transform.Position.
void FixedUpdate () {
Vector3 difference = player.position - transform.position;
float rotationZ = Mathf.Atan2(difference.y, difference.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(0.0f, 0.0f, rotationZ);
float speed = 1.5f;
// replace Translate to MoveTowards
transform.position = Vector3.MoveTowards(transform.position, player.position, Time.deltaTime * speed);
}
I want to move my player character(human) on a curved surface. But at the same time character shall stay perpendicular to the surface normals and it should face in the movement direction and can handle collisions(if there is a wall ahead, shall not be able to go through it).
I tried to make a parent stay over normals and change the child local rotation towards direction of motion of its parent. But it has several limitations as of now.
Here is the code what i was using:
[SerializeField] float raycastLength = 1f;
bool canPlayerMove = true;
public float speed = 2f;
public Vector3 offset; //object's position offset to ground / surface
public Quaternion childDirection;
private void Update()
{
float moveHorizontal = SimpleInput.GetAxis("Horizontal");
float moveVertical = SimpleInput.GetAxis("Vertical");
Ray ray = new Ray(transform.position, -transform.up);
RaycastHit hitInfo;
if (Physics.Raycast(ray, out hitInfo, raycastLength))
{
transform.rotation = Quaternion.LookRotation(Vector3.up, hitInfo.normal);
transform.position = hitInfo.point + offset;
Debug.DrawLine(ray.origin, hitInfo.point, Color.red);
}
if (canPlayerMove)
{
Vector3 movement = new Vector3(moveHorizontal, 0, moveVertical);
if (movement != Vector3.zero)
{
childDirection = Quaternion.Slerp(transform.GetChild(0).localRotation, Quaternion.LookRotation(movement), 0.15F);
transform.GetChild(0).localRotation = childDirection;
}
transform.Translate(movement * speed * Time.deltaTime, Space.Self);
}
}
first to not make your player go thru walls you want to add a collider to your walls and not set it as trigger, you will also need a rigidbody on your player and this will help in the next steps.
Secondly you will need to acces the rigidBody in code using this: (if you Check Use Gravity it will also stay on your terrain that you made)
private Rigidbody rb;
private float speed = 7.5f;
private void Start()
{
//this gets the rigidbody on the gameObject the script is currently on.
rb = this.GetComponent<Rigidbody>();
}
private void Update()
{
float hor = Input.GetAxis("Horizontal");
float vert = Input.GetAxis("Vertical");
//this will move your player frame independent.
rb.MovePosition(this.transform.position + new Vector3(hor, 0, vert) * speed *
Time.deltaTime);
}
Also make sure that you have a rigidBody on your player, else it will throw an error.
I have been working for a long time trying to get a clamp implemented on my 2D object in Unity2D. I have struggled with this issue for weeks now. I have a rocketship that moves upwards on it's own, and I'm trying to make it where you can't go further than 40 degrees rotating. Some details to note is that the ship rotates(x-axis of course) and moves toward the mouse(x-axis, doesn't go further up more then the current velocity) as a way to avoid obstacles which are coming soon. Here's my code for the ship:
public class MiniGameRocketShipController : MonoBehaviour
{
public Vector2 velocity = new Vector2(0f, 1500f);
private Vector2 directionX;
private Vector2 directionY;
public Rigidbody2D rb;
public new Camera camera;
public float moveSpeed = 100f;
public float rotation;
private void Start()
{
rotation = rb.rotation;
}
private void Update()
{
rb.AddForce(velocity * Time.deltaTime);
rb.velocity = new Vector2(directionX.x * moveSpeed, directionY.y * moveSpeed);
FaceMouse();
}
void FaceMouse()
{
if (rotation > 180)
{
rotation = 360;
}
rotation = Mathf.Clamp(rotation, -40f, 40f);
rb.rotation = rotation;
Vector3 mousePosition = Input.mousePosition;
mousePosition = camera.ScreenToWorldPoint(mousePosition);
directionX = (mousePosition - transform.position).normalized;
directionY = transform.position.normalized;
transform.up = directionX;
}
}
You can use Vector2.SignedAngle and Mathf.Clamp to do this. Explanation in comments:
// world direction to clamp to as the origin
Vector2 clampOriginDirection = Vector2.up;
// what local direction should we compare to the clamp origin
Vector2 clampLocalDirection = Vector2.up;
// how far can the local direction stray from the origin
float maxAbsoluteDeltaDegrees = 40f;
void ClampDirection2D()
{
// world direction of the clamped local
// could be `... = transform.up;` if you are ok hardcoding comparing local up
Vector2 currentWorldDirection = transform.TransformDirection(clampLocalDirection);
// how far is it from the origin?
float curSignedAngle = Vector2.SignedAngle(clampOriginDirection, currentWorldDirection);
// clamp that angular distance
float targetSignedAngle = Mathf.Clamp(curSignedAngle, -maxAbsoluteDeltaDegrees,
maxAbsoluteDeltaDegrees);
// How far is that from the current signed angle?
float targetDelta = targetSignedAngle - curSignedAngle;
// apply that delta to the rigidbody's rotation:
rb.rotation += targetDelta;
}
I am making a top down game on Unity, so I am using the x and z axis as my plane. I have my character rotated x 90, y 0, z 0 so that it is flat on the plane. As soon as I hit play the character is rotated vertical?! I think it has something to do with my script to face the mouse position.
What it should look like:
When I hit play:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public static float moveSpeed = 10f;
private Rigidbody rb;
private Vector3 moveInput;
private Vector3 moveVelocity;
// Update is called once per frame
void Start()
{
rb = GetComponent<Rigidbody>();
mainCamera = FindObjectOfType<Camera>();
}
void Update()
{
// Setting up movement along x and z axis. (Top Down Shooter)
moveInput = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical"));
moveVelocity = moveInput * moveSpeed;
//Make character look at mouse.
var dir = Input.mousePosition - Camera.main.WorldToScreenPoint(transform.position);
var angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.AngleAxis(angle, Vector3.up);
}
void FixedUpdate()
{
// Allows character to move.
rb.velocity = moveVelocity;
}
}
Figured it out: I am answering my own question to help others.
Vector3 difference = Input.mousePosition - Camera.main.WorldToScreenPoint(transform.position);
float rotZ = Mathf.Atan2(difference.y, difference.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(90f, 0f, rotZ -90);
This does EXACTLY what I wanted!
I'm watching unity tutorials and in the control code of the ship I would like to make it rotate on its axis, that is, it can rotate 360 degrees continuously while I press the right key for example.
playerController:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class Boundary
{
public float xMin, xMax, zMin, zMax;
}
public class PlayerController : MonoBehaviour {
[Header("Movement")]
public float speed;
public float tilt;
public Boundary boundary;
private Rigidbody rig;
[Header("Shooting")]
public GameObject shot;
public Transform shotSpawn;
public float fireRate;
private float nextFire;
void Awake () {
rig = GetComponent<Rigidbody>();
}
void Update () {
if (Input.GetButton ("Fire1") && Time.time > nextFire) {
nextFire = Time.time + fireRate;
Instantiate (shot, shotSpawn.position, Quaternion.identity);
}
}
void FixedUpdate () {
float moveHorizontal = Input.GetAxis ("Horizontal");
float moveVertical = Input.GetAxis ("Vertical");
Vector3 movement = new Vector3(moveHorizontal, 0f, moveVertical);
rig.velocity = movement * speed;
rig.position = new Vector3 (Mathf.Clamp (rig.position.x, boundary.xMin, boundary.xMax), 0f, Mathf.Clamp (rig.position.z, boundary.zMin, boundary.zMax));
rig.rotation = Quaternion.Euler (0f, 0f, rig.velocity.x * -tilt);
}
}
How can I edit it to do what I want?
example:
You can use Transform.Rotate()
Your code would look like this, with the example you provided:
void FixedUpdate () {
float moveHorizontal = Input.GetAxis ("Horizontal");
float moveVertical = Input.GetAxis ("Vertical");
Vector3 movement = new Vector3(moveHorizontal, 0f, moveVertical);
rig.velocity = movement * speed;
rig.position = new Vector3 (Mathf.Clamp (rig.position.x, boundary.xMin, boundary.xMax), 0f, Mathf.Clamp (rig.position.z, boundary.zMin, boundary.zMax));
if(moveHorizontal > 0){ //if your "right key" is pressed
// Rotate the object around its local X axis at 1 degree per second
transform.Rotate(Vector3.right * Time.deltaTime);
}
}
For faster Rotation you can simply multiply the Vector3.right with some value.
To Rotate around another Axis, use other Vector directions, like Vector3.up.
For example:
transform.Rotate(Vector3.Up * moveHorizontal * Time.deltaTime);
transform.Rotate(Vector3.Forward * moveHorizontal * Time.deltaTime);
When you multiply the Vector3.Right with your moveHorizontal it should also work when you press the "left key" and that should result in rotating in the other direction.
transform.Rotate(Vector3.right * moveHorizontal * Time.deltaTime);
Notes:
That only works if your PlayerController is attached to your ship gameobject. If it is not attached you have to use the transform of your ship of course.
World Space vs Local Space
When you click on your object in the scene view, you should see the transform and the 3 arrows (red, green, blue) pointing on different directions (the 3 axis). When you use the method with the parameters provided above, you are using these arrows as rotation axis.
You can also rotate arround the WorldSpace axis.
transform.Rotate(Vector3.up, Time.deltaTime, Space.World);
When to use transform.Rotate?
When you change position or rotation of a transform, using the transforms
methods, it will be applied at the end of the frame. These changes ignore physics.
-> Use transforms methods if you don't care about collisions
When to use rigidbody.MoveRotation?
When you change rigidbody.position or rigidbody.MoveRotation using rigidbodies methods, it will be applied at the end of the next physics step. These changes care about physics (collisions and stuff)
-> Use rigidbodies methods if you care about collisions
Thanks to Helium for that hint.
The third possibility:
Instead of directly calling transform.Rotate or rigidbody.MoveRotation, you can also rotate your Object, using an animation, which changes the transforms rotation.
Example of Transform.Rotate()
You can clearly see, that the collision checks are ignored on that object while it's rotating through the ground. (I packed that gif into a spoiler to reduce noise. You'll need to hover over it, if you want to see it)
This will be the speed and axis of your rotation.
public Vector3 eulerAngleVelocity = new Vector3(0f,0f,1000f);
Since you want to rotate a rigidbody so use MoveRotation
Quaternion deltaRotation = Quaternion.Euler(-moveHorizontal * eulerAngleVelocity * Time.deltaTime);
rig.MoveRotation(rig.rotation * deltaRotation);
Final code will look like this
// NEW CODE BEGIN------
public Vector3 eulerAngleVelocity = new Vector3(0f,0f,1000f);
// NEW CODE END------
void FixedUpdate () {
float moveHorizontal = Input.GetAxis ("Horizontal");
float moveVertical = Input.GetAxis ("Vertical");
// NEW CODE BEGIN------
Vector3 movement = new Vector3(0f, 0f, moveVertical); // Notice I have removed moveHorizontal, this will make sure your gameobject doesnt go left and right. We will use move horizontal for rotating the gameobject.
// NEW CODE END------
rig.velocity = movement * speed;
rig.position = new Vector3 (Mathf.Clamp (rig.position.x, boundary.xMin, boundary.xMax), 0f, Mathf.Clamp (rig.position.z, boundary.zMin, boundary.zMax));
// NEW CODE BEGIN------
Quaternion deltaRotation = Quaternion.Euler(-moveHorizontal * eulerAngleVelocity * Time.deltaTime);
rig.MoveRotation(rig.rotation * deltaRotation);
// NEW CODE END------
}
You can play with eulerAngleVelocity to get the desired speed. Hope this helps. ;)