Unity 5.1 player control of secondary object relative to player object - c#

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

Related

Tilt first person camera's z axis while turning

I am working on controls for a bipedal tank, that has a boost/dash feature.
When the boost/dash mode is activated by holding a button the tank changes from a traditional WASD strafing movement to something like a jet fighter but on ground.
Now when the player tries to turn while boosting the camera needs to tilt with the turning direction to make it feel smoother.
Here is a video clip showing my problem
This is the script added to the main camera
public class PlayerCameraTilt : MonoBehaviour
{
private Camera viewCam;
private CharacterController characterController;
public float tiltAmount = 10f;
private void Start()
{
viewCam = GetComponent<Camera>();
characterController = GetComponentInParent<CharacterController>();
}
private void Update()
{
if (characterController.GetComponent<PlayerController>().movementState == MovementModes.boost)
{
float inputValue = Input.GetAxis("MoveHorizontal") * tiltAmount;
Vector3 euler = transform.localEulerAngles;
euler.z = Mathf.Lerp(euler.z, -inputValue, 5f * Time.deltaTime);
transform.localEulerAngles = euler;
}
else
{
Vector3 euler = transform.localEulerAngles;
euler.z = Mathf.Lerp(euler.z, 0f, 5f * Time.deltaTime);
transform.localEulerAngles = euler;
}
}
}
It kind of works, BUT only in one rotation direction, namely the positive one. If i turn the other way the angle becomes negative and does a whole 360 degree rotation until it ends up at 0 degrees.
I tried around with the built in Quaternion methods like Quaternion.Rotate() or Quaternion.AngleAxis()
but they didn't work because Quaternion.Rotate() doesn't tilt the Camera but completely rotates it. Quaternion.AngleAxis() works with the whole tilt angle limit but also does something to the other axis which i don't want to modify.
Is there a way to prevent the camera from doing a complete 360° rotation when the axis input becomes negative and just tilt in a slight angle?
Wraparound issues aside, the z component is "applied" first among the euler components, so I wouldn't modify it directly like that in this application.
Instead, I would convert the input to an angle to lean, rotate the direction of the local up before localRotation is applied (Quaternion.Inverse(transform.localRotation) * transform.up) by that angle, then use Quaternion.LookRotation to find a rotation with the current forward and that up, then use Quaternion.Slerp to slerp toward that rotation:
private void Update()
{
if (characterController.GetComponent<PlayerController>().movementState == MovementModes.boost)
{
float inputValue = Input.GetAxis("MoveHorizontal") * tiltAmount;
Vector3 upOfParent = Quaternion.Inverse(transform.localRotation) * transform.up;
Vector3 upDir = Quaternion.AngleAxis(-inputValue, transform.forward) * upOfParent;
Quaternion goalRot = Quaternion.LookRotation(transform.forward, upDir);
transform.rotation = Quaternion.Slerp(transform.rotation, goalRot, 5f * Time.deltaTime);
}
else
{
float inputValue = Input.GetAxis("MoveHorizontal") * tiltAmount;
Vector3 upOfParent = Quaternion.Inverse(transform.localRotation) * transform.up;
Quaternion goalRot = Quaternion.LookRotation(transform.forward, upOfParent);
transform.rotation = Quaternion.Slerp(transform.rotation, goalRot, 5f * Time.deltaTime);
}
}
Just keep in mind that using t = 5f * Time.deltaTime for lerp/slerp operations does not always guarantee that the output will ever equal the to parameter.

Camera follow the player only on his Z axis Unity 3D

I made the script bellow for the camera to follow the player, but I don't want it to follow him on his X axis. So it should follow the player smoothly only on his Z axis. Any suggestions?
Camera Script:
public Transform Ball;
public float SmoothSpeed = 0.125f;
public Vector3 offset;
bool MoveCamera = true;
void Start () {
}
public void FixedUpdate () {
if(MoveCamera == true){
Vector3 desiredPosition = Ball.position + offset;
Vector3 SmoothedPosition = Vector3.Lerp(transform.position, desiredPosition, SmoothSpeed);
transform.position = SmoothedPosition;
}
if (transform.position.z >= 2f)
{
MoveCamera = false;
}
}
What about only changing the z component?
public void FixedUpdate () {
if(MoveCamera == true){
Vector3 desiredPosition = new Vector3(0, Ball.position.y + offset.y, Ball.position.z + offset.z);
Vector3 SmoothedPosition = Vector3.Lerp(transform.position, desiredPosition, SmoothSpeed);
transform.position = SmoothedPosition;
}
if (transform.position.z >= 2f)
{
MoveCamera = false;
}
}
public gameObject toFollow;
Vector3 camDistance;
public void Start(){
camDistance = toFollow.transform.position-Camera.main.position;
}
public void Update(){
Vector3 following = new Vector3(Camera.main.x,Camera.main.z,toFollow.transform.position.z-Camera.main.position.z);
Camera.main.transform.position = following;
}
i just threw this together so it may require a few tweaks, but it should get you where you need to go. alternatively.... you can add a rigidbody to the camera, and in the inspector tick the boxes for constraints on the x and y axis. this will allow it to move only on the z (if you go this route remember to turn of the gravity for the cameras rigidbody as well)
Anybody in 2019 need a camera to be offset a specific position from their 3d character? Here is an easy solution! Attach this script to the camera you want to follow the player. Select the player as your target in the Unity editor.
This script sets the new position of the camera to
(EDIT: Where did the LaTeX go?)
$v = <targetPosition.x + xOffset, targetPosition.y + yOffset, targetPosition.z + zOffset>$
Benefits of this script:
Camera orientation does not change, even if character turns (because camera is not parented to the player).
Works regardless of player position
Limitations:
- Because camera orientation is fixed, you need to make sure that your character is not obscured by scenery.
- This style of camera control is best-suited for a 3rd person orthographic projection.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraController : MonoBehaviour
{
public Transform target;
Vector3 targetPos;
void Update()
{
targetPos = target.position;
transform.position = new Vector3(targetPos.x + 10f, targetPos.y + 8.571067812f, targetPos.z - 10f);
// transform.LookAt(target.transform);
}
}
My use-case required an orthographic projection. Orthographic angles are usually at a 30 degree angle of declination, so my offset is back 10 units, over 10 units, and up half that hypotenuse (that is, up 10*sqrt2 * 1/2). I also bumped the y-position up by the character's height (1.5 units), which I thought would give a better center for the camera target. This gives a y-offset of about 8.571067812.

2D Ricochet Physics not working in Unity

I am trying to make a game where a ball will be ricocheting off several different colliders on screen. Based on what I researched I got some code that seemed to make sense at first but does not seem to work at all. I am testing it out just by having the ball fall down onto a 2D floor with a box collider attached to it. However once it collides with the floor it just stops there and doesn't bounce back up. I tried disabling "Queries Start in Colliders" in the project settings and offsetting the origin position of the RaycastHit2D so that it would not detect the collider its attached to but that still didn't work. I don't think the RaycastHit2D is working at all because it doesn't even display anything in the console. Here is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyBehavior : MonoBehaviour {
Rigidbody2D rigidbody;
public Vector2 startPos;
public float speed;
public float hitOffset;
Vector3 vector = new Vector3(0, 5,0); //used to offset origin point of RaycastHit2D hit
private void OnEnable()
{
rigidbody = GetComponent<Rigidbody2D>();
this.transform.position = startPos;
}
private void Update()
{
transform.Translate(Vector2.down * speed * Time.deltaTime);
RaycastHit2D hit = Physics2D.Raycast(transform.position + vector, - transform.up, speed * Time.deltaTime + hitOffset);
Debug.DrawRay(transform.position, -transform.up, Color.red);
Ray2D ray = new Ray2D(transform.position, -transform.up);
if (hit)
{
Debug.Log(speed * Time.deltaTime + hitOffset);
Debug.Log(hit.collider.name);
Vector2 reflectDir = Vector2.Reflect(ray.direction,hit.normal);
float rotation = Mathf.Atan2(reflectDir.y, reflectDir.x) * Mathf.Rad2Deg;
transform.eulerAngles = new Vector3(0, 0, rotation);
}
}
}
Is my math bad? Any help is very appreciated, thanks.

Controlling both angles of a Ray

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

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

Categories

Resources