Controlling both angles of a Ray - c#

I'm new to Unity and have a problem I can't figure out. I want objects to spawn randomly at a distance of 20 from a FPS player. You could say the objects need to spawn on the surface of a half sphere with the player as the center. But: not all of that sphere can be used. The "highest" part is too high for objects to spawn, so basically it's a sphere with the top cut off.
What I tried:
thePosition = Random.onUnitSphere * 20 + object2.transform.position;
Obviously, this takes into account the whole sphere (should be only half a sphere) and doesn't take into account the "cut off" part.
So I thought: I basically want to make a ray that can pivot on the ground (so the max angle is 360°), and can go up and down, with a max angle of 90°. Think of it like a canon that can turn (pivot) and go up/down with an angle. Here's an image of what I mean:
So I tried:
Vector3 raydirection = new Vector3 (1f, 1f, 0);
raydirection = Quaternion.Euler (45, 0, 0) * raydirection;
Ray ray = new Ray (player.transform.position, raydirection);
thePosition = ray.GetPoint (20);
But that doesn't allow me to control the pivot angle (angle 1) and the "up-down" angle (angle 2) separately.
So my question is: how can I make it so that I can control both angles of this ray? Because if I can do that, I can just take a random number between 0 and 360 for the pivoting part, and between 0 and 90 for the up/down part.
Any help is much appreciated!

Coincidentally, I needed something very similar to this. The following Behavior will spawn a certain prefab (objectToSpawn) exactly spawnCount times within the set parameters.
The helper class (bottom code) generates a Vector from Yaw, Pitch and a Vector (basically the distance in your case).
What it does:
Pick a random direction (yaw and pitch) within set parameters
Pick a random distance (sounds like you can omit this step)
Calculate the vector
Spawn object
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RandomSpawner : MonoBehaviour {
public GameObject objectToSpawn;
public int spawnCount;
public float minDistance = 2;
public float maxDistance = 10;
public float minPitchDegrees = 0;
public float maxPitchDegrees = 45;
public float minYawDegrees = -180;
public float maxYawDegrees = 180;
void Start ()
{
for (int i = 0; i < spawnCount; i++)
{
float distance = minDistance + Random.value * (maxDistance - minDistance);
float yaw = minYawDegrees + Random.value * (maxYawDegrees - minYawDegrees);
float pitch = minPitchDegrees + Random.value * (maxPitchDegrees - minPitchDegrees);
Vector3 position = RotationHelper.ConvertYawPitch (Vector3.forward * distance, yaw, pitch);
Instantiate (objectToSpawn, position, Quaternion.identity);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public static class RotationHelper {
public static Vector3 ConvertYawPitch(Vector3 vector, float yaw, float pitch)
{
Quaternion yawRotation = Quaternion.AngleAxis (yaw, Vector3.up);
Vector3 yawedZAxis = yawRotation * Vector3.left;
Quaternion pitchRotation = Quaternion.AngleAxis (pitch, yawedZAxis);
Vector3 yawedVector = yawRotation * vector;
Vector3 position = pitchRotation * yawedVector;
return position;
}
}
In your specific case, the parameters should be:
minDistance = 20
maxDistance = 20
minPitchDegrees = 0
maxPitchDegrees = 0-90, whatever the angle is after you "cut off the top"
minYawDegrees = -180
maxYawDegrees = 180

I want objects to spawn randomly at a distance of 20 from a FPS
player.
What I understood from this is that you want to spawn objects on the ground, distant 20 units away from the player, in random directions.
You could say the objects need to spawn on the surface of a half
sphere with the player as the center.
Now, this is just another way to make things complex. No need to use the sphere to solve this.
If you want to spawn objects on the surface, easiest solution will be to get a random angle in relation with Vector3.up and walk for 20 units to find the desired point.
Script:
public class Spawner : MonoBehaviour
{
public Transform player;
public Transform prefab;
[Range(10,50)]
public float distance = 20f;
IEnumerator Start()
{
while (true)
{
yield return new WaitForSeconds(0.05f);
Spawn();
}
}
[ContextMenu("Spawn")]
public void Spawn()
{
Vector3 spawnPoint = FindPoint(player.position, distance, Random.Range(0, 360));
Instantiate(prefab, spawnPoint, Quaternion.identity, transform);
}
[ContextMenu("Clear")]
public void Clear()
{
foreach (var item in transform.GetComponentsInChildren<Transform>())
{
if (item != transform)
DestroyImmediate(item.gameObject);
}
}
Vector3 FindPoint(Vector3 center, float radius, int angle)
{
return center + Quaternion.AngleAxis(angle, Vector3.up) * (Vector3.right * radius);
}
}
Result:
Calculates random point based on player's position:
Hope this helps :)

Related

Make object face and move towards player

Im making a simple 2d ship game for my school project. Right now the ai will face the player and follow them at a certain distance, then when close enough, rotate so that it can fire at the player from each side of the ship. But with my current code the enemy will move sideways towards the player at the firing range, which obviously shouldn't happen.
So how can i make the enemy only move forwards and remain perpendicular to the player.
Here is my current code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NewBehaviourScript : MonoBehaviour
{
public float speed, stopD, retreatD, Chase_range, distance,firing_range;
// declare player and enemy as transform to retrieve their positions
public Transform player;
void Start()
{
player = GameObject.FindGameObjectWithTag("Player").GetComponent<Transform>();
Chase_range = 14;
firing_range = 10;
stopD = 6;
retreatD = 4;
}
// Update is called once per frame
void Update()
{
// Distance between the enemy and player positions
distance = (Vector2.Distance(transform.position, player.position));
transform.position = Vector2.MoveTowards(transform.position, player.position, speed * Time.deltaTime);
if (distance > stopD)
{
// move towards the player if the distance is greater than stop distance
speed = 10;
}
else if (distance < stopD & distance > retreatD)
{
speed = 0;
}
else if (distance < retreatD)
{
// move away from the player if the distance is less than stop distance (avoid collision)
speed = -10;
}
if (distance > Chase_range)
{
// have to use vector3 since vector2 does not work with the z axis
// calculate the difference in the position vectors
Vector3 vectorToTarget = player.position - transform.position;
// Calculate the angle between the objects and convert the result to degrees
float angle = Mathf.Atan2(vectorToTarget.y, vectorToTarget.x) * Mathf.Rad2Deg;
// convert the angle into a quaternion so that it can be used by slerp
Quaternion playerq = Quaternion.AngleAxis(angle, Vector3.forward);
transform.rotation = Quaternion.Slerp(transform.rotation, playerq, Time.deltaTime * speed);
}
if (distance < firing_range)
{
float Rfactor;
Vector3 vectorToTarget = player.position - transform.position;
float angle = Mathf.Atan2(vectorToTarget.y, vectorToTarget.x) * Mathf.Rad2Deg;
if (angle < 0)
{
Rfactor = 90;
}
else if (angle > 0)
{
Rfactor = -90;
}
Quaternion playerq = Quaternion.AngleAxis(angle + Rfactor, Vector3.forward);
transform.rotation = Quaternion.Slerp(transform.rotation, playerq, Time.deltaTime * speed);
}
}
And don't mind the shitty comments, its for school.
Any other comments about the code would also be greatly appreciated
With some quick googling I found this answer:
https://answers.unity.com/questions/1125044/how-do-i-move-an-object-relative-to-another-object.html
Using this you can move the enemy forward relative to the player. If you rotate the player now you will have the desired result: the enemy will move with the player but stay focused on it

2 rotations with transform.Rotate() and transform.localEulerAngles() doesn't work

I'm trying to make a simple car simulator, and when I use the transform.Rotate() to rotate the wheels foreword (x axis), and the transform.localEulerAngles() to rotate in the turning direction (y axis), only the localEulerAngles() works. when I use only one method, the wheel foreword rotation (x axis) works, but I can't manage to make them both work. Do you have any ideas how to make them work together?
float ro = 20f; // 20 degrees turn
//to preserve the x and z values of rotation
Vector3 rot = wheel.gameObject.transform.rotation.eulerAngles;
//rotates the wheels angle
wheel.gameObject.transform.localEulerAngles = new Vector3(rot.x, ro, rot.z);
float vel = wheel.rpm * 2 * Mathf.PI / 60 * Time.deltaTime * Mathf.Rad2Deg;
//rotates the wheels forward
wheel.gameObject.transform.Rotate(vel, 0, 0);
You should not be using localEulerAngles when you are having two different rotations, because when you have 2 movements if you use localEulerAngles you change the local angles with the movements you are providing themselves so you loose the reference of where the angular position of your axis is at.
You can indicate to the rotate method to rotate respect to the world, transform.rotate rotates the transform of your object relative to the world so you can handle simultaneus rotations together because world axis do not change.
Find below the code snippet that I tried, and the picture of my axis orientation which is not exactly the same as yours.
public class WheelTurning : MonoBehaviour
{
float rpm=10f;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.A))
{
turn(direction.left);
}
if (Input.GetKeyDown(KeyCode.D))
{
turn(direction.right);
}
float vel = this.rpm * 2 * Mathf.PI / 60 * Time.deltaTime * Mathf.Rad2Deg;
rotate(vel);
}
enum direction
{
left,
right
}
void turn(direction dir)
{
Vector3 rot = this.gameObject.transform.rotation.eulerAngles;
//rotates the wheels angle
float rotation = dir == direction.left ? 20f : -20f;
gameObject.transform.Rotate(new Vector3(0, rotation, 0), Space.World);
}
void rotate (float speed)
{
gameObject.transform.Rotate(0, speed, 0);
}
}
However it is much more operative to use quaternions, try to check that out.
Hope that helps!
I do not get your point. If you want to turn a wheel it will alwiys be respect to its previous rotational postion. With that reference you can choose the value you like in the rotation vector Vector3(0, rotation, 0). If you want an absolute reference you can make a parent object and rotate this. Objects have the same world axis and when rotated or transformed, the children "suffer" the same transformation. You have to meke sure that the posistion in the childre is 0,0,0 as this is the distance respect to the parent position that will be the rotation pivot.
void setRotation()
{
Vector3 rot = this.gameObject.transform.rotation.eulerAngles;
GameObject wheelParent = gameObject.transform.parent.gameObject;
Vector3 rotationVector = new Vector3(0, 30, 0);//absolute turn set
Quaternion rotation = Quaternion.Euler(rotationVector);
wheelParent.transform.localRotation = rotation;
}
Hope that helps.

How can I rotate an object from on one axis randomly between two directions?

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");

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).

Unity 5.1 player control of secondary object relative to player object

I've done a lot of searching but can't find a sollution to this problem.
What I want is to control a game object that rotates around the player object at a fixed distance based on right analogue stick input, indepentant of player movement. I also want the object to face outwards as it moves around the player.
I have left analogue stick set to player movement and working and have right analogue stick set up and tested so I know the input is working.
I've tried transform.rotate, RotateAround, .position and quaternions etc but can't figure it out or find anything that might help. I am fairly new to this so there's probably a simple sollution, I just can't see it!
Appreciate any help you might be able to give :) Thanks a mil
EDIT 2: The second attempt
so I've got this so far now:
public class ShieldMovement : MonoBehaviour {
public Transform target; //player shield is attaced to
float distance = 0.8f; // distance from player so it doesn't clip
Vector3 direction = Vector3.up;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
float angle = Mathf.Atan2 (Input.GetAxisRaw("rightH"), Input.GetAxisRaw("rightV"))* Mathf.Rad2Deg;
if(Input.GetAxis("rightH") != 0f || Input.GetAxis("rightV") != 0f)
{
direction = new Vector3(Input.GetAxis("rightH"),Input.GetAxis("rightV"), 0.0f ) ;
}
Ray ray = new Ray(target.position, direction);
transform.position = ray.GetPoint(distance);
if(Input.GetAxis("rightH") != 0f || Input.GetAxis("rightV") != 0f)
{
transform.rotation = Quaternion.AngleAxis(angle,Vector3.forward*-1);
}
}
}
I'd like it to be a smooth rotation of the shield around the player going to a new location instead of teleporting there. I've tried some lerps and some slerps, so far all I've been able to so is interpolate from one point on the circumference in a direct line to the new point. Can't think of a way to get it to rotate around the player as it does if you just rotate the stick. Hope that makes sense!
Any ideas you wonderful folk you?
Heres two ways of accomplishing what you want
Please note: this has not been tested
With a custom normalized procession (0f is exactly Vector3.forward, 1f brings you back to Vector3.forward)
using UnityEngine;
using System.Collections;
public class Follower : MonoBehaviour {
float distance = 10f;
Vector3 direction = Vector3.forward;
float procession = 0f; // exactly at world forward 0
Transform target;
void Update() {
float circumference = 2 * Mathf.PI * distance;
angle = (procession % 1f) * circumference;
direction *= Quaternion.AngleAxis(Mathf.Rad2Deg * angle, Vector3.up);
Ray ray = new Ray(target.position, direction);
transform.position = ray.GetPoint(distance);
transform.LookAt(target);
}
}
With Input.GetAxis
using UnityEngine;
using System.Collections;
public class Follower : MonoBehaviour {
float distance = 10f;
Vector3 direction = Vector3.forward;
float speed = .01f;
Transform target;
void Update() {
direction *= Quaternion.AngleAxis(Input.GetAxis("Horizontal") * speed * Time.deltaTime, Vector3.up);
Ray ray = new Ray(target.position, direction);
transform.position = ray.GetPoint(distance);
transform.LookAt(target);
}
}

Categories

Resources