How to make camera relative movement - c#

I'm learning unity and c#, and want to make my movement to be camera relative movement instead of world relative movement. How do I do that?
I'm learning unity and c#, my unity version is 2018.3.12f1. I would be happy for help.
just to let know, instead of moving the cam I'm rotating the player.
void Update()
{
float AxisY = Player.transform.eulerAngles.y;
/* Movement starts here */
Vector3 Movement = new Vector3 (Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) { //running code
Player.transform.position += Movement * running_speed * Time.deltaTime;
} else {
Player.transform.position += Movement * speed * Time.deltaTime;
}
/*Movement ends here */
/* Rotation controller starts here */
Quaternion target = Quaternion.Euler(Player.transform.eulerAngles.x, Player.transform.eulerAngles.y, Player.transform.eulerAngles.z);
/*if (Player.transform.eulerAngles.x != 0 || Player.transform.eulerAngles.z != 0 || Player.transform.eulerAngles.y != 0) {
Player.transform.rotation = Quaternion.Euler(0,0,0);
}*/
if (Input.GetKey(KeyCode.E))
{
Debug.Log("E got pressed");
//float AxisYPositive = Player.transform.eulerAngles.y;
AxisY = AxisY+1;
Player.transform.rotation = Quaternion.Euler(0, AxisY, 0);
} else if (Input.GetKey(KeyCode.Q))
{
Debug.Log("Q got pressed");
//float AxisYNegetive = Player.transform.eulerAngles.y;
AxisY=AxisY-1;
Player.transform.rotation = Quaternion.Euler(0, AxisY, 0);
}
}
}
The player's movement is world relative, how to make the movement camera relative?

If you want to make the movements relative to the gameObject, call the method Transform.Rotate() on the transform of the gameObject you want to rotate rather than modifying its Quaternion directly. Just make sure the final argument is set to Space.Self.
if (Input.GetKey(KeyCode.E))
{
Debug.Log("E got pressed");
//float AxisYPositive = Player.transform.eulerAngles.y;
AxisY = AxisY+1;
Player.transform.Rotate(Quaternion.Euler(0, AxisY, 0), Space.Self);
}

In general you don't want to directly mess with objects transform.rotation, at least not unless you at least somewhat understand quaternions (I don't!).
I can see a few issues with your code, but the common thread seems to be that you don't really understand how transforms work. Specifically, you might want to look into World/Local space.
The usual way to control a player goes roughly like this:
void DoMovement(Transform player)
{
//If you move first your controls might feel 'drifty', especially at low FPS.
Turn(player);
Move(player);
}
void Turn(Transform player)
{
float yaw = Input.GetAxis("Yaw") * time.deltaTime; //Aka turn left/right
player.Rotate(0, yaw, 0, Space.Self);
// Space.Self is the default value, but I put it here for clarity.
//That means the player will rotate relative to themselves,
//...instead of relative to the world-axis, like in your code.
}
You didn't ask about movement, but as-is your character will always move relative to the world. The below should make it move relative to the camera.
Transform _cameraTransform; //Assumes this is set druing Start()
void Move(Transform player)
{
var forwardMove = _cameraTransform.Forward; //Get whatever direction is 'forward' for camera
forwardMove.Y = 0; //Don't want movement up and down.
forwardMove = forwardMove.normalized; //Normalize sets the 'power' of the vector to 1.
//If you set Y to 0 and don't normalize you'll go slower when camera looks down
//...than when camera is flat along the plane
player.position += forwardMove * Input.GetAxis("Vertical") * time.deltaTime;
//Here you could do the same for strafe/side to side movement.
//Would be same as above, but using the transform.right and Horizontal axis
}
Now, I'm making some assumptions here since you haven't specified what kind of game it is and what kind of controls you want. I'm assuming you have a character running around on a mostly flat plane (no aircraft/spaceship controls), and that the camera is attached to the player. This might not not actually be the case.
In any case I advice you to check out the tutorials, especially the Roll-a-Ball tutorial which I have found is good for beginners to get a grasp on basic players controls that are not just world-relative. The other tutorials, too, are pretty good if you think they're interesting.
Aside from the official Unity tuts a ton of decent to amazing tutorials out there, including video tutorials, so for something like this you could just search for <game type> tutorial and pick whatever seems good to you. While getting started I advice you to avoid the shortest videos, as you will likely benefit greatly from explanation that only fits in longer videos. Of course, that doesn't mean you should pick the longest videos either.

In case someone needs to move an object and don't care about colliders, you can use transform.Translate and assign to his second parameter relativeTo your camera (or any transform) to automatically calculate the translation relative to the object assigned.

Related

Polishing a Character Controller collision detection to make it flush with surfaces

I've been looking through the Source SDK and read through how it handles player collision with the space around it. Inspired by that, I've been writing a character controller that should have similar behavior.
I have code to control a Transform transform by computing its velocity vecVelocity and trying to move it along that vector. I represent the player by a capsule of radius capsuleRadius and sphere centers capsuleStart, capsuleEnd, but I haven't given it a proper collider as of yet
When trying to deal with collisions, I came up with the following function
void TryPlayerMove()
{
Vector3 transDirection;
float transMagnitude;
Vector3 translation;
Vector3 currentMotion = Vector3.zero;
int i, j, maxHits = 4; //Bump up to 4 times before giving up
RaycastHit capsule;
bool collision;
Vector3[] collNormal = new Vector3[maxHits];
float collDistance;
//Find starting translation vector
translation = vecVelocity * Time.deltaTime;
//Store initial displacement
Vector3 oldTranslation = translation;
for (i = 0; i < maxHits; i++)
{
//Find translation vector magnitude and direction
transDirection = translation.normalized;
transMagnitude = translation.magnitude;
//If we wouldn't move anyway, feel free to break the loop
if (transMagnitude == 0)
{
break;
}
//Shoot a capsule to desired endpoint
collision = Physics.CapsuleCast(capsuleStart + currentMotion, capsuleEnd + currentMotion, capsuleRadius, transDirection, out capsule, transMagnitude);
if (collision)
{
//If we hit something, hug it and take off what is left of translation in that direction
collNormal[i] = capsule.normal.normalized;
collDistance = capsule.distance;
//currentMotion += transDirection * collDistance; //WHY DOESN'T THIS WORK???
translation = ClipVector(translation*(1f-collDistance/transMagnitude), collNormal[i]);
Debug.Log(transDirection.magnitude);
//If we're going towards something we've hit before, stop moving so we don't go into weird corner loops
for (j = 0; j < i; j++)
{
if (Vector3.Dot(translation, collNormal[j]) < 0)
{
translation = Vector3.zero;
break;
}
}
}
else
{
//Just move
currentMotion += translation;
break;
}
}
//Translate the player character and take note of its velocity for future computation
vecVelocity = currentMotion / Time.deltaTime;
transform.Translate(currentMotion, Space.World);
}
and the clipping function just makes sure our vector is REALLY not pointing at the collider
Vector3 ClipVector(Vector3 inputVector, Vector3 normalVector)
{
float projection;
Vector3 outputVector;
//Determine how much to take out
projection = Vector3.Dot(inputVector, normalVector);
//Subtract the perpendicular component
outputVector = inputVector - normalVector * projection;
//Iterate once more just to make sure
float adjust = Vector3.Dot(outputVector, normalVector);
if (adjust < 0f)
{
outputVector -= normalVector * adjust;
}
return outputVector;
}
This works fine as it is, and the controls respond quite as I would expect, but with a cosmetic fault: each time the player "collides" with something, it does so stopping at a different distance from the object, I've logged it and it fluctuates around 5-10 percent of the capsule's radius away from the player.
In the TryPlayerMove() function, there is a command that I would expect to make it hug the collider, it's commented and decorated with a note of desperation
//currentMotion += transDirection * collDistance; //WHY DOESN'T THIS WORK???
Whenever I uncomment this, the controller invariably goes through absolutely any collider it touches and completely messes up whatever motion it was supposed to have. I have no idea why this happens, though.
How could I implement a functionality for my player to hug the colliders it touches, seeing this doesn't seem to work at all?
I found a hacky fix for it out of the blue. I have a float deltaGroundI use to check for ground. I changed the command to
currentMotion += transDirection * (collDistance-deltaGround/20f);
but the 20 could have been any reasonably sized number. This makes it much more consistent but introduces some jitter in player motion when I try to ram myself against a wall. Still taking more suggestions.
I had tried Mathf.Epsilon but it didn't fix anything, by the way.

Rotate player with platform without parenting

I'm currently making a small platformer 3D game, but unfortunately I can't make the player to rotate properly when it is riding the platform, the thing here is that I don't want to make the player child of the platform, so far I've managed to make him move smoothly along with the platform, but the rotation is still going nowhere, here is the code I'm using for the rotation:
player.transform.rotation *= platform.rotation;
and here is the effect I got:
Rotation Error
not very nice :(
I guess the solution is something simple, some formula, but unfortunately I'm not very good with math :( So, thank you guys, I hope you can help me.
I'll show you a simple script example which makes a cube rotate by input while reacting to the rotation of the platform on which it stands:
using UnityEngine;
public class CubeRotation : MonoBehaviour {
public GameObject Platform;
Quaternion PreviousPlatformRotation;
public float rotationSpeed = 50;
private void Start() {
PreviousPlatformRotation = Platform.transform.rotation;
}
private void Update() {
//Rotate the cube by input
if (Input.GetKey(KeyCode.A)) {
transform.Rotate(Vector3.up, Time.deltaTime * rotationSpeed);
}
if (Input.GetKey(KeyCode.D)) {
transform.Rotate(Vector3.up, -Time.deltaTime * rotationSpeed);
}
//Adjust rotation due to platform rotating
if (Platform.transform.rotation != PreviousPlatformRotation) {
var platformRotatedBy = Platform.transform.rotation * Quaternion.Inverse(PreviousPlatformRotation);
transform.rotation *= platformRotatedBy;
PreviousPlatformRotation = Platform.transform.rotation;
}
}
}
The logic of the adjustment to the platform rotation is this:
Get at start the rotation quaternion of the platform (in your case, get it when the cube object climbs on the platform)
With A and D rotate the cube normally around the local Y axis.
Afterwards check if the platform's rotation has changed, if yes:
3.a Get how much the platform rotated since the previous frame, with the operation Actual rotation * Inverse(Previous Rotation); this operation it's akin to a difference between two quaternions
3.b Add that quaternion to the cube's rotation with the *= operator
3.c Set the platform's previous rotation value to the new one.
That's pretty much it.

Follow a GameObject and change camera view at the same time

I am working on a 3D side scroller game in which my camera is following my character using Vector3.lerp.
Currently my camera is looking at the player from a side but at certain points I want the camera to transition to TopView (look at the character from the top) while also keeping a certain height from the character.
I have done this by creating camera settings naming SideView and TopView. Now the problem is that the camera does transitions from SideView to TopView but during transition the camera shakes when it is at the end of the lerp (when the camera is almost at the target position).
How to make this smooth (stop camera shake)?
Note: both camera and character are moving.
Here the source code of my camera follow:
void LateUpdate ()
{
if(currentCameraSettings != null && target.transform != null)
{
targetPos = SetCameraTargetPos(target.transform);
if(duration != 0)
{
transform.position = Vector3.Lerp(transform.position,targetPos,(Time.time - startTime ) / duration );
}
else
{
transform.position = targetPos;
}
SetCameraRotation();
}
}
SetCameraTargetPos returns the target position after adding height and z-axis distance from the target character.
Sounds like you have the wrong situation for a Lerp. I think what you want to use here is MoveTowards.
float step = speed * Time.deltaTime;
transform.position = Vector3.MoveTowards(transform.position, target.position, step);
if its still "jaggedy" for you, try using Time.smoothDeltaTime.
Sorry for bad english.
If you want prevent to camera shaking your use rigidbody properties.
Freeze x,y,z.
or
Create empty object inside the your object with all the same control settings and followed the empty space not the your object.

Calculating/Predicting a way

I'm just starting with physics, so I'm not always sure about what I'm doing. It's a 2D project but I'm using 3D physical objects like SphereCollider etc..
What I have:
Objects floating in space and affecting each other through gravity:
protected virtual IEnumerator OnTriggerStay(Collider other) {
yield return new WaitForFixedUpdate();
if(other.attachedRigidbody) {
Vector3 offsetVector = this.transform.position - other.transform.position;
float distance = offsetVector.magnitude;
float gravityForce = (other.rigidbody.mass * mass) / Mathf.Pow(distance, 2);
// Clamp gravity.
if(gravityForce > 1.0F) {
gravityForce = 1.0F;
}
other.attachedRigidbody.constantForce.force = offsetVector.normalized * gravityForce;
}
}
There are controllable objects on which the player can click and drag a line away from the object in order to give it a force (shoot) in the opposite direction.
What I want to achieve:
The player should see a rough prediction of the way while aiming. That means that the way-prediction needs to take in account the current velocity, the force which would be applied when the player release the mouse button and the gravity of the surrounding objects.
What I have tried so far:
For testing purposes I just save the computed/predicted positions in an array and draw those positions in OnDrawGizmos().
I wrote a method which returns the gravity influence for a certain position called computeGravityForPosition(Vector3 position).
And thats how I try to calculate the positions:
private void drawWayPrediction() {
Vector3 pos = this.transform.position;
// The offsetVector for the shooting action.
Vector3 forceVector = pos - Camera.main.ScreenToWorldPoint(Input.mousePosition);
forceVector.z = 0.0F;
// The predicted momentum scaled up to increase the strength.
Vector3 force = (forceVector.normalized * forceVector.magnitude);
// 1. I guess that this is wrong, but don't know how to do it properly.
momentum = this.rigidbody.velocity + force;
for(int i = 0; i < predictionPoints.Length; i++) {
float t = i * Time.fixedDeltaTime;
momentum += computeGravityForPosition(pos);
pos += momentum * t * t;
predictionPoints[i] = pos;
}
}
At the beginning, when the objects just slowly approaching each other it looks okay. After the first shot, the prediction is completely wrong. I guess it is because of 1. in the code. Just adding the force to the velocity is probably horrible wrong.
Thank you very much for your time.
EDIT:
I removed seemingly unnessecary parts.
I still think that the main problem lays in 1. in the code. I just don't know how to mix up the current movement of the object (from which I only have the current velocity as far as I know the physics engine of unity) with the new created force:
Vector3 forceVector = pos - Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector3 force = (forceVector.normalized * forceVector.magnitude);
So if you are using a new version of unity probably above 2018, you can use the nice method
Physics.Simulate(dt); // delta time, dt, is the amount of time to simulate.
https://docs.unity3d.com/ScriptReference/Physics.Simulate.html
https://docs.unity3d.com/2018.3/Documentation/ScriptReference/PhysicsScene.Simulate.html
By using this function you can manually advance the simulation.
This method should be applied to a different physics scene.
Therefore I suggest that when you click you will simulate a few physics steps (the more you will simulate the more accurate indication the player will get),
with every step you store the position of the object and when you are done simulating draw a line between all the points.
In my opinion, it should run quite fast if done correctly.
The code should look something like this:
public PhysicsScene physicsScene;
GameObject actualBall;
GameObject simulatedBall;
OnClick() {
simulatedBall.SetPosition(actualBall.transform.position);
if (!physicsScene.IsValid())
return; // do nothing if the physics Scene is not valid.
for (int i=0; i < 10; i++) {
physicsScene.Simulate(Time.fixedDeltaTime);
// store the position.
myPoints.append(simulatedBall.rb.position);
}
// draw a line from the stored points.
}
In addition there is this video that I hope will help, good luck
https://www.youtube.com/watch?v=GLu1T5Y2SSc
I hope I answered your question and if not tell me :)
Disclaimer : Unfortunately I suck at math so can't provide any code for the calculations.
Now that the legal stuff is out of the way :)
In my opinion you are looking at this all wrong. What you need is to calculate the curve (path of the objects trajectory) and then simply plot the curve in OnDrawGizmos with a line renderer.
You don't need to simulate the behaviour of the object. Not only is this a LOT faster but it's also simpler in terms of TimeScale shenanigans. By changing the TimeScale you are also affecting the TimeScale of your trajectory simulation which will most likely look and feel weird.
By doing a basic trajectory calculation you will not have this issue.
PS: This link might help.

Unity C# How to turn item in hands when player turns

I am pretty new to unity but i have a basic FPS game made, when holding a gun, i would like to make it so when your player turns, the item in hands rotates to show turning. For example, when playing call of duty, the gun rotates when you rotate your character. This is the code i have but it is not working
void Update(){
this.rotateEquppedOnTurn();
}
private void rotateEquppedOnTurn(){
if(this.equippedItem != null){
InteractEquppableItem equip = this.equippedItem.gameObject.GetComponent<Interaction>() as InteractEquppableItem;
if(equip.rotatesWhenTurn){
float rotX = Input.GetAxis("Mouse X");
float rotY = Input.GetAxis("Mouse Y");
Quaternion tempRot = new Quaternion();
Quaternion tempCam = GameObject.Find("PlayerCamera").transform.rotation;
tempRot.x = tempCam.x + rotX;
tempRot.y = tempCam.y + rotY;
tempRot.z = tempCam.z;
this.equippedItem.gameObject.transform.rotation = tempRot;
}
}
}
when turning the character with this code, the gun just rotates in a weird way, its not quite what i expected from the rotation script
Quaternions are not vectors.
I suggest you start by watching the vector tutorial on Unity's web site.
The last bit of the tutorial goes over what cross products are and why you would use them - specifically, you can use them to obtain a relative axis around which you may want to rotate something.
Don't directly assign rotation like this.
this.equippedItem.gameObject.transform.rotation = tempRot;
instead of that use something like this
this.equippedItem.gameObject.transform.Rotate(new Vector3(x,y,z));
you can derive x,y,z using mouse motion

Categories