Follow a GameObject and change camera view at the same time - c#

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.

Related

Instantiate a prefab pointing in the direction of another object

The Issue
I'm making a 2D Unity game where the main weapon of your character is a fireball gun. The idea is that a fireball will shoot out of the player's hand at the same angle the player's hand is pointing. I have 3 issues:
When I shoot the fireball, since the fireball is a RididBody, it pushes the player. This is because I've made the centre of the player's arm (the same place where the fireball shoots from) the point at which the arm rotates around the player (what is meant to be the shoulder);
To instantiate the fireball prefab on the arm, the only way I know how to do it is by using a piece of code which requires the arm to be a RigidBody. This means that the arm is affected by gravity and falls off the player on start unless I freeze the arm's y-axis movement, which means that when the player jumps, while the arm does not fall, it floats at the same y-position as where it started while moving along the x-axis; and
When the fireball is shot, the angle from which it is propelled after being shot is not the same angle as the angle of the player's arm.
Instantiating the Fireball
if (Input.GetKeyDown(KeyCode.Space))
{
pew.Play();
var fireballTransform = Instantiate(fireballPrefab); //creates a new shot sprite
fireballTransform.position = new Vector3(transform.position.x + horizMultiplier, transform.position.y, transform.position.z);
fireballTransform.rotation = orientation;
fireballTransform.transform.Rotate(0, 0, transform.rotation.z);
}
if (Input.GetKeyDown(KeyCode.D)) // moves right
{
orientation = 0;
horizMultiplier = 0.08F;
}
if (Input.GetKeyDown(KeyCode.A)) // moves left
{
orientation = 180;
horizMultiplier = -0.08F;
}
This piece of code is located within the script applied to the player's arm. The movement of the arm works fine and the problem seems to be either within this piece of code or the code for my fireball (which I will put next). A few definitions:
pew is a sound effect played when the fireball is shot;
horizMultiplier is the distance from the arm's centre which I would like the fireball to instantiate (also dependant of if the player) is facing left or right); and
orientation is which direction the player is facing (left or right). The fireball is then instantiated facing that same direction.
Fireball Script
public Vector2 speed = new Vector2(); // x and y forces respectively
private Rigidbody2D rb; // shorthand
private float rotation;
void Start()
{
rb = GetComponent<Rigidbody2D>(); // shorthand
rotation = rb.rotation;
if (rotation == 0)
{
rb.AddForce(Vector3.right * speed.x); // propels right
}
if (rotation == 180)
{
rb.AddForce(Vector3.left * speed.x); // propels left
}
}
I believe this code is explanatory enough with comments (if not please comment and I'll address any question). I believe an issue could also be in this piece of code because of the lines: rb.AddForce(Vector3.right * speed.x); and rb.AddForce(Vector3.left * speed.x); as these add directional forces to the object. I don't know is this is objective direction (right or left no matter what direction the object the force is being applied to is facing) or if it's right or left in terms of the object-- say if an object was rotated 90 degrees clockwise and that object had a force applied so that it moves right making the object move downwards.
What I'm expecting to happen is the player's arm will turn so that when a fireball is fired it is fired in the direction the arm is facing. The arms turning mechanics are fine, it's just trying to properly instantiate the fireball. Can anyone help with any of the issues I've laid out?
How I would go about this:
Add an empty transform as child of the arm and move it to where the fireball should spawn. Also make sure the rotation of it is such that its forward vector (the local z-axis) points to where the fireball should go. To see this, you need to set "Local" left of the play button.
Spawn the fireball at the transform's position with its rotation.
Ignore collision between the arm's/player's collider and the fireball's collider with https://docs.unity3d.com/ScriptReference/Physics.IgnoreCollision.html. If necessary, you can enable the collision between the two colliders again after 1s or so.
Set the fireball rigidbody's velocity to speed * rigidbody.forward.
If you need help with the code, please post it so I can see what's going on.
Edit:
The arm definitely doesn't require a Rigidbody.
You can just use Instantiate(prefab, position, rotation) as shorthand.
Is this for a 2D game?
Also, I'm going to sleep now but I'll gladly try to help tomorrow.
Your Issues
1) You should be masking the fireball's layer not to collide with your player's layer.
You can find more info about this here: https://docs.unity3d.com/Manual/LayerBasedCollision.html
(note: make sure you're on the Physics2D panel, and not the Physics one, as that's for 3D)
2) There is a setting called gravityScale in RigidBody2D. You should set this to 0 if you don't want gravity to be affecting your object. More info: https://docs.unity3d.com/Manual/class-Rigidbody2D.html
Fireball Instantiate
if (Input.GetKeyDown(KeyCode.Space))
{
pew.Play();
var position = hand.transform.position + hand.transform.forward * horizMultiplier;
var rotation = hand.transform.rotation;
var fireball = Instantiate<Fireball>(fireballPrefab, position, rotation);
fireball.StartMoving();
}
Fireball Script
private Rigidbody2D rb; // shorthand
private float rotation;
public float Speed;
public void StartMoving()
{
rb = GetComponent<Rigidbody2D>(); // shorthand
rb.velocity = transform.forward * Speed;
}
void OnTriggerEnter(....) { .... }

How to make camera relative movement

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.

Unity 2D C# - Collider not working

I am working on a 2D TopDown game in Unity 5. The collision is not working at all.
The player and the obstacle both have a 2D Collider and they are not Trigger. The player has a 2D Rigidbody with Kinematic set to false, gravity scale is equal to zero. Maybe the movement code has something to do with it.
The code is a bit long so I'll just show you the code for moving up:
if (Input.GetAxis ("Up") > 0) {
if (movingDown == false) {
posY += speed * Time.deltaTime;
movingUp = true;
}
} else {
movingUp = false;
}
/.../
transform.position = new Vector3 (posX, posY, 0);
It is always setting the value of the position as long as you are pressing the up button. Is there a way to fix this?
I think that the problem is that you are setting the position directly. So at each frame, you are telling unity exactly were the object should be, which overrides the position that would be computed from collision.
To fix this, you need to modify your movement code to add a force to your rigidbody and leave the position untouched ( see rigidbody doc, and function AddForce (https://docs.unity3d.com/ScriptReference/Rigidbody.html)
Try using
rb.velocity = new Vector3 (rb.velocity.x, rb.velocity.y, ConstantZValue);
This replaces your system with a velocity-based system as updating the transform.postition of a rigidbody is not recommended. With the system you have, the collision is not being detected because the rigidbody isn't being updated.

How to reverse gravity immediately?

I'm making a 2D game. I have a Cube Object falling down from the top of the screen, and I have pipes which the cube should pass by reversing gravity.
Well my object is falling down from the top of the screen. I tap on the screen to reverse gravity but it's not going immediately up: it takes time to change the gravity orientation. When i tap the screen my object continues falling and then goes up. My movement is forming the shape of a U when I tap the screen. The same thing happens when it goes up: I tap it to go down and in that case my movement forms the shape of a ∩.
What I want to achieve is that when I tap the screen my object's movement has instant response.
In addition, I want some sort of attenuation, damping, or smoothing.
I've tried these examples without success:
http://docs.unity3d.com/ScriptReference/Vector2.Lerp.html
http://docs.unity3d.com/Documentation/ScriptReference/Vector3.SmoothDamp.html
This is my code:
public class PlayerControls : MonoBehaviour
{
public GameObject playerObject = null;
Rigidbody2D player;
public float moveSpeed;
public float up;
Vector2 targetVelocity;
void Start()
{
player = playerObject.GetComponent<Rigidbody2D>();
// Set the initial target velocity y component to the desired up velocity.
targetVelocity.y = up;
}
void Update()
{
for (int i = 0; i < Input.touchCount; i++)
{
Touch touch = Input.GetTouch(i);
if (touch.phase == TouchPhase.Ended && touch.tapCount == 1)
{
// Flip the target velocity y component.
targetVelocity.y = -targetVelocity.y;
}
}
}
void FixedUpdate()
{
// Ensure if moveSpeed changes, the target velocity does too.
targetVelocity.x = moveSpeed;
// Change the player's velocity to be closer to the target.
player.velocity = Vector2.Lerp(player.velocity, targetVelocity, 0.01f);
}
}
This script is attached to the falling cube.
When affected by a constant force (in your code, gravity), an object will move in a parabola. You're observing a parabola: that U shape. To avoid the parabola and get instant response (a V shape), don't use forces. Replace the gravityScale code with something like:
Vector2 velocity = player.velocity;
velocity.y = -velocity.y;
player.velocity = velocity;
This inverts the velocity's y component, making the player move upwards.
You also mentioned you want to smooth this out. I suggest adding another variable: the target velocity.
To implement this, add Vector2 targetVelocity to your component. Then, during each FixedUpdate you can interpolate from the current velocity towards the target velocity to gradually change speed. Replace this line in your current code:
player.velocity = new Vector2(moveSpeed, player.velocity.y);
With this:
player.velocity = Vector2.Lerp(player.velocity, targetVelocity, 0.01f);
This will slowly change the velocity to be the same as the target velocity, smoothing out the movement. 0.01f is the place between the old velocity and the new velocity that the player's velocity is set to. Choose a larger number to interpolate more quickly, and a smaller number to change direction more slowly.
Then, instead of changing velocity when the screen is tapped, change targetVelocity. Make sure that the x component of targetVelocity is equal to moveSpeed, or the object won't move horizontally at all!
Combining these two changes, the Start and FixedUpdate methods should look similar to this:
void Start()
{
player = playerObject.GetComponent<Rigidbody2D>();
// Set the initial target velocity y component to the desired up velocity.
targetVelocity.y = up;
}
void Update()
{
for (int i = 0; i < Input.touchCount; i++)
{
Touch touch = Input.GetTouch(i);
if (touch.phase == TouchPhase.Ended && touch.tapCount == 1)
{
// Flip the target velocity y component.
targetVelocity.y = -targetVelocity.y;
}
}
}
void FixedUpdate()
{
// Ensure if moveSpeed changes, the target velocity does too.
targetVelocity.x = moveSpeed;
// Change the player's velocity to be closer to the target.
player.velocity = Vector2.Lerp(player.velocity, targetVelocity, 0.01f);
}
Note that you shouldn't use Input in FixedUpdate, because inputs are updated every frame and FixedUpdate may run multiple times per frame. This could cause the input to be read twice, especially if the frame rate gets slower and the fixed update is more likely to need to run multiple times per frame.

How do I make character play another animation when moving left?

I have script which makes character (gameobject) move right.
How do I make so when he reaches right border, and character starts moving left with playing another animation (moving left)?
Here is my script of character's moving right and left
void MovementRight()
{
_character.transform.Translate (Vector2.right 100f Time.deltaTime);
}
void MovementLeft()
{
_character.transform.Translate (Vector3.left 100f Time.deltaTime);
}
I would suggest you handle animations with Animator, and make the horizontal animation to be handled by a float variable in Animator, (so when it is -1 animate walking left, and 1 animate walking right).
So you will need to make 3 scripts.
//This will handle the animator getting the speed of the character from MovingHandler
AnimationHandler.cs
//This will actually move the character based on input handler
MovingHandler.cs
//Here you will hear to inputs
InputHandler.cs
And to make suere the character won't move outside the screen you will need to get the screenbounds on MovingHandler and make a check before moving the character, if it is in the same spot in x as the bounds of the screen you will make a return; also if it is going to the left and the character is <= than left bound then return; and the same on the right bound >= then return;
Something like this:
Transform charTransform;
float leftHorizontalBound;
float rightHorizontalBound;
void Start()
{
charTransform = this.transform;
leftHorizontalBound = camera.ViewportToWorldPoint (new Vector3 (0,0, camera.nearClipPlane)).x;
rightHorizontalBound= camera.ViewportToWorldPoint (new Vector3 (1,0, camera.nearClipPlane)).x;
}
void Update()
{
if(charTransform.position.x <= leftHorizontalBound)
{
charTransform.position = new vector2(leftHorizontalBound + 0.1f);
return;
}
if(charTransform.position.x >= rightHorizontalBound)
{
charTransform.position = new vector2(rightHorizontalBound - 0.1f);
return;
}
//MAKE HERE YOUR MOVEMENT BASED ON INPUT.
}
Dont use that code just use the idea, since i didn't test it, i just code it here.
I hope it helped.

Categories